/* ============================================================================
   SMS component library — buttons, inputs, tables, chips, meters, overlays,
   skeletons, signature-specific components. Consumes assets/tokens.css only.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4 { font-family: var(--font-display); margin: 0; font-weight: 600; }
h1 { font-size: var(--t-h1); letter-spacing: -0.015em; line-height: 1.2; }
h2 { font-size: var(--t-h2); letter-spacing: -0.01em; line-height: 1.25; }
h3 { font-size: var(--t-h3); letter-spacing: -0.005em; line-height: 1.3; }
p  { margin: 0; }
a  { color: inherit; }
button, input, select, textarea { font: inherit; color: inherit; }
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--r-xs); }
::selection { background: var(--accent-soft); color: var(--fg); }

/* Icons ship with width/height presentation attributes (see ui.js icon()), so
   they can never fall back to the 300×150 replaced-element default. These two
   properties are all they need on top; every existing `.x svg { width: … }`
   rule still overrides the intrinsic size. */
svg[data-ico] { flex: none; vertical-align: middle; }

/* numerals: every figure in this product is tabular */
.num, td.num, .mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-feature-settings: 'zero' 1; letter-spacing: -0.01em; }

.eyebrow {
  font-size: var(--t-xs); font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--faint);
}
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.sm { font-size: var(--t-sm); }
.xs { font-size: var(--t-xs); letter-spacing: 0.015em; }
.row { display: flex; align-items: center; gap: var(--s2); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); }
.col { display: flex; flex-direction: column; }
.wrap { flex-wrap: wrap; }
.grow { flex: 1; }
.gap-1 { gap: var(--s1) } .gap-2 { gap: var(--s2) } .gap-3 { gap: var(--s3) }
.gap-4 { gap: var(--s4) } .gap-6 { gap: var(--s6) }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; inset-inline-start: var(--s4); top: -100px; z-index: var(--z-skip);
  background: var(--accent); color: var(--accent-fg); padding: var(--s2) var(--s4);
  border-radius: var(--r); font-weight: 600;
}
.skip-link:focus { top: var(--s2); }

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 34px; padding: 0 var(--s3);
  border: 1px solid var(--border-strong); border-radius: var(--r);
  background: var(--surface); color: var(--fg);
  font-size: var(--t-sm); font-weight: 500; letter-spacing: 0.01em;
  cursor: pointer; white-space: nowrap;
  transition: background var(--motion), border-color var(--motion), transform 90ms ease, box-shadow var(--motion);
}
.btn:hover { background: var(--surface-3); border-color: var(--border-strong); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
.btn svg { width: 15px; height: 15px; flex: none; }

.btn-primary {
  background: var(--accent); border-color: transparent; color: var(--accent-fg);
  font-weight: 600;
  box-shadow: 0 1px 0 oklch(0 0 0 / 0.10) inset, 0 1px 2px oklch(0.40 0.12 45 / 0.30), 0 2px 8px oklch(0.40 0.12 45 / 0.18);
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-ghost { background: transparent; border-color: transparent; }
.btn-ghost:hover { background: var(--surface-3); }
.btn-danger { background: var(--danger); border-color: transparent; color: #fff; font-weight: 600; }
.btn-danger:hover { filter: brightness(1.08); }
.btn-success { background: var(--success); border-color: transparent; color: #fff; font-weight: 600; }
.btn-success:hover { filter: brightness(1.08); }
.btn-sm { min-height: 28px; padding: 0 var(--s2); font-size: var(--t-xs); }
.btn-lg { min-height: 44px; padding: 0 var(--s6); font-size: var(--t-body); }
.btn-icon { width: 34px; min-height: 34px; padding: 0; }
.btn-group { display: inline-flex; }
.btn-group .btn { border-radius: 0; margin-inline-start: -1px; }
.btn-group .btn:first-child { border-start-start-radius: var(--r); border-end-start-radius: var(--r); margin-inline-start: 0; }
.btn-group .btn:last-child  { border-start-end-radius: var(--r); border-end-end-radius: var(--r); }
.btn-group .btn[aria-pressed="true"] { background: var(--surface-3); color: var(--fg); font-weight: 600; z-index: 1; }

/* ----------------------------------------------------------------- inputs */
.field { display: flex; flex-direction: column; gap: 5px; }
.label { font-size: var(--t-xs); font-weight: 600; letter-spacing: 0.02em; color: var(--muted); text-transform: uppercase; }
.input, .select, .textarea {
  width: 100%; min-height: 34px; padding: 6px var(--s3);
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--border-strong); border-radius: var(--r);
  font-size: var(--t-sm);
  transition: border-color var(--motion), box-shadow var(--motion);
}
.input::placeholder, .textarea::placeholder { color: var(--faint); }
.input:hover, .select:hover { border-color: var(--muted); }
.input:focus, .select:focus, .textarea:focus { border-color: var(--accent); box-shadow: var(--ring); outline: none; }
.textarea { min-height: 76px; resize: vertical; line-height: 1.5; }
.select { appearance: none; padding-inline-end: var(--s6); cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 14px center, right 9px center; background-size: 5px 5px; background-repeat: no-repeat; }
[dir="rtl"] .select { background-position: left 14px center, left 9px center; }
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--danger); }
/* Label row: the label, and an optional affordance pushed to the far end.
   Logical properties only, so it mirrors under RTL without an override. */
.field-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); }
.field-label-row > .label { margin-inline-end: auto; }
/* Inline text link. Underlined by default — colour alone is not a sufficient
   affordance, and at --t-xs the accent hue against --muted body copy falls
   below 3:1 for non-text contrast. */
