/* ============================================================================
   WWCD design system
   Spec: docs/DESIGN-SYSTEM.md · Legacy grounding: docs/UX-LEGACY-STUDY.md

   Neutral : graphite with a violet cast (h≈258, chroma 2–6%) — chosen, not
             inherited. The previous ramp was the Tailwind slate default.
   Accent  : indigo "ledger ink" #3A3FCB. Green/amber/red are load-bearing
             collections semantics (kept promise / due / overdue), so the accent
             must sit far from all three. Teal failed that test — it reads as
             the "good" state at a glance in a dense table.
   Rule    : colour is a scarce operational signal, spent on state, not chrome.

   COMPATIBILITY CONTRACT — four other tracks are writing templates against this
   file concurrently. Every pre-existing class name and token name still works;
   legacy tokens are aliases onto the new ramp. Nothing was renamed. New names
   are additive. See the append-only section at the very end of this file.
   ========================================================================== */
:root{
  /* ---- neutral ramp (light) -------------------------------------------- */
  --n-0:#FFFFFF;   --n-25:#FAFAFC;  --n-50:#F6F6F9;  --n-100:#EEEEF3;
  --n-150:#E7E7EF; --n-200:#DCDCE6; --n-300:#C7C7D5; --n-400:#A8A8BC;
  --n-500:#85859B; --n-600:#5E5E72; --n-700:#45455A; --n-800:#33333F;
  --n-900:#232330; --n-950:#14141C;

  /* ---- surfaces --------------------------------------------------------- */
  --surface-canvas:var(--n-50);
  --surface-panel:var(--n-0);
  --surface-raised:var(--n-25);
  --surface-sunken:var(--n-100);
  --surface-overlay:var(--n-0);
  --surface-rail:var(--n-950);
  --surface-inset:var(--n-150);

  /* ---- ink -------------------------------------------------------------- */
  --ink:var(--n-900);          /* 15.50:1 on panel */
  --ink-muted:var(--n-600);    /*  6.33:1 on panel */
  --ink-faint:var(--n-500);    /*  3.60:1 — placeholders/disabled only */
  --ink-on-rail:#C9C9DB;
  --ink-on-rail-strong:#FFFFFF;
  --ink-on-rail-dim:#9292AC;   /*  5.34:1 on the rail ground */

  /* ---- lines ------------------------------------------------------------ */
  --line:var(--n-200);         /* decorative hairline */
  --line-strong:var(--n-300);  /* panel edges, table rules */
  --line-control:var(--n-500); /* form controls — 3.60:1, WCAG 1.4.11 */

  /* ---- accent ----------------------------------------------------------- */
  --accent:#3A3FCB;            /* 7.63:1 on panel */
  --accent-hover:#2E32A8;      /* white on it: 9.82:1 */
  --accent-ink:#3A3FCB;
  --accent-quiet:#E8E9FB;
  --accent-edge:#B9BCF2;
  --on-accent:#FFFFFF;

  /* ---- state ------------------------------------------------------------ */
  --good-fg:#166534; --good-bg:#DCF5E3; --good-edge:#9BDCB2;  /* 6.18:1 */
  --warn-fg:#8A5300; --warn-bg:#FCF0D6; --warn-edge:#EFCE87;  /* 5.60:1 */
  --bad-fg:#B42318;  --bad-bg:#FBE4E2;  --bad-edge:#F0AFA9;   /* 5.42:1 */
  --info-fg:var(--accent-ink); --info-bg:var(--accent-quiet); --info-edge:var(--accent-edge);

  /* ---- rail-scoped state — theme-invariant ------------------------------
     The rail is a dark ground in BOTH themes, so the panel-tuned --good-fg
     (#166534) measures 2.27:1 there. These are pinned light-on-dark.        */
  --rail-good:#5FD68A;   /* 8.84:1 on the rail */
  --rail-warn:#F0B84E;   /* 9.00:1 */
  --rail-bad:#FF8A80;    /* 7.09:1 */

  /* ---- rows ------------------------------------------------------------- */
  --row-hover:#F0F0F7;
  --row-selected:#E8E9FB;

  /* ---- data series ------------------------------------------------------ */
  --data-1:#3A3FCB; --data-2:#00857A; --data-3:#8A5300; --data-4:#6E3FA8;

  /* ---- typography ------------------------------------------------------- */
  --font-ui:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,
            "Helvetica Neue",Arial,"Noto Sans",sans-serif;
  --font-mono:ui-monospace,"SF Mono","Cascadia Mono","JetBrains Mono",
            "Roboto Mono",Menlo,Consolas,monospace;
  /* scale: base 14px, ratio 1.125 (major second), rounded to 0.5px */
  --text-micro:11px; --text-caption:12.5px; --text-body:14px;
  --text-body-lg:15.5px; --text-h4:17.5px; --text-h3:20px;
  --text-h2:22.5px; --text-h1:25px;
  --leading-tight:1.25; --leading-snug:1.35; --leading-normal:1.5;
  --weight-normal:400; --weight-medium:500; --weight-semi:600; --weight-bold:700;
  --tracking-label:.06em; --tracking-tight:-.011em;

  /* ---- spacing — 4px base, plus 2/6px for dense operational rows -------- */
  --space-05:2px;  --space-1:4px;   --space-15:6px;  --space-2:8px;
  --space-3:12px;  --space-4:16px;  --space-5:20px;  --space-6:24px;
  --space-8:32px;  --space-10:40px; --space-12:48px; --space-16:64px;

  /* ---- radius — dense tools want small radii ---------------------------- */
  --radius-xs:3px; --radius-sm:5px; --radius-md:8px; --radius-lg:12px;
  --radius-pill:999px;

  /* ---- elevation — see docs/DESIGN-SYSTEM.md §3.4.
     NEVER the keyword `none`: these compose into comma-separated shadow
     lists (.kpi.accent), and `none` in a list voids the declaration.        */
  --elev-0:0 0 #0000;
  --elev-1:0 1px 2px rgba(20,20,28,.06), 0 1px 3px rgba(20,20,28,.04);
  --elev-2:0 8px 24px rgba(20,20,28,.12), 0 2px 6px rgba(20,20,28,.06);
  --elev-rail:1px 0 0 rgba(20,20,28,.08);

  /* ---- motion ----------------------------------------------------------- */
  --dur-1:120ms; --dur-2:180ms; --dur-3:260ms;
  --ease-out:cubic-bezier(.2,.8,.3,1);
  --ease-in-out:cubic-bezier(.4,0,.2,1);

  /* ---- layout ----------------------------------------------------------- */
  --rail-w:212px; --rail-w-collapsed:56px; --contextbar-h:46px;
  --row-h:32px; --content-gutter:var(--space-5);
  --content-form:720px; --content-reading:68ch;
  --stack-top:var(--contextbar-h);

  /* ======================================================================
     UI REFRESH — read-aloud emphasis, table system, de-emphasis.
     docs/UI-ARCHITECTURE.md §1.4 (names) · docs/UI-DESIGN.md §2.3, §4.1 (values)
     ====================================================================== */

  /* Display steps for the read-aloud band only — never a 32px table row. */
  --text-display:34px; --text-display-sm:28px;

  /* Band sizes; the table-scope override cuts them to row size. */
  --say-1-size:var(--text-display);     --say-1-weight:var(--weight-bold);
  --say-2-size:var(--text-display-sm);  --say-2-weight:var(--weight-bold);
  --say-2-ident-size:22px;
  --say-3-size:var(--text-h4);          --say-3-weight:var(--weight-semi);
  --say-cell-size:var(--text-body-lg);  --say-cell-weight:var(--weight-semi);
  --say-mute-size:var(--text-caption);  --say-mute-ink:var(--ink-muted);
  --say-ink:var(--ink);

  /* Table system. The gridline is deliberately NOT --line: at 1.36:1 on the
     light panel a hairline is too faint to track a row across 13 columns.
     Measured on the real panel — light 1.54:1, dark 1.62:1.                 */
  --table-rule:#CFCFDD;                 /* every cell edge                   */
  --table-rule-head:var(--line-strong); /* header dividers and the head band */
  --table-rule-raised:#CACAD9;          /* gridlines on a raised tfoot       */
  --table-pad-y:var(--space-15);        /* 6px — density is unchanged, see   */
  --table-pad-x:var(--space-2);         /* 8px   UI-ARCHITECTURE §3.3        */

  /* De-emphasis is a colour, never opacity — that is how audit m1 happened. */
  --ink-dim:#5E5E72;

  /* Edge cue for a horizontally scrollable table.                           */
  --scroll-cue:rgba(20,20,28,.20);

  /* ======================================================================
     LEGACY TOKEN ALIASES — other tracks' unmerged CSS references these.
     They are live aliases onto the ramp above, never separate values.
     ====================================================================== */
  --topbar-bg:var(--surface-rail);
  --topbar-ink:var(--ink-on-rail);
  --topbar-ink-strong:var(--ink-on-rail-strong);
  --topbar-who:var(--ink-on-rail-dim);
  --brand:var(--accent);
  --brand-strong:var(--accent-hover);
  --brand-ink:var(--accent-ink);
  --on-brand:var(--on-accent);
  --good-line:var(--good-edge);
  --warn-line:var(--warn-edge);
  --danger-fg:var(--bad-fg);
  --danger-bg:var(--bad-bg);
  --danger-line:var(--bad-edge);
  --danger:var(--bad-fg);
  --warn:var(--warn-fg);
  --surface-page:var(--surface-canvas);
  --bg:var(--surface-rail);
  --panel:var(--surface-panel);
  --muted:var(--ink-muted);
  --shadow-card:var(--elev-1);
}

