/* One hand-written stylesheet. No build step, no framework, no compile.
   Light and dark both supported via prefers-color-scheme. */

/* ONYX / "THE CLOCK" — the same palette, the same two faces and the same depth model as the
   public site (~/repo/casework-portal, funnel.mjs THEME). All three surfaces — the marketing
   page, app.mcpdisputeos.com and my.mcpdisputeos.com — are one firm, and a client who walks
   login -> apply -> portal must never wonder whether they changed companies.

   Royal purple is the house colour and the only one with a job: it marks what is PRESSABLE,
   what is ACTIVE or CURRENT, and the day the clock is on. Never decoration, never a gradient.
   Semantic colour (oxblood / amber / green) is small and separate, so "this is wrong" never
   competes with "this is a button".

   Depth comes from a light source: a layered shadow BELOW plus a 1px highlight along the TOP
   edge of anything raised. Five elevations, and there is no sixth.

   The organising figure of THIS surface is the DAY. A date, a day count, a round number and a
   count of clients are the same kind of thing and are set the same way — Newsreader, tabular,
   tracked. Everything that gets read is Public Sans. The full argument: design/DESIGN-PLAN.md. */

/* Served from public/fonts by the /static mount. groundwork sends no CSP, so no header changed.
   The same three files the marketing server serves, so the two sites cannot drift on type. */
@font-face { font-family: "Newsreader"; font-style: normal; font-weight: 200 800;
  font-display: swap; src: url(/static/fonts/Newsreader.woff2) format("woff2"); }
@font-face { font-family: "Newsreader"; font-style: italic; font-weight: 200 800;
  font-display: swap; src: url(/static/fonts/Newsreader-italic.woff2) format("woff2"); }
@font-face { font-family: "Public Sans"; font-style: normal; font-weight: 100 900;
  font-display: swap; src: url(/static/fonts/PublicSans.woff2) format("woff2"); }

:root {
  color-scheme: light;
  --bg: #f0eef4;
  --panel: #ffffff;
  --panel-2: #f4f3f7;
  --ink: #14121a;        /* 15.9:1 on white */
  --ink-soft: #4f4a61;   /*  8.4:1 */
  /* WAS #8e879f, which is 3.1:1 and was the source of eleven serious contrast failures in the
     client roster. A subtitle that says WHY a client is in a segment is not decoration. */
  --ink-faint: #67617a;  /*  5.5:1 */
  --line: #e0dde7;
  --line-2: #c6c2d2;

  /* 7.1:1 on white. The old #7c3aed was 5.1:1 and carried white button text at 4.2:1. */
  --accent: #6d28d9;
  --accent-2: #a78bfa;     /* the same job on the black ground — 8.0:1 on onyx */
  --accent-deep: #4c1d95;
  --accent-ink: #ffffff;
  --accent-soft: #efe9fd;
  --accent-line: #ded2ff;
  /* Ink for violet ON the violet wash. It is a token because the pair inverts between themes:
     the deep violet that reads at 11:1 on the light wash is 4.0:1 on the dark one, which is
     exactly how the active roster segment failed in dark mode. */
  --accent-on-soft: #4c1d95;

  --onyx: #0d0b12;
  --onyx-2: #171320;
  --onyx-3: #221d2f;
  /* three inks on the black, matching the three on the white */
  --inv: #ffffff;
  --inv-soft: #c4bed0;
  --inv-faint: #948da5;
  --rule-inv: rgba(255, 255, 255, .14);

  --warn-bg: #fdf3e0;
  --warn-line: #e8c37a;
  --warn-ink: #7a4a00;
  --err-bg: #fbeef1;
  --err-line: #eec3ce;
  --err-ink: #9a2f4e;
  --ok-bg: #e6f3ec;
  --ok-line: #bcdcc9;
  --ok-ink: #12724a;

  /* depth — layered, hue-matched to the paper, vertical offset >= 2x horizontal, never black */
  --edge: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --edge-dark: inset 0 1px 0 rgba(255, 255, 255, 0.16);
  --lift-1: 0 1px 2px rgba(24,18,48,.06);
  --lift-2: 0 2px 5px rgba(24,18,48,.07), 0 10px 24px rgba(24,18,48,.09);
  --lift-3: 0 3px 7px rgba(24,18,48,.07), 0 12px 26px rgba(24,18,48,.10), 0 36px 66px rgba(24,18,48,.12);
  --lift-dark: 0 4px 8px rgba(8,4,22,.30), 0 18px 40px rgba(8,4,22,.38);
  --press: 0 2px 8px rgba(109,40,217,.40);

  --sans: "Public Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --serif: "Newsreader", "Iowan Old Style", "Palatino Linotype", Palatino, Charter, Georgia, ui-serif, serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --radius: 9px;
  --radius-sm: 6px;
  --radius-lg: 13px;
  --ease: cubic-bezier(.16, 1, .3, 1);
}

/* THE DARK THEME EXISTED AND NOTHING COULD REACH IT.
   It was written ONLY inside this media query, on a bare `:root`. That means the OS was the sole
   input: `data-theme="dark"` did nothing on a light desktop, and `data-theme="light"` did nothing
   on a dark one. A toggle could set the attribute all day and the page would not move — which is
   exactly what happened the first time one was added.

   Three blocks are needed, not one. The media query is now guarded so an explicit LIGHT choice
   beats a dark OS, and the tokens are repeated under [data-theme="dark"] so an explicit DARK
   choice beats a light OS. The bare :root at the top of this file is the light palette and stays
   the default when nothing is stamped. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #08070b;
    --panel: #131019;
    --panel-2: #1a1622;
    --ink: #f0edf6;
    --ink-soft: #b3abc8;
    --ink-faint: #8d85a4;
    --line: #241f30;
    --line-2: #332c44;

    --accent: #a78bfa;
    --accent-2: #c4b5fd;
    --accent-deep: #8b5cf6;
    --accent-ink: #14101f;
    --accent-soft: #241a3f;
    --accent-line: #3d3163;
    --accent-on-soft: #c4b5fd;

    --warn-bg: #2a2113; --warn-line: #6b5220; --warn-ink: #e0b155;
    --err-bg: #2c1614;  --err-line: #6b2b2b;  --err-ink: #f4877e;
    --ok-bg: #12271c;   --ok-line: #2c5a41;   --ok-ink: #6fca97;

    --edge: var(--edge-dark);
    --lift-1: 0 1px 1px rgba(0,0,0,.5);
    --lift-2: 0 2px 6px rgba(0,0,0,.5), 0 12px 30px rgba(0,0,0,.45);
    --lift-3: 0 6px 14px rgba(0,0,0,.55), 0 30px 70px rgba(0,0,0,.6);
    --lift-dark: var(--lift-2);
    --press: 0 2px 8px rgba(167,139,250,.34);
  }
}

/* The same tokens again, for an explicit choice. Kept adjacent so the two cannot drift. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #08070b;
  --panel: #131019;
  --panel-2: #1a1622;
  --ink: #f0edf6;
  --ink-soft: #b3abc8;
  --ink-faint: #8d85a4;
  --line: #241f30;
  --line-2: #332c44;

  --accent: #a78bfa;
  --accent-2: #c4b5fd;
  --accent-deep: #8b5cf6;
  --accent-ink: #14101f;
  --accent-soft: #241a3f;
  --accent-line: #3d3163;
  --accent-on-soft: #c4b5fd;

  --warn-bg: #2a2113; --warn-line: #6b5220; --warn-ink: #e0b155;
  --err-bg: #2c1614;  --err-line: #6b2b2b;  --err-ink: #f4877e;
  --ok-bg: #12271c;   --ok-line: #2c5a41;   --ok-ink: #6fca97;

  --edge: var(--edge-dark);
  --lift-1: 0 1px 1px rgba(0,0,0,.5);
  --lift-2: 0 2px 6px rgba(0,0,0,.5), 0 12px 30px rgba(0,0,0,.45);
  --lift-3: 0 6px 14px rgba(0,0,0,.55), 0 30px 70px rgba(0,0,0,.6);
  --lift-dark: var(--lift-2);
  --press: 0 2px 8px rgba(167,139,250,.34);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  /* The lavender radial wash that used to sit under every page was the one thing on this app
     that read as a consumer fintech product rather than a firm's system. One ground, no bloom. */
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* The workspace is a raised white sheet on the tinted ground, not the ground itself. */
main {
  max-width: 68rem; margin: 1.5rem auto 4rem; padding: 1.9rem 1.9rem 3rem;
  background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--lift-2), var(--edge);
}
main.narrow { max-width: 27rem; padding: 1.75rem; }
@media (max-width: 46rem) {
  main { margin: 0; border-radius: 0; border-left: 0; border-right: 0; padding: 1.4rem 1.1rem 3rem; }
}