.text-link {
  color: var(--accent-ink, var(--fg));
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  display: inline-flex; align-items: center; gap: 4px;
  border-radius: var(--r-xs);
}
.text-link:hover { text-decoration-thickness: 2px; }
.text-link:focus-visible { box-shadow: var(--ring); outline: none; }
.text-link svg { opacity: 0.6; }
.field-error { font-size: var(--t-xs); color: var(--danger); display: flex; align-items: center; gap: 5px; }
.field-hint { font-size: var(--t-xs); color: var(--faint); }

.checkbox, .radio {
  appearance: none; width: 16px; height: 16px; flex: none; cursor: pointer;
  border: 1.5px solid var(--border-strong); border-radius: var(--r-xs);
  background: var(--surface); display: grid; place-content: center;
  transition: background var(--motion), border-color var(--motion);
}
.radio { border-radius: 50%; }
.checkbox:checked, .radio:checked { background: var(--accent); border-color: var(--accent); }
.checkbox:checked::after {
  content: ''; width: 9px; height: 5px; border: 2px solid var(--accent-fg);
  border-top: 0; border-inline-end: 0; transform: rotate(-45deg) translate(1px, -1px);
}
.radio:checked::after { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--accent-fg); }
.checkbox:indeterminate { background: var(--accent); border-color: var(--accent); }
.checkbox:indeterminate::after { content: ''; width: 9px; height: 2px; background: var(--accent-fg); border: 0; transform: none; }

.switch {
  appearance: none; width: 34px; height: 20px; border-radius: var(--r-full);
  background: var(--border-strong); cursor: pointer; position: relative;
  transition: background var(--motion); flex: none;
}
.switch::after {
  content: ''; position: absolute; top: 2px; inset-inline-start: 2px;
  width: 16px; height: 16px; border-radius: 50%; background: #fff;
  box-shadow: var(--shadow-sm); transition: transform var(--motion);
}
.switch:checked { background: var(--accent); }
.switch:checked::after { transform: translateX(14px); }
[dir="rtl"] .switch:checked::after { transform: translateX(-14px); }

.search {
  display: flex; align-items: center; gap: var(--s2);
  min-height: 34px; padding: 0 var(--s3);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r); transition: border-color var(--motion), box-shadow var(--motion);
}
.search:focus-within { border-color: var(--accent); box-shadow: var(--ring); background: var(--surface); }
.search input { flex: 1; border: 0; background: none; outline: none; font-size: var(--t-sm); min-width: 0; }
.kbd {
  font-family: var(--font-mono); font-size: 10.5px; padding: 2px 5px;
  border: 1px solid var(--border-strong); border-block-end-width: 2px;
  border-radius: var(--r-xs); color: var(--faint); background: var(--surface);
  white-space: nowrap;
}

/* ------------------------------------------------------------------ cards */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
}
.card-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  padding: var(--s3) var(--s4); border-block-end: 1px solid var(--border);
}
.card-body { padding: var(--s4); }
.card-body.flush { padding: 0; }
/* A table brings its own frame: .toolbar, .table-wrap and .pagination each draw
   a 1px border and round the outer corners. Sat flush against a card that draws
   the very same border, every hairline doubled and the table was jammed into
   the corner with nothing around it — which is what "no padding" looked like.
   Give that block its inset back, so the two frames read as one panel inside a
   card rather than two outlines fighting over the same pixel. Cards whose
   content is genuinely edge-to-edge — a matrix, an alert feed — have no
   .table-wrap and stay flush. */
.card-body.flush:has(.table-wrap) { padding: var(--s4); }
/* Screens stack two filter strips (a scope picker above a search bar). They are
   one control, so only the first rounds its top and only the last closes the
   box — otherwise the second one's rounded corners cut a notch into the first.
   With the card inset restored, that strip and the table below it are two
   deliberate panels, so give them real rhythm instead of butting them together. */