/* ---- dark theme --------------------------------------------------------
   Authored separately, not inverted. Two override layers: the OS preference
   (guarded so an explicit light choice wins), then the manual [data-theme].  */
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --n-0:#17171F;   --n-25:#1B1B24;  --n-50:#0E0E14;  --n-100:#0A0A0F;
    --n-150:#232330; --n-200:#2A2A36; --n-300:#3A3A49; --n-400:#52526A;
    --n-500:#6B6B84; --n-600:#9C9CB2; --n-700:#BDBDD0; --n-800:#D5D5E2;
    --n-900:#E8E8F0; --n-950:#08080C;

    --surface-canvas:#0E0E14; --surface-panel:#17171F; --surface-raised:#1F1F29;
    --surface-sunken:#131319;  --surface-overlay:#1F1F29; --surface-rail:#08080C;
    --surface-inset:#232330;

    --ink:#E8E8F0; --ink-muted:#9C9CB2; --ink-faint:#6B6B84;
    --ink-on-rail:#A8A8C0; --ink-on-rail-strong:#FFFFFF; --ink-on-rail-dim:#9292AC;

    --line:#2A2A36; --line-strong:#3A3A49; --line-control:#6B6B84;

    --accent:#4B51D8; --accent-hover:#5C62E8; --accent-ink:#A2A8FF;
    --accent-quiet:#232338; --accent-edge:#3D3D6B; --on-accent:#FFFFFF;

    --good-fg:#5FD68A; --good-bg:#12281B; --good-edge:#1F5236;
    --warn-fg:#F0B84E; --warn-bg:#2C2110; --warn-edge:#5C4416;
    --bad-fg:#FF8A80;  --bad-bg:#2E1614;  --bad-edge:#632822;

    --row-hover:#1E1E2A; --row-selected:#232338;
    --data-1:#8A90FF; --data-2:#3FCFBE; --data-3:#F0B84E; --data-4:#C08CF0;

    /* In dark, elevation is surface lightness + border, not shadow. */
    --elev-0:0 0 #0000; --elev-1:0 0 #0000;
    --elev-2:0 12px 32px rgba(0,0,0,.62), 0 2px 8px rgba(0,0,0,.48);
    --elev-rail:1px 0 0 rgba(0,0,0,.6);

    /* UI refresh. The rest of the block resolves through tokens that already
       flip; only these three carry their own value per theme. */
    --table-rule:#3A3B4A;         /* 1.62:1 on the dark panel */
    --table-rule-raised:#424354;
    --ink-dim:#8A8AA2;            /* 5.29:1 on the dark panel */
    --scroll-cue:rgba(0,0,0,.55);
  }
}
:root[data-theme="dark"]{
  --n-0:#17171F;   --n-25:#1B1B24;  --n-50:#0E0E14;  --n-100:#0A0A0F;
  --n-150:#232330; --n-200:#2A2A36; --n-300:#3A3A49; --n-400:#52526A;
  --n-500:#6B6B84; --n-600:#9C9CB2; --n-700:#BDBDD0; --n-800:#D5D5E2;
  --n-900:#E8E8F0; --n-950:#08080C;

  --surface-canvas:#0E0E14; --surface-panel:#17171F; --surface-raised:#1F1F29;
  --surface-sunken:#131319;  --surface-overlay:#1F1F29; --surface-rail:#08080C;
  --surface-inset:#232330;

  --ink:#E8E8F0; --ink-muted:#9C9CB2; --ink-faint:#6B6B84;
  --ink-on-rail:#A8A8C0; --ink-on-rail-strong:#FFFFFF; --ink-on-rail-dim:#9292AC;

  --line:#2A2A36; --line-strong:#3A3A49; --line-control:#6B6B84;

  --accent:#4B51D8; --accent-hover:#5C62E8; --accent-ink:#A2A8FF;
  --accent-quiet:#232338; --accent-edge:#3D3D6B; --on-accent:#FFFFFF;

  --good-fg:#5FD68A; --good-bg:#12281B; --good-edge:#1F5236;
  --warn-fg:#F0B84E; --warn-bg:#2C2110; --warn-edge:#5C4416;
  --bad-fg:#FF8A80;  --bad-bg:#2E1614;  --bad-edge:#632822;

  --row-hover:#1E1E2A; --row-selected:#232338;
  --data-1:#8A90FF; --data-2:#3FCFBE; --data-3:#F0B84E; --data-4:#C08CF0;

  --elev-0:0 0 #0000; --elev-1:0 0 #0000;
  --elev-2:0 12px 32px rgba(0,0,0,.62), 0 2px 8px rgba(0,0,0,.48);
  --elev-rail:1px 0 0 rgba(0,0,0,.6);

  /* UI refresh — see the note in the prefers-color-scheme block above. */
  --table-rule:#3A3B4A;
  --table-rule-raised:#424354;
  --ink-dim:#8A8AA2;
  --scroll-cue:rgba(0,0,0,.55);
}
/* Explicit light restoration, so the manual toggle wins in BOTH directions. */
:root[data-theme="light"]{
  --n-0:#FFFFFF;   --n-25:#FAFAFC;  --n-50:#F6F6F9;  --n-100:#EEEEF3;
  --n-150:#E7E7EF; --n-200:#DCDCE6; --n-300:#C7C7D5; --n-400:#A8A8BC;
  --n-500:#85859B; --n-600:#5E5E72; --n-700:#45455A; --n-800:#33333F;
  --n-900:#232330; --n-950:#14141C;

  --surface-canvas:var(--n-50); --surface-panel:var(--n-0);
  --surface-raised:var(--n-25); --surface-sunken:var(--n-100);
  --surface-overlay:var(--n-0); --surface-rail:var(--n-950);
  --surface-inset:var(--n-150);

  --ink:var(--n-900); --ink-muted:var(--n-600); --ink-faint:var(--n-500);
  --ink-on-rail:#C9C9DB; --ink-on-rail-strong:#FFFFFF; --ink-on-rail-dim:#9292AC;

  --line:var(--n-200); --line-strong:var(--n-300); --line-control:var(--n-500);

  --accent:#3A3FCB; --accent-hover:#2E32A8; --accent-ink:#3A3FCB;
  --accent-quiet:#E8E9FB; --accent-edge:#B9BCF2; --on-accent:#FFFFFF;

  --good-fg:#166534; --good-bg:#DCF5E3; --good-edge:#9BDCB2;
  --warn-fg:#8A5300; --warn-bg:#FCF0D6; --warn-edge:#EFCE87;
  --bad-fg:#B42318;  --bad-bg:#FBE4E2;  --bad-edge:#F0AFA9;

  --row-hover:#F0F0F7; --row-selected:#E8E9FB;
  --data-1:#3A3FCB; --data-2:#00857A; --data-3:#8A5300; --data-4:#6E3FA8;

  --elev-0:0 0 #0000;
  --elev-1:0 1px 2px rgba(20,20,28,.06), 0 1px 3px rgba(20,20,28,.04);
  --elev-2:0 8px 24px rgba(20,20,28,.12), 0 2px 6px rgba(20,20,28,.06);
  --elev-rail:1px 0 0 rgba(20,20,28,.08);

  /* UI refresh — restored explicitly, or a dark OS preference would leave the
     gridlines and dimmed rows dark while the user has asked for light. */
  --table-rule:#CFCFDD;
  --table-rule-raised:#CACAD9;
  --ink-dim:#5E5E72;
  --scroll-cue:rgba(20,20,28,.20);
}

@media (prefers-reduced-motion: reduce){
  :root{ --dur-1:1ms; --dur-2:1ms; --dur-3:1ms; }
  *,*::before,*::after{
    animation-duration:1ms !important; animation-iteration-count:1 !important;
    transition-duration:1ms !important; scroll-behavior:auto !important;
  }
}

/* ============================================================================
   Base
   ========================================================================== */
*{box-sizing:border-box}
/* Components set display:flex/grid, which silently overrides the `hidden`
   attribute. Make `hidden` mean hidden everywhere. */
[hidden]{display:none!important}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; font-family:var(--font-ui); font-size:var(--text-body);
  line-height:var(--leading-normal); color:var(--ink);
  background:var(--surface-canvas); -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{margin:0;line-height:var(--leading-tight);text-wrap:balance;
  font-weight:var(--weight-semi)}
h1{font-size:var(--text-h2);letter-spacing:var(--tracking-tight)}
h2{font-size:var(--text-h4);margin:0 0 var(--space-2)}
h3{font-size:var(--text-body-lg)}
p{margin:0 0 var(--space-2)}
a{color:var(--accent-ink);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:2px}

.muted{color:var(--ink-muted)}
.faint{color:var(--ink-faint)}
.small{font-size:var(--text-caption)}
.mono,.ident{font-family:var(--font-mono);font-size:.95em;letter-spacing:-.01em}
.ident{white-space:nowrap}
.label-micro{font-size:var(--text-micro);font-weight:var(--weight-semi);
  letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--ink-muted)}
.nowrap{white-space:nowrap}
.spacer{flex:1}
.inline{display:inline}
.stack{display:flex;flex-direction:column;gap:var(--space-3)}
.cluster{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}

/* Digits that line up or tick must not jitter. */
.tabular,table.data td,table.data th,.kpi-v,.amount,.num,.money,
.dialler .timer,dl.kv dd{
  font-variant-numeric:tabular-nums lining-nums;
  font-feature-settings:"tnum" 1,"lnum" 1;
}
.amount,.money{text-align:right;white-space:nowrap}

/* Keyboard focus visibility (both themes) — verified-fix, do not regress. */
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--radius-xs)}
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap}
.skip-link{position:absolute;left:var(--space-2);top:-100px;z-index:100;
  padding:var(--space-2) var(--space-3);background:var(--surface-overlay);
  border:1px solid var(--line-strong);border-radius:var(--radius-sm)}
.skip-link:focus{top:var(--space-2)}

/* Inline SVG icon system (CSP-safe; replaces emoji). */
.icon{width:16px;height:16px;display:inline-block;vertical-align:-.15em;
  stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;
  fill:none;flex:none}
.icon-sm{width:13px;height:13px}
.icon-lg{width:20px;height:20px}

/* ============================================================================
   App shell — persistent rail + context bar
   `chrome` and `body` stay SIBLINGS in base.html (auth screens blank chrome),
   so the rail is fixed and <body> padding reserves its column.
   ========================================================================== */
.app-shell{padding-left:var(--rail-w)}
:root[data-rail="collapsed"] .app-shell{padding-left:var(--rail-w-collapsed)}
.app-bare{padding-left:0}
@media (max-width:900px){ .app-shell{padding-left:var(--rail-w-collapsed)} }

.rail{
  position:fixed;inset-block:0;left:0;width:var(--rail-w);z-index:30;
  display:flex;flex-direction:column;
  background:var(--surface-rail);color:var(--ink-on-rail);
  box-shadow:var(--elev-rail);
  transition:width var(--dur-2) var(--ease-out);
}
:root[data-rail="collapsed"] .rail{width:var(--rail-w-collapsed)}
.rail-brand{display:flex;align-items:center;gap:var(--space-2);
  height:var(--contextbar-h);padding:0 var(--space-3);flex:none;
  color:var(--ink-on-rail-strong);font-weight:var(--weight-bold);
  letter-spacing:.01em;white-space:nowrap;overflow:hidden}
.rail-brand .mark{width:22px;height:22px;border-radius:var(--radius-xs);
  background:var(--accent);display:grid;place-items:center;flex:none;
  color:#fff;font-size:11px;font-weight:var(--weight-bold);letter-spacing:-.04em}
.rail-nav{display:flex;flex-direction:column;gap:1px;padding:var(--space-2);
  overflow-y:auto;overflow-x:hidden;flex:1}
.rail-sect{padding:var(--space-3) var(--space-2) var(--space-1);
  font-size:var(--text-micro);font-weight:var(--weight-semi);
  letter-spacing:var(--tracking-label);text-transform:uppercase;
  color:var(--ink-on-rail-dim);white-space:nowrap}
.rail-link{
  display:flex;align-items:center;gap:var(--space-3);
  padding:var(--space-2) var(--space-2);border-radius:var(--radius-sm);
  color:var(--ink-on-rail);font-size:var(--text-body);
  font-weight:var(--weight-medium);white-space:nowrap;overflow:hidden;
  transition:background var(--dur-1) var(--ease-out),color var(--dur-1) var(--ease-out);
}
.rail-link:hover{background:rgba(255,255,255,.07);color:var(--ink-on-rail-strong);
  text-decoration:none}
.rail-link[aria-current="page"]{background:rgba(255,255,255,.11);
  color:var(--ink-on-rail-strong)}
.rail-link .icon{color:currentColor}
.rail-link .badge{margin-left:auto;font-size:var(--text-micro);
  font-weight:var(--weight-semi);padding:0 6px;border-radius:var(--radius-pill);
  background:var(--rail-bad);color:#1A0906;font-variant-numeric:tabular-nums}
:root[data-rail="collapsed"] .rail-link span,
:root[data-rail="collapsed"] .rail-sect,
:root[data-rail="collapsed"] .rail-brand .brandname{display:none}
.rail-foot{flex:none;border-top:1px solid rgba(255,255,255,.09);
  padding:var(--space-2);display:flex;flex-direction:column;gap:var(--space-2)}
.rail .rail-toggle{width:100%;justify-content:flex-start;gap:var(--space-2);
  --btn-h:26px;padding:0 var(--space-15);
  background:transparent;border-color:transparent;
  color:var(--ink-on-rail-dim);font-size:var(--text-caption);
  font-weight:var(--weight-medium)}
.rail .rail-toggle:hover{background:rgba(255,255,255,.08);
  border-color:transparent;color:var(--ink-on-rail-strong)}
:root[data-rail="collapsed"] .rail .rail-toggle{justify-content:center;padding:0}
:root[data-rail="collapsed"] .rail .rail-toggle span{display:none}

.contextbar{
  position:sticky;top:0;z-index:20;height:var(--contextbar-h);
  display:flex;align-items:center;gap:var(--space-3);
  padding:0 var(--content-gutter);
  background:var(--surface-canvas);
  border-bottom:1px solid var(--line);
}
.cb-crumb{display:inline-flex;align-items:center;gap:var(--space-1);
  font-size:var(--text-caption);color:var(--ink-muted)}
.cb-who{font-size:var(--text-caption);color:var(--ink-muted)}

/* Content width rules per screen type. `.container` is KEPT as an alias of
   `.content` (templates and other tracks still emit it) but no longer clamps
   operational screens to a 1000px reading column. */
.content,.container{padding:var(--space-5) var(--content-gutter) var(--space-12);
  max-width:none;margin:0}
.content--form,.container.auth-wrap{max-width:var(--content-form);margin-inline:auto}
.content--reading{max-width:var(--content-reading)}
.app-bare .content,.app-bare .container{padding-top:var(--space-8)}

.page-header{padding:var(--space-5) var(--content-gutter) 0;display:flex;
  align-items:flex-start;gap:var(--space-4);flex-wrap:wrap}
.page-header .ph-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--space-1)}
.page-header .ph-actions{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}
.page-eyebrow{font-size:var(--text-micro);font-weight:var(--weight-semi);
  letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--ink-muted)}