/* NAMES IN THE SERIF, PROSE IN THE SANS — the split this app has always had, now drawn in the
   two faces the public site uses. An h1 here is almost always a NAME (a client, a screen), so
   it is Newsreader; a section heading is a label on a block of work, so it is Public Sans. */
h1 {
  font-family: var(--serif); font-size: 1.8rem; font-weight: 400;
  letter-spacing: -0.02em; line-height: 1.14; margin: 0 0 1rem; text-wrap: balance;
}
h2 {
  font-family: var(--sans); font-size: 1.02rem; font-weight: 700;
  letter-spacing: -0.02em; line-height: 1.3; margin: 1.9rem 0 .6rem;
}
h3 { font-size: 0.95rem; font-weight: 700; letter-spacing: -0.012em; margin: 1.4rem 0 .4rem; }

/* THE FIGURE. A date, a day count, a round, a count of clients — one kind of thing, set one way.
   This is the console's tie to the public site, where the statutory day count is the recurring
   numeral, and it is why a deadline no longer reads as body text. */
.fig {
  font-family: var(--serif); font-weight: 400; letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
  color: var(--ink);
}
.fig-lg { font-size: 1.3rem; font-weight: 300; line-height: 1.1; }
/* The day the clock is on. Violet is doing its third job here: current. */
.fig-now { color: var(--accent); }

/* One caption style, used by table headers and tile labels and nowhere else. */
.cap {
  font-size: 0.69rem; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink-faint);
}

a { color: var(--accent); }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, summary:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.muted { color: var(--ink-soft); }
small { color: var(--ink-soft); display: block; font-size: 0.875rem; margin-top: 0.15rem; }

/* Nothing on this app needs to move for a person who has asked it not to. There was no such
   rule at all before, on a page with a dozen transitions. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
}

/* THE MARK — a sheet with two ruled lines on it, the same glyph the public site's nav carries.
   It is the one piece of drawing in the identity, and it is drawn in CSS so it costs no request
   and inherits the ground it sits on. */
.mk {
  /* --inv, not --panel: this glyph only ever sits on the onyx (the rail, a door band), and in
     dark mode --panel is near-black, which made the mark vanish into the rail it sits on. */
  width: 22px; height: 22px; border-radius: 5px; background: var(--inv);
  flex: none; position: relative;
}
.mk::before, .mk::after {
  content: ""; position: absolute; left: 5px; right: 5px; height: 2px; border-radius: 1px;
  background: var(--onyx);
}
.mk::before { top: 6px; }
.mk::after { top: 11px; opacity: .45; }

/* ---- THE DOOR ---------------------------------------------------------------------------
   Every page a stranger or a locked-out operator sees — sign-in, register, reset, two-factor,
   the public application, and all four client-portal doors — opens the way the public site
   opens: a black band handing over to white at a HARD edge, with the mark and the page's own
   headline on the black.

   The reason is not decoration. A client is handed a domain, types it, and lands here straight
   off a black marketing page; what they used to find was an unbranded white card floating on a
   lavender wash, with nothing on it saying whose software this is. Continuity IS the design
   requirement. No copy was written for this band: it carries the <h1> and the lede the page
   already had. */
.doorpage { --door-w: 30rem; background: var(--panel); }
.doorpage-wide { --door-w: 42rem; }
.door {
  position: relative; overflow: clip;
  background: var(--onyx); color: var(--inv);
}
/* ONE light source, top left, exactly as on the public site's band. */
.door::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(820px 520px at 16% -26%, rgba(124,58,237,.26), transparent 68%);
}
.door-in {
  position: relative;
  width: min(var(--door-w), 100% - 2.4rem); margin-inline: auto;
  padding: .95rem 0 clamp(1.9rem, 4vw, 3rem);
}
.door-row { display: flex; align-items: center; gap: .95rem; min-height: 44px; }
.door-mark {
  display: inline-flex; align-items: center; gap: .6rem;
  font-family: var(--serif); font-weight: 600; font-size: 1.02rem;
  letter-spacing: -0.018em; color: var(--inv); text-decoration: none;
}
.door-who { margin-left: auto; font-size: 0.875rem; color: var(--inv-soft); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The one headline on the site set in the sans at 800 — the public site's headline treatment. */
.door h1 {
  font-family: var(--sans); font-weight: 800; color: var(--inv);
  font-size: clamp(29px, 4.2vw, 44px); letter-spacing: -0.035em; line-height: 1.02;
  margin: clamp(1.3rem, 3vw, 2.1rem) 0 0;
}
.door p { color: var(--inv-soft); max-width: 54ch; margin: .9rem 0 0; font-size: 0.95rem;
  text-wrap: pretty; }
.door a { color: var(--accent-2); }
.door a:focus-visible, .door button:focus-visible { outline-color: var(--accent-2); }

/* Under the hard edge there is no card. The form sits on the page, in the same column the
   headline sits in, separated by rules and space rather than by a border and a shadow. */
.doorpage main,
.doorpage main.narrow,
.doorpage main.portal {
  width: min(var(--door-w), 100% - 2.4rem); max-width: none;
  margin: clamp(1.7rem, 4vw, 2.6rem) auto 4rem; padding: 0;
  background: none; border: 0; border-radius: 0; box-shadow: none;
}
/* The ruled rows below already draw every line this page needs; an <hr> between them was a
   second rule 25px from the first. It keeps its meaning in the markup and gives up its ink. */
.doorpage hr { border: 0; height: 0; margin: 1.5rem 0 0; }
/* The secondary routes off a door — "forgot your password", "are you a client, not staff" —
   are ruled rows, not a paragraph pile. */
.doorpage main > p.muted { margin: 0; padding: .85rem 0; border-bottom: 1px solid var(--line); }
.doorpage main > p.muted:first-of-type { border-top: 1px solid var(--line); margin-top: 1.5rem; }

/* ---- forms ---- */
.stack { display: flex; flex-direction: column; gap: 1rem; }
label { display: block; font-size: 0.875rem; font-weight: 600; letter-spacing: -.005em; }
input, select, textarea {
  display: block; width: 100%; margin-top: 0.32rem;
  padding: 0.55rem 0.65rem; min-height: 40px;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--radius-sm);
  font: inherit;
  box-shadow: inset 0 1px 2px rgba(24,18,48,.05);
  transition: border-color .16s var(--ease), box-shadow .18s var(--ease);
}
input:hover, select:hover, textarea:hover { border-color: var(--ink-faint); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft), inset 0 1px 2px rgba(24,18,48,.05);
}
/* A door is read by someone who has never seen this app; its fields are bigger than a
   console field, and 16px so iOS does not zoom the viewport on focus. */
.doorpage input, .doorpage select, .doorpage textarea { min-height: 46px; font-size: 16px; }
.doorpage label { min-height: 22px; }

button, .button {
  font: inherit; cursor: pointer;
  padding: 0.48rem 0.9rem; min-height: 36px;
  border: 1px solid var(--line-2); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--ink);
  text-decoration: none; display: inline-flex; align-items: center; justify-content: center;
  gap: .4rem;
  box-shadow: var(--lift-1), var(--edge);
  /* A CONSOLE IS NOT A LANDING PAGE. Every button and every tile used to rise 1px under the
     pointer, which on a screen of forty rows is a page that twitches. Colour and edge only. */
  transition: background-color .16s var(--ease), border-color .16s var(--ease),
              box-shadow .18s var(--ease), transform .12s var(--ease);
}
button:hover, .button:hover { background: var(--panel-2); border-color: var(--ink-faint); }
button:active, .button:active { transform: scale(.97); }
button.primary, .button.primary {
  background: var(--accent);
  color: var(--accent-ink); border-color: transparent; font-weight: 600;
  box-shadow: var(--press), var(--edge-dark);
}
button.primary:hover, .button.primary:hover {
  background: var(--accent-deep); border-color: transparent;
}
:root[data-theme="dark"] button.primary:hover, :root[data-theme="dark"] .button.primary:hover {
  background: var(--accent-2);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) button.primary:hover,
  :root:not([data-theme="light"]) .button.primary:hover { background: var(--accent-2); }
}
button.linklike {
  background: none; border: none; padding: 0; min-height: 24px; color: var(--ink-soft);
  text-decoration: underline; box-shadow: none;
}
button.linklike:hover { background: none; box-shadow: none; color: var(--ink); }

