/* ============================================================================
   THE SHELL — the portal redesign he approved on 2026-08-07 ("I approve this
   wireframe"), built from `organization\portal-redesign-wireframe.html` v3.

   THIS SHEET IS THE DESIGN SYSTEM NOW. It loads LAST, after tokens.css,
   chart.css, app.css and grid.css, and it does three things in this order:

     1. THE TYPE, SELF-HOSTED. Instrument Sans and IBM Plex Mono as real files
        in `static\fonts\`, declared here with @font-face.
     2. THE TOKENS, exactly as the approved wireframe carries them — light and
        dark, both hand-tuned, prefers-color-scheme as the default and a manual
        override on :root[data-theme].
     3. THE BRIDGE, then the surfaces. Every legacy token from the July build
        is re-pointed at a new one, so the screens that were RE-HOMED rather
        than rewritten (the statistics wall, the org board panel, the despatch
        record, every dialog) adopt the new palette and the new face in one
        move instead of being re-authored line by line. The shell chrome and
        the Today surface are then drawn against the new tokens directly.

   ── WHY THE FONTS ARE SELF-HOSTED, AND WHY THAT WAS NOT A PREFERENCE ───────

   The wireframe links Google Fonts. This application ships under

       default-src 'self'; script-src 'self'; style-src 'self'; ...

   (`vercel.json`). There is no `font-src`, so fonts fall back to `default-src`,
   which is `'self'` — and `style-src 'self'` blocks the Google stylesheet
   before the fonts are ever reached. Both halves of the wireframe's <link>
   are refused by the policy as it stands.

   THE FIX IS NOT TO WIDEN THE HEADER. Adding `https://fonts.googleapis.com`
   and `https://fonts.gstatic.com` to a Content-Security-Policy in exchange for
   a typeface spends a security property on a cosmetic one, and it is the same
   trade `theme.js` refuses in its own header comment. The two families are SIL
   Open Font Licence and are served from this application's own origin:
   127 KB of woff2 for the whole portal, latin and latin-ext only.

   ── WHAT THE BOARD KEEPS ───────────────────────────────────────────────────

   NOTHING HERE TOUCHES THE ORG BOARD. `chart.css` re-locks `--ink`, `--surface`
   and the sheet tokens on `.board` and on `.despatch-sheet`, so the chart stays
   printed matter in source's own colours on paper in both themes. Measured, not
   assumed: those are the only two places in the app that read `--ink`/`--surface`
   and both re-declare them on themselves.

   ── ONE DEVIATION FROM THE WIREFRAME'S LITERAL PIXELS, DECLARED ────────────

   THE APPROVED WIREFRAME SETS ITS SMALL TYPE AT 9.5px TO 12.5px — the division
   labels, the count line, the condition chip, the strip note. THIS PORTAL HAS A
   MEASURED 13px FLOOR (STYLE-GUIDE §3 rule 4; `_audit.html` reports
   `below-floor:0` or the build fails, at all three reading steps in both
   themes), and it exists for his eyesight, not for tidiness. Measured before
   deciding: the wireframe's own sizes put SEVENTY elements under it.

   So every one of those steps is `var(--ui-caption)` — 13px, and it rises with
   his reading step instead of staying pinned. The layout, the hierarchy, the
   colour and the proportions are the approved design exactly; the smallest
   labels are one step larger than the picture. HIS TO OVERRULE: if he wants the
   picture's exact sizes, the floor is one constant in `_audit.html` and one
   token here.

   ── THE LAW THIS SHEET ENFORCES ────────────────────────────────────────────

   DIVISION COLOURS ARE IDENTITY, NEVER DATA (the brief, 2026-08-07). The
   board's certified seven appear here as a 3px top rail on a tile and as a
   swatch beside a text label, and NOWHERE ELSE: never as a text colour, never
   as a sparkline stroke, never as a fill that carries a value. The palette
   validator measured them failing as mark colours — Division 1's yellow reads
   1.11:1, Division 5's grey reads grey, two pairs blur under colour-vision
   deficiency. So data marks wear ink (`--spark`), and colour that MEANS
   something is exactly three: red is alarm, amber is caution, viridian is the
   one action colour.
   ============================================================================ */

/* ==========================================================================
   1. THE TYPE — self-hosted, latin + latin-ext, nothing else.
   Instrument Sans is one VARIABLE file per subset and carries 400-700 in it;
   IBM Plex Mono ships static instances, so 400/500/600 are three files each.
   ========================================================================== */
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/static/fonts/InstrumentSans-var-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: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/static/fonts/InstrumentSans-var-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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/IBMPlexMono-400-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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/IBMPlexMono-400-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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/IBMPlexMono-500-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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/IBMPlexMono-500-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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/IBMPlexMono-600-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: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/IBMPlexMono-600-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;
}

/* ==========================================================================
   2. THE TOKENS — the approved wireframe's own set, carried value for value.

   THE ORDER OF THE THREE BLOCKS IS THE MECHANISM, not a formatting choice:

     :root                                   the light values, always defined
     @media (prefers-color-scheme: dark)     the device's own preference wins
                                             when he has expressed no choice
     :root[data-theme="light"|"dark"]        HIS choice, when he has made one,
                                             beats both — it is written last so
                                             it wins on cascade order at equal
                                             specificity, and `theme.js` sets
                                             the attribute before first paint

   Every surface, text and status token is themed BOTH ways. There is no token
   in this block that exists in one theme and not the other; the harness
   measures that, in the browser, rather than trusting the eye on it.
   ========================================================================== */
