/* ==========================================================================================
   app.css — the operator surface's one design system.
   ==========================================================================================

   Every console page loads this and nothing else. It exists because there were four pages, each
   with its own copy of the same tokens, buttons and tables, and they had drifted: a button was
   32px on one screen and 28px on the next, three of them had no visible focus ring at all, and
   the same status word was green on one page and grey on another. An operator moving between
   them was re-learning the interface each time.

   Served as a static file rather than inlined. The console's CSP is `style-src 'self' <nonce>`,
   so a file from this origin needs no nonce, and one file the browser caches beats the same
   40KB re-sent inside every no-store page.

   Structure:
     1. Tokens          — colour, type, space. The only place a literal colour appears.
     2. Reset & base
     3. Layout          — the rail, the content column, page headers
     4. Type
     5. Controls        — buttons, fields, segmented controls
     6. Surfaces        — panels, cards, KPIs
     7. Data            — tables, tags, empty states
     8. Overlays        — dialogs, drawers, toasts
     9. Utilities
   ====================================================================================== */

/* ========================================================================== 1. tokens */
:root {
  /* Institutional green on a cool neutral. Chosen for the subject: this is the surface a
     compliance officer at a regulated brokerage reads, not a consumer product. */
  --ground: #F2F5F3;
  --surface: #FFFFFF;
  --raised: #FAFCFB;
  --sunken: #E9EEEB;

  --ink: #0D1512;
  --ink-2: #384540;

  /* Both raised off the values the pages shipped with — --muted was #6A7A73 (3.9:1 on --surface)
     and --faint #93A199 (2.4:1) — because both carry text an operator has to read carefully:
     timestamps, units, costs, helper text. --muted now measures 6.1:1. */
  --muted: #56655E;
  /* Raised again, to 4.6:1 on --surface. The previous value was 3.9:1 and the comment claimed it
     was "only ever used for non-essential ornament" — it was not. It carried the data-freshness
     stamp, the result count, transcript timestamps and model ids in the compliance drawer, the
     audit resource id, and the cost and latency on an assistant's turn, which the console itself
     documents as information an agent needs. A policy the code does not follow is not a policy. */
  --faint: #606F67;

  /* A visible boundary. --line-2 measures 1.6:1 on --surface, and an input has no fill to
     distinguish it from the panel behind it, so that border IS the control — WCAG 2.2 SC 1.4.11
     asks for 3:1. --line and --line-2 stay where they are for dividers, which are exempt.

     #93A19A was the first attempt and measured 2.69:1 on --surface and 2.45:1 on --ground: it
     missed the target its own comment cited. This one clears 3:1 against both. */
  --line-3: #78877F;

  /* Foreground colours for the semantic fills. Three badges hardcoded #fff, which is 2.1:1 on
     dark --critical and 1.9:1 on dark --ok — and one of them is the SLA-breach count, the most
     urgent thing in the rail. */
  --on-critical: #FFFFFF;
  --on-ok: #FFFFFF;
  --knob: #FFFFFF;

  --line: #DCE3DE;
  --line-2: #C3CDC7;

  --accent: #0F5C46;
  --accent-2: #0B4534;
  --accent-ink: #FFFFFF;
  --accent-soft: #E1EDE7;

  /* Semantic, and deliberately separate from the accent: "this is fine" must never be the same
     colour as "this is the primary action". */
  --critical: #97281A;
  --critical-soft: #FAE7E3;
  --warn: #7A5108;
  --warn-soft: #F8F0DC;
  --ok: #1F6244;
  --ok-soft: #E1EFE7;
  --info: #1A5170;
  --info-soft: #E3EEF4;

  --focus: #1373A8;

  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
          sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;

  /* A 1.2 scale from 12px. Small, because these screens are dense by nature and a comfortable
     reading size would halve how much of a queue fits above the fold. */
  --t-xs: 11px;
  --t-sm: 12px;
  --t-md: 13px;
  --t-lg: 15px;
  --t-xl: 19px;
  --t-2xl: 24px;

  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;

  --r-sm: 4px;
  --r-md: 6px;
  --r-lg: 10px;

  --rail: 232px;
  --shadow-1: 0 1px 2px rgba(13, 21, 18, .06);
  --shadow-2: 0 4px 16px rgba(13, 21, 18, .10), 0 1px 3px rgba(13, 21, 18, .06);
  --shadow-3: 0 16px 48px rgba(13, 21, 18, .18), 0 2px 8px rgba(13, 21, 18, .08);

  color-scheme: light dark;
}