/* ---- alerts ---- */
/* A NOTE IN THE MARGIN, not a floating object. The left rule says which kind of note it is; the
   shadow it used to carry made a sentence look like a control. */
.alert {
  padding: 0.85rem 1.05rem; border-radius: var(--radius); border: 1px solid var(--line);
  margin-bottom: 1.25rem; background: var(--panel);
  border-left-width: 3px; border-left-color: var(--accent);
}
.alert ul { margin: 0; padding-left: 1.1rem; }
.alert-error { background: var(--err-bg); border-color: var(--err-line); border-left-color: var(--err-ink); }
.alert-warn { background: var(--warn-bg); border-color: var(--warn-line); border-left-color: var(--warn-ink); }

.chip {
  display: inline-block; padding: 0.12rem 0.5rem; border-radius: 999px;
  font-size: 0.69rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--ink-soft);
  text-decoration: none;
}
.chip-warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn-ink); }

/* ---- tiles: the figures ----
   These are the console's numerals, and they are the same numerals as the public site's: the
   serif at 300, tabular, tracked. The white gradient wash that used to lie over every one of
   them is gone -- it was a gloss effect standing in for depth the light source already gives. */
.tiles { display: flex; flex-wrap: wrap; gap: 0.7rem; margin: 1.25rem 0; }
.tile {
  position: relative;
  flex: 1 1 9rem; padding: 0.9rem 1rem 1rem;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  display: flex; flex-direction: column; gap: 0.3rem;
  box-shadow: var(--lift-1), var(--edge);
  transition: border-color .18s var(--ease), box-shadow .2s var(--ease);
}
.tile:hover { box-shadow: var(--lift-2), var(--edge); border-color: var(--line-2); }
.tile-n {
  font-family: var(--serif); font-size: 2.35rem; font-weight: 300;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums; line-height: .95;
  color: var(--ink);
}
/* A WORD IS NOT A FIGURE. The serif at figure size turns a stage name into a headline; the
   template says so in its own comment and the CSS was not listening. */
.tile-word { font-family: var(--sans); font-size: 1.02rem; font-weight: 700;
  letter-spacing: -0.005em; line-height: 1.25; padding-top: .35rem; }
.tile-l { font-size: 0.69rem; text-transform: uppercase; letter-spacing: 0.11em; color: var(--ink-faint); font-weight: 700; }