:root {
  color-scheme: light dark;

  --canvas: #F6F7F9;
  --surface: #FFFFFF;
  --ink: #0F172A;
  --muted: #5B6472;
  --line: #E4E7EC;
  /* THE BRAND KEY (his ruling, 2026-08-07): NAVY PRIMARY, GOLD ACCENT, both
     measured off the company's own logo. Primary carries every act — buttons,
     active nav, links, focus. Gold carries identity — the mark, a selected
     marker, a chosen row. GREEN IS NOT A BRAND COLOUR ANY MORE: the viridian
     survives as `--good` and means one thing, status-good. */
  --accent: #19273A;
  --accent-soft: #E9EDF3;
  --accent-ink: #19273A;
  --gold: #856528;
  --gold-soft: #F4EEE2;
  --gold-ink: #856528;
  --on-gold: #FFFFFF;
  --gold-hover: #6E5321;
  --good: #0B7A5C;
  --good-soft: #E7F4EF;
  --good-ink: #0B7A5C;
  --on-good: #FFFFFF;
  --danger: #B42318;
  --danger-soft: #FEF0EE;
  --danger-line: #F5D5D0;
  --amber: #B54708;
  --amber-soft: #FDF3E7;
  --quiet: #CBD2DB;
  --spark: #33415C;

  /* --- derived from the fifteen above, WRITTEN OUT rather than computed in
         the sheet. `color-mix()` is perfectly legal CSS and the browser draws
         it correctly, but `getComputedStyle` hands a custom property back as
         the UNRESOLVED function text — so a token declared with it cannot be
         measured, and a value nobody can measure is a value nobody can hold to
         a contrast floor. Measured, not assumed: the audit instrument crashed
         on `color-mix(in srgb, #2A313B 38%, #1A1F26)` the first time this
         shipped. Each value below is the same mix, resolved once. --------- */

  /* --- THE LABEL ON A FILLED CHIP OR BUTTON -----------------------------
     DECLARED DIVERGENCE FROM THE APPROVED WIREFRAME, and it is a MEASUREMENT,
     not a taste. The wireframe writes `color: #fff` on the accent button and on
     the DANGER chip. In light that is right: white on #0B7A5C measures 5.31:1
     and white on #B42318 measures 6.57:1. IN DARK IT IS NOT: white on the dark
     accent #34A183 measures 3.20:1 and white on the dark danger #E5695C
     measures 3.23:1, both under the 4.5:1 floor this application's own audit
     enforces on a 13px semibold label.

     So the label colour became a token instead of a literal. The approved
     values are untouched in light -- #FFFFFF, exactly as he approved -- and in
     dark the label is the canvas ink, which measures 5.72:1 and 5.66:1 on the
     same two fills. The button is still a solid viridian button and the chip is
     still a solid red chip; only the ink on them moves, and only in dark.
     RAISED TO HIM AS A DIVERGENCE rather than settled here. */
  --on-accent: #FFFFFF;
  --on-danger: #FFFFFF;

  --bg-2: #F5F6F8;          /* line 38% over surface — grid head, zebra, washes */
  --bg-hover: #F0F2F5;      /* line 55% over surface */
  --brand-hover: #22344B;   /* navy lifted one step — white on it reads 12.64:1 */
  --scrim: rgba(15, 23, 42, .42);
  --shadow-pop:   0 2px 6px rgba(15, 23, 42, .10),
                  0 12px 32px rgba(15, 23, 42, .14);
  --shadow-lift:  0 4px 12px rgba(15, 23, 42, .12),
                  0 24px 64px rgba(15, 23, 42, .18);
  --shadow-panel: -12px 0 48px rgba(15, 23, 42, .20);

  --sans: 'Instrument Sans', -apple-system, 'Segoe UI', system-ui, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --canvas: #12151B;
    --surface: #1A1F26;
    --ink: #E7EAEF;
    --muted: #98A2B0;
    --line: #2A313B;
    /* THE BRAND KEY IN DARK. Navy is invisible on a dark canvas, so the
       primary is the measured steel lift #52709A (white on it, 5.07:1) and
       the primary-as-TEXT step is lifted again to #7C97BF, which clears the
       floor on the canvas (6.12), on a surface (5.55) and on its own wash
       (4.85). Gold lifts to #C9A35E. */
    --accent: #52709A;
    --accent-soft: #1E2A3C;
    --accent-ink: #7C97BF;
    --gold: #C9A35E;
    --gold-soft: #33291A;
    --gold-ink: #C9A35E;
    --on-gold: #12151B;
    --gold-hover: #B78F4B;
    --good: #34A183;
    --good-soft: #14322A;
    --good-ink: #4CBD9C;
    --on-good: #12151B;
    --danger: #E5695C;
    --danger-soft: #341E1B;
    --danger-line: #4A2A25;
    --amber: #E1A13E;
    --amber-soft: #34290F;
    --quiet: #3A4450;
    --spark: #AFBDD6;

    /* --- derived, written out for the same reason as the light block ------- */

    /* the label on a filled chip or button -- see the light block for the
       measurement that put these here. THE NAVY PRIMARY REVERSES THE DARK
       RULE: white on the steel #52709A measures 5.07:1 and the canvas ink
       measures 3.61:1, so on this fill the white label is the one that
       clears. The danger chip is unchanged and still takes the canvas ink. */
    --on-accent: #FFFFFF;
    --on-danger: #12151B;
    --bg-2: #20262E;
    --bg-hover: #232932;
    --brand-hover: #47638F;
    --scrim: rgba(4, 6, 10, .62);
    --shadow-pop:   0 2px 6px rgba(0, 0, 0, .35),
                    0 12px 32px rgba(0, 0, 0, .45);
    --shadow-lift:  0 4px 12px rgba(0, 0, 0, .40),
                    0 24px 64px rgba(0, 0, 0, .55);
    --shadow-panel: -12px 0 48px rgba(0, 0, 0, .55);
  }
}