/* Dark is the OS default state — nothing is stamped on the root — so it is a media query, and
   `:not([data-theme="light"])` lets an explicit light choice win over a dark OS. Only tokens are
   redefined; no component rule lives in here, which is what keeps the two themes in step. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #090D0B;
    --surface: #111815;
    --raised: #172019;
    --sunken: #0C1210;

    --ink: #EAEFEC;
    --ink-2: #BFCAC4;
    --muted: #94A29B;
    /* Measured against --raised, not only --surface: the previous #74827B cleared 4.48:1 on one
       and 4.15:1 on the other, and half of what it carries sits on a raised panel. */
    --faint: #8B9992;

    --line: #232E29;
    --line-2: #32403A;
    --line-3: #61736B;

    --accent: #4CBE93;
    --accent-2: #6AD2AB;
    --accent-ink: #06120E;
    --accent-soft: #0F2620;

    --critical: #F0A08D;
    --critical-soft: #2E1714;
    --warn: #E0B76D;
    --warn-soft: #271C0E;
    --ok: #6BD0A5;
    --ok-soft: #0F2419;
    --info: #86BEDD;
    --info-soft: #0E1E28;

    /* Dark ink on the light semantic fills, not white. */
    --on-critical: #2E1714;
    --on-ok: #0A1D14;
    /* Dark ink on the light-green dark-theme accent. A pale knob on it measured 1.93:1 — the
       checked state of every switch was a faint smudge in the dark theme. */
    --knob: #06120E;

    --focus: #6FB9E4;

    --shadow-1: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-2: 0 4px 16px rgba(0, 0, 0, .5);
    --shadow-3: 0 16px 48px rgba(0, 0, 0, .6);
  }
}

:root[data-theme="dark"] {
  --ground: #090D0B;
  --surface: #111815;
  --raised: #172019;
  --sunken: #0C1210;
  --ink: #EAEFEC;
  --ink-2: #BFCAC4;
  --muted: #94A29B;
  --faint: #8B9992;
  --line: #232E29;
  --line-2: #32403A;
  --line-3: #61736B;
  --accent: #4CBE93;
  --accent-2: #6AD2AB;
  --accent-ink: #06120E;
  --accent-soft: #0F2620;
  --critical: #F0A08D;
  --critical-soft: #2E1714;
  --warn: #E0B76D;
  --warn-soft: #271C0E;
  --ok: #6BD0A5;
  --ok-soft: #0F2419;
  --info: #86BEDD;
  --info-soft: #0E1E28;
  --on-critical: #2E1714;
  --on-ok: #0A1D14;
  --knob: #E6ECE9;
  --focus: #6FB9E4;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-2: 0 4px 16px rgba(0, 0, 0, .5);
  --shadow-3: 0 16px 48px rgba(0, 0, 0, .6);
}

/* ========================================================================== 2. reset */
*, *::before, *::after { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 var(--t-md)/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { max-width: 100%; }
[hidden] { display: none !important; }

/* One focus treatment for the whole surface. Three of the four pages had none: an operator
   working by keyboard could not see where they were. :focus-visible so it does not appear on a
   mouse click, which is what made the previous authors remove it. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

::selection { background: var(--accent-soft); color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* Visible only to a screen reader. Used for live regions and for labels a sighted operator gets
   from position but a screen-reader user does not. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--s-3); top: -60px;
  z-index: 100;
  padding: var(--s-2) var(--s-4);
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: var(--r-md);
  font-weight: 600;
  text-decoration: none;
  transition: top .12s ease;
}
.skip-link:focus { top: var(--s-3); }

/* ========================================================================== 3. layout */
.app {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  min-height: 100vh;
}

.rail {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  padding: var(--s-4) var(--s-3);
  background: var(--surface);
  border-right: 1px solid var(--line);
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-2);
  font-weight: 650;
  letter-spacing: -.01em;
  font-size: var(--t-lg);
}
.brand-mark {
  display: grid;
  place-items: center;
  width: 24px; height: 24px;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--t-sm);
  font-weight: 700;
  flex: none;
}
.brand small {
  display: block;
  font-size: var(--t-xs);
  font-weight: 400;
  color: var(--muted);
  letter-spacing: 0;
}