.page-header + .content,.page-header + main{padding-top:var(--space-4)}

/* ============================================================================
   Buttons — legacy names (.button/.primary/.danger/.link/.small) all preserved
   ========================================================================== */
.btn,button,.button{
  --btn-h:30px;
  display:inline-flex;align-items:center;justify-content:center;gap:var(--space-15);
  height:var(--btn-h);padding:0 var(--space-3);
  font:inherit;font-size:var(--text-body);font-weight:var(--weight-medium);
  color:var(--ink);background:var(--surface-panel);
  border:1px solid var(--line-control);border-radius:var(--radius-sm);
  cursor:pointer;white-space:nowrap;user-select:none;text-decoration:none;
  transition:background var(--dur-1) var(--ease-out),
             border-color var(--dur-1) var(--ease-out),
             color var(--dur-1) var(--ease-out);
}
.btn:hover,button:hover,.button:hover{background:var(--surface-sunken);text-decoration:none}
.btn:active,button:active{transform:translateY(.5px)}
.btn:disabled,button:disabled,.btn[aria-disabled="true"]{
  opacity:.5;cursor:not-allowed;pointer-events:none}

button.primary,.primary.button,.btn--primary{
  background:var(--accent);border-color:var(--accent);color:var(--on-accent);
  font-weight:var(--weight-semi)}
button.primary:hover,.primary.button:hover,.btn--primary:hover{
  background:var(--accent-hover);border-color:var(--accent-hover)}

button.danger,.danger.button,.btn--danger{
  background:var(--bad-fg);border-color:var(--bad-fg);color:#fff;
  font-weight:var(--weight-semi)}
button.danger:hover,.danger.button:hover{filter:brightness(1.08)}

button.link,.btn--link{
  height:auto;padding:0;background:none;border:none;color:var(--accent-ink);
  font-weight:var(--weight-medium)}
button.link:hover,.btn--link:hover{background:none;text-decoration:underline;
  text-underline-offset:2px}

.btn--ghost{background:transparent;border-color:transparent;color:var(--ink-muted)}
.btn--ghost:hover{background:var(--surface-sunken);color:var(--ink)}

button.small,.small.button,.btn--sm{--btn-h:24px;padding:0 var(--space-2);
  font-size:var(--text-caption)}
.btn--lg{--btn-h:36px;padding:0 var(--space-4);font-size:var(--text-body-lg)}
.btn--icon{width:var(--btn-h);padding:0}
.btn-row{display:flex;gap:var(--space-2);flex-wrap:wrap;align-items:center}

/* ============================================================================
   Forms
   ========================================================================== */
.field{display:flex;flex-direction:column;gap:var(--space-15);min-width:0}
.field-label,label > span.field-label{font-size:var(--text-caption);
  font-weight:var(--weight-semi);color:var(--ink-muted)}
.field-hint{font-size:var(--text-caption);color:var(--ink-muted)}
.field-error{font-size:var(--text-caption);color:var(--bad-fg);
  font-weight:var(--weight-medium)}
.req{color:var(--bad-fg);font-weight:var(--weight-semi)}

/* Legacy pattern: <label>Text<input></label>. Preserved. */
label{display:block;margin:0 0 var(--space-3);font-size:var(--text-caption);
  font-weight:var(--weight-semi);color:var(--ink-muted)}
label input,label select,label textarea{display:block;width:100%;
  margin-top:var(--space-15);font-weight:var(--weight-normal);color:var(--ink)}