.toolbar + .toolbar { border-block-start: 0; border-start-start-radius: 0; border-start-end-radius: 0; }
.card-body.flush:has(.table-wrap) > .col { gap: var(--s3); }
.card-body.flush:has(.table-wrap) .toolbar:not(:has(+ .toolbar)) {
  border-block-end: 1px solid var(--border);
  border-end-start-radius: var(--r-lg); border-end-end-radius: var(--r-lg);
}
.card-foot { padding: var(--s3) var(--s4); border-block-start: 1px solid var(--border); background: var(--surface-2);
  border-end-start-radius: var(--r-lg); border-end-end-radius: var(--r-lg); }
.grid { display: grid; gap: var(--s4); }
.g2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.g3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.g4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1180px) { .g4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .g3, .g2 { grid-template-columns: minmax(0, 1fr); } }
/* Below this a two-up KPI row is two ~150px columns of clipped figures, so it
   goes single-column rather than staying technically-responsive and unreadable. */
@media (max-width: 560px)  { .g4 { grid-template-columns: minmax(0, 1fr); } }

/* KPI tile — no left-border accent stripe; hierarchy comes from the figure */
/* A KPI ribbon is read across, not down: the four figures have to sit on one
   line and the four footnotes on another, whatever length each label runs to.
   The tile fills its grid track and the footer takes the slack, so a two-line
   hint on one tile cannot shunt its neighbour's figure out of alignment. */
.kpi { padding: var(--s4); display: flex; flex-direction: column; gap: var(--s2); height: 100%; }
.kpi-foot { margin-block-start: auto; padding-block-start: var(--s2); display: flex; flex-direction: column; gap: 4px; }
.kpi-val { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 28px; font-weight: 600; letter-spacing: -0.02em; line-height: 1; }
.kpi-label { font-size: var(--t-sm); color: var(--muted); }
.kpi-delta { font-size: var(--t-xs); display: inline-flex; align-items: center; gap: 4px; }
.kpi-delta.up { color: var(--success); } .kpi-delta.down { color: var(--danger); }

/* ------------------------------------------------- status system (shared) */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px var(--s2); border-radius: var(--r-full);
  font-size: var(--t-xs); font-weight: 600; letter-spacing: 0.03em;
  white-space: nowrap; border: 1px solid transparent;
}
.badge::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: none; }
.badge-neutral  { background: var(--surface-3); color: var(--muted); border-color: var(--border); }
.badge-info     { background: var(--info-soft);    color: var(--info); }
.badge-success  { background: var(--success-soft); color: var(--success); }
.badge-warning  { background: var(--warning-soft); color: var(--warning); }
.badge-error    { background: var(--danger-soft);  color: var(--danger); }
.badge-critical { background: var(--danger); color: #fff; border-color: transparent; }
.badge-critical::before { background: #fff; animation: pulse 1.6s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.badge-plain::before { display: none; }

.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px var(--s2); border-radius: var(--r-sm);
  background: var(--surface-3); border: 1px solid var(--border);
  font-size: var(--t-xs); color: var(--muted); white-space: nowrap;
}
.chip-accent { background: var(--accent-soft); border-color: transparent; color: var(--accent-hover); font-weight: 600; }
[data-theme="dark"] .chip-accent { color: var(--accent); }
.chip button { border: 0; background: none; cursor: pointer; color: inherit; padding: 0; line-height: 1; opacity: 0.6; }
.chip button:hover { opacity: 1; }

/* confidence / match-score meter with a threshold marker */
.meter { display: flex; flex-direction: column; gap: 5px; min-width: 130px; }
.meter-track {
  position: relative; height: 7px; border-radius: var(--r-full);
  background: var(--surface-3); overflow: visible;
}
.meter-fill { height: 100%; border-radius: var(--r-full); transition: width 420ms cubic-bezier(0.32,0.72,0,1); }
.meter-fill.ok   { background: var(--success); }
.meter-fill.warn { background: var(--warning); }
.meter-fill.bad  { background: var(--danger); }
.meter-threshold {
  position: absolute; top: -3px; width: 2px; height: 13px;
  background: var(--fg); border-radius: 1px; opacity: 0.75;
}
.meter-threshold::after {
  content: attr(data-label); position: absolute; top: 15px; inset-inline-start: 50%;
  transform: translateX(-50%); font-size: 10px; font-family: var(--font-mono);
  color: var(--faint); white-space: nowrap;
}
[dir="rtl"] .meter-threshold::after { transform: translateX(50%); }
.meter-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2); }
.meter-val { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 600; font-size: var(--t-h3); letter-spacing: -0.01em; }