.rail nav { display: flex; flex-direction: column; gap: 1px; }

.rail-group {
  margin: var(--s-3) 0 var(--s-1);
  padding: 0 var(--s-2);
  font-size: var(--t-xs);
  font-weight: 650;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--faint);
}
.rail-group:first-child { margin-top: 0; }

.rail a, .rail button.nav-item {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 7px var(--s-2);
  border: 0;
  border-radius: var(--r-md);
  background: none;
  color: var(--ink-2);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  width: 100%;
}
.rail a:hover, .rail button.nav-item:hover { background: var(--sunken); color: var(--ink); }
.rail [aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}
.rail .nav-count {
  margin-left: auto;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--sunken);
  color: var(--muted);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}
.rail [aria-current="page"] .nav-count { background: var(--accent); color: var(--accent-ink); }
.rail .nav-count.attention { background: var(--critical); color: var(--on-critical); }

.rail-foot {
  margin-top: auto;
  padding-top: var(--s-3);
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  font-size: var(--t-sm);
  color: var(--muted);
}

/* A flex column, so a child can be told to fill what is left rather than being given a number.
   Three screens hardcoded calc(100vh - 53px) for the topbar's height; the topbar grows with the
   user's font-size setting, and at 150% scaling the agent console's composer was pushed below the
   fold with no scroll to reach it. An agent could not reply. */
.main { min-width: 0; display: flex; flex-direction: column; min-height: 100vh; }
.main > .fill { flex: 1; min-height: 0; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-5);
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.topbar h1 { margin: 0; font-size: var(--t-lg); font-weight: 650; letter-spacing: -.01em; }
.topbar .spacer { flex: 1; }

/* Which entity this screen is acting on, where the operator's eye already is. It was only in
   small muted text at the bottom-left of the rail, and every mutation on the configuration screen
   — a key replaced, a channel enabled, a document uploaded — acts on it. */
.entity-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: var(--surface);
  font-size: var(--t-sm);
  font-weight: 600;
  white-space: nowrap;
}
.entity-chip .role { font-weight: 400; color: var(--muted); }

.content { padding: var(--s-5); display: flex; flex-direction: column; gap: var(--s-5); }
.content > section { display: flex; flex-direction: column; gap: var(--s-3); }

.page-head { display: flex; flex-direction: column; gap: var(--s-1); }
.page-head h2 { margin: 0; font-size: var(--t-xl); font-weight: 650; letter-spacing: -.015em; }
.page-head p { margin: 0; color: var(--muted); max-width: 78ch; }

.cols { display: grid; gap: var(--s-4); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cols-3 { display: grid; gap: var(--s-4); grid-template-columns: repeat(3, minmax(0, 1fr)); }
.row { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }

/* Classes the Operating-settings and Jurisdictions screens were already using and that were
   never defined here, so five tables rendered unstyled and their action columns unaligned.
   Defined rather than deleted: the markup reads correctly, it was the stylesheet that was
   missing, and a table on a compliance screen should look like the others. */
.table { width: 100%; border-collapse: collapse; font-size: 0.9375rem; }
.table th {
  text-align: left;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  padding: 0 var(--s-2) var(--s-1) 0;
}
.table td { padding: var(--s-2) var(--s-2) var(--s-2) 0; border-bottom: 1px solid var(--line); }
.table tr:last-child td { border-bottom: 0; }

/* The action column of a row: right-aligned, buttons on one line, wrapping on a narrow screen
   rather than pushing the table sideways. */
.cell-actions {
  text-align: right;
  white-space: nowrap;
}
.cell-actions .btn + .btn { margin-left: var(--s-1); }
.right { text-align: right; }
.gap { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }
.flex-1 { flex: 1; }

/* A dialog whose body scrolls while its header and footer stay put.
   Set as classes rather than through element.style: the console runs under a strict
   Content-Security-Policy, and although a residual style-src-attr keeps inline styles working
   today, the day that is closed these dialogs would lose their scroll and their layout with no
   error anywhere. */
.dialog-column { display: flex; flex-direction: column; }
.dialog-scroll { overflow-y: auto; flex: 1; }

/* A chip that does something when clicked. */
.chip-action { cursor: pointer; }

/* A field shown for context that cannot be edited, because it identifies the row. Distinct from
   disabled: it is still submitted, still focusable, and still readable by a screen reader. */
.locked {
  background: var(--sunk, rgba(0, 0, 0, 0.04));
  color: var(--muted);
  cursor: not-allowed;
}

/* One editable platform setting: a label, why it matters, the field, where the value came from,
   and the actions. Stacked rather than a two-column form because the explanation is the point —
   these are settings somebody changes rarely and needs to understand before they do. */
.setting {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--line);
}
.setting:last-child { border-bottom: 0; padding-bottom: 0; }
.setting > p { margin: 0; max-width: 70ch; }
.setting input, .setting select { max-width: 34rem; }
.setting .label { display: flex; align-items: center; gap: var(--s-1); }
.row-end { margin-left: auto; }