/* ---- tabs (pure CSS: radios drive which panel shows, no JS) ---- */
.tabs .tab-radio { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
/* A RULED ROW, NOT A FOLDER TAB. The rounded-top-corner tab was the only place in the app with
   its own radius, and its `role="tablist"` over plain <label>s was a critical axe violation
   (aria-required-children) for no gain -- the labels drive hidden radios and were already
   keyboard-operable. Same mechanism, drawn as the public site draws its nav: a rule underneath,
   violet on the one that is current. */
.tab-nav {
  display: flex; gap: 1.5rem; flex-wrap: wrap;
  border-bottom: 1px solid var(--line); margin: 1.5rem 0 1.75rem;
}
.tab-nav label {
  padding: 0.5rem 0 0.55rem; cursor: pointer; font-size: 0.875rem; font-weight: 600;
  color: var(--ink-soft); border-bottom: 2px solid transparent;
  margin-bottom: -1px; user-select: none;
  transition: color .16s var(--ease), border-color .16s var(--ease);
}
.tab-nav label:hover { color: var(--ink); border-bottom-color: var(--line-2); }
.tab-count {
  display: inline-block; margin-left: 0.3rem;
  font-family: var(--serif); font-size: 0.875rem; font-weight: 400; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.tab-panel { display: none; }

#tab-overview:checked  ~ .tab-nav label[for="tab-overview"],
#tab-record:checked    ~ .tab-nav label[for="tab-record"],
#tab-accounts:checked  ~ .tab-nav label[for="tab-accounts"],
#tab-paperwork:checked ~ .tab-nav label[for="tab-paperwork"],
#tab-documents:checked ~ .tab-nav label[for="tab-documents"] {
  color: var(--ink); border-bottom-color: var(--accent); font-weight: 700;
}
#tab-overview:checked  ~ .tab-panels .tab-panel[data-tab="overview"],
#tab-record:checked    ~ .tab-panels .tab-panel[data-tab="record"],
#tab-accounts:checked  ~ .tab-panels .tab-panel[data-tab="accounts"],
#tab-paperwork:checked ~ .tab-panels .tab-panel[data-tab="paperwork"],
#tab-documents:checked ~ .tab-panels .tab-panel[data-tab="documents"] { display: block; }

/* keyboard focus lands on the hidden radio -- reflect it on the visible label */
.tab-radio:focus-visible ~ .tab-nav label[for="tab-overview"],
.tab-radio:focus-visible ~ .tab-nav label[for="tab-record"],
.tab-radio:focus-visible ~ .tab-nav label[for="tab-accounts"],
.tab-radio:focus-visible ~ .tab-nav label[for="tab-paperwork"],
.tab-radio:focus-visible ~ .tab-nav label[for="tab-documents"] { outline: 2px solid var(--accent); outline-offset: 2px; }

/* outcome chips on the track record */
/* Semantic chips, on tokens so both themes come from one place. There used to be two competing
   .chip-ok rules with different hardcoded greens; the later one silently won. */
.chip-ok { background: var(--ok-bg); color: var(--ok-ink); border-color: var(--ok-line); }
.chip-info { background: var(--accent-soft); color: var(--accent-on-soft); border-color: var(--accent-line); }
/* The per-theme overrides that used to live here are gone: the tokens above already carry both
   themes, so a second set of hardcoded values could only ever drift out of step with them. */

/* record-a-dispute capture form on the track record */
.record-add { margin:.4rem 0 1.6rem; border:1px solid var(--line); border-radius:var(--radius); background:var(--panel); }
.record-add summary { cursor:pointer; padding:.6rem .9rem; font-weight:560; color:var(--accent); list-style:none; }
.record-add summary::-webkit-details-marker { display:none; }
.record-add[open] summary { border-bottom:1px solid var(--line); }
.record-add form { padding:0 .9rem 1rem; }

/* the "get your reports" option buttons — show every route at once */
.report-options { display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:.6rem; margin-top:.8rem; }
.report-options .button { display:flex; flex-direction:column; align-items:flex-start; text-align:left; gap:.15rem; padding:.75rem .95rem; height:100%; }
.report-options .button small { margin-top:0; }

pre.secret {
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 0.7rem 0.85rem; font-family: var(--mono); font-size: 1.02rem; letter-spacing: 0.08em;
  overflow-x: auto; word-break: break-all; white-space: pre-wrap;
}
/* The six-character activation code, read down a phone line. It is the one string in the console
   that is not language, so it is set like the public site sets its article number. */
pre.secret.code { font-size: 1.45rem; letter-spacing: .25em; text-align: center; }

/* ---- layout helpers ---- */
.row-between { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.row { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }
.right { text-align: right; }
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.pair3 { display: grid; grid-template-columns: 2fr 0.6fr 1fr; gap: 1rem; }
@media (max-width: 32rem) { .pair, .pair3 { grid-template-columns: 1fr; } }

/* A section is separated by a rule and by space -- the same idiom as the public site's ruled
   rows, and the reason almost nothing in here needs to be a box. */
section { margin: 2rem 0; padding-top: 1.25rem; border-top: 1px solid var(--line); }
section > h2:first-child { margin-top: 0; }

/* There were two h2 rules with different families and the later one only overrode half of it.
   One rule now, above, and this is where the second one used to be. */

select, textarea {
  display: block; width: 100%; margin-top: 0.32rem; padding: 0.55rem 0.65rem;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--radius-sm); font: inherit;
}
textarea { font-family: var(--mono); font-size: 0.875rem; }

.checklist { list-style: none; padding: 0; margin: 0.5rem 0; }
.checklist li { padding: 0.3rem 0; border-bottom: 1px solid var(--line); font-size: 0.875rem; }

.alert-ok { background: var(--ok-bg); border-color: var(--ok-line); border-left-color: var(--ok-ink); }

code.tiny { font-size: 0.78rem; color: var(--ink-soft); }

.account {
  border-bottom: 1px solid var(--line);
  padding: 0.9rem 0 1rem; margin-bottom: 0; background: none;
}
.inline-form { margin-top: 0.6rem; display: flex; gap: 0.5rem; align-items: flex-end; flex-wrap: wrap; }
/* IN A TABLE CELL A FORM IS ONE ROW, NOT A COLUMN. A full-width <select> stacked over its button
   made every queue row 90px tall, so ten rows of work needed a scroll to count. */
td .inline-form { margin-top: 0; align-items: center; gap: .4rem; }
td .inline-form label { flex: 0 1 auto; }
td .inline-form select, td .inline-form input { width: auto; min-width: 11.5rem; margin-top: 0; }
td .inline-form button { flex: none; }
.inline-form label.inline { flex: 1 1 18rem; }
.inline-form small { flex-basis: 100%; }

details { margin: 0.5rem 0; }
summary { cursor: pointer; color: var(--ink-soft); font-size: 0.875rem; }

/* ---- consumer portal: plainer, larger, no operator chrome ---- */
main.portal { max-width: 42rem; font-size: 16px; }
/* .portal-head is gone: the firm's name and the client's name now sit on the door band, and a
   rule under them repeated a separation the hard edge already makes. */
/* WHAT IS SIGNED AND WHAT IS NOT, as a ruled checklist rather than a row of glyphs. A client
   reads this once, on a phone, and the only question it has to answer is "what is left". */
.steps { list-style: none; padding: 0; margin: 0 0 1.9rem; display: flex;
  flex-direction: column; border-top: 1px solid var(--line); }
.steps li {
  font-size: 0.875rem; color: var(--ink-soft); padding: .6rem 0;
  border-bottom: 1px solid var(--line);
}
.steps li.done { color: var(--ink); }
.steps li a { text-decoration: none; }
.steps li a:hover { text-decoration: underline; }
.doc { margin-bottom: 1.5rem; }
.doc-body {
  white-space: pre-wrap; word-wrap: break-word;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1rem 1.1rem; font: inherit; font-size: 0.95rem; line-height: 1.62;
  max-height: 26rem; overflow-y: auto;
}
.doc-body:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
label.check { display: flex; gap: 0.6rem; align-items: flex-start; font-weight: 400; min-height: 24px; }
/* 13px is what a browser draws by default and it is under every touch guideline there is. The
   whole label is the target (it is a flex row), but the box itself has to be hittable too. */
label.check input[type="checkbox"] { width: 17px; height: 17px; min-height: 0; margin-top: .28rem;
  accent-color: var(--accent); flex: none; }
label.check input { width: auto; min-height: 0; margin-top: 0.3rem; }
.signout { margin-top: 2.5rem; padding-top: 1.2rem; border-top: 1px solid var(--line); }

/* ---- the client's mail, drawn as the record it is ----------------------------------------
   A client comes here to answer one question: has the bureau run out of time yet. The date it
   runs out is therefore the figure on the row, in the same serif the public site sets its day
   counts in, and everything else on the row is support for it. */
.mailing { padding: 1.15rem 0 1.25rem; border-bottom: 1px solid var(--line); }
.mailing:first-of-type { border-top: 1px solid var(--line); }
.mailing .row-between { align-items: center; }
.mailing h3 { margin: 0; font-family: var(--serif); font-weight: 500; font-size: 1.15rem;
  letter-spacing: -.015em; }
.mailing .fig { font-size: 1.05rem; }
/* The sentence around the figure was 12.5px, which made a 17px date look like a mistake rather
   than like emphasis. The support line reads at .875rem and the figure sits above it. */
.mailing p small { font-size: 0.875rem; line-height: 1.6; }
.mailing .timeline { margin: .7rem 0 0; }

/* Wide content scrolls inside its own container; the page body never scrolls sideways. */
.scroll-x { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; }
th, td { text-align: left; padding: 0.6rem 0.7rem; border-bottom: 1px solid var(--line); font-size: 0.875rem; }
th:first-child, td:first-child { padding-left: 0; }
/* The one heavy rule on a table is the one under its head, which is what makes a long list read
   as a ledger rather than as a grid. */
thead th {
  font-size: 0.69rem; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
  color: var(--ink-faint); border-bottom: 1.5px solid var(--ink); padding-bottom: .45rem;
}
tbody tr:hover > td { background: var(--panel-2); }
/* A date, a day count and a tracking number are all figures; they line up or they cannot be
   scanned. Applied to the whole table so no column can forget. */
td { font-variant-numeric: tabular-nums; }
/* A link alone in a cell is a target, not a word in a sentence: it gets a real hit area.
   Four of these were the only serious target-size failures on the work queue. */
td > a { display: inline-block; min-height: 24px; padding-block: 2px; }

/* ---- consumer-protection area ----
   These services are not credit repair, and the UI says so before the operator reads a word: its own
   bar, its own accent. Keeping the two worlds visually apart is a compliance habit, not decoration --
   it is harder to describe one service in the other's language when they never share a screen. */
.protection-bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: .25rem;
  margin: -0.5rem 0 1.5rem; padding: .5rem .75rem;
  background: var(--panel); border: 1px solid var(--line);
  border-left: 3px solid #0e7490; border-radius: var(--radius);
}
.protection-mark {
  font-size: 0.69rem; font-weight: 750; text-transform: uppercase; letter-spacing: .07em;
  color: #0e7490; margin-right: .75rem;
}
.protection-bar a {
  padding: .25rem .6rem; border-radius: 999px; text-decoration: none;
  font-size: 0.875rem; color: var(--ink-soft);
}
.protection-bar a:hover { color: var(--ink); }
.protection-bar a.on { background: #0e7490; color: #fff; font-weight: 600; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .protection-bar { border-left-color: #22d3ee; }
  :root:not([data-theme="light"]) .protection-mark { color: #22d3ee; }
  :root:not([data-theme="light"]) .protection-bar a.on { background: #22d3ee; color: #10202a; }
}
:root[data-theme="dark"] .protection-bar { border-left-color: #22d3ee; }
:root[data-theme="dark"] .protection-mark { color: #22d3ee; }
:root[data-theme="dark"] .protection-bar a.on { background: #22d3ee; color: #10202a; }

/* ---- PHONE ----
   Measured, not guessed: at 390px every page scrolled sideways (document 666px), because the top bar
   is a single non-wrapping row of six links. The practical consequence was worse than untidy —
   Settings and Sign out sat off the right edge and could not be reached at all.

   Three fixes, in order of how much they mattered:
     1. the bar wraps, and the nav takes its own full-width row, so every destination is reachable;
     2. wide tables get a min-width INSIDE their .scroll-x box, so the TABLE scrolls instead of the
        whole page -- `width: 100%` alone just crushes the columns and pushes the document wide;
     3. inputs go to 16px, because iOS Safari zooms the viewport whenever a focused field is smaller,
        which is most of the "it feels glitchy" on a phone. */
@media (max-width: 40rem) {
  main { padding: 1.25rem 0.9rem 3rem; }
  h1 { font-size: 1.3rem; }

  .topbar { flex-wrap: wrap; gap: 0.4rem 0.9rem; padding: 0.6rem 0.9rem; }
  .brand { white-space: nowrap; font-size: 1.02rem; }
  .topbar-right { margin-left: auto; gap: 0.6rem; font-size: 0.875rem; }
  .topbar nav { order: 3; width: 100%; flex-wrap: wrap; gap: 0.5rem 0.95rem; padding-top: 0.15rem; }
  .topbar nav a { font-size: 0.875rem; }

  /* The table scrolls; the page does not. */
  .scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .scroll-x table { min-width: 34rem; }

  /* Long unbroken strings -- portal links, letters -- must wrap rather than widen the page. */
  pre.secret, pre.doc-body { white-space: pre-wrap; word-break: break-word; }

  /* iOS zooms on focus below 16px. */
  input, select, textarea { font-size: 16px; }

  .tab-nav { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .tab-nav label { white-space: nowrap; padding: 0.5rem 0.75rem; }

  .tile { flex: 1 1 7.5rem; }
  .inline-form { gap: 0.4rem; }
}

/* ============================================================================
   THE WORKSPACE — rail-free master/detail for the client roster.

   A flat table of clients stops working at about ten rows: everything looks
   equally urgent, so nothing does. Here the roster is its own column, segmented
   and searchable, and it STAYS beside the open file. Moving between clients is
   one click, never a round trip through a list. Fifty clients read as calmly as
   five, because you are only ever looking at one segment of them.
   ============================================================================ */
main.workspace {
  max-width: 82rem;
  padding: 0;
  overflow: hidden;
  margin-bottom: 1.5rem;
}
/* A FIXED FRAME with independently scrolling columns — the roster must not stretch the page,
   or a long client list drags the whole document down and the file you are reading scrolls away
   with it. The frame is the viewport minus the top bar and the sheet's own margins. */
.ws {
  display: grid; grid-template-columns: 19rem 1fr;
  height: calc(100vh - 8.5rem); min-height: 32rem;
}
.ws-detail { padding: 1.7rem 1.8rem 3rem; min-width: 0; overflow-y: auto; }
.ws-back { display: none; }

/* ---- the roster column ---- */
.roster {
  display: flex; flex-direction: column; min-width: 0;
  background: var(--panel-2);
  border-right: 1px solid var(--line);
  /* Without this the column grows to fit the list and pushes its own footer -- and the
     "Add a client" button with it -- clean off the bottom of the frame. */
  overflow: hidden;
}
.roster-head { padding: .8rem .8rem .6rem; }
.roster-search input { margin-top: 0; font-size: 0.875rem; }

.segs { display: flex; flex-direction: column; gap: .1rem; padding: 0 .55rem .6rem; }
.seg {
  display: flex; align-items: center; gap: .5rem;
  padding: .4rem .55rem; border-radius: var(--radius-sm);
  font-size: 0.875rem; color: var(--ink-soft); text-decoration: none;
  transition: background .15s, color .15s;
}
.seg:hover { background: var(--panel); color: var(--ink); }
/* ACTIVE. Violet, but as a MARK rather than as a slab: a solid violet bar in the same column as
   the one violet button made two things shout the same colour, and only one of them is an action.
   The vertical gradient it used to carry was also the one thing here pretending to be an object. */
.seg.on {
  background: var(--accent-soft); color: var(--accent-on-soft); font-weight: 700;
  box-shadow: inset 2px 0 0 var(--accent);
}
.seg-n { margin-left: auto; font-family: var(--serif); font-size: 0.875rem; font-weight: 400;
  font-variant-numeric: tabular-nums; }
.seg:not(.on) .seg-n { color: var(--ink-faint); }

.roster-list { flex: 1 1 auto; overflow-y: auto; padding: .35rem .5rem .6rem; min-height: 0; }
.cli {
  position: relative; display: flex; align-items: center; gap: .55rem;
  padding: .5rem .55rem; margin-bottom: .12rem;
  border-radius: var(--radius); text-decoration: none; color: inherit;
  transition: background .15s, box-shadow .2s, transform .18s;
}
.cli:hover { background: var(--panel); box-shadow: var(--lift-1); }
.cli.on { background: var(--panel); box-shadow: var(--lift-2), var(--edge); }
/* The selected client gets a spine in the same violet that marks the active segment. Flat, and
   without the 10px glow: a console with six glows on screen has none. */
.cli.on::after {
  content: ""; position: absolute; left: 0; top: 16%; bottom: 16%; width: 3px;
  border-radius: 0 3px 3px 0; background: var(--accent);
}
/* SIX PURPLE AVATARS IS NOT ONE JOB. The initials are an identifier, not an action, so they are
   drawn in the neutral -- and the one client whose file is open takes the violet, which is the
   whole point of having a house colour. */
.cli-av {
  flex: none; width: 28px; height: 28px; border-radius: var(--radius-sm);
  display: grid; place-items: center;
  font-size: 0.69rem; font-weight: 700; letter-spacing: .02em;
  color: var(--ink-soft); background: var(--panel);
  border: 1px solid var(--line-2);
}
.cli.on .cli-av { background: var(--accent); border-color: transparent; color: var(--accent-ink); }
.cli-body { min-width: 0; display: flex; flex-direction: column; }
.cli-name { font-size: 0.875rem; font-weight: 600; letter-spacing: -.005em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* WAS 3.1:1 and eleven serious axe failures. This line says WHY a client is in this segment;
   it is the reason, and the reason is never the thing that gets dimmed. */
.cli-sub { font-size: 0.78rem; color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dot { flex: none; margin-left: auto; width: 7px; height: 7px; border-radius: 50%; }
.dot-stop { background: var(--err-ink); }
.dot-soon { background: var(--warn-ink); }
.dot-ok   { background: var(--line-2); }
.roster-empty { padding: 1rem .7rem; font-size: 0.875rem; }
.roster-foot { flex: none; padding: .7rem; border-top: 1px solid var(--line); background: var(--panel-2); }
.button.wide { width: 100%; text-align: center; }

/* ---- the "nobody selected yet" panel ---- */
.ws-empty { padding-top: 2.5rem; max-width: 42ch; }
.ws-empty h1 { margin-bottom: .5rem; }
.ws-empty .lede { font-family: var(--serif); font-size: 1.05rem; line-height: 1.5; color: var(--ink); }

/* ---- phone: the columns stack, and the file takes the screen ---- */
@media (max-width: 52rem) {
  main.workspace { max-width: none; }
  .ws { grid-template-columns: 1fr; }
  .roster { border-right: 0; border-bottom: 1px solid var(--line); }
  .roster-list { max-height: 20rem; }
  .ws-detail { padding: 1.2rem 1.1rem 3rem; }
  /* On a client's file the roster would push the file below the fold, so it is put away and
     the back link brings it back. */
  .ws-detail .ws-back { display: inline-flex; align-items: center; min-height: 24px;
    margin-bottom: .7rem; font-size: 0.875rem; font-weight: 600; text-decoration: none; }
  body:has(.ws-detail .ws-back) .roster { display: none; }
}

/* ---- tiles that actually go somewhere --------------------------------------------------
   These were <div>s. An operator clicked the numbers, nothing happened, and concluded the app
   had lost the work. They are anchors now, so they must not inherit link underline or colour --
   the tile IS the affordance and the hover lift already says it is pressable. */
a.tile { text-decoration: none; color: inherit; cursor: pointer; }
a.tile:hover .tile-l { color: var(--ink); }
a.tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Horizontal filter bar on the track record. `.seg` is the vertical sidebar variant; this
   reuses its look without its column layout. */
.seg-nav { display: flex; flex-wrap: wrap; gap: .35rem; margin: 1rem 0 1.5rem; }
.seg-nav .seg { border: 1px solid var(--line); min-height: 32px; }
.seg-nav .seg.on { border-color: transparent; }
/* The count rides on the violet when its filter is the active one, so it takes the violet's ink.
   Left as --ink-faint it was faint grey on violet -- a serious contrast failure I introduced. */
.seg.on .tab-count, .seg.on .seg-n { color: var(--accent-on-soft); }
/* On the horizontal filter bar the same state is a filled pill, because there it is the only
   violet on the row and there is no button beside it to compete with. */
.seg-nav .seg.on { background: var(--accent); color: var(--accent-ink); box-shadow: var(--edge-dark); }
.seg-nav .seg.on .tab-count, .seg-nav .seg.on .seg-n { color: var(--accent-ink); }

/* A row of buttons under a heading. */
.actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0 .35rem; }

/* The log-a-scan / log-an-answer strip that hangs off a track-record row. Recessed so it reads
   as belonging to the row above rather than as another record. */
tr.subrow > td { background: var(--panel); border-top: 0; padding-top: .35rem; padding-bottom: .8rem; }
tr.subrow .inline-form { margin-top: .35rem; }

/* Disputes nested under the bureau they were mailed to. */
.indent { margin: .3rem 0 .9rem 1.4rem; display: flex; flex-direction: column; gap: .35rem; }

/* ---- the theme switch -------------------------------------------------------------------
   app.css has carried a complete dark theme for a long time and nothing could reach it: the
   page followed prefers-color-scheme and offered no control, so an operator on a light desktop
   never learned it existed. Same shape as the 2-step chip — a capability nobody can press does
   not exist. Three states, cycled: light, dark, and follow-the-OS. */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; min-height: 2rem; padding: 0;
  background: var(--panel); color: var(--ink-soft);
  border: 1px solid var(--line); border-radius: var(--radius-sm, .4rem);
  cursor: pointer; font-size: 0.95rem; line-height: 1;
  transition: background .15s, color .15s, border-color .15s;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--accent-line, var(--line-2)); }
.theme-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Show the icon for the theme you would GET, not the one you are in — a sun while dark means
   "press for light", which is the only reading that does not need a caption. */
.theme-toggle-sun { display: none; }
.theme-toggle-moon { display: inline; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle-sun { display: inline; }
  :root:not([data-theme="light"]) .theme-toggle-moon { display: none; }
}
:root[data-theme="dark"] .theme-toggle-sun { display: inline; }
:root[data-theme="dark"] .theme-toggle-moon { display: none; }
:root[data-theme="light"] .theme-toggle-sun { display: none; }
:root[data-theme="light"] .theme-toggle-moon { display: inline; }

/* ---- MATTERS ----------------------------------------------------------------------------
   Ported from design/prototypes/panel-gloss.html, which was a mockup with typed-in data. The
   look was finished there; only the wiring was missing. The tokens are already identical
   between the two files, so this is the prototype's CSS expressed in this stylesheet's names.

   The point of the component: the rest of the page lists what EXISTS. A matter row says what to
   DO, and the sort order (stop, soon, run, won) is the order an operator works in. */
.matters { margin: 1.25rem 0 1.75rem; }
.matters-headline {
  font-size: 1.02rem; line-height: 1.5; margin: 0 0 1.1rem;
  color: var(--ink-soft); max-width: 46rem;
}
.matters-headline strong { color: var(--ink); font-weight: 600; }
.matters-h {
  font-size: 0.69rem; text-transform: uppercase; letter-spacing: .14em;
  color: var(--ink-faint); font-weight: 700; margin: 0 0 .5rem;
}

.matter-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .3rem; }
/* A RULED ROW. The matter list is the console's docket, and a docket is ruled, not stacked out
   of little cards -- which is also how the public site draws every list it has. */
.matter {
  display: grid; grid-template-columns: 1fr auto; gap: .6rem; align-items: center;
  padding: .7rem .5rem .75rem 0; border-radius: 0;
  background: none; border-bottom: 1px solid var(--line);
  transition: background-color .16s var(--ease);
}
.matter-list > .matter:first-child { border-top: 1px solid var(--line); }
.matter:hover { background: var(--panel-2); }
.matter-main { display: block; text-decoration: none; color: inherit; min-width: 0; }
.matter-title {
  font-size: 0.875rem; font-weight: 600; display: flex; gap: .45rem;
  align-items: center; flex-wrap: wrap;
}
.matter-detail { display: block; font-size: 0.78rem; color: var(--ink-faint); margin-top: .15rem; }
/* THE DAY COUNT, WHICH IS WHAT THIS ROW IS ABOUT. It was .755rem of faint grey at the far right
   of the row -- the least legible thing on the screen was the number the operator acts on. */
.matter-right {
  font-family: var(--serif); font-size: 1.02rem; font-weight: 400; color: var(--ink);
  font-variant-numeric: tabular-nums; letter-spacing: -.015em;
  text-align: right; white-space: nowrap; display: flex; align-items: center; gap: .45rem;
}
.matter .chev { font-family: var(--sans); color: var(--ink-faint); font-size: 0.875rem;
  transition: color .16s var(--ease); }
.matter:hover .chev { color: var(--accent); }

.tag {
  font-size: 0.69rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  padding: .15rem .4rem; border-radius: var(--radius-sm);
}
/* Semantic, and separate from the accent — these say what KIND of attention a row wants. */
.tag-stop { background: var(--ox-soft, #fbeef1); color: var(--ox, #9a2f4e); }
.tag-soon { background: var(--amber-soft, #fdf3e0); color: var(--amber, #7a4a00); }
.tag-won  { background: var(--ok-soft, #e6f3ec); color: var(--ok, #12724a); }

/* HOW MUCH OF THE WINDOW IS SPENT. The one bar in the app, and it is a clock face laid flat. */
.matter-bar {
  display: block; height: 4px; max-width: 215px; margin-top: .5rem;
  background: var(--panel-2); border-radius: 999px; overflow: hidden;
  box-shadow: inset 0 1px 2px rgba(24, 18, 48, .12);
}
.matter-bar i { display: block; height: 100%; border-radius: 999px; background: var(--accent); }
/* A window nearly gone reads as urgent without needing to be read. */
.matter-bar.hot i { background: var(--ox, #9a2f4e); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ox: #f4877e; --ox-soft: #2c1614;
    --amber: #e0b155; --amber-soft: #2a2113;
    --ok-soft: #12271c;
  }
}
:root[data-theme="dark"] {
  --ox: #f4877e; --ox-soft: #2c1614;
  --amber: #e0b155; --amber-soft: #2a2113;
  --ok-soft: #12271c;
}
:root[data-theme="light"] {
  --ox: #9a2f4e; --ox-soft: #fbeef1;
  --amber: #7a4a00; --amber-soft: #fdf3e0;
  --ok-soft: #e6f3ec;
}

/* ---- THE RAIL ---------------------------------------------------------------------------
   Primary navigation moved from a top bar to a left rail, following
   design/prototypes/panel-gloss.html. On a client file this turns two panes into three: rail,
   client roster, the file itself.

   The reason is width, not taste. With navigation on top, a wide screen spends its width on one
   column of content and its height on chrome. Down the side, chrome costs width the screen has
   spare and gives the height back to the work — which on this app is a list of accounts that
   never fits.

   Below 62rem it becomes the old top bar again. A phone has no width to spend. */
.app { display: grid; grid-template-columns: 13.5rem 1fr; min-height: 100vh; align-items: start; }

/* NO RAIL, NO RAIL COLUMN.
   The rail renders only for a signed-in session (shell.njk), but this grid declared its
   13.5rem track unconditionally -- so on a logged-out page <main> became the FIRST grid item
   and was laid out in the rail's column. Measured, not guessed: the sign-in card was 215px
   wide at x=0 on a 1920px screen, which is 13.5rem to the pixel. It hit sign-in, the TOTP
   step, signup and password reset -- every page a stranger or a locked-out operator sees,
   including the one where a new firm registers. */
.app-bare { grid-template-columns: 1fr; }

.rail {
  position: sticky; top: 0; align-self: stretch;
  display: flex; flex-direction: column; gap: 1.1rem;
  height: 100vh; padding: 1.15rem 0.7rem 1rem;
  /* Flat onyx with ONE light source off the top left -- the same band the public site opens on.
     The vertical gradient underneath it was a second, contradictory light. */
  background: var(--onyx);
  box-shadow: inset -1px 0 0 var(--rule-inv), var(--lift-dark);
  color: var(--inv-soft);
}
.rail::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(420px 300px at 18% -6%, rgba(124, 58, 237, .30), transparent 68%);
}
.rail > * { position: relative; }
.rail .brand {
  display: flex; align-items: center; gap: .6rem;
  font-family: var(--serif); font-size: 1.05rem; line-height: 1.2; font-weight: 600;
  letter-spacing: -.018em; color: var(--inv); text-decoration: none;
  padding: .2rem .5rem .1rem; min-height: 44px;
}
.rail-nav { display: flex; flex-direction: column; gap: .08rem; }
.rail-nav a {
  display: block; padding: .45rem .6rem; border-radius: var(--radius-sm);
  color: var(--inv-soft); text-decoration: none; font-size: 0.875rem;
  transition: background-color .15s var(--ease), color .15s var(--ease);
}
.rail-nav a:hover { background: rgba(255, 255, 255, .07); color: var(--inv); }
.rail-nav a:focus-visible { outline: 2px solid var(--accent-2); outline-offset: -2px; }
/* WHERE AM I. The rail marked nothing as current, so on eight destinations the answer was
   "read the heading". Violet's second job, done once per screen. */
.rail-nav a.on {
  color: var(--inv); font-weight: 600; background: rgba(255, 255, 255, .09);
  box-shadow: inset 2px 0 0 var(--accent-2);
}
.rail-nav .nav-platform { color: var(--accent-2); }

.rail-foot { margin-top: auto; display: flex; flex-direction: column; gap: .55rem; padding: 0 .5rem; }
.rail-foot-row { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.rail-foot .tenant { font-size: 0.78rem; color: var(--inv-faint); line-height: 1.3; min-width: 0; word-break: break-word; }
.rail-foot .linklike { color: var(--inv-faint); font-size: 0.78rem; padding: 0; }
.rail-foot .linklike:hover { color: var(--inv); background: none; }
/* On the rail the toggle sits on the dark ground, not on a panel. */
.rail .theme-toggle { background: rgba(255,255,255,.08); border-color: var(--rule-inv); color: var(--inv-soft); flex-shrink: 0; }
.rail .theme-toggle:hover { background: rgba(255,255,255,.16); color: var(--inv); }
/* The two-factor prompt is a standing reminder, not an alarm: it reads on the black ground it
   sits on rather than importing a whole amber panel into the rail. */
.rail .chip.chip-warn {
  background: transparent; color: var(--inv); border-color: var(--rule-inv);
  padding: .3rem .55rem; min-height: 24px;
}
.rail .chip.chip-warn:hover { background: rgba(255,255,255,.09); }

/* The workspace fills the rail's height rather than the old bar's. */
.app .ws { height: calc(100vh - 3.4rem); }
.app main { margin-top: 1.5rem; }

@media (max-width: 62rem) {
  .app { grid-template-columns: 1fr; }
  .rail {
    position: relative; height: auto; flex-direction: row; align-items: center;
    flex-wrap: wrap; gap: .75rem 1.1rem; padding: .72rem 1.1rem;
    box-shadow: inset 0 -1px 0 var(--rule-inv), var(--lift-dark);
  }
  .rail-nav a.on { box-shadow: inset 0 -2px 0 var(--accent-2); }
  .rail-nav { flex-direction: row; flex-wrap: wrap; gap: .1rem .35rem; }
  .rail-foot { margin-top: 0; margin-left: auto; flex-direction: row; align-items: center; gap: .8rem; padding: 0; }
  .rail-foot-row { gap: .8rem; }
  .app main { margin-top: 0; }
  .app .ws { height: auto; }
}

/* ---- DESKTOP: the workspace is an application, not a document ---------------------------
   The rail turned <main> into a grid item, and `main { margin: 1.5rem auto }` came with it.
   An AUTO margin on a grid item centres AND shrink-wraps it, so on a 1920px screen the
   workspace collapsed to 700px and floated with ~500px of dead space either side. Measured,
   not guessed: main was 700px wide starting at x=718.

   A client file is a workspace — roster plus detail — and it should use the screen the way an
   application does. A reading page (settings, billing) is still a document and keeps its
   measure, because a 1700px line of prose is unreadable. Two different jobs, two rules. */
@media (min-width: 62rem) {
  .app main.workspace {
    max-width: none;
    margin: 0;
    border-radius: 0;
    border-top: 0; border-bottom: 0; border-right: 0;
    height: 100vh;
  }
  .app main.workspace .ws { height: 100vh; }

  /* The roster gets a little more room on a wide screen; the file gets the rest. */
  .app main.workspace .ws { grid-template-columns: 21rem 1fr; }

  /* A reading page sits beside the rail instead of floating in the middle of the leftover
     space — the auto margin that centred it is what made it look adrift. */
  .app:not(.app-bare) main:not(.workspace) {
    margin-left: 0;
    margin-right: auto;
    max-width: 62rem;
  }
}

/* Very wide screens: the file column stops growing rather than running to 1400px of line
   length, and the space goes to a calmer margin instead of to unreadable measure. */
@media (min-width: 110rem) {
  .app main.workspace .ws-detail > * { max-width: 74rem; }
  .app:not(.app-bare) main:not(.workspace) { max-width: 68rem; }
}

/* A WORKSPACE IS NEVER A DOCUMENT, at any width.
   The desktop fix above started at 62rem, and `main { margin: 1.5rem auto }` still shrink-wrapped
   between 46rem and 62rem — a tablet, or a browser window dragged to half a screen, got a 373px
   workspace inside 768px. Found by bin/layout-check.py, which is the entire reason it exists.
   The rule belongs on the element, not on a width. */
main.workspace { margin-left: 0; margin-right: 0; max-width: none; }

/* GRID BLOWOUT. A grid item's min-width defaults to `auto`, which is min-content — so a flex row
   of eight nav links refuses to shrink below its natural width and pushes the whole page sideways.
   On a 390px phone the folded rail measured 573px and the page scrolled horizontally on every
   screen. Found by bin/layout-check.py; invisible to the eye because the overflow is off-screen.
   `min-width: 0` lets the track do its job. */
.app > .rail, .app > main { min-width: 0; }

@media (max-width: 62rem) {
  /* Room to wrap onto a second line rather than one long unbreakable row. */
  .rail { row-gap: .5rem; }
  .rail-nav { flex: 1 1 100%; }
  .rail-foot { flex: 1 1 100%; margin-left: 0; justify-content: space-between; }
  .rail .brand { flex: 0 0 auto; }
}

/* A token is shown exactly once. It has to be selectable and unambiguous — a wrapped credential
   with a character you cannot tell from another is a support call. */
code.token {
  display: block; margin: .5rem 0; padding: .6rem .75rem;
  font-family: var(--mono);
  font-size: 0.875rem; letter-spacing: .01em; word-break: break-all;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-sm, .4rem);
  user-select: all;
}

/* ---- the decision surfaces ----------------------------------------------------------------
   Three screens ask an operator to JUDGE something rather than read it: the drafts slate, the
   results the reports were read out of, and the standing authorisations. They earned their own
   container because the failure mode on all three is identical and expensive -- an item skipped
   because the page did not give the reader enough to decide with. So a card is a decision, not a
   box: it stands off the page, it carries its own reasoning, and it never runs edge to edge.

   Built entirely from the ONYX tokens above. Nothing here introduces a colour. */

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--lift-1), var(--edge);
  padding: 1rem 1.15rem;
  margin: 0 0 1rem;
}
.card > h2 { margin: 0 0 0.35rem; }
.card > .check { margin-bottom: 0.5rem; }
.card > p:last-child { margin-bottom: 0; }

/* THE REASON, INDENTED UNDER THE CLAIM. The accent rail is doing one job: it says "this is why",
   and it is the same purple that marks everything else actionable, because the reason IS the
   thing being acted on. */
.finding {
  border-left: 2px solid var(--accent-line);
  padding: 0.15rem 0 0.15rem 0.8rem;
  margin: 0.5rem 0 0.75rem;
}
.finding p { margin: 0 0 0.25rem; }
.finding p:last-child { margin: 0; }
.finding strong { font-weight: 600; }

/* Something is wrong, as opposed to something needs attention. Kept distinct from .alert-warn:
   amber is "read this before you act", oxblood is "this did not happen". */
.alert-bad { background: var(--err-bg); border-color: var(--err-line); border-left-color: var(--err-ink); }
.chip-bad { background: var(--err-bg); color: var(--err-ink); border-color: var(--err-line); }

/* ---- the sixteen that were never written --------------------------------------------------
   Every rule below was already being asked for by a template and defined nowhere, so those
   elements rendered with browser defaults on live pages while the whole suite stayed green.
   `test/styled-classes.test.mjs` is what found them and is what stops it happening again.

   Five of them are on the CLIENT-facing portal, which is the part of this product a stranger
   judges it by. */

/* THE CLIENT'S TIMELINE — what happened to their letter, in their own portal.
   A rail with a dot per event, because "delivered" and "answered" are the two moments a client
   is actually waiting for and a bare <ol> buries them in a list. */
.timeline { list-style: none; margin: 0.6rem 0 0; padding: 0 0 0 1.1rem; position: relative; }
.timeline::before {
  content: ""; position: absolute; left: 0.22rem; top: 0.45rem; bottom: 0.45rem;
  width: 2px; background: var(--line-2); border-radius: 2px;
}
.timeline li { position: relative; padding: 0 0 0.75rem 0.55rem; }
.timeline li:last-child { padding-bottom: 0; }
.timeline li::before {
  content: ""; position: absolute; left: -0.98rem; top: 0.42rem;
  width: 0.5rem; height: 0.5rem; border-radius: 999px;
  background: var(--panel); border: 2px solid var(--line-2);
}
/* The most recent event is the one they came to read. */
.timeline li:first-child::before { background: var(--accent); border-color: var(--accent); }
.timeline li strong { font-weight: 600; }

/* NUMBERED INSTRUCTIONS A CLIENT FOLLOWS. Generous spacing on purpose: these are read once, on a
   phone, by someone who has never done this before and is about to leave for another site. */
.howto { margin: 1rem 0; padding-left: 1.35rem; }
.howto > li { margin: 0 0 1.1rem; padding-left: 0.2rem; line-height: 1.55; }
.howto > li:last-child { margin-bottom: 0; }
.howto > li::marker { font-weight: 650; color: var(--accent); }
.howto .button { margin-top: 0.45rem; }

/* THE AFFILIATE DISCLOSURE. Rendered exactly as stored and hashed with the referral, so it must
   never be visually minimised -- it sits ON the alert it shares, slightly tighter, never smaller. */
.disclosure p { margin: 0.4rem 0 0; }
.disclosure strong { display: block; margin-bottom: 0.1rem; }

/* A MESSAGE THAT STOPS SOMEONE GETTING IN. On the two portal doors -- the set-up code and the
   six-digit sign-in -- this is the only text that matters when it appears, and it was rendering
   as an unstyled paragraph on the one screen where a client is already unsure. */
.error {
  margin: 0 0 1rem; padding: 0.65rem 0.85rem;
  background: var(--err-bg); border: 1px solid var(--err-line);
  border-left: 3px solid var(--err-ink); border-radius: var(--radius);
  color: var(--err-ink); font-size: 0.875rem;
}

/* ---- the operator side ---- */

/* One reason among many on the dispute form. The whole row is the target, not just the radio. */
.reason { align-items: flex-start; padding: 0.5rem 0.6rem; border-radius: var(--radius); }
.reason:hover { background: var(--accent-soft); }
/* Blocked because this bureau did not report the field the reason points at. Dimmed, never
   hidden: the operator has to be able to see WHY an option is unavailable. */
.reason.blocked { opacity: 0.62; }
.reason.blocked:hover { background: transparent; }
.blocked { opacity: 0.62; }
small.warn { display: block; color: var(--warn-ink); margin-top: 0.15rem; }

/* The letter as it will be sent. Monospace, wrapped, and never horizontally scrolling. */
.letter {
  white-space: pre-wrap; overflow-wrap: anywhere;
  font-family: var(--mono);
  font-size: 0.875rem; line-height: 1.5;
}

/* The reader's progress on an uploaded report, inline in the roster. */
.extraction { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.78rem; color: var(--ink-soft); }

/* Rows that hold two things, and forms that sit on one line inside a table cell. */
.row-2 { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.row-2 > * { flex: 1 1 12rem; min-width: 0; }
.row-inline { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin: 0; }
.row-inline select, .row-inline input, .row-inline textarea { width: auto; margin-top: 0; }

/* Collapsed sections: one for editing identity, one for what cannot be undone. Both are <details>
   so the page does not lead with them. */
.edit-details, .danger-zone { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.edit-details > summary, .danger-zone > summary {
  cursor: pointer; font-weight: 560; color: var(--accent); list-style: none;
}
.edit-details > summary::-webkit-details-marker, .danger-zone > summary::-webkit-details-marker { display: none; }
.danger-zone > summary { color: var(--err-ink); }
.danger-zone[open] { border-color: var(--err-line); }

/* Platform console: a census, so the digits line up and the row you are standing in is marked. */
.num { text-align: right; font-family: var(--serif); font-weight: 400;
  font-variant-numeric: tabular-nums; letter-spacing: -.015em; }
.is-current { background: var(--accent-soft); }
.is-current td { border-color: var(--accent-line); }
.pill {
  display: inline-block; margin-left: 0.4rem; padding: 0.08rem 0.45rem; border-radius: 999px;
  font-size: 0.69rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  background: var(--accent-soft); color: var(--accent-on-soft); border: 1px solid var(--accent-line);
}

/* ---- the drafts screen: one card per account, one tick per bureau ------------------------- */
.bureau-row {
  border-top: 1px solid var(--line);
  padding: 0.8rem 0 0.2rem;
  margin-top: 0.8rem;
}
.bureau-row:first-of-type { border-top: 0; }
.bureau-row > .check { margin-bottom: 0.4rem; }

/* The wire text is what gets SENT, not what gets decided, so it folds away. It was the loudest
   thing on the card and the finding was the quiet one, which is the wrong way round. */
.wording { margin: 0.5rem 0 0; }
.wording > summary {
  cursor: pointer; list-style: none;
  font-size: 0.875rem; color: var(--accent); font-weight: 560;
}
.wording > summary::-webkit-details-marker { display: none; }
.wording > summary::before { content: "▸ "; }
.wording[open] > summary::before { content: "▾ "; }
.wording textarea { margin-top: 0.4rem; }

/* THE ACTION FOLLOWS YOU DOWN THE PAGE. Ticking something at the top used to mean scrolling past
   everything else to act on it, which is its own way of losing an item.

   A sticky bar sits OVER the page, so without this the last rows of the last card can never be
   scrolled clear of it -- there is simply no scroll position where they are visible. The bar was
   covering a tickable bureau row the first time it was drawn. Room is made for it in the flow. */
.drafts-form { padding-bottom: 4.5rem; }
.action-bar {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem;
  margin: 1.25rem 0 0; padding: 0.85rem 1rem;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 -6px 18px rgba(24, 18, 48, .09), var(--edge);
}
.action-bar .muted { flex: 1 1 18rem; }

/* ---- what the templates ask for, added with the 2026-09-05 pass -------------------------- */

/* A CODE READ DOWN A PHONE LINE. Three inputs take one — the portal set-up code, the six-digit
   sign-in code and a team invite — and all three carried the same inline style, hand-copied.
   It is the one string in this product that is not language, which is why it is the one place
   in the console that gets the mono face. */
.code-in {
  font-family: var(--mono); letter-spacing: .18em;
}
.code-in-lg { font-size: 1.45rem; text-align: center; }
.code-in-upper { text-transform: uppercase; }

/* THE DUE COLUMN, which is the whole point of the work queue. The count of days leads and the
   date supports it, rather than the other way round. */
td.due { white-space: nowrap; }
td.due .fig { font-size: 1.05rem; display: block; }
td.due .due-left { font-size: 0.78rem; color: var(--ink-soft); }

/* A win is the one figure on the dashboard that is allowed a semantic colour, because it is the
   only one that means something good rather than something due. */
.tile-n-ok { color: var(--ok-ink); }

/* The 20-digit certified article number. A machine's number, read back to a clerk digit by
   digit — the second and last job mono has here. */
code.art { font-family: var(--mono); font-size: 0.78rem; color: var(--ink-faint); letter-spacing: .01em; }

/* THE APPLICATION'S ONE CONDITIONAL BLOCK. It was a bare <fieldset>, which browsers draw with a
   3D groove border and a notched legend — the single most undesigned element on any public page
   here. It is a question inside a question, so it is set as one: a ruled block with the legend
   as its heading, indented off the accent the rest of the brand reserves for what matters. */
fieldset.ask {
  margin: .6rem 0 .2rem; padding: 0 0 0 1rem;
  border: 0; border-left: 2px solid var(--accent-line);
}
fieldset.ask legend {
  padding: 0; margin-bottom: .5rem;
  font-family: var(--sans); font-size: 1.02rem; font-weight: 700; letter-spacing: -.02em;
  color: var(--ink);
}
fieldset.ask > p { margin: 0 0 1rem; }
fieldset.ask > label { margin-bottom: 1rem; display: block; }
/* A checkbox and its words are ONE target and one line. `display:block` above was flattening the
   row that label.check builds, putting the box on its own line above the sentence. */
fieldset.ask > label.check { display: flex; }
fieldset.ask > label:last-child { margin-bottom: 0; }
.doorpage form > fieldset.ask { margin-bottom: .9rem; }

/* The door's own error line, on the two portal doors where it is the only thing that matters. */
.doorpage .error { margin-top: 0; }

/* A DOOR ON A PHONE. The band gives back its vertical padding before the headline does. */
@media (max-width: 40rem) {
  .door-in { padding-top: .8rem; }
  .doorpage main, .doorpage main.narrow, .doorpage main.portal { margin-top: 1.5rem; }
}