/* ----------------------------------------------------------------- tables */
/* The grid scrolls in its own pane, the way every ERP grid does — and that is
   what makes the sticky column headers below actually work.

   This said `max-height: 100%`, which against an auto-height parent resolves to
   `none`: the wrap grew to fit every row and never scrolled. `overflow: auto`
   still made it the scroll container, so `position: sticky` on the header cells
   was measured against a container that never moved — the pinned headers were
   dead CSS, and scrolling a long queue carried the column names off the top of
   the screen with everything else.

   A cap only ever bites on a table taller than it, so short tables are
   untouched; `table(cols, rows, { maxHeight })` still overrides it inline. */
.table-wrap {
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); overflow: auto;
  max-height: min(72vh, 900px);
}
/* A dialog or drawer already scrolls its own body — a second scrollbar nested
   inside one is worse than a long table. */
.modal-body .table-wrap, .drawer .table-wrap { max-height: none; }
table.tbl { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--t-sm); }
.tbl thead th {
  position: sticky; top: 0; z-index: var(--z-sticky);
  background: var(--surface-3); color: var(--muted);
  font-size: var(--t-xs); font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
  text-align: start; padding: var(--s2) var(--s3); white-space: nowrap;
  border-block-end: 1px solid var(--border);
}
.tbl thead th.sortable { cursor: pointer; user-select: none; }
.tbl thead th.sortable:hover { color: var(--fg); }
.tbl thead th .sort-caret { opacity: 0.4; font-size: 9px; margin-inline-start: 4px; }
.tbl thead th[aria-sort] .sort-caret { opacity: 1; color: var(--accent); }
.tbl tbody td { padding: var(--cell-y) var(--s3); border-block-end: 1px solid var(--border); vertical-align: middle; }
.tbl tbody tr:last-child td { border-block-end: 0; }
.tbl tbody tr { transition: background var(--motion); }
.tbl tbody tr:hover { background: var(--surface-2); }
.tbl tbody tr[aria-selected="true"] { background: var(--accent-soft); }
.tbl tbody tr.clickable { cursor: pointer; }
/* Keyboard cursor (J/K). Deliberately not the accent: selection means "this is
   in my batch", the cursor only means "this is where I am". A surface tint and
   an inset rule on the leading edge, so it reads under a selected row too. */
.tbl tbody tr.is-cursor { background: var(--surface-2); box-shadow: inset 3px 0 0 0 var(--border-strong); }
.tbl tbody tr.is-cursor .pin { background: var(--surface-2); }
[dir="rtl"] .tbl tbody tr.is-cursor { box-shadow: inset -3px 0 0 0 var(--border-strong); }
.tbl .pin { position: sticky; inset-inline-start: 0; background: var(--surface); z-index: 1; }
.tbl thead .pin { background: var(--surface-3); z-index: calc(var(--z-sticky) + 1); }
.tbl tbody tr:hover .pin { background: var(--surface-2); }
.tbl td.tight, .tbl th.tight { width: 1%; white-space: nowrap; }
.tbl caption { caption-side: top; text-align: start; padding: var(--s3); font-size: var(--t-xs); color: var(--faint); }

.toolbar {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  padding: var(--s2) var(--s3); background: var(--surface-2);
  border: 1px solid var(--border); border-block-end: 0;
  border-start-start-radius: var(--r-lg); border-start-end-radius: var(--r-lg);
}
.toolbar + .table-wrap { border-start-start-radius: 0; border-start-end-radius: 0; }
.bulkbar {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3); background: var(--accent-soft);
  border: 1px solid var(--accent); border-block-end: 0; color: var(--fg);
  font-size: var(--t-sm);
}
.pagination { display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  padding: var(--s2) var(--s3); border: 1px solid var(--border); border-block-start: 0;
  border-end-start-radius: var(--r-lg); border-end-end-radius: var(--r-lg); background: var(--surface-2); font-size: var(--t-xs); color: var(--muted); }

/* ---------------------------------------------------------------- banners */
.banner {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3) var(--s4); border-radius: var(--r);
  border: 1px solid var(--border); background: var(--surface-2);
  font-size: var(--t-sm);
}
.banner svg { width: 17px; height: 17px; flex: none; margin-top: 1px; }
.banner-info     { background: var(--info-soft);    border-color: color-mix(in oklch, var(--info) 34%, transparent); }
.banner-success  { background: var(--success-soft); border-color: color-mix(in oklch, var(--success) 34%, transparent); }
.banner-warning  { background: var(--warning-soft); border-color: color-mix(in oklch, var(--warning) 40%, transparent); }
.banner-error    { background: var(--danger-soft);  border-color: color-mix(in oklch, var(--danger) 40%, transparent); }
.banner-critical { background: var(--danger); color: #fff; border-color: transparent; }
.banner-critical .btn { background: oklch(1 0 0 / 0.15); border-color: oklch(1 0 0 / 0.35); color: #fff; }
.banner-title { font-weight: 600; }

/* --------------------------------------------------------- empty / states */
.empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s3); padding: var(--s8) var(--s6); text-align: center; min-height: 280px;
}
.empty-glyph {
  width: 52px; height: 52px; border-radius: var(--r-lg);
  display: grid; place-content: center; color: var(--faint);
  background: var(--surface-3); border: 1px solid var(--border);
}
.empty-glyph svg { width: 24px; height: 24px; }
.empty h3 { font-size: var(--t-h3); }
.empty p { max-width: 44ch; color: var(--muted); font-size: var(--t-sm); }