/* The rail collapses into a horizontal strip rather than vanishing. It used to be display:none
   below 900px, which took every other screen with it: an operator on a narrow window could reach
   the page they were on and nothing else. */
@media (max-width: 940px) {
  .app { grid-template-columns: 1fr; }
  .rail {
    position: static;
    height: auto;
    flex-direction: row;
    align-items: center;
    gap: var(--s-2);
    overflow-x: auto;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  .rail nav { flex-direction: row; gap: var(--s-1); }

  /* The group headings go. The FOOT does not: it holds the entity switcher, and on a platform
     whose most dangerous action is an irreversible per-entity purge, "which entity am I acting
     on" is the last thing to sacrifice for width. It used to be hidden along with the nav, so a
     narrow window meant an operator could not see the entity, switch it, or sign out. */
  .rail-group { display: none; }
  .rail-foot {
    margin: 0 0 0 auto;
    padding: 0 0 0 var(--s-3);
    border-top: 0;
    border-left: 1px solid var(--line);
    flex-direction: row;
    align-items: center;
    gap: var(--s-2);
  }
  .rail-foot .field { flex-direction: row; align-items: center; }
  .rail-foot .field > span { display: none; }
  .rail-foot > .truncate { display: none; }

  .rail a, .rail button.nav-item { white-space: nowrap; width: auto; }
  .cols, .cols-3 { grid-template-columns: 1fr; }
  .content { padding: var(--s-4); }
}

/* ========================================================================== 4. type */
h1, h2, h3, h4 { text-wrap: balance; }
h3 { margin: 0; font-size: var(--t-md); font-weight: 650; }

p { margin: 0 0 var(--s-2); }
p:last-child { margin-bottom: 0; }

a { color: var(--accent); text-underline-offset: 2px; }

code, .mono, .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
code {
  font-size: .93em;
  padding: 1px 5px;
  border-radius: var(--r-sm);
  background: var(--sunken);
}
pre {
  margin: 0;
  padding: var(--s-3);
  overflow-x: auto;
  background: var(--sunken);
  border-radius: var(--r-md);
  font: var(--t-sm)/1.55 var(--mono);
}

.muted { color: var(--muted); }
.faint { color: var(--faint); }
.small { font-size: var(--t-sm); }
.xs { font-size: var(--t-xs); }
.strong { font-weight: 600; }
/* A caution stated in text rather than as a pill: used where the note is a sentence, not a
   status. Measured against every ground by ContrastTests, same as the other text tokens. */
.warn-text { color: var(--warn); }

.label {
  font-size: var(--t-xs);
  font-weight: 650;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}

.note {
  padding: var(--s-3);
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-2);
  background: var(--raised);
  color: var(--ink-2);
  font-size: var(--t-sm);
}
.note.info { border-left-color: var(--info); background: var(--info-soft); }
.note.ok { border-left-color: var(--ok); background: var(--ok-soft); }
.note.warn { border-left-color: var(--warn); background: var(--warn-soft); }
.note.bad { border-left-color: var(--critical); background: var(--critical-soft); }
.note strong { color: var(--ink); }

