/* ═══════════════════════════════════════════════════════════════════════════
   ATTEST — the design language, app-side.
   Loads AFTER lib/design-kit-meridian (untouchable — Meridian stays for
   sibling apps) and AFTER main.css. It replaces Meridian's LOOK by
   re-declaring the same token vocabulary and restyling the same classes;
   it introduces the provenance voices ATTEST is named for.

   The law-book lives in docs/ATTEST.md. The short form:
     LAW 1  Typeface encodes provenance. Sans = the system speaking.
            Serif = the record (verbatim, human-authored) — with a source
            ref adjacent where it is a quote. Mono = checkable identity:
            refs, keys, endpoints. Dates and counts are QUANTITIES and are
            set in tabular sans, not mono.
     LAW 2  Colour means state (ok/warn/err, AA-locked per theme) or
            interaction (one iron-gall-blue accent). Category NEVER takes
            a hue — position, shape, weight instead.
     LAW 3  Structure is rules and hairlines. Shadows only on floating
            layers. 2px document rule, 1px section, hairline rows.
     LAW 4  Light-first dual theme. Day is warm paper; night is warm
            graphite, never blue-cast. The portal pins day.
     LAW 5  Floors: nothing under 12px; every text/surface pair AA
            (4.5:1; 3:1 at ≥19px), both themes, verified on pixels.
     LAW 6  Marks: proposed/agent-created = open/dashed ◇; committed =
            filled/solid ▪; computed counts carry no claim-mark.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 0 · FONTS — self-hosted woff2 subsets, zero network fetches ──────────
   Source Sans 3 / Source Serif 4 / Source Code Pro (Adobe, OFL): one
   designed family in three classes, so a change of voice reads as a change
   of register, not of brand. Fallbacks are platform faces of the same class. */
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal; font-weight: 400 700; font-display: swap;
  src: url('../fonts/SourceSans3-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal; font-weight: 400 700; font-display: swap;
  src: url('../fonts/SourceSans3-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: italic; font-weight: 400 600; font-display: swap;
  src: url('../fonts/SourceSans3-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal; font-weight: 400 600; font-display: swap;
  src: url('../fonts/SourceSerif4-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal; font-weight: 400 600; font-display: swap;
  src: url('../fonts/SourceSerif4-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Source Code Pro';
  font-style: normal; font-weight: 400 600; font-display: swap;
  src: url('../fonts/SourceCodePro-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Code Pro';
  font-style: normal; font-weight: 400 600; font-display: swap;
  src: url('../fonts/SourceCodePro-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── 1 · TOKENS — DAY (default; the working theme) ─────────────────────────
   Two vocabularies, one palette:
     · ATTEST-native names (--field, --sheet, --act, --attend, --broken …)
       are the contract Phase Adoption writes against.
     · Meridian's names (--bg, --surface, --accent, --warn …) are re-pointed
       at the same values so every existing kit/app selector retints without
       a markup edit. lib/** is untouched — this file simply loads later.
   Contrast ratios in comments are sRGB vs field / sheet / sheet-2 / well. */
:root,
[data-theme="light"] {
  /* surfaces — warm paper. The sheet is the brightest thing on the page,
     because sheets are where content is read. */
  --field:   #F4F2EE;
  --sheet:   #FFFFFF;
  --sheet-2: #FAF9F6;
  --well:    #EFECE6;

  /* ink — warm near-black, four steps, all ≥4.5:1 on every surface above */
  --ink-1: #1A1917;   /* 15.71 / 17.57 / 16.69 / 15.01 */
  --ink-2: #454239;   /*  8.98 / 10.04 /  9.54 /  8.58 */
  --ink-3: #63605A;   /*  5.60 /  6.27 /  5.95 /  5.35 */
  --ink-4: #6C675C;   /*  5.02 /  5.61 /  5.33 /  4.79 — labels only */

  /* structure — hairlines */
  --rule:   #E3DFD8;
  --rule-2: #C8C3B9;

  /* interaction — ONE iron-gall-blue accent */
  --act:       #2445C9;  /* 6.78 on field / 7.58 on sheet */
  --act-hover: #1B36A6;
  --act-soft:  #E9EBFB;  /* act on act-soft = 6.41 */
  --act-line:  #8B9CE8;
  --act-ink:   #FFFFFF;  /* on solid act = 7.58 */

  /* state — ok / warn(attend) / err(broken), AA-locked on all surfaces */
  --ok:          #1A6E3C;  /* 5.62 / 6.29 / 5.97 / 5.33 */
  --ok-soft:     #E4F2E6;  /* ok on ok-soft = 5.43 */
  --ok-line:     #8FBF9C;
  --ok-ink:      #FFFFFF;
  --attend:      #8A5300;  /* 5.66 / 6.33 / 6.01 / 5.37 */
  --attend-soft: #FAF1E0;  /* attend on attend-soft = 5.64 */
  --attend-line: #C9973F;
  --attend-ink:  #FFFFFF;
  --broken:      #B32218;  /* 5.95 / 6.65 / 6.31 / 5.64 */
  --broken-soft: #FBEBE8;  /* broken on broken-soft = 5.75 */
  --broken-line: #DC7E74;
  --broken-ink:  #FFFFFF;  /* on solid broken = 6.65 */

  --scrim-c: rgba(26, 25, 23, .50);
  --lift-1: 0 1px 2px rgba(26,25,23,.06), 0 6px 18px rgba(26,25,23,.09);
  --lift-2: 0 8px 22px rgba(26,25,23,.12), 0 30px 70px rgba(26,25,23,.16);

  /* ── Meridian token remap — the kit retints through these ── */
  --bg:          var(--field);
  --surface:     var(--sheet);
  --surface-2:   var(--sheet-2);
  --bg-inset:    var(--well);
  --line:        var(--rule);
  --line-strong: var(--rule-2);
  --accent:      var(--act);
  --accent-hover:var(--act-hover);
  --accent-mid:  var(--act-line);
  --accent-dim:  var(--act-soft);
  --accent-ink:  var(--act-ink);
  --ok-dim:      var(--ok-soft);
  --warn:        var(--attend);
  --warn-dim:    var(--attend-soft);
  --err:         var(--broken);
  --err-dim:     var(--broken-soft);
  --scrim:       var(--scrim-c);
  --elev-1:      var(--lift-1);
  --elev-2:      var(--lift-2);
  --sheen:       none;
  --focus-ring:  0 0 0 3px color-mix(in srgb, var(--act) 25%, transparent);

  /* LAW 2 — category never takes a hue. Meridian's seven-hue stage spectrum
     collapses to ink + neutral wells: phase is position on a path, not a
     colour to memorise. */
  --stage-1: var(--ink-3);  --stage-1-dim: var(--well);
  --stage-2: var(--ink-3);  --stage-2-dim: var(--well);
  --stage-3: var(--ink-3);  --stage-3-dim: var(--well);
  --stage-4: var(--ink-3);  --stage-4-dim: var(--well);
  --stage-5: var(--ink-3);  --stage-5-dim: var(--well);
  --stage-6: var(--ink-3);  --stage-6-dim: var(--well);
  --stage-7: var(--ink-3);  --stage-7-dim: var(--well);
}

/* ── 1b · TOKENS — NIGHT (warm graphite, never blue-cast) ─────────────────
   The app toggle writes data-theme="dark"; that attribute now means ATTEST
   night. Same laws, same structure, its own measured ramp. */
[data-theme="dark"] {
  --field:   #14130F;
  --sheet:   #1C1A16;
  --sheet-2: #24211C;
  --well:    #0F0E0B;

  --ink-1: #F0EDE6;   /* 15.90 / 14.86 / 13.72 / 16.51 */
  --ink-2: #C6C1B6;   /* 10.36 /  9.68 /  8.94 / 10.76 */
  --ink-3: #A29C90;   /*  6.81 /  6.37 /  5.88 /  7.07 */
  --ink-4: #948E81;   /*  5.70 /  5.33 /  4.92 /  5.92 */

  --rule:   #2C2924;
  --rule-2: #423E36;

  --act:       #9DB0FF;  /* 8.93 / 8.35 */
  --act-hover: #B9C6FF;
  --act-soft:  #1E2233;  /* act on act-soft = 7.57 */
  --act-line:  #5C6BB4;
  --act-ink:   #0F1220;

  --ok:          #74C689;  /* 9.01 / 8.42 / 7.78 / 9.36 */
  --ok-soft:     #12271A;  /* ok on ok-soft = 7.65 */
  --ok-line:     #3A6B4A;
  --ok-ink:      #14130F;
  --attend:      #E8B25E;  /* 9.70 / 9.07 / 8.37 / 10.07 */
  --attend-soft: #2A2318;  /* attend on attend-soft = 8.11 */
  --attend-line: #8A6A2E;
  --attend-ink:  #14130F;
  --broken:      #F08A7E;  /* 7.65 / 7.15 / 6.60 / 7.94 */
  --broken-soft: #2E1B18;  /* broken on broken-soft = 6.72 */
  --broken-line: #8E4A42;
  --broken-ink:  #14130F;

  --scrim-c: rgba(8, 8, 6, .62);
  --lift-1: 0 1px 2px rgba(0,0,0,.4), 0 6px 18px rgba(0,0,0,.36);
  --lift-2: 0 8px 22px rgba(0,0,0,.5), 0 30px 70px rgba(0,0,0,.44);

  /* Meridian remap — night. (Custom properties don't cascade through var()
     indirection per-theme reliably when the fallback block re-declares them,
     so the remap is restated verbatim.) */
  --bg:          var(--field);
  --surface:     var(--sheet);
  --surface-2:   var(--sheet-2);
  --bg-inset:    var(--well);
  --line:        var(--rule);
  --line-strong: var(--rule-2);
  --accent:      var(--act);
  --accent-hover:var(--act-hover);
  --accent-mid:  var(--act-line);
  --accent-dim:  var(--act-soft);
  --accent-ink:  var(--act-ink);
  --ok-dim:      var(--ok-soft);
  --warn:        var(--attend);
  --warn-dim:    var(--attend-soft);
  --err:         var(--broken);
  --err-dim:     var(--broken-soft);
  --scrim:       var(--scrim-c);
  --elev-1:      var(--lift-1);
  --elev-2:      var(--lift-2);
  --sheen:       none;
  --focus-ring:  0 0 0 3px color-mix(in srgb, var(--act) 32%, transparent);

  --stage-1: var(--ink-3);  --stage-1-dim: var(--well);
  --stage-2: var(--ink-3);  --stage-2-dim: var(--well);
  --stage-3: var(--ink-3);  --stage-3-dim: var(--well);
  --stage-4: var(--ink-3);  --stage-4-dim: var(--well);
  --stage-5: var(--ink-3);  --stage-5-dim: var(--well);
  --stage-6: var(--ink-3);  --stage-6-dim: var(--well);
  --stage-7: var(--ink-3);  --stage-7-dim: var(--well);
}

/* ── 1c · TOKENS — theme-invariant ────────────────────────────────────────── */
:root {
  /* the three voices */
  --sans:  'Source Sans 3', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --serif: 'Source Serif 4', Charter, 'Iowan Old Style', Georgia, 'Times New Roman', serif;
  --mono:  'Source Code Pro', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --font:       var(--sans);
  --font-serif: var(--serif);
  --font-mono:  var(--mono);

  /* ATTEST scale — the floor is 12px, nothing smaller exists.
     Meridian tier names are re-pointed at ATTEST steps: the bottom tiers
     land on label/meta/scan, the read tier lands on 15.5px. */
  --t-label: 12px;     --t-10: var(--t-label);
                       --t-11: var(--t-label);
  --t-meta:  12.5px;   --t-12: var(--t-meta);
  --t-scan:  13.5px;   --t-13: var(--t-scan);
                       --t-14: 14px;
  --t-ui:    15px;     --t-15: var(--t-ui);
  --t-read:  15.5px;   --t-16: var(--t-read);
  --t-h3:    17px;     --t-17: var(--t-h3);
                       --t-18: 18px;
  --t-h2:    21px;     --t-20: var(--t-h2);
  --t-title: 24px;     --t-24: var(--t-title);
  --t-lead:  17.5px;
  --t-h1:    30px;
  --t-fig:   21px;
  --t-fig-lg:27px;

  --lh-read: 1.62;
  --lh-scan: 1.45;

  /* space steps the kit lacks (main.css already leans on --s6) */
  --s2: 2px;  --s6: 6px;  --s40: 40px;  --s56: 56px;
  --measure-read:  68ch;
  --measure-lead:  46ch;
  --measure-quote: 70ch;

  /* geometry — square structure, softened controls, no 999px pills on
     record fields (--r-full survives for genuine circles: dots, knobs) */
  --r0: 0px;  --r1: 3px;  --r2: 5px;  --r4: 3px;  --r6: 5px;  --r8: 8px;

  /* motion — one curve, one beat */
  --ease: cubic-bezier(.22,.68,.24,1);
  --dt-fast: 110ms;
}

/* ── 2 · BASE ─────────────────────────────────────────────────────────────
   The kit's reset already points html at var(--font) and var(--bg); the
   remap carries those. What follows is what tokens cannot reach. */
body {
  font-variant-numeric: tabular-nums lining-nums;   /* every quantity aligns */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
b, strong { font-weight: 600; }

/* native controls follow the theme (the kit only handled dark) */
input, select, textarea { color-scheme: light; }
[data-theme="dark"] input,
[data-theme="dark"] select,
[data-theme="dark"] textarea { color-scheme: dark; }

/* ── 3 · VOICE PRIMITIVES — the provenance contract (LAW 1) ────────────────
   These are the classes Phase Adoption writes into the markup. They exist
   now so the contract is stable before a single page is touched.

     .voice-record  the record's voice: verbatim quotes, requirement and AC
                    statements, human-authored text. Serif, read density.
                    Where the text is a quote, a .src-ref must sit adjacent.
     .voice-figure  a checkable quantity: counts, sums, dates, durations.
                    Tabular lining sans. Can you add it? It's a figure.
     .src-ref       a checkable identity: REQ-014, DOC-021, G1, keys,
                    endpoints. Mono. Can you only match it? It's a ref.
     .voice-system  explicit return to the system's voice inside a record
                    block (captions, interjections). */
.voice-record {
  font-family: var(--serif);
  font-size: var(--t-read);
  line-height: var(--lh-read);
  color: var(--ink-1);
  max-width: var(--measure-quote);
  font-variant-numeric: normal;
}
.voice-figure {
  font-family: var(--sans);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
  letter-spacing: -.005em;
}
.src-ref, .voice-ref {
  font-family: var(--mono);
  font-size: max(12px, .93em);       /* optical correction, floored at 12px */
  letter-spacing: -.01em;
  font-variant-numeric: normal;
}
.voice-system {
  font-family: var(--sans);
  font-variant-numeric: tabular-nums lining-nums;
}

/* the evidence block — QUOTED in full dress: serif behind a 2px rule,
   closed by its ref line. An .ev without an .ev-src is a defect. */
.ev {
  font-family: var(--serif);
  font-size: var(--t-read);
  line-height: 1.6;
  color: var(--ink-1);
  max-width: var(--measure-quote);
  border-left: 2px solid var(--ink-3);
  padding: 2px 0 2px var(--s16);
  font-variant-numeric: normal;
}
.ev-src {
  font-family: var(--sans);
  font-size: var(--t-meta);
  color: var(--ink-3);
  margin-top: var(--s8);
  display: flex; flex-wrap: wrap; gap: var(--s8); align-items: baseline;
}
.ev-src .sep { color: var(--ink-4); }

/* claim marks (LAW 6): proposed = open ◇, committed = filled ▪.
   Computed counts carry NO mark. */
/* In a flex or grid row the mark centres against the WHOLE text block beside it — one line:
   on that line; two lines: between them; three: on the middle row. Inline, inside prose, the
   property is inert and the -1px optical nudge does the job as before. */
.att { flex: none; display: inline-block; line-height: 1; position: relative; top: -1px; align-self: center; }
.att--held::before     { content: '▪'; color: var(--ink-1); }
.att--proposed::before { content: '◇'; color: var(--attend); }
.att--quoted::before   { content: '❝'; color: var(--ink-3); }
.tag-unheld {
  font-size: var(--t-meta); color: var(--attend); white-space: nowrap;
  font-weight: 500; letter-spacing: .01em;
}

/* the zero, the nothing and the unknown are three different things */
.n-zero    { color: var(--ink-1); }
.n-none    { color: var(--ink-4); }
.n-unknown { color: var(--ink-4); border-bottom: 1px dotted var(--ink-4); cursor: help; }

/* uppercase micro-label — the page's skeleton, not emphasis */
.u-label {
  font-size: var(--t-label); font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-4); line-height: 1;
}

/* read-mode prose block (Signet's own voice at reading density) */
.read {
  font-size: var(--t-read); line-height: var(--lh-read);
  max-width: var(--measure-read); color: var(--ink-2);
}
.read strong, .read b { color: var(--ink-1); font-weight: 600; }

/* ── 4 · KIT RESTYLES — where the token remap cannot reach ────────────────
   Same class vocabulary, equal-or-higher specificity, no !important sprawl. */

/* LAW 3 · mono means identity, sans means quantity. Meridian set every
   readout in mono; ATTEST returns the QUANTITY slots to tabular sans and
   keeps mono for refs/keys/code. The .mono utility itself stays mono (it
   marks refs all over the app) but gains the optical floor. */
.mono, .text-mono {
  font-family: var(--mono);
  font-size: max(12px, .93em);
  letter-spacing: -.01em;
}
code, kbd { font-size: max(12px, .85em); }
.stat-value {                       /* a count is a figure, not a console dump */
  font-family: var(--sans);
  font-variant-numeric: tabular-nums lining-nums;
}
.page-meta, .detail-card-meta {     /* dates and tallies — quantities */
  font-family: var(--sans);
  font-variant-numeric: tabular-nums lining-nums;
}
.nav-activity-count {               /* a count in the nav rail */
  font-family: var(--sans);
  font-variant-numeric: tabular-nums;
}

/* the brand tick was Meridian's accent meridian; under ATTEST the wordmark
   opens with the document rule — ink, not interaction colour */
.brand::before { background: var(--ink-1); }

/* dark theme: the committing control stays FILLED (the kit tinted it).
   Night act is bright, its ink is near-black — measured 8.35:1. */
[data-theme="dark"] .btn-accent {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
[data-theme="dark"] .btn-accent:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: var(--accent-ink);
}

/* LAW 2 · category never takes a hue — Meridian's five category tag colours
   collapse to monochrome; the label and position carry the kind. */
.meta-tag--cat-1, .meta-tag--cat-2, .meta-tag--cat-3, .meta-tag--cat-4, .meta-tag--cat-5,
[data-theme="dark"] .meta-tag--cat-1, [data-theme="dark"] .meta-tag--cat-2,
[data-theme="dark"] .meta-tag--cat-3, [data-theme="dark"] .meta-tag--cat-4,
[data-theme="dark"] .meta-tag--cat-5 {
  color: var(--ink-2);
  background: var(--surface-2);
  border-color: var(--line);
}

/* state text on solid state fills is per-theme ink, not hardcoded white */
.impersonation-banner .btn { background: var(--warn); color: var(--attend-ink); border: none; }
.impersonation-banner .btn:hover { opacity: .85; color: var(--attend-ink); }
.btn-danger:hover { background: var(--err); border-color: var(--err); color: var(--broken-ink); }
.toggle-input:checked + .toggle-slider::after { background: var(--sheet); }

/* headings carry warm ink and quiet tracking (kit values, re-affirmed for
   the swapped face) */
h1, h2, h3, h4, h5, h6 { letter-spacing: -.015em; }

/* focus ring — the one interaction accent */
:focus-visible { outline-color: var(--act); }

/* ── 5 · APP CLASSES (read-* / chat-* / client-* / tl-* vocabulary) ────────
   Reconciled IN PLACE in styles/main.css, which draws every colour and size
   from the tokens above — so it retints and revoices through the remap:
     .read-quote           → the record's voice (serif, read size, full ink)
     .read-prose/.chat-text→ read density via --t-16 = 15.5px
     .tl-date, .nav-activity-count, .notification-item-time, .notification-badge
                           → quantities in tabular sans; mono stays for refs
                             (.tl-chain) and the badge ink is per-theme.
   Nothing to override here; this section exists so the next reader does not
   re-add duplicates. */

/* ── 7 · THE PAGE KIT — one vocabulary for every operator page ─────────────
   Eleven tabs were built by eleven hands and each invented its own shell,
   masthead, zone head, instrument rail, sheet and table head. This section is
   the ONE registration of that shared machinery; pages carry these classes and
   add only what is genuinely theirs. The React side lives in
   src/client/components/PageKit.tsx (PageShell · PageHeader · Zone · ZoneHead ·
   Rail · RailCell · Sheet).

   THE OPERATOR'S DESIGN LAW, kit-level: content sits on WHITE SHEETS against
   the warm field; controls live INSIDE those sheets; the field shows only
   between sheets. The rail is a sheet. The masthead is type on the field,
   closed by the page's one 2px ink rule — the only structure that may sit on
   the field besides zone heads and spacing.

   Scoped to .shell (the operator console). The portal keeps its own pinned
   document look and never picks these up. */

/* the shell: every page starts at the same coordinates */
.shell .page { max-width: 1180px; margin: 0 auto; padding: 26px 24px 90px; }

/* ── masthead — one page header: same face, same sizes, same baseline ── */
.mast { border-bottom: 2px solid var(--ink-1); padding-bottom: 14px; }
.mast-top { display: flex; align-items: flex-end; gap: var(--s24); flex-wrap: wrap; }
.mast-main { flex: 1 1 auto; min-width: 0; }
.mast h1 {
  margin: 0; font-size: var(--t-h1); line-height: 1.1;
  font-weight: 600; letter-spacing: -.02em;
}
.mast h1 .src-ref { font-size: inherit; font-weight: 500; color: var(--ink-2); }
.mast-sub {
  margin-top: var(--s6); font-size: var(--t-meta); color: var(--ink-3);
  display: flex; gap: var(--s8); flex-wrap: wrap; align-items: baseline;
}
.mast-verbs {
  margin-left: auto; display: flex; gap: var(--s8); align-items: center;
  flex-wrap: wrap; justify-content: flex-end;
}
.mast-notice { font-size: var(--t-meta); color: var(--warn); }
.mast .sep, .zone-head .sep, .fd .sep { color: var(--ink-4); }

/* ── the instrument — a five-cell rail ON A SHEET, riding the mast rule ── */
.instr {
  background: var(--sheet); border: 1px solid var(--rule); border-top: 0;
  border-radius: 0 0 var(--r2) var(--r2); overflow: hidden;
}
.rail { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); }
.fld {
  display: block; width: 100%; text-align: left; min-width: 0; color: var(--ink-1);
  padding: 12px var(--s16) 13px; border: 0; border-left: 1px solid var(--rule);
  background: none; font: inherit; transition: background var(--dt-fast) var(--ease);
}
.rail .fld:first-child { border-left: 0; }
button.fld { cursor: pointer; }
button.fld:disabled { cursor: default; }
button.fld:hover:not(:disabled) { background: var(--sheet-2); }
button.fld:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.fld[aria-pressed="true"] { background: var(--act-soft); }
.fld[aria-pressed="true"] .go { color: var(--act); }
.fl { display: flex; align-items: center; gap: var(--s8); white-space: nowrap; }
.fl .go {
  margin-left: auto; letter-spacing: 0; text-transform: none; font-weight: 400;
  color: var(--ink-4); font-size: var(--t-meta);
}
button.fld:hover:not(:disabled) .go { color: var(--act); }
.fv {
  display: block; margin-top: 5px; font-size: var(--t-fig); line-height: 1.05;
  color: var(--ink-1); font-weight: 600; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.fv .u {
  font-size: var(--t-meta); color: var(--ink-3); letter-spacing: .02em;
  margin-left: 5px; font-weight: 400; text-transform: none;
}
.fv--warn, .fv--attend { color: var(--warn); }
.fv--ok { color: var(--ok); }
.fv--err, .fv--broken { color: var(--err); }
.fv--nil { color: var(--ink-4); font-weight: 400; }
/* B8: 'not counted' is not 'counted as zero' — dotted, hugging the glyph */
.fv--unknown {
  color: var(--ink-4); font-weight: 400;
  text-decoration: underline dotted; text-underline-offset: 4px;
}
.fd {
  display: block; margin-top: 5px; min-height: 17px; font-size: var(--t-meta);
  line-height: 17px; color: var(--ink-3); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* a control strip INSIDE the instrument sheet (filter bar, master switch, status line) */
.instr-band {
  display: flex; align-items: center; gap: var(--s12); flex-wrap: wrap;
  padding: var(--s8) var(--s16); border-top: 1px solid var(--rule);
  background: var(--surface-2);
}
.instr > .instr-band:first-child { border-top: 0; }

@media (max-width: 1080px) {
  .rail { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .rail .fld:nth-child(4) { border-left: 0; }
  .rail .fld:nth-child(n+4) { border-top: 1px solid var(--rule); }
}
@media (max-width: 720px) {
  .shell .page { padding: 16px 16px 60px; }
  .rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rail .fld { border-left: 1px solid var(--rule); }
  .rail .fld:nth-child(odd) { border-left: 0; }
  .rail .fld:nth-child(n+3) { border-top: 1px solid var(--rule); }
}

/* ── zone rhythm — the 1px rule sits UNDER the zone head ── */
.zone { margin-top: 30px; scroll-margin-top: calc(var(--topbar-h, 48px) + var(--s16)); }
.zone-head {
  display: flex; align-items: baseline; gap: var(--s12); flex-wrap: wrap;
  border-bottom: 1px solid var(--rule); padding-bottom: 7px; margin-bottom: 14px;
}
.zone-head h2.u-label { margin: 0; }
.zone-count {
  font-size: var(--t-meta); color: var(--ink-3); font-weight: 400;
  letter-spacing: 0; text-transform: none; font-variant-numeric: tabular-nums;
}
.zone-count--attend { color: var(--warn); }
.zone-note {
  font-size: var(--t-meta); color: var(--ink-3); font-weight: 400;
  letter-spacing: 0; text-transform: none;
}
.zone-note--end { margin-left: auto; text-align: right; }
.zone-route {
  margin-left: auto; font-size: var(--t-meta); color: var(--act);
  background: none; border: 0; cursor: pointer; padding: 0; font-family: inherit;
  font-weight: 400; letter-spacing: 0; text-transform: none;
}
.zone-route:hover { color: var(--act-hover); text-decoration: underline; }
.zone-empty {
  font-size: var(--t-read); line-height: var(--lh-read); color: var(--ink-2);
  max-width: var(--measure-read);
}

/* ── the sheet — the standard bordered white container ── */
.sheet { background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--r2); }
.sheet--clip { overflow: hidden; }
.sheet--strong { border-color: var(--rule-2); }
/* the zone's ONE loud card: a 2px ink edge (state hues may override the edge) */
.sheet--edge { border-color: var(--rule-2); border-left: 2px solid var(--ink-1); }
.sheet--pad { padding: var(--s16) var(--s20); }
.sheet-head {
  display: flex; align-items: center; gap: var(--s12); flex-wrap: wrap;
  padding: var(--s8) var(--s16); background: var(--surface-2);
  border-bottom: 1px solid var(--rule);
}
.sheet-foot {
  display: flex; align-items: center; gap: var(--s12); flex-wrap: wrap;
  padding: var(--s8) var(--s16); background: var(--surface-2);
  border-top: 1px solid var(--rule); font-size: var(--t-meta); color: var(--ink-3);
}

/* ── one table head for every grid ── */
table.tbl { width: 100%; border-collapse: collapse; }
.tbl thead th {
  font-size: var(--t-label); font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-4); text-align: left;
  padding: 7px 8px; background: var(--surface-2);
  border-bottom: 1px solid var(--rule); white-space: nowrap;
}
.tbl th:first-child, .tbl td:first-child { padding-left: var(--s16); }
.tbl th:last-child, .tbl td:last-child { padding-right: var(--s16); }
.tbl td {
  padding: 9px 8px; font-size: var(--t-scan); vertical-align: top;
  border-bottom: 1px solid var(--rule);
}
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl td.num, .tbl th.num { text-align: right; font-variant-numeric: tabular-nums lining-nums; }

/* ── the row's overflow — a destructive cascade is not a peer of a routing
      control; it lives behind one quiet ⋯. Portaled to <body> (PageKit.tsx
      RowMenu) and positioned in JS off the trigger's own rect — `fixed`, not
      `absolute` off `.rowmenu-wrap`, so a `.sheet--clip` ancestor's clip
      never crops it and a row against the window's lower edge gets the menu
      flipped above the trigger instead of cut off below it. ── */
.rowmenu-wrap { position: relative; }
.rowmenu {
  position: fixed; z-index: 50; min-width: 210px;
  background: var(--sheet); border: 1px solid var(--rule-2); border-radius: var(--r2);
  box-shadow: var(--lift-1); padding: 5px;
}
.rowmenu button {
  display: block; width: 100%; text-align: left; border: 0; background: transparent;
  padding: 7px 10px; border-radius: var(--r1); font-size: var(--t-scan);
  cursor: pointer; color: var(--ink-2); font-family: inherit;
}
.rowmenu button:hover { background: var(--well); color: var(--ink-1); }
.rowmenu .rowmenu-danger { color: var(--err); }

/* ── the text-level verb — a link, never a boxed peer of a filled control ── */
.lnk {
  font: inherit; font-size: var(--t-meta); color: var(--act); background: none;
  border: 0; padding: 0; cursor: pointer; text-align: left;
  text-decoration: underline; text-decoration-color: var(--act-line);
  text-underline-offset: 3px;
}
.lnk:hover { text-decoration-color: var(--act); }
.lnk:disabled { opacity: .45; cursor: not-allowed; text-decoration: none; }

/* ── one tag shape: a state, a kind, a flag — square-ish, never a pill ── */
.tag {
  display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-label);
  padding: 1px 7px; border: 1px solid var(--rule-2); border-radius: var(--r1);
  color: var(--ink-2); background: var(--sheet-2); white-space: nowrap; line-height: 1.6;
}
.tag--ok { color: var(--ok); border-color: var(--ok-line); background: var(--ok-soft); }
.tag--attend { color: var(--attend); border-color: var(--attend-line); background: var(--attend-soft); }
.tag--broken { color: var(--broken); border-color: var(--broken-line); background: var(--broken-soft); }
.tag--ghost { border-style: dashed; color: var(--ink-3); background: none; }

/* ═══ PHONE — shell chrome ≤820px ═══════════════════════════════════════════
   One breakpoint for the console frame: below it the sidebar goes off-canvas
   (AppShell.tsx flips a `drawer` state via matchMedia — the desktop collapse
   toggle at ≥821px is untouched), the topbar stays a single 48px row by
   compacting rather than wrapping (a second row would desync every fixed
   `top: var(--topbar-h)` anchor downstream), and the kit's modal sizing gets
   a phone-safe ceiling. Page-level overrides (tables, roadmap, form rows)
   are in main.css under the matching banner. */
@media (max-width: 820px) {
  /* the drawer: .sidebar is already `position: fixed` (Meridian) — phone just
     starts it off-screen and slides it in as an overlay instead of a column
     that pushes .content. */
  .sidebar {
    transform: translateX(-100%);
    width: var(--sidebar-w, 216px);
    max-width: 84vw;
    z-index: 195;
    box-shadow: var(--elev-2);
    transition: transform var(--dt-mid, 200ms) var(--ease);
  }
  .shell--drawer-open .sidebar { transform: translateX(0); }

  /* the drawer never pushes content — it floats over it, sidebar or not.
     `min-width: 0` overrides the flex item's automatic (content-based)
     minimum — without it `.content` refuses to shrink past whatever
     unbreakable content it holds (a wide table, a nowrap label) and drags
     `.shell`, and the whole page, out past the viewport instead of leaving
     that content to its own overflow-x. */
  .shell .content { margin-left: 0; min-width: 0; }

  /* the scrim (AppShell.tsx renders it only while the drawer is open) —
     started below the topbar so the topbar stays reachable/legible while open */
  .shell-scrim {
    position: fixed; top: var(--topbar-h, 48px); left: 0; right: 0; bottom: 0;
    background: var(--scrim); z-index: 194;
  }

  /* the engagement select shrinks with the topbar instead of holding its
     desktop min-width and pushing the right cluster off the row. Basis 0,
     not auto: an engagement's `CODE · Name` option text is long, and an
     auto basis would size the select (and so `.topbar-right`, and so what's
     left for `.topbar-left`) off that full text instead of the space
     actually available — the brand mark would lose the fight for room and
     the select's own box would paint over it. */
  .topbar-eng-select { flex: 1 1 0%; min-width: 0; }
  /* the brand keeps its few natural pixels no matter how the select flexes —
     `.topbar-left` starts from the kit's `min-width: 0`, which otherwise
     lets it shrink UNDER "☰ SIGNET" and hand the overflow to the select's
     opaque background to paint over */
  .topbar-left { flex-shrink: 0; }

  /* logout goes icon-only once the row is genuinely tight; the icon+label
     pair is always in the DOM (one accessible name throughout), CSS just
     picks which half shows */
  .logout-btn { display: inline-flex; align-items: center; gap: var(--s6, 6px); }
  .logout-icon { display: none; font-size: var(--t-13); line-height: 1; }

  /* kit modals (lib modals.css, z 200) — the existing width:90%/max-height:90vh
     already fits a phone; this just puts a hard viewport ceiling under it and
     lets a footer with several actions wrap instead of squeezing. */
  .modal, .modal-wide { max-width: min(480px, calc(100vw - var(--s24, 24px))); }
  .modal-full { max-width: calc(100vw - var(--s16, 16px)); }
  .modal-footer { flex-wrap: wrap; }
}
@media (max-width: 820px) and (prefers-reduced-motion: reduce) {
  .sidebar { transition: none; }
}

/* very narrow phones (≤480px): the topbar sheds its least essential pixels
   before anything is allowed to overflow or wrap onto a second row. Even
   so, six controls plus a select in one 48px row leaves the select genuinely
   narrow at this width — a known limitation (see the phone report), not
   something a topbar this populated can fully out-compact. */
@media (max-width: 480px) {
  .brand-page, .brand-sep { display: none; }
  .topbar { padding: 0 var(--s8, 8px); }
  .topbar-left, .topbar-right { gap: var(--s6, 6px); }
  .logout-label { display: none; }
  .logout-icon { display: inline; }
  /* the EN/DE/FR switch gives back its desktop padding — still three legible
     two-letter buttons, just not three 8px-cushioned ones */
  .topbar .lang-switch-btn { padding: 3px 5px; }
  /* deliberately NO min-width floor on the select here: six controls plus a
     select in one unwrapping 48px row is a fixed budget, and flex-shrink
     down to the `min-width: 0` above is what GUARANTEES the row never
     overflows past the viewport — a floor that only sometimes fits was
     tried and produced exactly that, the logout button pushed a few px past
     the edge. Below ~400px the select genuinely narrows to a sliver; that's
     the honest trade, not a bug (see the phone report). */
}

/* ── 9 · PRINT — documents leave the screen ────────────────────────────────
   Positive polarity is already the default; print just drops the chrome. */
@media print {
  :root { --field: #fff; --sheet: #fff; --sheet-2: #fff; --well: #f4f2ee; }
  .topbar, .sidebar, .toast-stack { display: none !important; }
  .content { margin-left: 0; }
  .shell { margin-top: 0; }
}