.input,input[type=text],input[type=password],input[type=email],input[type=number],
input[type=search],input[type=tel],input:not([type]),select,textarea{
  width:100%;height:30px;padding:0 var(--space-2);
  font:inherit;font-size:var(--text-body);color:var(--ink);
  background:var(--surface-panel);
  border:1px solid var(--line-control);border-radius:var(--radius-sm);
  transition:border-color var(--dur-1) var(--ease-out),
             box-shadow var(--dur-1) var(--ease-out);
}
textarea{height:auto;padding:var(--space-2);line-height:var(--leading-normal);resize:vertical}
select{appearance:none;padding-right:var(--space-6);
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                   linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 14px) 13px,calc(100% - 9px) 13px;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat}
.input:focus,input:focus,select:focus,textarea:focus{
  outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
::placeholder{color:var(--ink-faint)}
[aria-invalid="true"],.is-invalid{border-color:var(--bad-fg)!important}
[aria-invalid="true"]:focus,.is-invalid:focus{
  box-shadow:0 0 0 3px color-mix(in srgb,var(--bad-fg) 22%,transparent)!important}
.input--mono{font-family:var(--font-mono);letter-spacing:-.01em}
input[type=file]{width:100%;font-size:var(--text-caption);color:var(--ink-muted)}

.checkbox{display:inline-flex;align-items:center;gap:var(--space-15);
  font-weight:var(--weight-normal);color:var(--ink);cursor:pointer;
  margin:0 var(--space-3) 0 0}
.checkbox input{width:auto;height:auto;margin:0;accent-color:var(--accent)}
.row{display:flex;gap:var(--space-3);flex-wrap:wrap}
.row > label,.row > .field{flex:1 1 170px;min-width:0}
fieldset{border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:var(--space-2) var(--space-3);margin:var(--space-3) 0}
legend{font-weight:var(--weight-semi);font-size:var(--text-caption);color:var(--ink-muted)}

/* ---- flatpickr date field ------------------------------------------------
   VERIFIED against app/static/vendor/flatpickr.min.js:
   with altInput:true flatpickr sets the ORIGINAL input to type="hidden" and
   creates a second visible input. Styling input[data-date] would therefore
   style an invisible node — we style the altInput (.date-field), whose class
   the enhancer now sets explicitly. The calendar is appended to <body>, so it
   is themed at document level; flatpickr.min.css ships light-only.           */
input.date-field,.input.date-field{
  height:34px;padding-left:30px;
  font-weight:var(--weight-medium);font-size:var(--text-body);
  color:var(--ink);background-color:var(--surface-panel);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%2385859B' stroke-width='1.5' stroke-linecap='round'%3E%3Crect x='2' y='3' width='12' height='11' rx='1.5'/%3E%3Cpath d='M2 6.5h12M5.5 1.5v3M10.5 1.5v3'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:9px 50%;
  border:1px solid var(--line-control);border-radius:var(--radius-sm);
  cursor:pointer;
}
:root[data-theme="dark"] input.date-field{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%236B6B84' stroke-width='1.5' stroke-linecap='round'%3E%3Crect x='2' y='3' width='12' height='11' rx='1.5'/%3E%3Cpath d='M2 6.5h12M5.5 1.5v3M10.5 1.5v3'/%3E%3C/svg%3E")}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) input.date-field{
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%236B6B84' stroke-width='1.5' stroke-linecap='round'%3E%3Crect x='2' y='3' width='12' height='11' rx='1.5'/%3E%3Cpath d='M2 6.5h12M5.5 1.5v3M10.5 1.5v3'/%3E%3C/svg%3E")}
}
input.date-field:focus,input.date-field.active{border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
/* Never let the hidden original resurface. */
input[data-date].flatpickr-input[type="hidden"]{display:none!important}

.flatpickr-calendar{
  background:var(--surface-overlay);border:1px solid var(--line-strong);
  border-radius:var(--radius-lg);box-shadow:var(--elev-2);
  color:var(--ink);font-family:var(--font-ui)}
.flatpickr-calendar.arrowTop::before,.flatpickr-calendar.arrowTop::after,
.flatpickr-calendar.arrowBottom::before,.flatpickr-calendar.arrowBottom::after{display:none}
.flatpickr-months{padding:var(--space-2) var(--space-2) 0}
.flatpickr-months .flatpickr-month,.flatpickr-current-month,
.flatpickr-current-month input.cur-year,.flatpickr-monthDropdown-months{
  color:var(--ink);fill:var(--ink);background:transparent;font-weight:var(--weight-semi)}
.flatpickr-monthDropdown-months option{background:var(--surface-overlay);color:var(--ink)}
.flatpickr-months .flatpickr-prev-month,.flatpickr-months .flatpickr-next-month{
  fill:var(--ink-muted);border-radius:var(--radius-sm);padding:5px 8px}
.flatpickr-months .flatpickr-prev-month:hover,
.flatpickr-months .flatpickr-next-month:hover{background:var(--surface-sunken)}
.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg{fill:var(--accent-ink)}
.flatpickr-weekdays{background:transparent}
span.flatpickr-weekday{color:var(--ink-muted);background:transparent;
  font-size:var(--text-micro);font-weight:var(--weight-semi);
  letter-spacing:var(--tracking-label);text-transform:uppercase}
.flatpickr-day{color:var(--ink);border-radius:var(--radius-sm);
  border:1px solid transparent;font-variant-numeric:tabular-nums;
  transition:background var(--dur-1) var(--ease-out)}
.flatpickr-day:hover{background:var(--surface-sunken)}
.flatpickr-day.today{border-color:var(--accent);font-weight:var(--weight-semi)}
.flatpickr-day.selected,.flatpickr-day.selected:hover,
.flatpickr-day.startRange,.flatpickr-day.endRange{
  background:var(--accent);border-color:var(--accent);color:var(--on-accent);
  font-weight:var(--weight-semi)}
.flatpickr-day.prevMonthDay,.flatpickr-day.nextMonthDay,
.flatpickr-day.flatpickr-disabled{color:var(--ink-faint)}
.flatpickr-innerContainer,.flatpickr-rContainer,.flatpickr-days,.dayContainer{
  background:transparent}
.flatpickr-time input,.flatpickr-time .flatpickr-am-pm,
.flatpickr-time .flatpickr-time-separator{color:var(--ink);background:transparent}

/* ============================================================================
   Panels — `.card` preserved as the general container
   ========================================================================== */
.panel,.card{background:var(--surface-panel);border:1px solid var(--line);
  border-radius:var(--radius-md);box-shadow:var(--elev-1);
  margin-bottom:var(--space-4)}
.card{padding:var(--space-4)}
.panel-head{display:flex;align-items:center;gap:var(--space-3);
  padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--line);
  background:var(--surface-sunken);
  border-radius:var(--radius-md) var(--radius-md) 0 0}
.panel-title{font-size:var(--text-body);font-weight:var(--weight-semi);flex:1;
  color:var(--ink)}
.panel-body{padding:var(--space-3)}
.panel-body--flush{padding:0}
.auth-card{max-width:400px;margin:8vh auto}
.auth-brand{display:flex;align-items:center;justify-content:center;
  gap:var(--space-2);margin-bottom:var(--space-4);font-weight:var(--weight-bold);
  font-size:var(--text-h4)}
.auth-brand .mark{width:26px;height:26px;border-radius:var(--radius-sm);
  background:var(--accent);color:#fff;display:grid;place-items:center;
  font-size:12px;letter-spacing:-.04em}

/* ============================================================================
   Collapsible section — THE density pattern (UX study §3.2, §3.3, §4.9)
   Built on <details>/<summary>: keyboard-accessible and functional with no JS.
   When folded, the header rewrites itself into a one-line summary of what was
   folded away, exactly as legacy ShowHideCase() did. A folded section is never
   a hole.
   ========================================================================== */
.section{background:var(--surface-panel);border:1px solid var(--line);
  border-radius:var(--radius-md);box-shadow:var(--elev-1);
  margin-bottom:var(--space-3)}
.section-head{
  display:flex;align-items:center;gap:var(--space-2);
  padding:var(--space-2) var(--space-3);cursor:pointer;list-style:none;
  background:var(--surface-sunken);border-radius:var(--radius-md);
  font-size:var(--text-body);
  transition:background var(--dur-1) var(--ease-out);
}
.section[open] > .section-head{border-bottom:1px solid var(--line);
  border-radius:var(--radius-md) var(--radius-md) 0 0}
.section-head:hover{background:var(--surface-inset)}
.section-head::-webkit-details-marker{display:none}
.section-head::marker{content:""}
.section-chev{transition:transform var(--dur-1) var(--ease-out);color:var(--ink-muted)}
.section[open] > .section-head .section-chev{transform:rotate(90deg)}
.section-title{font-weight:var(--weight-semi);color:var(--ink);white-space:nowrap}
.section-count{font-size:var(--text-caption);color:var(--ink-muted);
  font-variant-numeric:tabular-nums}
/* The collapsed-section summary: visible ONLY when folded. */
.section-summary{
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:var(--text-caption);color:var(--ink-muted);text-align:right}
.section[open] > .section-head .section-summary{display:none}
.section-actions{margin-left:auto;display:flex;gap:var(--space-2);align-items:center}
.section[open] > .section-head .section-actions{margin-left:0}
/* Section titles are <h2> so the page has a real outline (audit C3). h2's own size and
   bottom margin would break the flex summary row: the element changes, the rendering
   does not. */
.section-title{margin:0;font-size:inherit;font-weight:var(--weight-semi);color:var(--ink)}
.section-body{padding:var(--space-3)}
.section-body--flush{padding:0}
/* A wrap nested inside a panel must not draw a second frame. The card, section and
   panel are already bordered, rounded and shadowed; without this the table sits in a
   frame inside a frame with a gap between them, on every one of the 51 templates that
   gained a wrapper. `background` is deliberately NOT reset — the scroll cue's cover
   layers are painted from it, and making it transparent kills the edge cue. */
.section-body--flush > .table-wrap,
.section-body > .table-wrap,
.card > .table-wrap{border:none;box-shadow:none;border-radius:0;margin-bottom:0}

/* Field grid inside a section — the density workhorse. */
.fieldgrid{display:grid;gap:var(--space-2) var(--space-4);
  grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
.fg-item{display:flex;flex-direction:column;gap:1px;min-width:0}
.fg-item .v{font-size:var(--text-body);color:var(--ink);
  overflow-wrap:anywhere;font-variant-numeric:tabular-nums lining-nums}
.fg-item .v.is-bad{color:var(--bad-fg);font-weight:var(--weight-semi)}
.fg-item .v.is-good{color:var(--good-fg);font-weight:var(--weight-semi)}
.fg-item .v.is-nil{color:var(--ink-faint)}
.fg-item .v.ident{font-family:var(--font-mono)}
.fg-wide{grid-column:1/-1}

/* ============================================================================
   Record header — the pinned account context bar
   ========================================================================== */
/* `.record-header` and its facts are gone: the read-aloud band replaces that component
   on the one page that used it (docs/UI-DESIGN.md §6). `.record-sub` survives because the
   band still renders the identity line through it. */
.record-sub{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}
.record-sub .sep{opacity:.5}

/* Record cursor (UX study §4.3) — page the work list in place. */
.record-cursor{display:inline-flex;align-items:center;gap:0;
  border:1px solid var(--line-control);border-radius:var(--radius-sm);
  overflow:hidden;background:var(--surface-panel)}
.record-cursor .btn{border:none;border-radius:0;--btn-h:26px;
  border-right:1px solid var(--line)}
.record-cursor .btn:last-child{border-right:none}
.record-cursor .pos{padding:0 var(--space-2);font-size:var(--text-caption);
  color:var(--ink-muted);font-variant-numeric:tabular-nums;white-space:nowrap;
  border-right:1px solid var(--line)}

/* ============================================================================
   Data table — dense, sticky header, zebra-free
   ========================================================================== */
.table-wrap{overflow-x:auto;border:1px solid var(--line);
  border-radius:var(--radius-md);background:var(--surface-panel);
  box-shadow:var(--elev-1);margin-bottom:var(--space-4)}
.table-wrap--tall{max-height:60vh;overflow-y:auto}
/* `width:100%` fit the container first and overflowed only when min-content forced
   it, so `.truncate` cells ellipsised BEFORE the table scrolled — that is what put
   "PTP…" at the frame edge. `border-collapse` MUST stay `separate`: thead th is
   sticky, and under `collapse` its border is painted by the table and scrolls away. */
table.data{width:auto;min-width:100%;border-collapse:separate;border-spacing:0;
  background:transparent;font-size:var(--text-body);line-height:var(--leading-snug)}
table.data thead th{
  position:sticky;top:0;z-index:2;   /* relative to .table-wrap, not the page */
  background:var(--surface-sunken);
  padding:var(--table-pad-y) var(--table-pad-x);
  text-align:left;white-space:nowrap;
  font-size:var(--text-caption);font-weight:var(--weight-semi);
  color:var(--ink-muted);
  /* The head band must stay distinct now that every cell is bordered, and the
     gridline colour is too faint on --surface-sunken to read at all. */
  border-right:1px solid var(--table-rule-head);
  border-bottom:2px solid var(--table-rule-head);
  font-family:var(--font-ui);
}
table.data td{padding:var(--table-pad-y) var(--table-pad-x);height:var(--row-h);
  border-right:1px solid var(--table-rule);
  border-bottom:1px solid var(--table-rule);vertical-align:middle;
  font-family:var(--font-ui);font-size:var(--text-body)}
/* The outer edge is drawn by the scroll container, so the last cell in each
   direction drops its own rule rather than doubling against it. */
table.data td:last-child,table.data thead th:last-child{border-right:none}
table.data tbody tr:last-child td{border-bottom:none}
table.data tbody tr{transition:background var(--dur-1) var(--ease-out)}
table.data tbody tr:hover{background:var(--row-hover)}
table.data tbody tr[aria-selected="true"]{background:var(--row-selected)}
table.data .num,table.data .amount{text-align:right}
table.data .ident,table.data .mono{font-family:var(--font-mono);font-size:.95em;
  letter-spacing:-.01em;white-space:nowrap}
table.data .shrink{width:1%;white-space:nowrap}
table.data .truncate{max-width:280px;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* A cell that must be READ, not hovered. Every one of the 356 remark cells on the
   flagship debtor page was clipped at 280px with the full text only in a `title`,
   and the remark is what the debtor actually said on the last call. Bounds are in
   ch because readable line length is measured in characters, not pixels. */
table.data td.prose{white-space:normal;min-width:38ch;max-width:56ch;
  overflow:visible;text-overflow:clip;overflow-wrap:anywhere;
  line-height:var(--leading-snug)}
/* A name is never cut and never wrapped — it takes the width it needs. A truncated
   debtor, client or product name is one the collector cannot read out, and letting it
   WRAP is no better: a wrapping cell collapses to min-content under auto table layout,
   so "Grab Servicing Malaysia" landed in a 72px column over three lines and every
   Accounts row went from 36px to 70px. Names are short and bounded (the longest client
   in the book is 32 characters), so the honest answer is to give the column its width
   and let the table scroll — which it now does. */
table.data td.noclip{white-space:nowrap}
table.data td.select,table.data th.select{width:1%;padding-right:0}
table.data td.select input{margin:0;accent-color:var(--accent)}

/* Sortable header affordance — arrow always present, dimmed until active. */
table.data th.sortable{padding:0}
table.data th.sortable > a{display:flex;align-items:center;gap:var(--space-1);
  width:100%;padding:var(--space-15) var(--space-2);
  font-size:var(--text-caption);font-weight:var(--weight-semi);
  color:inherit;text-decoration:none}
table.data th.sortable:hover{background:var(--surface-inset)}
table.data th.sortable .icon{opacity:.3;transition:opacity var(--dur-1) var(--ease-out)}
table.data th.sortable:hover .icon{opacity:.7}
table.data th[aria-sort]{color:var(--ink)}
table.data th[aria-sort] .icon{opacity:1;color:var(--accent-ink)}
table.data th.num.sortable > a{justify-content:flex-end}

/* Row state — a left edge, so the amount column stays readable. */
table.data tr.row--overdue td:first-child{box-shadow:inset 2px 0 0 var(--bad-fg)}
table.data tr.row--due td:first-child{box-shadow:inset 2px 0 0 var(--warn-fg)}
table.data tr.row--good td:first-child{box-shadow:inset 2px 0 0 var(--good-fg)}
table.data tbody tr[aria-selected="true"] td:first-child{
  box-shadow:inset 2px 0 0 var(--accent)}
/* Was `opacity:.55` — audit m1. It composited --ink to 3.62:1 on white, dimmed the
   row's chips and borders too, and landed after the cascade where no token could
   reach it. An explicit ink fixes the contrast and leaves the chip at full strength:
   a closed account's status code is why it is closed. Light 6.33:1, dark 5.29:1. */
table.data tr.is-closed td{color:var(--ink-dim)}

/* ============================================================================
   Pills (derived state) vs chips (literal status CODE)
   Status codes differ per Volare instance and are NOT machine-classifiable,
   so a chip is neutral unless the caller passes an explicit bucket.
   ========================================================================== */
.pill{display:inline-flex;align-items:center;gap:var(--space-1);
  padding:1px var(--space-2);border-radius:var(--radius-pill);
  font-size:var(--text-caption);font-weight:var(--weight-semi);line-height:1.55;
  border:1px solid transparent;white-space:nowrap}
.pill .dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.pill--neutral,.pill.muted{background:var(--surface-sunken);color:var(--ink-muted);
  border-color:var(--line)}
.pill--good,.pill.good{background:var(--good-bg);color:var(--good-fg);
  border-color:var(--good-edge)}
.pill--warn,.pill.due{background:var(--warn-bg);color:var(--warn-fg);
  border-color:var(--warn-edge)}
.pill--bad,.pill.overdue{background:var(--bad-bg);color:var(--bad-fg);
  border-color:var(--bad-edge)}
.pill--info{background:var(--info-bg);color:var(--info-fg);border-color:var(--info-edge)}
/* BUG FIX: agent_bar.html:11 has always emitted `pill warn`, which was never
   defined — the "dialler not synced" warning rendered as unstyled bare text.
   Mapped to the AMBER set (it IS a warning), matching .pill--warn. */
.pill.warn{background:var(--warn-bg);color:var(--warn-fg);border-color:var(--warn-edge)}

.chip,.tag{display:inline-block;padding:0 var(--space-15);
  border-radius:var(--radius-xs);font-family:var(--font-mono);
  font-size:var(--text-caption);font-weight:var(--weight-semi);
  letter-spacing:.01em;white-space:nowrap;line-height:1.7;
  background:var(--surface-sunken);color:var(--ink);
  border:1px solid var(--line-strong)}
.chip--good{background:var(--good-bg);color:var(--good-fg);border-color:var(--good-edge)}
.chip--warn{background:var(--warn-bg);color:var(--warn-fg);border-color:var(--warn-edge)}
.chip--bad{background:var(--bad-bg);color:var(--bad-fg);border-color:var(--bad-edge)}
.chip--info{background:var(--info-bg);color:var(--info-fg);border-color:var(--info-edge)}

/* ============================================================================
   KPI tiles
   ========================================================================== */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(165px,1fr));
  gap:var(--space-2);margin-bottom:var(--space-4)}
.kpi{display:flex;flex-direction:column;gap:1px;background:var(--surface-panel);
  border:1px solid var(--line);border-radius:var(--radius-md);
  padding:var(--space-2) var(--space-3);box-shadow:var(--elev-1);
  transition:border-color var(--dur-1) var(--ease-out)}
a.kpi:hover{border-color:var(--line-control);text-decoration:none}
.kpi-l{font-size:var(--text-micro);font-weight:var(--weight-semi);
  letter-spacing:var(--tracking-label);text-transform:uppercase;
  color:var(--ink-muted);font-family:var(--font-ui)}
.kpi-v{font-size:var(--text-h3);font-weight:var(--weight-semi);
  letter-spacing:var(--tracking-tight);color:var(--ink);line-height:1.2;
  font-family:var(--font-ui)}
.kpi-sub{font-size:var(--text-caption);color:var(--ink-muted)}
/* Variants tint the value and a top edge — never the whole tile. */
.kpi.accent{box-shadow:var(--elev-1),inset 0 2px 0 var(--accent)}
.kpi.good{box-shadow:var(--elev-1),inset 0 2px 0 var(--good-fg)}
.kpi.good .kpi-v{color:var(--good-fg)}
.kpi.warn-k{box-shadow:var(--elev-1),inset 0 2px 0 var(--warn-fg)}
.kpi.warn-k .kpi-v{color:var(--warn-fg)}
.kpi.danger{box-shadow:var(--elev-1),inset 0 2px 0 var(--bad-fg)}
.kpi.danger .kpi-v{color:var(--bad-fg)}

/* ============================================================================
   Filter bar — a tool, so elevation level 0 (subordinate to results)
   ========================================================================== */
.filterbar,.filters{display:flex;align-items:flex-end;gap:var(--space-3);
  flex-wrap:wrap;padding:var(--space-3);margin-bottom:var(--space-3);
  background:var(--surface-sunken);border:1px solid var(--line);
  border-radius:var(--radius-md);box-shadow:var(--elev-0)}
.filters.card{background:var(--surface-sunken);box-shadow:var(--elev-0)}
.filterbar label,.filters label{flex:1 1 170px;min-width:140px;margin-bottom:0}
.filters .row{flex-wrap:wrap;width:100%}
.filters .checkbox{align-self:center;flex:0 0 auto}
.filter-actions{display:flex;gap:var(--space-2);margin-left:auto}
.filter-chips{display:flex;gap:var(--space-2);flex-wrap:wrap;
  margin-bottom:var(--space-3);align-items:center}
.filter-chip{display:inline-flex;align-items:center;gap:var(--space-1);
  padding:1px var(--space-2);border-radius:var(--radius-pill);
  background:var(--accent-quiet);color:var(--accent-ink);
  border:1px solid var(--accent-edge);font-size:var(--text-caption);
  font-weight:var(--weight-medium)}

.searchbar{display:flex;gap:var(--space-2);max-width:560px;align-items:stretch}
.searchbar input{flex:1;font-family:var(--font-mono);height:34px}
.searchbar .btn,.searchbar button{--btn-h:34px}

/* ============================================================================
   Key/value list
   ========================================================================== */
dl.kv{display:grid;grid-template-columns:minmax(96px,auto) 1fr;
  gap:var(--space-15) var(--space-3);margin:0}
dl.kv dt{color:var(--ink-muted);font-size:var(--text-caption)}
dl.kv dd{margin:0;color:var(--ink)}
dl.kv dd.ident{font-family:var(--font-mono)}

/* ============================================================================
   Timeline / activity log
   ========================================================================== */
.timeline{display:flex;flex-direction:column}
.logentry{position:relative;display:flex;flex-direction:column;gap:var(--space-1);
  padding:0 0 var(--space-4) var(--space-5);border-left:none}
.logentry::before{content:"";position:absolute;left:5px;top:14px;bottom:0;
  width:1px;background:var(--line)}
.logentry:last-child::before{display:none}
.logentry::after{content:"";position:absolute;left:0;top:4px;width:11px;height:11px;
  border-radius:50%;background:var(--surface-panel);border:2px solid var(--line-control)}
.logentry--good::after{border-color:var(--good-fg)}
.logentry--bad::after{border-color:var(--bad-fg)}
.logentry--call::after{border-color:var(--accent)}
.logmeta{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;
  font-size:var(--text-caption)}
.logmeta .channel{font-weight:var(--weight-semi);color:var(--ink);
  font-size:var(--text-body)}
.logmeta .small{font-family:var(--font-ui)}
.logmeta time{color:var(--ink-muted);font-variant-numeric:tabular-nums}
.logremark{white-space:pre-wrap;color:var(--ink);margin-top:var(--space-1);
  padding:var(--space-2);background:var(--surface-sunken);
  border-radius:var(--radius-sm);font-size:var(--text-body)}
.logptp{display:inline-flex;align-items:center;gap:var(--space-1);
  font-size:var(--text-caption);color:var(--good-fg);font-weight:var(--weight-medium)}

/* ============================================================================
   Alerts / empty / skeleton / toast / pagination / tabs
   ========================================================================== */
.alert{display:flex;align-items:flex-start;gap:var(--space-2);
  padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);
  border:1px solid transparent;font-size:var(--text-body);
  margin:0 0 var(--space-3)}