/* ========================================================================== 5. controls */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 32px;
  padding: 0 var(--s-3);
  border: 1px solid var(--line-3);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  font: 500 var(--t-md)/1 var(--sans);
  cursor: pointer;
  white-space: nowrap;
  transition: background .1s ease, border-color .1s ease;
}
.btn:hover:not(:disabled) { background: var(--sunken); border-color: var(--line-2); }
.btn:active:not(:disabled) { transform: translateY(.5px); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.btn.primary:hover:not(:disabled) { background: var(--accent-2); border-color: var(--accent-2); }

.btn.danger { color: var(--critical); border-color: color-mix(in srgb, var(--critical) 40%, var(--line)); }
.btn.danger:hover:not(:disabled) { background: var(--critical-soft); }

.btn.ghost { border-color: transparent; background: none; }
.btn.ghost:hover:not(:disabled) { background: var(--sunken); }

/* 26px square at minimum. WCAG 2.2 SC 2.5.8 asks for 24x24, and these are the densest and most
   destructive controls on the surface: remove branch, remove condition, reorder, close. */
.btn.sm { height: 26px; min-width: 26px; padding: 0 var(--s-2); font-size: var(--t-sm); }
.btn.icon { min-width: 28px; height: 28px; padding: 0; }
.btn.lg { height: 38px; padding: 0 var(--s-4); font-size: var(--t-lg); }
.btn.block { width: 100%; }

/* A button that is doing something says so, and cannot be pressed again while it is. */
.btn[data-busy="1"] { pointer-events: none; opacity: .7; }
.btn[data-busy="1"]::before {
  content: "";
  width: 11px; height: 11px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin .6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.field > span, label.label { color: var(--muted); }

input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="search"], input[type="url"], select, textarea {
  width: 100%;
  padding: 7px var(--s-2);
  border: 1px solid var(--line-3);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  font: 400 var(--t-md)/1.4 var(--sans);
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  border-color: var(--focus);
  outline: 2px solid color-mix(in srgb, var(--focus) 35%, transparent);
  outline-offset: 0;
}
input::placeholder, textarea::placeholder { color: var(--faint); }
input:disabled, select:disabled, textarea:disabled { background: var(--sunken); color: var(--muted); }
textarea { resize: vertical; min-height: 76px; font-family: var(--mono); font-size: var(--t-sm); }

/* An invalid field is announced as well as coloured — colour alone is not a message. */
[aria-invalid="true"] { border-color: var(--critical) !important; }
.field-error { color: var(--critical); font-size: var(--t-sm); }

.seg {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--sunken);
  border-radius: var(--r-md);
}
.seg button {
  padding: 4px var(--s-3);
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--muted);
  font: 500 var(--t-sm)/1.4 var(--sans);
  cursor: pointer;
}
.seg button[aria-pressed="true"] {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-1);
}

.switch { display: inline-flex; align-items: center; gap: var(--s-2); cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
/* A border, so the OFF state has a 3:1 boundary regardless of its fill. --line-2 alone measured
   1.63:1 — and these are the toggles that enable individual CRM operations, the most consequential
   switches on the platform. An operator has to be able to see which are off. */
.switch .track {
  width: 34px; height: 20px;
  border-radius: 999px;
  background: var(--line-2);
  border: 1px solid var(--line-3);
  position: relative;
  transition: background .12s ease;
  flex: none;
}
.switch input:checked + .track { border-color: var(--accent); }
.switch .track::after {
  content: "";
  position: absolute;
  top: 1px; left: 1px;
  width: 16px; height: 16px;
  border-radius: 50%;
  /* The last literal colour outside the token block, now a token. */
  background: var(--knob);
  box-shadow: var(--shadow-1);
  transition: transform .12s ease;
}
.switch input:checked + .track { background: var(--accent); }
.switch input:checked + .track::after { transform: translateX(14px); }
.switch input:focus-visible + .track { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ========================================================================== 6. surfaces */
.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.panel-head {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line);
  background: var(--raised);
}
.panel-head h3 { flex: 1; min-width: 0; }
.panel-body { padding: var(--s-4); display: flex; flex-direction: column; gap: var(--s-3); }
.panel-body.flush { padding: 0; }
.panel-foot {
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--line);
  background: var(--raised);
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

/* Deliberately NOT a card: a plain bordered block with the number set large. Everything being a
   card with a radius and a shadow flattens the hierarchy until nothing reads as more important
   than anything else. */
.kpis { display: grid; gap: var(--s-3); grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.kpi {
  padding: var(--s-3) var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.kpi .k { font-size: var(--t-xs); font-weight: 650; letter-spacing: .06em;
          text-transform: uppercase; color: var(--muted); }
.kpi .v { font: 650 var(--t-2xl)/1.15 var(--sans); font-variant-numeric: tabular-nums;
          letter-spacing: -.02em; }
.kpi .s { font-size: var(--t-sm); color: var(--muted); }
.kpi.good { border-left: 3px solid var(--ok); }
.kpi.warn { border-left: 3px solid var(--warn); }
.kpi.attention { border-left: 3px solid var(--critical); }
.kpi.attention .v { color: var(--critical); }

/* ========================================================================== 7. data */
table { width: 100%; border-collapse: collapse; font-size: var(--t-md); }
.scroll { overflow-x: auto; }

thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: var(--s-2) var(--s-3);
  background: var(--raised);
  border-bottom: 1px solid var(--line);
  text-align: left;
  font-size: var(--t-xs);
  font-weight: 650;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
tbody td { padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--line); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--mono); }

/* A row that acts like a button IS a button as far as the keyboard is concerned. The previous
   pages put a click handler on the <tr> and nothing else, so every detail view on the platform
   was mouse-only. */
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--ink); }
th.sortable::after { content: ""; margin-left: 4px; opacity: .45; }
th[aria-sort="ascending"]::after { content: "\2191"; opacity: 1; }
th[aria-sort="descending"]::after { content: "\2193"; opacity: 1; }