:root[data-theme="light"] {
  color-scheme: light;
  --canvas: #F6F7F9;
  --surface: #FFFFFF;
  --ink: #0F172A;
  --muted: #5B6472;
  --line: #E4E7EC;
  --accent: #19273A;
  --accent-soft: #E9EDF3;
  --accent-ink: #19273A;
  --gold: #856528;
  --gold-soft: #F4EEE2;
  --gold-ink: #856528;
  --on-gold: #FFFFFF;
  --gold-hover: #6E5321;
  --good: #0B7A5C;
  --good-soft: #E7F4EF;
  --good-ink: #0B7A5C;
  --on-good: #FFFFFF;
  --danger: #B42318;
  --danger-soft: #FEF0EE;
  --danger-line: #F5D5D0;
  --amber: #B54708;
  --amber-soft: #FDF3E7;
  --quiet: #CBD2DB;
  --spark: #33415C;

  /* --- derived from the fifteen above, WRITTEN OUT rather than computed in
         the sheet. `color-mix()` is perfectly legal CSS and the browser draws
         it correctly, but `getComputedStyle` hands a custom property back as
         the UNRESOLVED function text — so a token declared with it cannot be
         measured, and a value nobody can measure is a value nobody can hold to
         a contrast floor. Measured, not assumed: the audit instrument crashed
         on `color-mix(in srgb, #2A313B 38%, #1A1F26)` the first time this
         shipped. Each value below is the same mix, resolved once. --------- */

  /* --- THE LABEL ON A FILLED CHIP OR BUTTON -----------------------------
     DECLARED DIVERGENCE FROM THE APPROVED WIREFRAME, and it is a MEASUREMENT,
     not a taste. The wireframe writes `color: #fff` on the accent button and on
     the DANGER chip. In light that is right: white on #0B7A5C measures 5.31:1
     and white on #B42318 measures 6.57:1. IN DARK IT IS NOT: white on the dark
     accent #34A183 measures 3.20:1 and white on the dark danger #E5695C
     measures 3.23:1, both under the 4.5:1 floor this application's own audit
     enforces on a 13px semibold label.

     So the label colour became a token instead of a literal. The approved
     values are untouched in light -- #FFFFFF, exactly as he approved -- and in
     dark the label is the canvas ink, which measures 5.72:1 and 5.66:1 on the
     same two fills. The button is still a solid viridian button and the chip is
     still a solid red chip; only the ink on them moves, and only in dark.
     RAISED TO HIM AS A DIVERGENCE rather than settled here. */
  --on-accent: #FFFFFF;
  --on-danger: #FFFFFF;

  --bg-2: #F5F6F8;          /* line 38% over surface — grid head, zebra, washes */
  --bg-hover: #F0F2F5;      /* line 55% over surface */
  --brand-hover: #22344B;   /* navy lifted one step — white on it reads 12.64:1 */
  --scrim: rgba(15, 23, 42, .42);
  --shadow-pop:   0 2px 6px rgba(15, 23, 42, .10),
                  0 12px 32px rgba(15, 23, 42, .14);
  --shadow-lift:  0 4px 12px rgba(15, 23, 42, .12),
                  0 24px 64px rgba(15, 23, 42, .18);
  --shadow-panel: -12px 0 48px rgba(15, 23, 42, .20);
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --canvas: #12151B;
  --surface: #1A1F26;
  --ink: #E7EAEF;
  --muted: #98A2B0;
  --line: #2A313B;
  --accent: #52709A;
  --accent-soft: #1E2A3C;
  --accent-ink: #7C97BF;
  --gold: #C9A35E;
  --gold-soft: #33291A;
  --gold-ink: #C9A35E;
  --on-gold: #12151B;
  --gold-hover: #B78F4B;
  --good: #34A183;
  --good-soft: #14322A;
  --good-ink: #4CBD9C;
  --on-good: #12151B;
  --danger: #E5695C;
  --danger-soft: #341E1B;
  --danger-line: #4A2A25;
  --amber: #E1A13E;
  --amber-soft: #34290F;
  --quiet: #3A4450;
  --spark: #AFBDD6;

  /* --- derived, written out for the same reason as the light block ------- */

  /* the label on a filled chip or button -- see the light block for the
     measurement that put these here */
  --on-accent: #FFFFFF;
  --on-danger: #12151B;
  --bg-2: #20262E;
  --bg-hover: #232932;
  --brand-hover: #47638F;
  --scrim: rgba(4, 6, 10, .62);
  --shadow-pop:   0 2px 6px rgba(0, 0, 0, .35),
                  0 12px 32px rgba(0, 0, 0, .45);
  --shadow-lift:  0 4px 12px rgba(0, 0, 0, .40),
                  0 24px 64px rgba(0, 0, 0, .55);
  --shadow-panel: -12px 0 48px rgba(0, 0, 0, .55);
}

/* ==========================================================================
   3. THE BRIDGE — the July vocabulary, re-pointed at the new system.

   Every screen that was re-homed rather than rewritten reads these names. One
   re-pointing carries all of them: the statistics wall, the staff grid, the
   despatch record, every dialog and the slide-over panel. This is what "re-home,
   don't rewrite" is, mechanically.

   THE PAIRS ARE NOT ARBITRARY:
     --bg-3  is the page          -> --canvas
     --bg-1  is a raised surface  -> --surface
     --bg-2  is a subtle fill     -> a wash mixed from the line colour
     --fg-1/2 are ink and         -> --ink / --muted
     --fg-3/4 are the quiet steps -> --muted / --quiet
     --brand is the one act       -> --accent
   The reading-step and spacing scales are untouched — they were never the
   thing he ruled on, and moving them would churn every screen for nothing.
   ========================================================================== */
:root,
:root[data-theme="light"],
:root[data-theme="dark"] {
  /* --- the faces ------------------------------------------------------- */
  --ui-font: var(--sans);
  --ui-font-display: var(--sans);
  /* THE SERIF IS RETIRED. It was the failed build's title face; his ruling of
     2026-08-06 dropped that look whole. The token stays declared so no rule in
     app.css or grid.css resolves to nothing — it simply names the sans now. */
  --ui-font-serif: var(--sans);
  --ui-mono: var(--mono);
  --face-text: var(--sans);
  --face-display: var(--sans);
  --face-mono: var(--mono);

  /* --- the ramp: REGULAR SIZING, per the wireframe ---------------------- */
  /* 15px reading, 14px chrome, 13px metadata. Only the reading step moved
     (16 -> 15): the chrome step and the caption floor are already what the
     wireframe asks for, and moving them would drop type under the 13px floor
     the audit instrument enforces. */
  --ui-body: 0.9375rem;      /* 15px — the wireframe's body */
  --ui-title: 1.375rem;      /* 22px — the wireframe's page title */
  --ui-title-lh: 1.2;
  --ui-track-title: -0.02em;

  /* --- ink and paper ---------------------------------------------------- */
  --fg-1: var(--ink);
  --fg-2: var(--muted);
  --fg-3: var(--muted);
  --fg-4: var(--quiet);

  --bg-3: var(--canvas);
  --bg-1: var(--surface);
  --stroke-1: var(--line);
  --stroke-2: var(--line);

  /* --- glass is retired: a flat surface and a hairline ------------------ */
  --glass-1: var(--surface);
  --glass-2: var(--surface);
  --glass-hover: var(--bg-hover);
  --glass-edge: transparent;
  --glass-blur: none;
  --glass-stroke: var(--line);
  --glass-inset: none;
  --scrim-blur: blur(3px);

  /* --- the one action colour -------------------------------------------
     TWO TOKENS, BECAUSE A FILL AND AN INK ARE NOT THE SAME COLOUR — and on
     this palette that is a measurement, not a nicety. The brand navy lifts to
     a steel (#52709A) on dark surfaces so that WHITE reads on it (5.07:1). The
     same steel used as TEXT on a dark surface reads 3.27:1 — under the floor.
     The older sheets (`app.css`, `grid.css`) paint links, chips and the active
     door with the brand colour, and after the navy/gold re-key of 2026-08-07
     every one of them was failing in dark theme; `test_restyle.py` §2 measured
     it. So:

       --brand      THE FILL. What a button, a rail or a chip's ground wears.
       --brand-ink  THE TEXT. Identical to --brand in light, where the navy is
                    already 15:1 on a surface, and the lifted ink in dark.

     Every `color:` that used to read --brand reads --brand-ink now. Nothing
     else moved. */
  --brand: var(--accent);
  --brand-ink: var(--accent-ink);
  --brand-wash: var(--accent-soft);
  --brand-fg: var(--on-accent);

  /* --- status: red is alarm, amber is caution, and nothing else -------- */
  --status-open-fg: var(--amber);
  --status-open-bg: var(--amber-soft);
  --flag-fg: var(--amber);
  --flag-bg: var(--amber-soft);
  --danger-bg: var(--danger-soft);

  --grid-head: var(--bg-2);
  --grid-zebra: var(--bg-2);

  /* --- shape: the wireframe's radii ------------------------------------ */
  --radius: 10px;
  --radius-lg: 12px;
  --radius-sm: 8px;
  --ctrl-h: 36px;
  --ctrl-px: 14px;
  --w-sidebar: 216px;

  /* --- depth: a surface step and a hairline, no shadow on anything at
         rest. Overlays keep a shadow because they float. ---------------- */
  --shadow-glass: none;
}