.alert .icon{margin-top:2px}
.alert.error{background:var(--bad-bg);color:var(--bad-fg);border-color:var(--bad-edge)}
.alert.warn{background:var(--warn-bg);color:var(--warn-fg);border-color:var(--warn-edge)}
.alert.good{background:var(--good-bg);color:var(--good-fg);border-color:var(--good-edge)}
.alert.info{background:var(--info-bg);color:var(--info-fg);border-color:var(--info-edge)}

.empty{display:flex;flex-direction:column;align-items:center;gap:var(--space-2);
  padding:var(--space-10) var(--space-6);text-align:center;color:var(--ink-muted)}
.empty .icon{width:22px;height:22px;color:var(--ink-faint)}
.empty-title{font-size:var(--text-body-lg);font-weight:var(--weight-semi);color:var(--ink)}
.empty-msg{max-width:46ch}
td.empty-cell{padding:0!important;height:auto}

.skeleton{background:linear-gradient(90deg,var(--surface-inset) 25%,
  color-mix(in srgb,var(--surface-inset) 55%,var(--surface-panel)) 37%,
  var(--surface-inset) 63%);background-size:400% 100%;
  border-radius:var(--radius-xs);animation:shimmer 1.4s ease-in-out infinite}
@keyframes shimmer{0%{background-position:100% 0}100%{background-position:0 0}}
@media (prefers-reduced-motion:reduce){.skeleton{animation:none;background:var(--surface-inset)}}
.skeleton-line{height:11px;margin:var(--space-15) 0}
.is-loading{opacity:.6;pointer-events:none;transition:opacity var(--dur-1) var(--ease-out)}

.toast-region{position:fixed;right:var(--space-4);bottom:var(--space-4);z-index:60;
  display:flex;flex-direction:column;gap:var(--space-2);pointer-events:none}
.toast{display:flex;align-items:flex-start;gap:var(--space-2);min-width:280px;
  max-width:400px;pointer-events:auto;padding:var(--space-3);
  border-radius:var(--radius-lg);background:var(--surface-overlay);
  border:1px solid var(--line-strong);box-shadow:var(--elev-2);
  font-size:var(--text-body);animation:toast-in var(--dur-2) var(--ease-out)}
@keyframes toast-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.toast--good{border-left:3px solid var(--good-fg)}
.toast--bad{border-left:3px solid var(--bad-fg)}
.toast-title{font-weight:var(--weight-semi)}
.toast-msg{color:var(--ink-muted);font-size:var(--text-caption)}

.pagination{display:flex;align-items:center;gap:var(--space-2);
  padding:var(--space-2) 0;font-size:var(--text-caption);color:var(--ink-muted)}
.pagination .range{font-variant-numeric:tabular-nums}

.tabs{display:flex;gap:var(--space-1);border-bottom:1px solid var(--line);
  margin-bottom:var(--space-3);overflow-x:auto}
.tab{padding:var(--space-2) var(--space-3);border:none;background:none;
  font:inherit;font-size:var(--text-body);font-weight:var(--weight-medium);
  color:var(--ink-muted);cursor:pointer;white-space:nowrap;
  border-bottom:2px solid transparent;margin-bottom:-1px;height:auto;
  transition:color var(--dur-1) var(--ease-out),border-color var(--dur-1) var(--ease-out)}
.tab:hover{color:var(--ink);background:none}
.tab[aria-selected="true"],.tab.is-active{color:var(--accent-ink);
  border-bottom-color:var(--accent)}

/* ============================================================================
   Dashboard bars + tile grid
   ========================================================================== */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));
  gap:var(--space-3)}
.grid.two{grid-template-columns:repeat(auto-fit,minmax(340px,1fr))}
.tile h3{margin:0 0 var(--space-1)}
.warn-tile{border-color:var(--warn-edge);background:var(--warn-bg)}
.warn-tile h3{color:var(--warn-fg)}
.bar-row{display:grid;grid-template-columns:minmax(120px,180px) 1fr auto;
  align-items:center;gap:var(--space-2);margin:var(--space-15) 0}
.bar-label{font-size:var(--text-caption);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.bar-track{background:var(--surface-inset);border-radius:var(--radius-xs);
  height:12px;overflow:hidden}
.bar-fill{background:var(--data-1);height:100%;border-radius:var(--radius-xs);
  transition:width var(--dur-3) var(--ease-out)}
.bar-fill.alt{background:var(--data-2)}
.bar-val{font-size:var(--text-caption);font-weight:var(--weight-semi);
  color:var(--ink);white-space:nowrap;font-variant-numeric:tabular-nums}

ul.codes{columns:2;font-size:var(--text-body-lg);font-family:var(--font-mono);
  padding-left:var(--space-5)}

/* ============================================================================
   Import — steps + dropzone
   ========================================================================== */
.dropzone{border:2px dashed var(--line-control);border-radius:var(--radius-md);
  padding:var(--space-8) var(--space-5);text-align:center;color:var(--ink-muted);
  background:var(--surface-sunken)}
.dropzone strong{color:var(--ink);font-weight:var(--weight-semi)}
.steps{display:flex;gap:0;list-style:none;padding:0;margin:0 0 var(--space-4);
  flex-wrap:wrap}
.steps li{flex:1;min-width:120px;font-size:var(--text-caption);
  padding:var(--space-2) var(--space-3);border-bottom:2px solid var(--line);
  color:var(--ink-muted);font-family:var(--font-ui)}
.steps li.active{border-bottom-color:var(--accent);color:var(--ink);
  font-weight:var(--weight-semi)}
.steps li.done{border-bottom-color:var(--good-fg);color:var(--ink)}

/* ============================================================================
   Agent presence — a titled group in the rail footer.
   Composition: a dim group label carrying the sync annotation, then the state
   as the one prominent line, then the controls behind a hairline. The three
   tiers are deliberately different sizes so the block reads as one component
   rather than four stacked rows.
   Class names, ids and data hooks are UNCHANGED (app.js swaps by container).
   ========================================================================== */
.agent-bar-wrap{padding:0}
.agent-bar{
  display:flex;flex-direction:column;align-items:stretch;gap:var(--space-15);
  padding:var(--space-15) var(--space-2) var(--space-2);
  border-radius:var(--radius-sm);
  background:rgba(255,255,255,.045);
  border:1px solid rgba(255,255,255,.08);
  font-size:var(--text-caption);
}

/* tier 1 — group label + sync annotation */
.agent-head{display:flex;align-items:center;gap:var(--space-15);min-height:14px}
.agent-label{font-size:var(--text-micro);font-weight:var(--weight-semi);
  letter-spacing:var(--tracking-label);text-transform:uppercase;
  color:var(--ink-on-rail-dim);white-space:nowrap}

/* tier 2 — the state, the one thing that must read at a glance */
.agent-line{display:flex;align-items:center;gap:var(--space-2);min-width:0}
.agent-dot{width:8px;height:8px;border-radius:50%;flex:none;
  background:var(--ink-on-rail-dim)}
.agent-state{font-family:var(--font-ui);font-size:var(--text-body);
  font-weight:var(--weight-semi);line-height:1.3;color:var(--ink-on-rail-strong);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}

/* tier 3 — controls, separated by a hairline so they read as actions */
.agent-actions{display:flex;flex-direction:column;align-items:stretch;
  gap:var(--space-15);margin-left:0;
  padding-top:var(--space-15);border-top:1px solid rgba(255,255,255,.08)}
.agent-pause-form{display:flex;flex-direction:column;align-items:stretch;
  gap:var(--space-15)}
.agent-bar form{width:100%}
.agent-bar select{height:26px;font-size:var(--text-caption);
  background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.18);
  color:var(--ink-on-rail-strong);padding:0 var(--space-5) 0 var(--space-15)}