/* skeletons — shape-match the real layout, never a bare spinner */
.sk { background: var(--surface-3); border-radius: var(--r-sm); position: relative; overflow: hidden; }
.sk::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, oklch(1 0 0 / 0.42), transparent);
  animation: shimmer 1.5s infinite;
}
[data-theme="dark"] .sk::after { background: linear-gradient(90deg, transparent, oklch(1 0 0 / 0.07), transparent); }
@keyframes shimmer { 100% { transform: translateX(200%); } }
.sk-line { height: 11px; }
.sk-title { height: 17px; width: 42%; }

.spinner {
  width: 15px; height: 15px; border-radius: 50%; flex: none;
  border: 2px solid color-mix(in oklch, currentColor 25%, transparent);
  border-top-color: currentColor; animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.progress { height: 5px; border-radius: var(--r-full); background: var(--surface-3); overflow: hidden; }
.progress > i { display: block; height: 100%; background: var(--accent); border-radius: inherit; transition: width 300ms ease; }

/* -------------------------------------------------------------- overlays */
.scrim {
  position: fixed; inset: 0; z-index: var(--z-scrim);
  background: oklch(0.20 0.01 260 / 0.48); backdrop-filter: blur(2px);
  display: grid; place-items: center; padding: var(--s6);
  animation: fade var(--motion);
}
@keyframes fade { from { opacity: 0; } }
.modal {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  width: min(560px, 100%); max-height: 86vh; display: flex; flex-direction: column;
  animation: rise 200ms cubic-bezier(0.32,0.72,0,1);
}
.modal-lg { width: min(920px, 100%); }
@keyframes rise { from { opacity: 0; transform: translateY(10px) scale(0.985); } }
.modal-head { padding: var(--s4); border-block-end: 1px solid var(--border); display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4); }
.modal-body { padding: var(--s4); overflow: auto; }
.modal-foot { padding: var(--s3) var(--s4); border-block-start: 1px solid var(--border); display: flex; justify-content: flex-end; gap: var(--s2); background: var(--surface-2); }

.drawer-scrim { position: fixed; inset: 0; z-index: var(--z-scrim); background: oklch(0.20 0.01 260 / 0.40); animation: fade var(--motion); }
.drawer {
  position: fixed; inset-block: 0; inset-inline-end: 0; z-index: var(--z-modal);
  width: min(560px, 100%); background: var(--surface);
  border-inline-start: 1px solid var(--border); box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; animation: slide-in 220ms cubic-bezier(0.32,0.72,0,1);
}
@keyframes slide-in { from { transform: translateX(100%); } }
[dir="rtl"] .drawer { animation-name: slide-in-rtl; }
@keyframes slide-in-rtl { from { transform: translateX(-100%); } }

.toasts {
  position: fixed; inset-block-end: var(--s6); inset-inline-end: var(--s6);
  z-index: var(--z-toast); display: flex; flex-direction: column; gap: var(--s2);
  width: min(380px, calc(100vw - 48px));
}
.toast {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3); border-radius: var(--r);
  background: var(--surface); border: 1px solid var(--border);
  box-shadow: var(--shadow-lg); font-size: var(--t-sm);
  animation: rise 200ms cubic-bezier(0.32,0.72,0,1);
}
.toast svg { width: 17px; height: 17px; flex: none; margin-top: 1px; }
.toast.success svg { color: var(--success); }
.toast.error svg   { color: var(--danger); }
.toast.info svg    { color: var(--info); }

.tip { position: relative; }
/* The label itself is a single element at the end of <body>, driven by ui.js —
   NOT a ::after on the trigger. A pseudo-element cannot escape an ancestor with
   overflow:auto, so every tooltip on a control inside a .table-wrap was clipped
   away and never seen, and one anchored to its trigger cannot flip when it runs
   out of room against the edge of the window. This can do both, and it sits at
   the top of the layer scale so nothing it explains can cover it. */
.tip-layer {
  position: fixed; inset-block-start: 0; inset-inline-start: 0;
  z-index: var(--z-tooltip); max-width: 264px;
  padding: 5px var(--s2); border-radius: var(--r-sm);
  background: var(--fg); color: var(--bg);
  font-size: var(--t-xs); line-height: 1.45; letter-spacing: 0.01em;
  box-shadow: var(--shadow-md);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(3px);
  transition: opacity var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out), visibility var(--t-fast);
}
.tip-layer[data-side="below"] { transform: translateY(-3px); }
.tip-layer[data-show="true"] { opacity: 1; visibility: visible; transform: translateY(0); }