/* THE BOARD IS PRINTED MATTER AND STAYS SO. chart.css re-locks --ink and
   --surface on .board and .despatch-sheet off the --sheet-* tokens, which this
   sheet does not touch. This rule only restores the two faces the chart uses,
   because the bridge above re-pointed --face-text and --face-display at the
   app's sans and the chart is not the app. */
.board,
.despatch-sheet {
  --face-text: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial,
               sans-serif;
  --face-display: "Bahnschrift", "DIN Alternate", "Franklin Gothic Medium",
                  "Segoe UI", system-ui, sans-serif;
}

/* ==========================================================================
   4. THE SHELL CHROME — the wireframe's frame, nav, topline.
   ========================================================================== */
body {
  font-family: var(--sans);
  background: var(--canvas);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

/* ---- THE FRAME: a top bar across the whole width, doors down the left -----
   The approved grid wireframe's own structure. The shell becomes a COLUMN —
   bar, then the frame — and the two full-height panes inside the frame are
   sized against the bar's height rather than against the viewport, so nothing
   scrolls off the bottom by exactly the height of the bar. */
:root { --topbar-h: 52px; }

.shell { display: flex; flex-direction: column; min-height: 100vh; }
.frame { display: flex; flex: 1 1 auto; min-height: 0; }

.topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  flex: 0 0 var(--topbar-h);
  height: var(--topbar-h);
  padding: 0 20px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 40;
}
.tb-brand {
  display: flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  color: var(--ink);
  font-size: var(--ui-ui);
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.tb-brand:hover { color: var(--accent-ink); }

/* ---- the search field, and its results ---------------------------------- */
.tb-search { position: relative; flex: 1 1 auto; max-width: 460px; }
.gs-ico {
  position: absolute;
  left: 10px;
  top: 50%;
  width: 15px;
  height: 15px;
  margin-top: -7.5px;
  color: var(--muted);
  pointer-events: none;
}
.gs-input {
  width: 100%;
  height: 32px;
  padding: 0 12px 0 31px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--ui-caption);
}
.gs-input::placeholder { color: var(--muted); }
.gs-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
  background: var(--surface);
}
.gs-results {
  position: absolute;
  left: 0;
  right: 0;
  top: 38px;
  max-height: 62vh;
  overflow-y: auto;
  padding: 5px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow-pop);
  z-index: 60;
}
.gs-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 7px 9px;
  border: none;
  border-radius: 7px;
  background: none;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.gs-row:hover, .gs-row.is-on { background: var(--accent-soft); }
.gs-main { min-width: 0; flex: 1 1 auto; }
.gs-label {
  display: block;
  font-size: var(--ui-caption);
  font-weight: 600;
  line-height: 1.35;
}
.gs-sub {
  display: block;
  font-size: var(--ui-caption);
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gs-kind {
  flex: 0 0 auto;
  font-size: var(--ui-caption);
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.03em;
}
.gs-none { padding: 9px 10px; font-size: var(--ui-caption); color: var(--muted); }

/* ---- the avatar and its menu -------------------------------------------- */
.tb-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.usermenu { position: relative; }
.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--sans);
  font-size: var(--ui-caption);
  font-weight: 700;
  cursor: pointer;
}
.avatar:hover { background: var(--brand-hover); }
.avatar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.usermenu-panel {
  position: absolute;
  right: 0;
  top: 40px;
  width: 244px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow-pop);
  z-index: 60;
}
.um-who {
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 4px;
}
.um-who b { display: block; font-size: var(--ui-caption); font-weight: 600; }
.um-who span {
  display: block;
  font-size: var(--ui-caption);
  color: var(--muted);
  margin-top: 1px;
}
/* AN ACCOUNT WITH NO POST ON THE BOARD LEAVES NO GAP where a second line would
   have been. The line is a fact or it is absent; it is never a blank row. */
.um-who span:empty { display: none; }
.um-item {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 7px;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: var(--ui-caption);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.um-item:hover { background: var(--accent-soft); color: var(--accent-ink); }

.sidebar {
  background: var(--surface);
  border-right: 1px solid var(--line);
  padding: 14px 12px;
  gap: 0;
  height: calc(100vh - var(--topbar-h));
  top: var(--topbar-h);
}
.content { height: calc(100vh - var(--topbar-h)); }

.brand { padding: 4px 12px 20px; }
.brand-name {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--sans);
  font-size: var(--ui-ui);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}
/* THE OFFICIAL MARK (his files, 2026-08-07) — the APG houses from
   organization\brand\, not an invented letter tile. Transparent ground, so it
   sits on either theme; in dark, a soft light plate keeps the navy house
   legible (measured need: navy #19273a on the dark canvas is near-invisible). */
.brand-mark {
  width: 26px;
  height: 26px;
  flex: 0 0 26px;
  display: inline-block;
  object-fit: contain;
}
:root[data-theme="dark"] .brand-mark,
[data-theme="dark"] .brand-mark {
  background: #F3F4F6;
  border-radius: 6px;
  padding: 2px;
}
.brand-sub {
  margin: 2px 0 0 30px;
  font-size: var(--ui-caption);
  color: var(--muted);
}

.nav { display: flex; flex-direction: column; }
/* ONE ROW PER DOOR: icon, name, count. `app.css` later re-declares `.nav-item`
   as a COLUMN (it carried a second `.nav-where` line under each name in the
   July build, and that second line no longer exists), so the direction is
   stated here rather than left to whichever rule wins. Measured, not assumed:
   before this, every door rendered 89px tall with its icon stacked above its
   name. */