.agent-bar select option{background:var(--surface-panel);color:var(--ink)}
.agent-bar .btn,.agent-bar button{width:100%;--btn-h:26px;
  font-size:var(--text-caption);
  background:rgba(255,255,255,.10);border-color:rgba(255,255,255,.18);
  color:var(--ink-on-rail-strong)}
.agent-bar .btn:hover,.agent-bar button:hover{background:rgba(255,255,255,.18)}
.agent-bar button.primary{background:var(--accent);border-color:var(--accent);
  color:var(--on-accent)}
.agent-bar button.primary:hover{background:var(--accent-hover)}

/* State colour MUST come from the rail-scoped tokens: the panel-tuned
   --good-fg measures 2.27:1 on this ground in light theme. */
.agent-active .agent-dot{background:var(--rail-good);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--rail-good) 24%,transparent)}
.agent-active .agent-state{color:var(--rail-good)}
.agent-paused .agent-dot{background:var(--rail-warn);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--rail-warn) 24%,transparent)}
.agent-paused .agent-state{color:var(--rail-warn)}
.agent-offline .agent-dot{background:var(--ink-on-rail-dim)}
.agent-offline .agent-state{color:var(--ink-on-rail)}

/* Pills on the rail ground. The sync warning is an annotation on the group
   label, not a full-width slab — it must never look like a button. */
.agent-bar .pill{font-size:var(--text-micro);padding:0 var(--space-15);
  line-height:1.5;margin-left:auto;flex:none;max-width:60%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.agent-bar .pill.warn{background:color-mix(in srgb,var(--rail-warn) 15%,transparent);
  color:var(--rail-warn);border-color:color-mix(in srgb,var(--rail-warn) 40%,transparent)}
.agent-bar .pill.overdue{background:color-mix(in srgb,var(--rail-bad) 15%,transparent);
  color:var(--rail-bad);border-color:color-mix(in srgb,var(--rail-bad) 40%,transparent)}

/* Collapsed rail (56px): the state dot is the whole component. */
:root[data-rail="collapsed"] .agent-bar{
  background:transparent;border-color:transparent;padding:var(--space-15) 0;
  align-items:center;gap:0}
:root[data-rail="collapsed"] .agent-bar .agent-head,
:root[data-rail="collapsed"] .agent-bar .agent-actions,
:root[data-rail="collapsed"] .agent-bar .agent-state{display:none}
:root[data-rail="collapsed"] .agent-bar .agent-line{justify-content:center}
:root[data-rail="collapsed"] .agent-bar .agent-dot{width:10px;height:10px}

/* ============================================================================
   Dialler — fixed call panel (.dialler / #dialler) and the transient inline
   status fragment (.dialler-bar). BOTH names are load-bearing in app.js:
   app.js:50 getElementById("dialler"); :103 querySelector(".dialler-bar");
   :117 constructs className "dialler-bar dialler-error". Preserved verbatim;
   `.callbar` was dropped at integration: Track A already owns that name
   for the per-agent calls-handled bar in reports. `.dialler` carries these rules.
   ========================================================================== */
main.has-dialler,.has-dialler{padding-bottom:78px}
.dialler{position:fixed;left:var(--rail-w);right:0;bottom:0;z-index:40;
  background:var(--surface-overlay);border-top:1px solid var(--line-strong);
  box-shadow:0 -6px 20px rgba(10,10,16,.14)}
/* A selector list cannot contain an at-rule; the whole construct was dropped, so the fixed
   dialler bar kept left:212px against a 56px collapsed rail — a 156px gap. Split in two. */
:root[data-rail="collapsed"] .dialler{left:var(--rail-w-collapsed)}
@media (max-width:900px){.dialler{left:var(--rail-w-collapsed)}}
.dialler-inner{
  max-width:none;                      /* was 1000px — see audit §1.4 */
  padding:var(--space-2) var(--content-gutter);
  display:flex;align-items:center;gap:var(--space-4);flex-wrap:wrap}
.dialler .num{font-family:var(--font-mono);font-size:var(--text-h4);
  font-weight:var(--weight-semi);color:var(--ink);letter-spacing:-.01em}
.dialler .dl-label{font-size:var(--text-micro);
  font-weight:var(--weight-semi);letter-spacing:var(--tracking-label);
  text-transform:uppercase;color:var(--ink-muted);font-family:var(--font-ui);
  display:block;margin-bottom:1px}
.dialler .timer{font-size:var(--text-h4);
  font-weight:var(--weight-semi);min-width:62px;color:var(--ink-muted)}
.dialler.is-live .timer{color:var(--good-fg)}
.dialler .spacer{flex:1}
.dialler .dl-status{display:inline-flex;align-items:center;
  gap:var(--space-15);font-size:var(--text-caption);color:var(--ink-muted);
  font-family:var(--font-ui)}
.dialler .dl-dot{width:8px;height:8px;border-radius:50%;
  background:var(--ink-faint)}
.dialler.is-live .dl-dot{background:var(--good-fg);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--good-fg) 26%,transparent)}
.dialler select{height:30px}
/* Disposition is the required exit — emphasised while a call is live. */
.dialler.is-live .dl-disposition select{border-color:var(--warn-edge);
  background:var(--warn-bg);color:var(--warn-fg);font-weight:var(--weight-semi)}

.dial-form{margin-left:var(--space-2);display:inline-block}
.dialler-bar{display:flex;align-items:center;gap:var(--space-2);
  padding:var(--space-2) var(--space-3);margin-bottom:var(--space-3);
  border:1px solid var(--line);border-radius:var(--radius-sm);
  font-size:var(--text-body)}
.dialler-dot{width:8px;height:8px;border-radius:50%;background:var(--ink-faint);flex:none}
.dialler-msg{font-weight:var(--weight-semi);font-family:var(--font-ui)}
.dialler-dismiss{margin-left:auto}
.dialler-dialling{background:var(--info-bg);color:var(--info-fg);
  border-color:var(--info-edge)}
.dialler-dialling .dialler-dot{background:var(--accent);animation:dialler-pulse 1s infinite}
.dialler-error{background:var(--bad-bg);color:var(--bad-fg);border-color:var(--bad-edge)}
.dialler-error .dialler-dot{background:var(--bad-fg)}
@keyframes dialler-pulse{0%,100%{opacity:1}50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){.dialler-dialling .dialler-dot{animation:none}}

/* Recordings */
audio{width:100%;max-width:420px;margin-top:var(--space-1)}

/* ============================================================================
   Print — a collector printing a job sheet should not print the chrome
   ========================================================================== */
@media print{
  .rail,.contextbar,.dialler,.toast-region,.page-header .ph-actions{display:none!important}
  .app-shell{padding-left:0}
  .section-body{display:block!important}
  body{background:#fff}
}

/* ============================================================================
   ===== track additions =====
   APPEND-ONLY BELOW THIS LINE. Other tracks (A/B/C/D/E) add their screen CSS
   here in their own labelled block, per docs/PARALLEL-WORK-PLAN.md §2.
   Do not edit the tokens or rules above; add a new block and use var(--…).
   ========================================================================== */

/* ============================================================================
   Masked PII + reveal (Track E — platform hardening)
   The masked value reads as data, the Reveal control as a deliberate action:
   revealing writes an audit row, so it should never look like idle chrome.
   ========================================================================== */
.pii{display:inline-flex;align-items:baseline;gap:6px;white-space:nowrap}
.pii-value{letter-spacing:.04em}
.pii-shown .pii-value{color:var(--ink);letter-spacing:normal}
.pii-reveal{border:1px solid var(--line);background:var(--surface-raised);color:var(--ink-muted);
  font:inherit;font-size:11px;line-height:1.6;padding:0 6px;border-radius:var(--radius-sm);
  cursor:pointer}
.pii-reveal:hover{color:var(--brand-strong);border-color:var(--brand)}
.pii-reveal:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.pii-reveal[disabled]{opacity:.5;cursor:progress}

/* ============================================================================
   Admin & configuration — scoped classes only, so no existing screen changes
   ========================================================================== */
.admin-set{border:1px solid var(--line);border-radius:var(--radius-md);margin:14px 0;padding:8px 14px 12px}
.admin-set legend{font-size:13px;font-weight:600;color:var(--ink-muted);padding:0 6px}
/* The product attribute editor puts bare inputs in table cells — give them the same
   affordance as inputs that sit inside a <label>. */
.fielddefs td{padding:6px 8px}
.fielddefs input,.fielddefs select{width:100%;padding:7px 9px;border:1px solid var(--line);
  border-radius:var(--radius-sm);font:inherit;font-weight:400;background:var(--surface-panel);color:var(--ink)}

/* ============================================================================
   Reports — subnav, wide numeric tables, per-agent call bars
   ========================================================================== */
.report-nav{display:flex;flex-wrap:wrap;gap:4px;margin:0 0 18px}
.report-nav a{font-size:13.5px;font-family:var(--font-mono);padding:5px 11px;border-radius:var(--radius-sm);
  color:var(--ink-muted);background:var(--surface-sunken);border:1px solid transparent}
.report-nav a:hover{color:var(--ink);text-decoration:none;border-color:var(--line)}
.report-nav a.is-active{background:var(--brand);color:var(--on-brand);font-weight:600}

.report-period{margin-bottom:8px}
.report-period .row{align-items:end}
.report-period-actions{display:flex;align-items:center;gap:10px;padding-bottom:2px}

.report-table td.num,.report-table th.num{text-align:right;white-space:nowrap}
.report-table td.strong{font-weight:700;color:var(--ink)}
.report-table tfoot td{background:var(--surface-raised);font-weight:700;border-top:2px solid var(--line)}
.report-table tr.is-absent td{background:var(--danger-bg)}
.report-table .flag{display:inline-block;margin:3px 4px 0 0;font-size:11px}

/* Inline "calls handled" bar under the agent name — the headline metric, at a glance. */
.callbar{height:4px;border-radius:3px;background:var(--surface-sunken);margin:4px 0 2px;max-width:180px}
.callbar span{display:block;height:100%;border-radius:3px;background:var(--data-1)}


/* ============================================================================
   Field Visit — priority pills (tokenised; field work is read at a glance)
   ========================================================================== */
.pill.fv-low{background:var(--surface-sunken);color:var(--ink-muted)}
.pill.fv-normal{background:var(--surface-sunken);color:var(--ink)}
.pill.fv-high{background:var(--warn-bg);color:var(--warn-fg)}
.pill.fv-urgent{background:var(--danger-bg);color:var(--danger-fg)}

/* ============================================================================
   Demand letters (Track F) — scoped classes only; nothing above is touched.
   The letter states carry semantic colour because "returned" is the one that
   must catch the eye: it means the address is dead.
   ========================================================================== */
.letter-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:18px}
.letter-actions .button{border:1px solid var(--line);background:var(--surface-panel);
  color:var(--ink);text-decoration:none;display:inline-block;font-weight:600;font-size:14px}
.letter-actions .button:hover{border-color:var(--brand);color:var(--brand-strong)}
.letter-actions .button.primary{border-color:var(--brand);color:var(--on-brand)}
.letter-actions .button.primary:hover{color:var(--on-brand)}

.pill.letter-draft{background:var(--surface-sunken);color:var(--ink-muted)}
.pill.letter-generated{background:var(--warn-bg);color:var(--warn-fg)}
.pill.letter-sent{background:var(--good-bg);color:var(--good-fg)}
.pill.letter-returned{background:var(--danger-bg);color:var(--danger-fg)}
.pill.letter-cancelled{background:var(--surface-sunken);color:var(--ink-muted);
  text-decoration:line-through}

/* Template editor: wording on the left, live preview on the right. */
.letter-edit{display:grid;grid-template-columns:1fr 1fr;gap:22px;align-items:start}
@media (max-width:900px){.letter-edit{grid-template-columns:1fr}}
.letter-body{font-family:var(--font-mono);font-size:13.5px;line-height:1.55}
.letter-preview h2{margin-top:0}
/* The preview is deliberately paper-like — a demand letter is judged on how it reads
   when it lands on a doormat, not on how it looks in an admin screen. */