/* ------------------------------------------------------------ tabs / nav */
.tabs { display: flex; gap: var(--s1); border-block-end: 1px solid var(--border); }
.tab {
  padding: var(--s2) var(--s3); border: 0; background: none; cursor: pointer;
  font-size: var(--t-sm); font-weight: 500; color: var(--muted);
  border-block-end: 2px solid transparent; margin-block-end: -1px;
  transition: color var(--motion), border-color var(--motion);
}
.tab:hover { color: var(--fg); }
/* The indicator itself is drawn by motion.css as .tab::after so it can animate
   without the 2px border nudging the label. This keeps the reserved space. */
.tab[aria-selected="true"] { color: var(--fg); font-weight: 600; }
.crumbs { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-xs); color: var(--faint); }
.crumbs a { text-decoration: none; }
.crumbs a:hover { color: var(--fg); text-decoration: underline; }

.stepper { display: flex; align-items: center; gap: var(--s2); }
.step { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-sm); color: var(--faint); }
.step-dot {
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-content: center;
  border: 1.5px solid var(--border-strong); font-size: var(--t-xs);
  font-family: var(--font-mono); font-weight: 600; flex: none;
}
.step[data-state="done"] { color: var(--muted); }
.step[data-state="done"] .step-dot { background: var(--success); border-color: transparent; color: #fff; }
.step[data-state="active"] { color: var(--fg); font-weight: 600; }
.step[data-state="active"] .step-dot { border-color: var(--accent); color: var(--accent); }
.step-line { width: 26px; height: 1px; background: var(--border-strong); }

/* ------------------------------------------------------------- timelines */
.timeline { display: flex; flex-direction: column; gap: 0; }
.tl-item { display: grid; grid-template-columns: 22px 1fr; gap: var(--s3); padding-block-end: var(--s4); position: relative; }
.tl-item::before {
  content: ''; position: absolute; inset-inline-start: 10px; top: 20px; bottom: 0;
  width: 1px; background: var(--border);
}
.tl-item:last-child { padding-block-end: 0; }
.tl-item:last-child::before { display: none; }
.tl-dot {
  width: 21px; height: 21px; border-radius: 50%; display: grid; place-content: center;
  background: var(--surface-3); border: 1px solid var(--border); color: var(--muted); z-index: 1;
}
.tl-dot svg { width: 11px; height: 11px; }
.tl-dot.success { background: var(--success-soft); color: var(--success); border-color: transparent; }
.tl-dot.error   { background: var(--danger-soft);  color: var(--danger);  border-color: transparent; }
.tl-dot.warning { background: var(--warning-soft); color: var(--warning); border-color: transparent; }
.tl-dot.accent  { background: var(--accent-soft);  color: var(--accent-hover); border-color: transparent; }

/* --------------------------------------- signature-specific components -- */
.spec {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  display: grid; place-items: center; position: relative; overflow: hidden;
  min-height: 108px;
  background-image: linear-gradient(oklch(0.55 0.02 258 / 0.05) 1px, transparent 1px),
                    linear-gradient(90deg, oklch(0.55 0.02 258 / 0.05) 1px, transparent 1px);
  background-size: 14px 14px;
}
.spec svg { width: 100%; height: 100%; }
.spec-ink { fill: none; stroke: var(--fg); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.spec-ink.ref  { stroke: var(--info); }
.spec-ink.diff { stroke: var(--danger); stroke-dasharray: 4 3; }
.spec-tag {
  position: absolute; inset-block-start: var(--s2); inset-inline-start: var(--s2);
  padding: 2px var(--s2); border-radius: var(--r-sm);
  background: var(--surface); border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
}
/* ------------------------------------------------------- scan crop frame --
   F001's crop module. The frame is dragged and resized, so it is a real
   control, not the dashed rectangle it used to be. Physical left/right on the
   handles on purpose: a scanned sheet does not mirror in RTL, so a corner
   grip must stay on the corner it names. */
.crop-frame { position: relative; overflow: hidden; border-radius: var(--r); touch-action: none; }
.crop-box {
  position: absolute; box-sizing: border-box;
  border: 1.5px dashed var(--accent); border-radius: var(--r-sm);
  cursor: move; touch-action: none;
  /* The spotlight: everything outside the selection dims. Clipped by
     .crop-frame — a 9999px spread escapes anything that does not clip. */
  box-shadow: 0 0 0 9999px color-mix(in oklch, var(--surface-3) 90%, transparent);
  transition: border-color var(--t-fast) var(--ease-out);
}
.crop-box:hover { border-color: var(--accent-hover, var(--accent)); }
.crop-box:focus-visible { outline: none; border-style: solid; }
.crop-handle {
  position: absolute; width: 13px; height: 13px;
  background: var(--surface); border: 1.5px solid var(--accent);
  border-radius: 2px; box-shadow: var(--shadow-sm);
}
.crop-handle.nw { top: -7px; left: -7px;  cursor: nwse-resize; }
.crop-handle.ne { top: -7px; right: -7px; cursor: nesw-resize; }
.crop-handle.sw { bottom: -7px; left: -7px;  cursor: nesw-resize; }
.crop-handle.se { bottom: -7px; right: -7px; cursor: nwse-resize; }
/* The sheet rotates under a fixed frame, the way it does on a scanner bed. */
.crop-sheet { transition: transform var(--t-base) var(--ease-out); transform-origin: 50% 50%; }

.spec-tools {
  position: absolute; inset-block-end: var(--s2); inset-inline-end: var(--s2);
  display: flex; gap: 3px; padding: 3px; border-radius: var(--r-sm);
  background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow-sm);
}
.compare { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--s3); align-items: center; }
@media (max-width: 820px) { .compare { grid-template-columns: 1fr; } }
.compare-vs { font-size: var(--t-xs); font-weight: 600; color: var(--faint); letter-spacing: 0.06em; }