.nav-item {
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  flex-wrap: nowrap;
  gap: 10px;
  min-height: 34px;
  padding: 8px 12px;
  border-left: none;
  border-radius: 8px;
  color: var(--muted);
  font-weight: 500;
  margin-bottom: 2px;
}
.nav-item .nav-ico {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  stroke: currentColor;
  stroke-width: 1.8;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.nav-item:hover { background: var(--bg-2); color: var(--ink); }
.nav-item.active,
.nav-item[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-weight: 600;
  border-left: none;
}
/* THE COUNT OF WHAT NEEDS HIM. It is drawn only when it is not zero — a badge
   reading 0 is a decoration, and the empty state says it in words instead. */
.nav-badge {
  margin-left: auto;
  font-family: var(--mono);
  font-size: var(--ui-caption);
  font-weight: 600;
  background: var(--danger-soft);
  color: var(--danger);
  border-radius: 99px;
  padding: 1px 7px;
}
.nav-foot {
  margin-top: auto;
  padding: 12px;
  font-size: var(--ui-caption);
  line-height: 1.45;
  color: var(--muted);
  border-top: 1px solid var(--line);
}

.sidebar-user {
  display: block;
  padding: 0 12px 12px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 8px;
}
.su-name { font-size: 13px; font-weight: 600; color: var(--ink); }
.su-role { display: block; font-size: var(--ui-caption); font-weight: 400; color: var(--muted); }
.su-signout {
  margin-top: 6px;
  padding: 0;
  font-size: var(--ui-caption);
  color: var(--accent-ink);
  background: none;
  border: none;
  cursor: pointer;
}

.masthead {
  padding: 22px 40px 0;
  border-bottom: none;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  align-items: baseline;
  justify-content: flex-start;
  gap: 14px;
}
/* The board's tab strip, the board's viewer bar and the compose slot each take
   their own line under the title rather than crowding it. */
.masthead > .board-tabs,
.masthead > .zoombar,
.masthead > .searchbar,
.masthead > .compose-slot { flex: 1 0 100%; }
.board-tabs {
  align-items: center;
  margin: 10px 0 0;
  border-bottom: 1px solid var(--line);
}
.board-tabs .tab { text-decoration: none; }
.board-tabs .ph-tag { margin: 0 0 0 auto; align-self: center; }
.masthead h1 {
  font-family: var(--sans);
  font-size: var(--ui-title);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.masthead .view-meta {
  font-size: var(--ui-caption);
  color: var(--muted);
}
.masthead-tail { margin-left: auto; display: flex; align-items: center; gap: 8px; }

/* THE THEME SWITCH AND THE TYPE-SIZE STEP ARE BOTH OFF THIS BAR (his layout
   reset, 2026-08-07). The theme moved into Settings, reached from the user
   menu; the type-size control was removed from the application entirely on his
   repeated order — "I still see type size" — and the reading variables stand
   at their standard values with no control anywhere that moves them. */

/* A LINK IS THE ACTION COLOUR, in both themes. Nothing on this portal is
   browser-blue, and nothing that is not a link is underlined. */
.scroller a { color: var(--accent-ink); }
.doc a { color: var(--accent-ink); }
.stripnote a { text-decoration: none; font-weight: 600; }
.stripnote a:hover { text-decoration: underline; }

.scroller { background: var(--canvas); }
/* FULL-SCREEN GRIDS ON EVERY DOOR. `.page-wide` is the whole width of the
   room; the prose measure survives only inside a rendered document, where it
   is legibility rather than a centred page. */
.page-wide, .page-pad { padding: 18px 40px 48px; max-width: none; }
.doc { max-width: 78ch; }

/* ==========================================================================
   4b. SETTINGS — appearance and account, and nothing that does not work.
   ========================================================================== */
.set-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 16px;
  align-items: start;
}
.set-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 20px 18px;
}
.set-title {
  margin: 0 0 12px;
  font-size: var(--ui-subtitle);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.set-field { margin-bottom: 4px; }
.set-lbl {
  display: block;
  font-size: var(--ui-caption);
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 6px;
}
.set-seg { display: inline-flex; gap: 0; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.set-opt {
  padding: 7px 16px;
  border: none;
  border-right: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--ui-caption);
  font-weight: 600;
  cursor: pointer;
}
.set-opt:last-child { border-right: none; }
.set-opt:hover { background: var(--bg-2); }
.set-opt.is-on { background: var(--accent); color: var(--on-accent); }
.set-note {
  margin: 10px 0 0;
  font-size: var(--ui-caption);
  color: var(--muted);
  max-width: 52ch;
}
.set-row {
  display: flex;
  gap: 14px;
  padding: 7px 0;
  border-top: 1px solid var(--line);
  font-size: var(--ui-caption);
}
.set-row:first-of-type { border-top: none; }
.set-k { flex: 0 0 128px; color: var(--muted); font-weight: 600; }
.set-v { color: var(--ink); }
#set-signout { margin-top: 14px; }

/* ==========================================================================
   5. THE CARD, THE BUTTON, THE DIALOG — the shared components, restyled.
   ========================================================================== */
.card {
  margin-bottom: 26px;
  border-radius: var(--radius);
  padding: 16px 20px 18px;
  background: var(--surface);
  border: 1px solid var(--line);
}
.card-title { font-family: var(--sans); font-weight: 600; }

.btn {
  height: var(--ctrl-h);
  border-radius: var(--radius-sm);
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
}
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
.btn-secondary,
.btn-subtle {
  background: var(--surface);
  color: var(--accent-ink);
  border: 1px solid var(--line);
}
.btn-secondary:hover, .btn-subtle:hover { background: var(--accent-soft); }

.p-title, .login-title, .dlg-title, .mdl-t {
  font-family: var(--sans);
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* ==========================================================================
   6. TODAY — the landing page. The data comes first.
   ========================================================================== */
.counts {
  display: flex;
  gap: 16px;
  margin: 4px 0 18px;
  font-size: var(--ui-caption);
  color: var(--muted);
}
.counts b { font-family: var(--mono); color: var(--ink); font-weight: 600; }

/* --- THE SEVEN-DIVISION KPI ROW -----------------------------------------
   SEVEN ACROSS, in the board's own order, spanning the full width of the room.
   It steps down at real widths rather than reflowing continuously, so the row
   never lands with one orphan tile on a line of its own. */
.strip {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 14px;
}
@media (max-width: 1500px) { .strip { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 1100px) { .strip { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 820px)  { .strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* A BUTTON CENTRES ITS OWN CONTENT VERTICALLY, and that is why this is a flex
   column rather than a block. The tiles carrying a figure are <button> (they
   open the OIC); the NO DATA tiles are <div> (nothing to open). Every tile
   stretches to the tallest in the row, and the browser's button layout then
   centred the short ones — measured 2026-08-07: the division names sat at 13px,
   22px and 32px from the top of their own tiles across one row. A row whose
   headings are on three different lines is read as seven cards, not as a row.
   Flex column tops them all out; `test_relayout.py` §6 measures it. */
.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px 14px 10px;
  overflow: hidden;
  font-family: var(--sans);
  color: var(--ink);
  cursor: pointer;
}
.tile:hover { border-color: var(--quiet); }
/* THE IDENTITY RAIL. The division's certified colour, 3px, along the top edge
   of its own tile and nowhere else on the tile. */
.tile::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--divc, var(--quiet));
}
/* THE NAME BLOCK RESERVES TWO LINES WHETHER OR NOT IT NEEDS THEM, so that the
   FIGURES sit on one line across the whole row. "5 · QUALIFICATIONS" wraps at
   seven-across and "7 · EXECUTIVE" does not; without the reservation the seven
   figures landed on four different lines. Same discipline the OIC card names
   already keep, and for the same reason: a wall is read across. */
.tile .who {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  min-height: 2.8em;
  font-size: var(--ui-caption);
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
/* THE DIVISION'S NAME TAKES THE ROOM AND THE CHIP TAKES WHAT IS LEFT. Before
   this the chip was absolutely positioned over the row and clipped the name to
   "2 · DISSEMINA" — measured on his own screen at seven-across.

   AND IT IS NOT TRUNCATED EITHER. An ellipsis is a clipped word wearing a
   costume: "5 · QUALIFICATIO…" tells him less than a name on two lines. The
   name wraps, every tile in the row stretches to the tallest of them (they are
   grid items), and the harness measures every one of them for a scroll extent
   it should not have. */
.tile .who-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}
/* THE SWATCH. `.swatch` is the app's one identity mark and it always carries a
   hairline — that is what keeps a certified colour from reading as a fill. */
.tile .who .swatch {
  width: 9px;
  height: 9px;
  margin-top: 4px;            /* centred on the FIRST line of the name */
  border-radius: 3px;
  background: var(--divc, var(--quiet));
  border: 1px solid var(--line);
  flex: 0 0 9px;
}
.tile .fig {
  font-family: var(--mono);
  font-size: 21px;
  font-weight: 600;
  margin: 6px 0 0;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.tile .fig small { font-size: var(--ui-caption); color: var(--muted); font-weight: 400; }
.tile .what {
  font-size: var(--ui-caption);
  color: var(--muted);
  margin-bottom: 6px;
  min-height: 1.4em;
}
.tile .what .prov { color: var(--amber); }

/* THE CHANGE SINCE LAST WEEK. No mini-graph on this row, by his ruling — a
   readable number instead of an unreadable curve. Green is better, red is
   worse, and better is not always up: a down-is-good line says so itself. */
/* NOTHING ON THIS ROW IS TRUNCATED. It used to be: at seven-across the change
   line read "▲ $3,465 vs la…" and the empty tiles read "fills when its records
   …", because the row was one nowrap line with an ellipsis on the end. A
   number he cannot finish reading is not a number. The foot wraps instead —
   the condition chip drops under the change when the tile is too narrow for
   both — and `test_relayout.py` §6 measures every element inside every tile
   for a scroll extent, in both themes, at 1920 and 768. */
.kpi-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 8px;
  /* THE FOOT SITS AT THE FOOT. Every tile stretches to the tallest in the row,
     so the change lines and the condition chips land on one line across the
     seven rather than each floating under its own figure. */
  margin-top: auto;
  padding-top: 4px;
}
.kpi-delta {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--mono);
  font-size: var(--ui-caption);
  color: var(--muted);
  overflow-wrap: anywhere;
}
.kpi-delta.is-up { color: var(--good-ink); }
.kpi-delta.is-down { color: var(--danger); }

.tile-cond {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: var(--ui-caption);
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 1px 6px;
  border-radius: 5px;
}
.tile-cond.is-danger { background: var(--danger); color: var(--on-danger); }
.tile-cond.is-caution { background: var(--amber-soft); color: var(--amber); }
.tile-cond.is-prov { background: var(--amber-soft); color: var(--amber); }
.tile-cond.is-good { background: var(--good-soft); color: var(--good-ink); }
.tile-cond.is-plain { background: var(--bg-2); color: var(--muted); }
.kpi-delta .prov { color: var(--amber); }

/* THE CAUTION STEP ON A CONDITION CHIP, wherever one is drawn. Emergency is
   the step below Danger and it is amber, not red — red is alarm only. */
.oic-cond.cond-caution,
.cond.cond-caution { background: var(--amber-soft); color: var(--amber); }

.tile.nodata { border-style: dashed; cursor: default; }
.tile.nodata .fig {
  color: var(--muted);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
  /* THE SAME MARGIN THE FIGURE TILES USE. It was 8px, and NO DATA then sat two
     pixels below every real figure on the row — small, and visible, because
     the eye reads a row of seven by its baseline. */
  margin-top: 6px;
}

.stripnote { font-size: var(--ui-caption); color: var(--muted); margin-bottom: 24px; }

/* --- THE DANGER PULSE ---------------------------------------------------- */
.pulse {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--danger-soft);
  border: 1px solid var(--danger-line);
  border-radius: 10px;
  padding: 11px 16px;
  margin-bottom: 26px;
  font-size: 13.5px;
  color: var(--ink);
}
.pulse .pulse-chip {
  font-family: var(--mono);
  font-size: var(--ui-caption);
  font-weight: 600;
  background: var(--danger);
  color: var(--on-danger);
  padding: 2px 8px;
  border-radius: 5px;
  flex: 0 0 auto;
}
.pulse a {
  color: var(--danger);
  font-weight: 600;
  margin-left: auto;
  text-decoration: none;
  white-space: nowrap;
}
.pulse a:hover { text-decoration: underline; }