.letter-paper{background:var(--surface-panel);border:1px solid var(--line);
  border-radius:var(--radius-md);padding:22px 24px;white-space:pre-wrap;
  font-family:Georgia,"Times New Roman",serif;font-size:14px;line-height:1.6;
  color:var(--ink);box-shadow:var(--shadow-card)}

.placeholders{margin:12px 0}
.placeholders summary{cursor:pointer;font-size:13px;font-weight:600;color:var(--ink-muted)}
table.data.compact th,table.data.compact td{padding:6px 10px;font-size:13px}
table.data.compact th{width:150px;background:transparent}
.letter-status-forms{display:grid;gap:6px}

/* =======================================================
   Commission — pending-accrual banner and payout share bar (tokenised)
   ========================================================================== */
.commission-pending{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.commission-share .bar-track{display:inline-block;width:120px;vertical-align:middle;margin-right:8px}

/* ============================================================================
   Payment reconciliation — match confidence
   Confidence is the whole product here, so it is colour-coded rather than left
   as plain text: an operator scanning 400 rows must see where the certainty
   drops without reading. Tokens only, so it survives dark mode.
   ========================================================================== */
.recon-exact,.recon-strong,.recon-weak,.recon-unmatched{
  display:inline-block;padding:1px 8px;border-radius:999px;font-size:12px;font-weight:600;
  border:1px solid transparent;white-space:nowrap}
.recon-exact{background:color-mix(in srgb,var(--good-fg) 14%,transparent);
  color:var(--good-fg);border-color:var(--good-fg)}
.recon-strong{background:color-mix(in srgb,var(--brand) 14%,transparent);
  color:var(--brand-strong);border-color:var(--brand)}
.recon-weak{background:var(--warn-bg);color:var(--warn-fg);border-color:var(--warn-line)}
.recon-unmatched{background:var(--danger-bg);color:var(--danger-fg);border-color:var(--danger-line)}

/* ============================================================================
   Settlements & instalment plans (Track J) — appended; tokens only, no existing
   rule touched. Plan health has to be readable at a glance on a worklist.
   ========================================================================== */
.settle-set{border:1px solid var(--line);border-radius:var(--radius-md);margin:14px 0;padding:8px 14px 12px}
.settle-set legend{font-size:13px;font-weight:600;color:var(--ink-muted);padding:0 6px}

.pill.plan-proposed{background:var(--surface-sunken);color:var(--ink-muted)}
.pill.plan-client-approved{background:var(--warn-bg);color:var(--warn-fg)}
.pill.plan-active{background:var(--good-bg);color:var(--good-fg)}
.pill.plan-completed{background:var(--good-bg);color:var(--good-fg)}
.pill.plan-broken{background:var(--danger-bg);color:var(--danger-fg)}
.pill.plan-cancelled{background:var(--surface-sunken);color:var(--ink-muted)}
.pill.plan-due{background:var(--surface-sunken);color:var(--ink)}
.pill.plan-partial{background:var(--warn-bg);color:var(--warn-fg)}
.pill.plan-paid{background:var(--good-bg);color:var(--good-fg)}
.pill.plan-missed{background:var(--danger-bg);color:var(--danger-fg)}

.plan-progress{display:inline-block;vertical-align:middle;width:120px;height:6px;border-radius:3px;
  background:var(--surface-sunken);margin-right:6px;overflow:hidden}
.plan-progress span{display:block;height:100%;border-radius:3px;background:var(--data-1)}

/* Broken arrangements on My Cases — a priority call-back, not a decoration. */
.plan-alert{border-color:var(--danger-line);background:var(--danger-bg)}
.plan-alert h2{color:var(--danger-fg);margin-top:0}

/* ============================================================================
   Bulk messaging — dry-run banner, rendered preview, confirmation block
   ========================================================================== */
.msg-dryrun{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 18px;padding:10px 14px;
  border:1px solid var(--warn-line);background:var(--warn-bg);color:var(--warn-fg);
  border-radius:var(--radius-md);font-size:14px}
.msg-dryrun-tag{font-family:var(--font-mono);font-weight:700;font-size:12px;letter-spacing:.06em;
  padding:2px 8px;border-radius:var(--radius-sm);background:var(--warn-fg);color:var(--surface-panel)}

/* The message as the debtor would see it — deliberately not styled like the rest of the app. */
.msg-preview{border:1px solid var(--line);border-left:3px solid var(--brand);
  border-radius:var(--radius-md);background:var(--surface-raised);padding:12px 14px;margin:10px 0}
.msg-subject{font-weight:700;margin-bottom:6px}
.msg-body{white-space:pre-wrap;font-family:var(--font-mono);font-size:13.5px;line-height:1.55}

.msg-actions{display:flex;align-items:end;padding-bottom:2px}
.msg-confirm{border-top:1px solid var(--line);padding-top:14px;margin-top:14px;
  display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.msg-confirm .checkbox{font-weight:400}
.msg-cancel{margin-top:10px}

/* ============================================================================
   Disputes & complaints (Track L) — scoped classes only; nothing above is touched.
   Case state and type carry semantic colour because "past SLA" and "on hold" are
   the two things a supervisor must never scroll past.
   ========================================================================== */
.pill.case-open{background:var(--warn-bg);color:var(--warn-fg)}
.pill.case-investigating{background:color-mix(in srgb,var(--brand) 14%,transparent);color:var(--brand-strong)}
.pill.case-awaiting_client{background:var(--surface-sunken);color:var(--ink-muted)}
.pill.case-resolved{background:var(--good-bg);color:var(--good-fg)}
.pill.case-rejected{background:var(--surface-sunken);color:var(--ink-muted);text-decoration:line-through}
.pill.case-held{background:var(--danger-bg);color:var(--danger-fg)}

.pill.case-type-dispute,.pill.case-type-complaint{background:var(--surface-sunken);color:var(--ink)}
.pill.case-type-bankruptcy,.pill.case-type-deceased,
.pill.case-type-legal_referral{background:var(--danger-bg);color:var(--danger-fg)}
.pill.case-type-akpk{background:var(--warn-bg);color:var(--warn-fg)}

/* The debtor's own words, and our answer to them — read as prose, not as data. */
.case-narrative{white-space:pre-wrap;margin:6px 0 14px;max-width:70ch}
.case-accounts{border:1px solid var(--line);border-radius:var(--radius-md);padding:12px 14px;margin:14px 0}
.case-accounts legend{font-size:13px;font-weight:600;color:var(--ink-muted);padding:0 6px}
.case-accounts .checkbox{display:flex;margin:4px 0}

/* ============================================================================
   Internal comms (Track N) — inbox tabs, announcements, the acknowledgement
   interstitial and the queue's reminder strip. Scoped classes only; the rail
   badge reuses the existing .rail-link .badge rule rather than adding one.
   ========================================================================== */

/* Sub-navigation shared by the inbox screens. */
.seg{display:flex;gap:var(--space-1);flex-wrap:wrap}
.seg--tabs{margin-bottom:var(--space-4)}
.seg-item{display:inline-flex;align-items:center;gap:var(--space-15);
  padding:var(--space-15) var(--space-3);border-radius:var(--radius-sm);
  font-size:var(--text-caption);font-weight:var(--weight-medium);
  color:var(--ink-muted);background:var(--surface-sunken)}
.seg-item:hover{color:var(--ink);text-decoration:none}
.seg-item.is-on{background:var(--accent);color:var(--on-accent)}
.seg-item .count{font-variant-numeric:tabular-nums;font-size:var(--text-micro);
  font-weight:var(--weight-semi);padding:0 6px;border-radius:var(--radius-pill);
  background:var(--accent);color:var(--on-accent)}
.seg-item.is-on .count{background:var(--surface-panel);color:var(--ink)}

.card-head{display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--space-3);flex-wrap:wrap}
.card-head h2{margin:0}
.form-actions{display:flex;align-items:center;gap:var(--space-3);margin-top:var(--space-4)}

/* An unread row is heavier, not coloured: colour on this table is reserved for state. */
tr.is-unread td{font-weight:var(--weight-semi)}
tr.is-overdue td{background:color-mix(in srgb,var(--bad-fg) 8%,transparent)}

/* Announcements. `--must` is the one that has to catch the eye. */
.ann{padding:var(--space-3) 0;border-bottom:1px solid var(--line)}
.ann:last-child{border-bottom:0}
.ann-head{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;
  margin-bottom:var(--space-15)}
.ann-body,.msg-body{white-space:pre-wrap;line-height:1.6}
.ann--must{border-left:3px solid var(--bad-fg);padding-left:var(--space-3)}

/* The acknowledgement interstitial: deliberately has no dismiss affordance. */
.ann-block{position:fixed;inset:0;z-index:100;display:flex;align-items:center;
  justify-content:center;padding:var(--space-4);
  background:color-mix(in srgb,var(--n-900) 62%,transparent)}
.ann-block-panel{background:var(--surface-overlay);border:1px solid var(--line-strong);
  border-radius:var(--radius-lg);box-shadow:var(--elev-2);
  padding:var(--space-6);max-width:560px;width:100%;
  display:flex;flex-direction:column;gap:var(--space-3)}
.ann-block-panel h2{margin:0;font-size:var(--text-h2)}

/* Reminder strip on My Cases — same shape as the broken-arrangement alert above it. */
.reminder-alert{border-left:3px solid var(--warn-fg)}
/* ==========================================================================
   Assignment — load tables and split previews (scoped classes only)
   Load is read by scanning a column, so the numbers are right-aligned and the
   one that matters (accounts) is the only one weighted.
   ========================================================================== */
.assign-load td.num,.assign-load th.num,.assign-plan td.num,.assign-plan th.num{
  text-align:right;white-space:nowrap}
.assign-load td.strong,.assign-plan td.strong{font-weight:700;color:var(--ink)}
.assign-plan tfoot td{background:var(--surface-raised);font-weight:700;border-top:2px solid var(--line)}
/* Over their cap: the row a supervisor is looking for. */
.assign-load tr.is-over td{background:var(--danger-bg)}

.assign-team{display:flex;flex-wrap:wrap;gap:4px 18px}
.assign-team legend{font-size:13px;font-weight:600;color:var(--ink-muted);padding:0 6px}
.assign-team .checkbox{margin-right:0}
.assign-commit{border-top:1px solid var(--line);margin-top:16px;padding-top:14px}
.cap-edit summary{cursor:pointer;font-size:12px;color:var(--ink-muted)}
.cap-edit .row{flex-wrap:wrap;align-items:end;gap:10px;margin-top:8px}

.assign-actions{margin-bottom:var(--space-4)}

/* ============================================================================
   Debtor conversations (Track 3) — inbound thread, agent inbox, rail badge.
   Scoped classes only; nothing above is touched. Inbound and outbound turns are
   distinguished by side and edge colour, not by a coloured bubble: the thread is
   read as a record, and semantic colour on this screen is reserved for state.
   ========================================================================== */
.rail-badge{margin-left:auto;font-size:var(--text-micro);font-weight:var(--weight-semi);
  padding:0 6px;border-radius:var(--radius-pill);background:var(--rail-bad);color:#1A0906;
  font-variant-numeric:tabular-nums}
:root[data-rail="collapsed"] .rail-badge{display:none}

.conv-thread{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;
  gap:var(--space-3)}
.conv-turn{border:1px solid var(--line);border-radius:var(--radius-md);
  padding:var(--space-3);background:var(--surface-panel);max-width:min(72ch,92%)}
.conv-turn--out{align-self:flex-start;border-left:3px solid var(--accent)}
/* The debtor's own words sit on the other side, so a thread is scannable for "did they answer". */
.conv-turn--in{align-self:flex-end;border-right:3px solid var(--good-fg);
  background:var(--surface-raised)}
.conv-meta{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;
  margin-bottom:var(--space-15)}
.conv-text{white-space:pre-wrap;line-height:1.6}

.conv-waiting{border-left:3px solid var(--warn-fg)}
.conv-unmatched{border-left:3px solid var(--bad-fg)}
.conv-link{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}
.conv-link input[name="party_id"]{width:8ch}
.conv-link .checkbox{font-weight:var(--weight-normal);margin:0}
/* ==========================================================================
   Outreach (Track 2) — the debtor contact panel and the multi-channel composer

   The panel's job is to make an unavailable action legible rather than absent: a
   disabled button with a reason beats a missing one, because a missing button reads
   as "we have no number for them".
   ========================================================================== */
.outreach-points td{vertical-align:top}
.outreach-actions{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.outreach-actions .btn[disabled]{opacity:.45;cursor:not-allowed}
/* The blocked reason sits under the buttons, not in a tooltip alone — a collector
   deciding what to do next should not have to hover to find out why they cannot. */
.outreach-why{margin-top:4px;max-width:38ch;line-height:1.35}
/* A point we have proved dead stays visible (it is evidence) but must not compete
   with a live one for attention. */
/* Same fix as tr.is-closed — de-emphasis is an ink, never opacity. */
.outreach-points tr.row-dead td{color:var(--ink-dim)}
/* Namespaced deliberately: `.msg-preview` is Track I's and already styled above, and a
   same-specificity redeclaration here would silently restyle their compose screen through
   the cascade — an edit to an existing rule that a reviewer diffing this file cannot see. */
.outreach-preview{white-space:pre-wrap;margin:0;padding:12px 14px;border-radius:var(--radius-md);
  background:var(--surface-sunken);border:1px solid var(--line);font:inherit;
  font-family:var(--font-mono);font-size:var(--text-caption)}
/* ==========================================================================
   Email deliverability (Track 5) — scoped classes only; nothing above is touched.
   Bounced, complained and over-ceiling carry danger colour deliberately: those
   are the states that get a sending domain blocked, so they must not read like
   ordinary rows.
   ========================================================================== */
.pill.email-queued{background:var(--surface-sunken);color:var(--ink-muted)}
.pill.email-sent{background:color-mix(in srgb,var(--brand) 14%,transparent);color:var(--brand-strong)}
.pill.email-delivered{background:var(--good-bg);color:var(--good-fg)}
.pill.email-bounced{background:var(--danger-bg);color:var(--danger-fg)}
.pill.email-complained{background:var(--danger-bg);color:var(--danger-fg);font-weight:700}
.pill.email-failed{background:var(--warn-bg);color:var(--warn-fg)}
.pill.email-suppressed{background:var(--surface-sunken);color:var(--ink-muted);text-decoration:line-through}

.pill.email-event-delivered{background:var(--good-bg);color:var(--good-fg)}
.pill.email-event-bounce{background:var(--danger-bg);color:var(--danger-fg)}
.pill.email-event-complaint{background:var(--danger-bg);color:var(--danger-fg)}
.pill.email-event-reject{background:var(--warn-bg);color:var(--warn-fg)}
.pill.email-event-open,.pill.email-event-click{background:var(--surface-sunken);color:var(--ink-muted)}

.email-over-ceiling{color:var(--danger-fg);font-weight:700}
/* The rendered bodies are shown verbatim — what we actually sent, not a reflow of it. */
.email-body{white-space:pre-wrap;word-break:break-word;background:var(--surface-sunken);
  border:1px solid var(--line);border-radius:var(--radius-md);padding:12px 14px;
  font-family:var(--font-mono);font-size:12.5px;line-height:1.5;max-height:320px;overflow:auto}

/* ============================================================================
   UI REFRESH — read-aloud emphasis. UI-ARCHITECTURE §2 (names) · UI-DESIGN §2.3.

   The test: does the collector SAY this value out loud on the call?
     .say-1  yes, and it leads the region  (one per region, never in tbody)
     .say-2  yes — who and which           (name; .ident for account / reference)
     .say-3  yes — spoken supporting facts (PTP, next call, client, product, IC)
     (none)  no  — read silently           ← the default, and most cells
     .say-mute  no — provenance: when / who / which record

   One vocabulary, two scopes: these are the BAND sizes, and the table scope below
   cuts them to row size. A 34px cell in a 32px row is nobody's intent.

   Emphasis is NEVER opacity/filter/visibility/font-size:0 — the mechanisms QA F5
   verified are not used for PII masking. Leaving them unused for decoration keeps
   that guarantee verifiable by grepping this file.
   ========================================================================== */
.say-label{display:block;margin-bottom:2px;
  font-size:var(--text-micro);font-weight:var(--weight-semi);
  letter-spacing:var(--tracking-label);text-transform:uppercase;
  color:var(--ink-muted);line-height:var(--leading-snug)}
.say-1{font-size:var(--say-1-size);font-weight:var(--say-1-weight);
  color:var(--say-ink);line-height:1.05;letter-spacing:-.02em;white-space:nowrap;
  font-variant-numeric:tabular-nums lining-nums}
.say-2{font-size:var(--say-2-size);font-weight:var(--say-2-weight);
  color:var(--say-ink);line-height:1.15;letter-spacing:-.015em;text-wrap:balance}
.say-3{font-size:var(--say-3-size);font-weight:var(--say-3-weight);
  color:var(--say-ink);line-height:1.3;
  font-variant-numeric:tabular-nums lining-nums}
.say-mute{font-size:var(--say-mute-size);font-weight:var(--weight-normal);
  color:var(--say-mute-ink)}
/* An identifier is read out character by character: mono face and a slashed zero at
   every level, never the tightened tracking. */
.say-1.ident,.say-2.ident,.say-3.ident,.say-1.mono,.say-2.mono,.say-3.mono{
  font-family:var(--font-mono);letter-spacing:0;
  font-variant-numeric:tabular-nums lining-nums slashed-zero}
.say-2.ident{font-size:var(--say-2-ident-size);font-weight:var(--weight-semi)}

/* Table scope. A 32px row cannot carry three display sizes, so every spoken level
   collapses to one step above body and the row height is unchanged. `.say-1` has no
   table rule on purpose — §2.4 N1 forbids it inside <tbody>. */
table.data td.say-2,table.data td.say-3,
table.data td .say-2,table.data td .say-3{
  font-size:var(--say-cell-size);font-weight:var(--say-cell-weight);
  line-height:var(--leading-snug);letter-spacing:normal}
table.data td.say-2.ident,table.data td .say-2.ident{
  font-size:var(--say-cell-size)}
table.data td.say-mute,table.data td .say-mute{line-height:var(--leading-snug)}

/* Emphasis says "this is spoken". State says "this is urgent". They are set
   independently and compose; state wins on the ink. */
.say-1.is-bad,.say-2.is-bad,.say-3.is-bad{color:var(--bad-fg)}
.say-1.is-good,.say-2.is-good,.say-3.is-good{color:var(--good-fg)}
.say-1.is-warn,.say-2.is-warn,.say-3.is-warn{color:var(--warn-fg)}

/* Visual size is a class; the heading level is the document outline. These exist
   so nobody ever demotes an h2 to an h3 again just to get a smaller heading. */
.h-lg{font-size:var(--text-h2)}
.h-md{font-size:var(--text-h4)}
.h-sm{font-size:var(--text-body)}

/* ============================================================================
   UI REFRESH — the table system. UI-ARCHITECTURE §3 · UI-DESIGN §4.1.
   Borders are on `table.data` above; this is the surrounding machinery — a
   scroll container for the 78 tables that have none, a visible cue that there
   is more to the right, an opt-in frozen column. Zero templates edited.
   ========================================================================== */

/* Alignment is a property of the COLUMN, not of the table's class. Un-scoping it
   covers the five class-less campaigns tables and any future forgotten class. */
td.num,th.num,td.amount,th.amount,td.money,th.money{
  text-align:right;white-space:nowrap}
/* An account number read aloud digit by digit is unreadable right-aligned. */
td.ident,th.ident{text-align:left}

/* The scroll container. Only `.table-wrap`, because it is the only box whose sole
   child is the table. A `.card` also holds the section's <h2> and description, and a
   scroll container scrolls ALL of its content — screenshotted at 2000px, scrolling to
   reach a column took the heading and the row-identifying first column off-screen.
   That is why all 94 tables have a real wrapper in the markup instead.
   Also the cue that there IS more to the right: overlay scrollbars draw nothing
   until the pointer is inside and moving, so a collector on a call gets no signal
   the PTP columns exist. The shadow shows at an edge only while content extends
   past it — `local` layers scroll with the content, `scroll` layers stay pinned. */
.table-wrap{
  overflow-x:auto;
  max-width:100%;            /* the PAGE never scrolls sideways; the table does */
  scrollbar-width:thin;
  scrollbar-color:var(--line-control) transparent;
  background-image:
    linear-gradient(to right,var(--surface-panel) 40%,transparent),
    linear-gradient(to left, var(--surface-panel) 40%,transparent),
    linear-gradient(to right,var(--scroll-cue),transparent 14px),
    linear-gradient(to left, var(--scroll-cue),transparent 14px);
  background-position:0 0,100% 0,0 0,100% 0;
  background-repeat:no-repeat;
  background-size:36px 100%,36px 100%,16px 100%,16px 100%;
  background-attachment:local,local,scroll,scroll}
.table-wrap::-webkit-scrollbar{height:10px}
.table-wrap::-webkit-scrollbar-thumb{
  background:var(--line-control);border-radius:var(--radius-pill);
  border:2px solid var(--surface-panel)}

/* Freeze the identifying column so a scrolled row stays attributable. Opt-in — it
   costs a paint layer. `background:inherit` on the cell plus a background on the row
   is what carries hover and selection through instead of a transparent hole. */
.data--stick1 tbody tr{background:var(--surface-panel)}
.data--stick1 tbody tr:hover{background:var(--row-hover)}
.data--stick1 tbody tr[aria-selected="true"]{background:var(--row-selected)}
.data--stick1 th:first-child,.data--stick1 td:first-child{
  position:sticky;left:0;z-index:1;background:inherit;
  border-right:1px solid var(--table-rule-head)}
.data--stick1 thead th:first-child{z-index:3;background:var(--surface-sunken)}

/* Totals sit on a raised surface, where the body gridline goes materially fainter
   in dark — hence the second rule token. */
table.data tfoot td{background:var(--surface-raised);
  border-right:1px solid var(--table-rule-raised);
  border-top:2px solid var(--table-rule-head);
  font-weight:var(--weight-bold)}
table.data tfoot td:last-child{border-right:none}

/* Density modifiers, opt-in. The default is unchanged: the UX audit names table
   density as one of the twelve things already right, so no row moves here. */
table.data.data--dense{--table-pad-y:var(--space-1);--table-pad-x:var(--space-15)}
table.data.data--roomy{--table-pad-y:var(--space-2);--table-pad-x:var(--space-3)}

/* ============================================================================
   UI REFRESH — the read-aloud band. UI-DESIGN §4.6. Replaces `.record-header`.
   A sticky strip carrying, in order, what a collector says out loud: name,
   outstanding, the promise, the next action. Sticky because the log form sits
   ~5,000px below the fold and the figure under discussion must not scroll away.
   No accent bar, no tint, no gradient — surface and one rule, with the hierarchy
   in size and weight alone, which is why it needs no per-theme retune.
   ========================================================================== */
.sayband{
  position:sticky;top:var(--stack-top);z-index:15;
  display:flex;align-items:flex-start;gap:var(--space-3) var(--space-6);flex-wrap:wrap;
  padding:var(--space-5) var(--content-gutter);
  background:var(--surface-raised);
  border-bottom:1px solid var(--line-strong);
  box-shadow:var(--elev-1)}
.sayband-id{flex:1 1 320px;min-width:280px}   /* name + identity, never squeezed */
.sayband-primary{flex:0 0 auto}               /* the one .say-1 amount */
.sayband-facts{display:flex;gap:var(--space-6);flex-wrap:wrap}
.sayband-actions{display:flex;gap:var(--space-2);flex-wrap:wrap;margin-left:auto}
@media (max-width:1100px){
  .sayband{gap:var(--space-4)}
  .sayband-id{flex-basis:100%}}
@media print{.sayband{position:static;box-shadow:none}}