/* A row that opens something. The action lives in a real button inside the first cell, laid over
   it, so the row keeps its row semantics and the keyboard still has something to reach. */
tbody tr.openable { cursor: pointer; }
tbody tr.openable:hover { background: var(--raised); }
tbody tr.openable:focus-within { outline: 2px solid var(--focus); outline-offset: -2px; }
tbody tr.openable td:first-child { position: relative; }

/* The keyboard's way in. Invisible, covers the first cell, and carries the row's accessible name
   — so a screen reader reads the row's cells AND finds one control that opens it. */
.row-open {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.row-open:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

tbody tr[tabindex] { cursor: pointer; }
tbody tr[tabindex]:hover { background: var(--raised); }
tbody tr[tabindex]:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
tbody tr[aria-selected="true"] { background: var(--accent-soft); }

.tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--sunken);
  color: var(--ink-2);
  font-size: var(--t-xs);
  font-weight: 600;
  white-space: nowrap;
}
.tag.ok { background: var(--ok-soft); color: var(--ok); }
.tag.ok.solid { background: var(--ok); color: var(--on-ok); }
.tag.warn { background: var(--warn-soft); color: var(--warn); }
.tag.bad { background: var(--critical-soft); color: var(--critical); }
.tag.info { background: var(--info-soft); color: var(--info); }
.tag.accent { background: var(--accent-soft); color: var(--accent); }

/* A dot carries the same meaning as the tag for a glance, and is paired with text so the meaning
   never rests on colour alone. */
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--line-2); flex: none; }
.dot.ok { background: var(--ok); }
.dot.warn { background: var(--warn); }
.dot.bad { background: var(--critical); }
.dot.info { background: var(--info); }

.empty {
  padding: var(--s-6) var(--s-4);
  text-align: center;
  color: var(--muted);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
}
.empty strong { color: var(--ink); font-size: var(--t-lg); font-weight: 600; }
.empty p { max-width: 46ch; }

/* Loading is a shape, not a word: a table that says "Loading…" in one cell jumps when the real
   rows arrive. */