/* --- THE GROUPS AND THE CARDS ------------------------------------------- */
.grp {
  font-size: var(--ui-caption);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  margin: 24px 0 10px;
}
/* THE WORK FLOWS THROUGH TWO COLUMNS — one order, read down the left and
   continued down the right, exactly as the approved wireframe draws it. A card
   is never split across the fold. */
.work-cols { columns: 2; column-gap: 14px; }
.work-cols > .tcard { break-inside: avoid; -webkit-column-break-inside: avoid; }
@media (max-width: 1100px) { .work-cols { columns: 1; } }

.tcard {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 15px 20px 15px 24px;
  margin-bottom: 14px;
  overflow: hidden;
}
/* CONDITION AND URGENCY AS A RAIL, never as the colour of the words. */
.tcard::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: var(--quiet);
}
.tcard.rail-danger::before { background: var(--danger); }
.tcard.rail-amber::before { background: var(--amber); }
.tcard.rail-accent::before { background: var(--accent); }
.tcard .due {
  float: right;
  margin-left: 12px;
  font-family: var(--mono);
  font-size: var(--ui-caption);
  color: var(--danger);
  font-weight: 600;
  background: var(--danger-soft);
  padding: 3px 9px;
  border-radius: 6px;
}
/* THE AGE, ON EVERY CARD. Quiet — it is a fact about the item, not an alarm —
   and it sits on the same line as the deadline chip when a card carries both. */