.dropzone {
  border: 1.5px dashed var(--border-strong); border-radius: var(--r-lg);
  padding: var(--s8) var(--s6); text-align: center; background: var(--surface-2);
  transition: border-color var(--motion), background var(--motion);
}
.dropzone:hover, .dropzone.over { border-color: var(--accent); background: var(--accent-soft); }

/* document ↔ fields split view */
.split { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: var(--s4); align-items: start; }
@media (max-width: 1060px) { .split { grid-template-columns: minmax(0, 1fr); } }
/* A short column beside a much taller one — a workspace next to a long form.
   Pinning the short side keeps it with you as the tall side scrolls, instead
   of leaving a growing void beneath it. Released the moment the split stacks
   into one column, where sticky would strand the panel over the content. */
.split-sticky { position: sticky; top: var(--s4); align-self: start; }
@media (max-width: 1060px) { .split-sticky { position: static; } }
/* ------------------------------------------------------- document viewer --
   The mandate the bank received is a Word document that somebody printed,
   signed and scanned. It should read as that piece of paper, not as another
   card with text in it — so it gets the whole apparatus: a grey editor canvas,
   a chrome strip carrying the file name and the page count, and a sheet with
   true page margins, square corners and the shadow of paper lying on a desk. */
.doc-viewer {
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface-3); overflow: hidden;
}
.doc-toolbar {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  padding: var(--s2) var(--s3);
  background: var(--surface-2); border-block-end: 1px solid var(--border);
  font-size: var(--t-xs); color: var(--muted);
}
.doc-toolbar .doc-file {
  font-family: var(--font-mono); color: var(--fg); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 34ch;
}
.doc-scroll {
  overflow: auto; max-height: 640px;
  padding: var(--s6) var(--s4);
  /* The faint plane the sheet sits on, so the page edge reads as an edge. */
  background-image: radial-gradient(oklch(0 0 0 / 0.05) 1px, transparent 1px);
  background-size: 22px 22px;
}
.doc-page {
  /* A4 at a readable scale. margin-inline auto centres the sheet in the canvas
     exactly the way a word processor does. */
  width: min(100%, 720px); margin-inline: auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 0;                      /* paper does not have rounded corners */
  padding: 52px 58px;                    /* the one-inch margin, to scale */
  min-height: 420px;
  /* Two shadows: the contact edge, and the soft cast further out. */
  box-shadow: 0 1px 2px oklch(0 0 0 / 0.16), 0 12px 28px oklch(0 0 0 / 0.13);
  /* Set as the document it is. Serif first for the Latin body, the Arabic face
     behind it so bilingual lines still resolve, and a generic at the end. */
  font-family: Cambria, 'Times New Roman', Georgia, 'IBM Plex Sans Arabic', serif;
  font-size: 14px; line-height: 1.85; color: var(--fg);
}
.doc-page + .doc-page { margin-block-start: var(--s6); }
/* Running head and page number, set small and grey the way Word sets them. */
.doc-runhead {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4);
  margin-block-end: 26px; padding-block-end: 6px;
  border-block-end: 1px solid var(--border);
  font-family: var(--font-body); font-size: 10px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint);
}
.doc-foot {
  margin-block-start: 30px; padding-block-start: 10px;
  border-block-start: 1px solid var(--border);
  font-family: var(--font-body); font-size: 10.5px; color: var(--faint);
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4);
}
.doc-hl { background: var(--accent-soft); border-block-end: 1.5px solid var(--accent); border-radius: 2px; padding: 0 2px; cursor: pointer; }
.doc-hl[data-active="true"] { background: var(--accent); color: var(--accent-fg); }