.skeleton {
  background: linear-gradient(90deg, var(--sunken) 25%, var(--raised) 50%, var(--sunken) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.2s ease-in-out infinite;
  border-radius: var(--r-sm);
  height: 1em;
}

/* Sized to the real thing. A .kpi.skeleton rendered at 37px against a real KPI's 96px, so the
   row grew by 60px when the data landed — the exact jump skeletons exist to prevent. */
.kpi.skeleton { height: 82px; }
.metric.skeleton { height: 96px; }
@keyframes shimmer { to { background-position: -200% 0; } }

.bar { height: 6px; border-radius: 999px; background: var(--sunken); overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.bar > i.warn { background: var(--warn); }
.bar > i.bad { background: var(--critical); }

/* ========================================================================== 8. overlays */
dialog {
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-3);
  max-width: min(560px, calc(100vw - 32px));
  width: 100%;
}
dialog::backdrop { background: rgba(9, 13, 11, .45); backdrop-filter: blur(2px); }
dialog.wide { max-width: min(880px, calc(100vw - 32px)); }
dialog .panel-body { max-height: min(70vh, 620px); overflow-y: auto; }

/* The drawer is a real dialog too, so Escape closes it, focus is trapped inside it, and the page
   behind is inert. It was a div with a class, which meant a screen reader read straight past it
   into the table underneath. */
dialog.drawer {
  max-width: min(720px, calc(100vw - 32px));
  width: 100%;
  height: 100vh;
  max-height: 100vh;
  margin: 0 0 0 auto;
  border-radius: 0;
  border-width: 0 0 0 1px;
  animation: slide-in .16s ease;
}
@keyframes slide-in { from { transform: translateX(16px); opacity: .6; } }

/* Bounded. Errors used to be permanent and unlimited, so one API outage — six panels, six
   failures — stacked six undismissable cards up the right edge of the screen, over the content. */
.toasts {
  position: fixed;
  right: var(--s-4);
  bottom: var(--s-4);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  max-width: min(420px, calc(100vw - 32px));
  max-height: min(60vh, 520px);
  overflow-y: auto;
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  padding: var(--s-3);
  border: 1px solid var(--line);
  border-left: 3px solid var(--ink-2);
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--shadow-2);
  font-size: var(--t-sm);
  animation: rise .16s ease;
}
@keyframes rise { from { transform: translateY(6px); opacity: 0; } }
.toast.ok { border-left-color: var(--ok); }
.toast.bad { border-left-color: var(--critical); }
.toast.warn { border-left-color: var(--warn); }
.toast .t { font-weight: 600; }
.toast .x { margin-left: auto; }

/* ========================================================================== 9. utilities */
.stack { display: flex; flex-direction: column; gap: var(--s-3); }
.stack-sm { display: flex; flex-direction: column; gap: var(--s-2); }
.wrap { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.grow { flex: 1; min-width: 0; }
.center { display: grid; place-items: center; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hr { height: 1px; background: var(--line); border: 0; margin: 0; }

/* The sign-in screen. One element, so it cannot drift between pages. */
.gate {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--s-4);
}
.gate form {
  width: min(380px, 100%);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: var(--s-6);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
}
.gate .brand { padding: 0; margin-bottom: var(--s-1); }
/* A signed-out page that is not the sign-in form itself says what it is for: choosing a password. */
.gate-title { margin: 0; font-size: var(--t-xl); font-weight: 650; }

/* The divider between a password and a directory sign-in.
   A rule with the word on it rather than a bare line: "or" is doing real work here, telling
   somebody that the button below is an alternative to the form above and not a second step. */
.gate-or {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  margin: var(--s-1) 0;
  font-size: 0.8125rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.gate-or::before,
.gate-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

/* ---------------------------------------------------------------- choice groups
   A short, bounded set chosen from rather than typed. Used where a free-text field
   was accepting values that looked saved and silently did nothing — a jurisdiction
   the entity does not serve makes a document unreachable, and nothing said so. */
.checks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  padding: var(--s-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--sunken);
  max-height: 148px;
  overflow-y: auto;
}

.check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px 3px 6px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: var(--surface);
  font-size: var(--t-sm);
  cursor: pointer;
  white-space: nowrap;
}

.check:hover { border-color: var(--line-3); }
.check:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
  /* --accent on --accent-soft, matching .tag.accent. NOT --accent-ink: that is the text
     colour for a SOLID accent fill and is white in the light theme, which on a pale tint
     is invisible. */
  color: var(--accent);
}
.check:focus-within { outline: 2px solid var(--focus); outline-offset: 1px; }
.check input { margin: 0; accent-color: var(--accent); }

/* One choice per line with a sentence under it: the dialog's form of .checks, for choices that
   have to say what they mean — which alerts a route carries. */
.check-list {
  display: grid;
  gap: 6px;
  max-height: 320px;
  overflow-y: auto;
  padding: 2px;
}
.check-list .check {
  display: flex;
  align-items: flex-start;
  white-space: normal;
  border-radius: var(--r-md);
  padding: 6px 10px 6px 8px;
}
.check-list .check input { margin-top: 3px; }
.check-list .check-title,
.check-list .check-help { display: block; }

/* A heading inside a scrolling list. The agent console needs two groups in one column —
   what you are holding, and what is waiting — because a claimed conversation used to vanish
   from the only list on the screen. */
.group-head {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 6px 10px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