.tcard .age {
  float: right;
  clear: right;
  margin-left: 12px;
  font-family: var(--mono);
  font-size: var(--ui-caption);
  font-weight: 600;
  color: var(--muted);
  background: var(--bg-2);
  padding: 3px 9px;
  border-radius: 6px;
}
.tcard h3 {
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 600;
  margin: 0 0 3px;
  color: var(--ink);
}
.tcard .tc-gate {
  font-size: var(--ui-caption);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
  margin-bottom: 4px;
}
.tcard p {
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.5;
  margin: 0 0 6px;
  max-width: 62ch;
}
.tcard .tc-ctx { font-size: var(--ui-caption); }
.tcard .acts {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 12px;
}
.tcard .dec-err { color: var(--danger); font-size: var(--ui-caption); margin-top: 8px; }

.quiet-state {
  text-align: center;
  color: var(--muted);
  font-size: 14px;
  padding: 28px 0 8px;
}
.quiet-state b { color: var(--ink); font-weight: 600; }

/* ==========================================================================
   7. MONEY and RECORDS — the two new rooms, in the same vocabulary.
   ========================================================================== */
.figrow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px;
  margin-bottom: 18px;
}
.figbox {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 14px 16px 12px;
}
.figbox .figlbl {
  font-size: var(--ui-caption);
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
}
.figbox .fig {
  font-family: var(--mono);
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin-top: 6px;
  color: var(--ink);
}
.figbox .fignote { font-size: var(--ui-caption); color: var(--muted); margin-top: 4px; }
.figbox .fignote .prov { color: var(--amber); }

/* A PLACEHOLDER IS MARKED AS ONE, ON ITS FACE. A dashed edge and a plain
   sentence naming what will land here and what is true today — never a
   half-built control he can press into nothing. */
.placeholder {
  border: 1px dashed var(--quiet);
  border-radius: 10px;
  padding: 16px 20px;
  background: transparent;
  margin-bottom: 18px;
}
.placeholder .ph-tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--ui-caption);
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 7px;
  margin-bottom: 8px;
}
.placeholder h3 {
  font-size: 14px;
  font-weight: 600;
  margin: 0 0 4px;
  color: var(--ink);
}
.placeholder p { font-size: 13px; color: var(--muted); margin: 0; max-width: 68ch; }

/* --- THE TWO AIRBNB UPLOAD CONTROLS -------------------------------------
   One card per export, side by side on a wide screen and stacked on a narrow
   one. Everything here is drawn from the same tokens as every other surface in
   this sheet, so both themes are hand-tuned by construction rather than by a
   second set of values kept in step by hand.

   THE DROP TARGET IS A `label` WRAPPING A FILE INPUT. The input is visually
   removed and NOT `display:none` — it keeps its place in the focus order, so
   the keyboard reaches the control exactly as the pointer does, and the focus
   ring is drawn on the card face through `:focus-within`.

   COLOUR CARRIES MEANING AND NOTHING ELSE: viridian is the one action colour,
   amber is caution only, red is alarm only. A result sentence takes the colour
   of what happened to the record and never of how the screen feels about it.
   ----------------------------------------------------------------------- */
.uprow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}
.upcard {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 16px 18px 18px;
}
.up-head { display: flex; align-items: center; gap: 10px; }
.up-head h3 {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  margin: 0;
}
.up-tag {
  font-family: var(--mono);
  font-size: var(--ui-caption);
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 7px;
}
.up-rule {
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--muted);
  margin: 8px 0 12px;
  max-width: 62ch;
}
.up-meta {
  display: grid;
  grid-template-columns: minmax(96px, auto) 1fr;
  gap: 4px 14px;
  margin: 0 0 14px;
  font-size: var(--ui-caption);
}
.up-meta dt { color: var(--muted); font-weight: 600; }
.up-meta dd { color: var(--ink); margin: 0; }
.up-drop {
  display: block;
  position: relative;
  border: 1px dashed var(--quiet);
  border-radius: 10px;
  background: var(--bg-2);
  padding: 18px 16px;
  text-align: center;
  cursor: pointer;
}
.up-drop:hover, .up-drop.over {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.up-drop:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.up-drop.busy { opacity: 0.65; cursor: progress; }
.up-file {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  inset: 0;
}
.up-face { display: block; }
.up-face-strong {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
}
.up-face-sub {
  display: block;
  font-size: var(--ui-caption);
  color: var(--muted);
  margin-top: 3px;
}
.up-note {
  font-size: var(--ui-caption);
  color: var(--muted);
  margin: 0;
}
.up-msg {
  font-size: 13.5px;
  line-height: 1.5;
  margin: 12px 0 0;
  padding: 9px 12px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--bg-2);
  color: var(--ink);
}
.up-msg.up-good { border-color: var(--good); background: var(--good-soft); }
.up-msg.up-warn { border-color: var(--amber); background: var(--amber-soft); }
.up-msg.up-bad  { border-color: var(--danger-line); background: var(--danger-soft); }
.up-msg.up-busy { color: var(--muted); }

/* --- Records: the tab strip and the rendered documents ------------------ */
.tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.tab {
  appearance: none;
  border: none;
  background: none;
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--muted);
  padding: 8px 12px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  cursor: pointer;
}
.tab:hover { color: var(--ink); }
.tab.on { color: var(--accent-ink); border-bottom-color: var(--accent); }