.xfield { display: grid; grid-template-columns: 1fr auto; gap: var(--s2) var(--s3); align-items: center;
  padding: var(--s2) var(--s3); border-radius: var(--r); border: 1px solid var(--border); background: var(--surface); }
.xfield.low { border-color: var(--warning); background: var(--warning-soft); }

/* the ops matrix grid */
.matrix { border-collapse: separate; border-spacing: 0; width: 100%; font-size: var(--t-sm); }
.matrix th, .matrix td { border: 1px solid var(--border); padding: var(--s2) var(--s3); text-align: center; }
.matrix th:first-child, .matrix td:first-child { text-align: start; background: var(--surface-2); font-weight: 500; white-space: nowrap; }
.matrix thead th { background: var(--surface-3); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }

/* ----------------------------------------------------------- small screens --
   Phone-width behaviour. The ops screens are desktop tools and are honest about
   it — tables scroll horizontally inside their own container rather than being
   reflowed into something that hides columns. What must not happen is the
   PAGE scrolling sideways, so everything outside a table stacks. */
@media (max-width: 560px) {
  .card-head { flex-wrap: wrap; }
  .card-body { padding: var(--s3); }
  .modal-body, .modal-head, .modal-foot { padding: var(--s3); }
  .scrim { padding: var(--s3); }
  .kpi { padding: var(--s3); }
  .kpi-val { font-size: 24px; }
  .compare { gap: var(--s2); }
  .compare-vs { display: none; }
  .empty { padding: var(--s6) var(--s3); min-height: 200px; }
  /* Page margins scale down with the sheet — a one-inch margin on a phone
     leaves no measure to read. */
  .doc-page { padding: var(--s6) var(--s4); min-height: 300px; }
  .doc-scroll { padding: var(--s4) var(--s3); max-height: none; }
  .toasts { inset-inline: var(--s3); inset-block-end: var(--s3); width: auto; }
  /* Long unbroken identifiers — IBANs, references — must not push the page wide. */
  .mono, .num { overflow-wrap: anywhere; }
}

/* ------------------------------------------------------------ touch input --
   The brief asks for an information-dense ops tool AND 44px touch targets.
   Those pull against each other, so they are resolved per input device rather
   than by compromising: a mouse keeps the 34px density that makes a 20-row
   queue readable, while a coarse pointer gets the full 44px. Hit area is grown
   with padding and min-height, not font-size, so the layout keeps its
   proportions. Anything genuinely tappable must appear in this block. */
@media (pointer: coarse) {
  .btn            { min-height: 44px; padding-inline: var(--s4); }
  .btn-sm         { min-height: 40px; padding-inline: var(--s3); }
  .btn-lg         { min-height: 48px; }
  .btn-icon       { width: 44px; min-height: 44px; }
  .btn-icon.btn-sm { width: 40px; min-height: 40px; }

  .input, .select, .search { min-height: 44px; }
  .field-label-row .text-link { min-height: 44px; }
  .textarea       { min-height: 88px; }

  /* The box stays visually 16-20px; the tap area around it grows. */
  .checkbox, .radio { width: 20px; height: 20px; outline-offset: 12px; }
  .checkbox::before, .radio::before {
    content: ''; position: absolute; inset: -12px; border-radius: var(--r-sm);
  }
  .checkbox, .radio { position: relative; }
  .switch         { width: 44px; height: 26px; }
  .switch::after  { width: 22px; height: 22px; }
  .switch:checked::after { transform: translateX(18px); }
  [dir="rtl"] .switch:checked::after { transform: translateX(-18px); }

  .tab            { min-height: 44px; padding-inline: var(--s4); }
  .state-btn      { min-height: 40px; padding-inline: var(--s4); }
  .menu-item      { min-height: 44px; }
  .rail-link      { min-height: 44px; }
  .chip button    { min-width: 32px; min-height: 32px; }
  .queue-chip, .undo-chip { min-height: 36px; padding-inline: var(--s3); }

  /* Row density follows the pointer too — a 34px row is not a tap target. */
  .tbl tbody td   { padding-block: 12px; }
  .tbl thead th   { padding-block: 10px; }

  /* Hover-only affordances do not exist on touch, so show them outright. */
  .spec-tools     { opacity: 1; }
  .tip::after     { display: none; }
}

/* -------------------------------------------------------------- printing */
@media print {
  .rail, .topbar, .state-switcher, .toasts { display: none !important; }
  .shell-main { padding: 0 !important; }
  .card { box-shadow: none; break-inside: avoid; }
}