.doc {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 24px 28px 28px;
  max-width: 78ch;
}
.doc h1 { font-size: 20px; font-weight: 700; margin: 0 0 4px; letter-spacing: -0.02em; }
.doc h2 {
  font-size: 15px;
  font-weight: 600;
  margin: 22px 0 6px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.doc h3 { font-size: 14px; font-weight: 600; margin: 16px 0 4px; }
.doc p, .doc li { font-size: 14px; line-height: 1.6; color: var(--ink); margin: 0 0 8px; }
.doc li { margin-bottom: 4px; }
.doc ul, .doc ol { margin: 0 0 10px 20px; }
.doc em { color: var(--muted); }
.doc code { font-family: var(--mono); font-size: var(--ui-caption); color: var(--muted); }
.doc hr { border: none; border-top: 1px solid var(--line); margin: 18px 0; }
.doc table { border-collapse: collapse; margin: 0 0 12px; font-size: 13.5px; }
.doc th, .doc td {
  border: 1px solid var(--line);
  padding: 6px 10px;
  text-align: left;
  vertical-align: top;
}
.doc th { background: var(--bg-2); font-weight: 600; }
.doc-note { font-size: var(--ui-caption); color: var(--muted); margin-bottom: 12px; }

.rec-list { list-style: none; margin: 0; padding: 0; }
.rec-row {
  display: flex;
  gap: 12px;
  align-items: baseline;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
  font-size: 13.5px;
}
.rec-row .rec-when {
  font-family: var(--mono);
  font-size: var(--ui-caption);
  color: var(--muted);
  flex: 0 0 15ch;
}
.rec-row .rec-what { color: var(--ink); flex: 1 1 auto; }
.rec-row .rec-who { color: var(--muted); font-size: var(--ui-caption); flex: 0 0 auto; }
.rec-ruling {
  color: var(--muted);
  font-size: 13px;
  margin: 4px 0 0;
  padding-left: 12px;
  border-left: 2px solid var(--line);
}

/* ==========================================================================
   7b. THE OIC WALL — his ruling of 2026-08-07: a wall, not a scroll.

   Compact uniform cards, many visible at once, laid out exactly as the board
   is: divisions in the chart's order, departments grouped inside them, an
   element's own lines under that element. Every card is the same size and
   carries the same twelve-week window, so the wall can be READ ACROSS — which
   is the whole reason it is a wall.
   ========================================================================== */
/* THE WALL'S ONE DECLARED AXIS AND SORT, on its face — the wireframe's own
   line, in the wireframe's own place. */
.oic-win {
  margin: -6px 0 14px;
  font-family: var(--mono);
  font-size: var(--ui-caption);
  color: var(--muted);
}

.oic-wall { display: flex; flex-direction: column; gap: 10px; }

.oic-div { border-top: 1px solid var(--line); padding-top: 8px; }
.oic-div:first-child { border-top: 0; padding-top: 0; }
.oic-div-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }
.oic-div-name {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
}
.oic-div-note { font-size: var(--ui-caption); color: var(--muted); }
.oic-group { margin-top: 10px; }
.oic-group-name {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}
.oic-element {
  margin: 8px 0 4px;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}
.oic-empty { margin: 2px 0 0; font-size: 13px; color: var(--muted); }

.oic-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, 224px);
  grid-auto-rows: 1fr;
  gap: 10px;
}

/* ONE SIZE, EVERY CARD. A wall is read across, so a card that grew because its
   name ran to two lines would put its graph on a different line from its
   neighbour's and the sweep of the eye would be gone. The name is clamped to
   two lines and reserves both whether or not it needs them; the height is
   fixed; the harness measures every card and fails if two differ. */
.oic-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  height: 158px;
  padding: 9px 10px 8px;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
  font: inherit;
  color: var(--ink);
}
.oic-card:hover { border-color: var(--accent); }
.oic-card.is-raised { border-left-color: var(--danger); }
.oic-card-name {
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.3;
  height: 2.6em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.oic-card-fig { display: flex; align-items: baseline; gap: 6px; height: 24px; }
.oic-card-n {
  font-family: var(--mono);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.02em;
}
.oic-card-n.is-nodata { font-size: 12px; color: var(--muted); letter-spacing: .06em; }
.oic-card-when { font-size: 11px; color: var(--muted); }

.oic-mini { display: block; width: 100%; height: 46px; flex: 0 0 46px; }
.oic-mini .oic-axis { stroke: var(--line); stroke-width: 1; }
.oic-mini .oic-ln { stroke: var(--accent); stroke-width: 1.5; fill: none; }
.oic-mini .oic-pt { fill: var(--accent); }
.oic-mini .oic-pt.is-running { fill: var(--surface); stroke: var(--accent); stroke-width: 1.2; }
.oic-card.is-raised .oic-mini .oic-ln { stroke: var(--danger); }
.oic-card.is-raised .oic-mini .oic-pt { fill: var(--danger); }

.oic-card-foot {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: auto;
  flex-wrap: nowrap;
  white-space: nowrap;
}
.oic-cond {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .03em;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--canvas);
  color: var(--muted);
}
.oic-cond.cond-raised { background: var(--danger-soft, #FDECEA); color: var(--danger); }
/* A CONDITION THAT IS NOT RAISED IS A GOOD STATE, AND GOOD IS GREEN — the one
   thing green means anywhere in this application since the brand key became
   navy and gold (his ruling, 2026-08-07). */
.oic-cond.cond-plain { background: var(--good-soft); color: var(--good-ink); }
.oic-prov {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--amber, #B54708);
}
.oic-card-weeks {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--muted);
}

/* ==========================================================================
   8. NARROW SCREENS — the wireframe's own breakpoint.
   ========================================================================== */
@media (max-width: 1000px) {
  /* THE COMPANY NAME STEPS OFF THE BAR BEFORE THE SEARCH FIELD DOES. The mark
     still says whose portal this is; a search box squeezed to nothing does
     not. */
  .tb-name { display: none; }
  .topbar { gap: 12px; padding: 0 14px; }
  .page-wide, .page-pad { padding: 16px 22px 40px; }
}
/* THE FRAME IS THE ROW NOW, NOT THE SHELL — and the narrow rule has to move
   with it. `app.css` still carries a 900px rule that stacks `.shell` and gives
   the navigation `width: 100%`; under the new structure that rule fires inside
   the ROW and squashes the content pane to nothing. Measured at 768px before
   this was written: the board's own view came back 40px wide.

   AND THE NAVIGATION IS NOT TAKEN AWAY. The old sheet hid the sidebar
   completely below 760px, which left a portal with no doors at all. It becomes
   a horizontal strip under the top bar instead. */
@media (max-width: 900px) {
  .frame { flex-direction: column; }
  .sidebar {
    position: static;
    flex: none;
    width: 100%;
    height: auto;
    border-right: none;
    border-bottom: 1px solid var(--line);
    padding: 8px 12px;
  }
  .sidebar .nav-scroll { width: 100%; overflow-x: auto; }
  .sidebar .nav { flex-direction: row; gap: 4px; }
  .sidebar .nav-item { white-space: nowrap; margin-bottom: 0; }
  .content { height: auto; min-height: 70vh; }
}
@media (max-width: 760px) {
  .sidebar { display: flex; }
  .masthead { padding: 16px 18px 0; }
  .page-wide, .page-pad { padding: 12px 18px 36px; }
}
