/* ════════════════════════════════════════════════════════════════════════════
   minerva-v2.css — Minerva (Gen 10) v2 design system, single source of truth.

   The canonical, self-contained shared stylesheet for the UI/UX redesign
   (docs/ui_design_system_gen10.md). A migrated surface links ONLY this file
   (+ optionally a tiny surface-specific stylesheet for unique bits like chart
   canvases) and drops its inline <style> :root + component re-declarations.

   Design language (LOCKED 2026-06-05): content-first, iOS + Scandinavian —
   quiet chrome, generous whitespace, hairlines over heavy boxes, one soft
   shadow OR a hairline (never both), calm ≤150ms fades (never scale/bounce),
   colour only where it carries meaning. Dark mode follows the OS appearance.

   Sections:
     1. Tokens (light)            6. Buttons
     2. Tokens (dark, OS-driven)  7. Badges / pills / chips / tags
     3. Density                   8. List rows / table
     4. Reset + base + a11y       9. Form controls
     5. Scaffold + tool accents   10. Action bar / states / banners / toast / tooltip / modal
                                  11. Responsive + print + reduced-motion
   ════════════════════════════════════════════════════════════════════════════ */

/* ── 0. Fonts ───────────────────────────────────────────────────────────────── */
/* None loaded. Every screen AND every report picture uses the system sans stack (--font-body). The last @font-face
   (Crimson Pro, for the generated report charts) was removed at s1501 decision 6 (owner Q5, FU-f-report-fonts): the
   report pictures now name the same stack (src/lib/chart_theme.js PRINT_FONT). Baskervville went at M6 (A-09). */
/* ── 1. Tokens (light) ──────────────────────────────────────────────────────── */
:root {
  /* neutrals (≈90% of every screen) */
  --bg: #F2F2F7;
  --card: #FFFFFF;
  --seg-on: #FFFFFF;   /* selected segment of a segmented control — raised above its --fill track (item 3, 2026-09-25) */
  --text-1: #1C1C1E;
  --text-2: #3C3C43;
  --text-3: #6D6D72;
  --text-4: #AEAEB2;
  --hair: rgba(60,60,67,0.10);
  --hair-strong: #E5E5EA;
  --fill: rgba(118,118,128,0.10);
  --fill-2: rgba(118,118,128,0.16);

  /* one accent — interaction only (primary action, link, selection, focus) */
  --accent: #0A6CFF;
  --accent-weak: rgba(10,108,255,0.10);
  --accent-hover: #0A5FE0;
  --on-accent: #FFFFFF;
  --info-fg: #0A3E8C;
  /* Ship U (UI review Q1b/Q3, user decisions 2026-09-16). FILLS vs TEXT: `--accent` stays the interaction
     colour for links, rings and selection. A control FILLED with the accent under white text reads
     `--accent-fill` (dark mode #0A6CFF, 4.56:1, where #0A84FF measured 3.65:1). Small accent/green TEXT on a
     tinted surface reads `--accent-text` / `--proceed-text`, which pass 4.5:1 on --card, --fill and
     --accent-weak in both themes (tools/check_ui_contrast_tokens.js). `--on-caution` is the text on an amber fill. */
  --accent-fill: var(--accent);
  --accent-fill-hover: var(--accent-hover);
  --accent-text: #0055CC;
  --proceed-text: #1B6E2F;
  --on-caution: #FFFFFF;
  --on-kill: #FFFFFF;      /* M1: text on a red fill (the score badges); dark: near-black, white measured 3.41:1 on #FF453A */
  /* V-2 / V-3 (v10.1213.0). A FILLED green control carries `--on-proceed` (text or switch knob) on
     `--proceed-fill`, exactly as an amber one carries `--on-caution` on `--caution`. `--proceed` itself is
     4.40:1 under white in light and 2.02:1 in dark, measured, so it is not a fill. `--control-off` is an OFF
     switch track: never a `--text-*` token, which is #FFFFFF in dark (the knob vanished: 1.00:1).
     Pinned by tools/check_ui_contrast_tokens.js (4.5:1 text, 3:1 control parts) and the browser smoke. */
  --proceed-fill: #1B6E2F;
  --on-proceed: #FFFFFF;
  --control-off: #6D6D72;
  /* M6 (lock D29, 2026-09-27). --control-border: the EDGE of a control you type into or pick (inputs, selects, chips,
     radio cards) at >= 3:1 against --card and --bg (WCAG 1.4.11; the hairline measured 1.19-1.26:1, A-05). Cards and
     tables keep --hair / --hair-strong. --text-disabled: a disabled control's words, SOLID (no opacity, D29c) — the
     dark text ramp is white, so disabled needs its own step. Tooltip / knob / scrim / print: the values that were
     literals in fast_tooltip.js, the range thumb, the dialog backdrop and the print block (A-18, A-37). */
  --control-border: #86868B;
  /* UR-22 F11 class (v10.1441.0): the default notice's edge. In Light --hair-strong (#E5E5EA) IS the notice fill on the
     page ground (1.01:1; 1.12:1 on a card), so the box had no edge; it takes the control edge (>= 3:1), as the
     respondent page's neutral notice does (D3'). Dark keeps --hair-strong (§2), unchanged. tests/test_ur22d_fixes.js §F11. */
  --notice-edge: var(--control-border);
  /* UR-22 re-check O3 (v10.1441.0): the edge of a status badge sitting on a notice of its own signal. In Light the badge
     fill IS the notice fill (--kill-badge-bg = --kill-bg #FBEBE9, proceed likewise), so "Closed · Killed" had no pill;
     the edge is the badge's own ink. Dark's badge fills differ from its notices, so Dark has none (§2), unchanged. */
  --badge-edge: currentColor;
  --text-disabled: #AEAEB2;
  /* Kit-1 (user decision 2026-10-02): THE placeholder colour — 5.15:1 on the card, 4.61:1 on the page ground. */
  --text-placeholder: #6D6D72;
  --tooltip-bg: #1C1C1E; --tooltip-text: #FFFFFF;
  --shadow-knob: 0 1px 2px rgba(0,0,0,0.2);
  --shadow-tooltip: 0 2px 8px rgba(0,0,0,0.25);
  --scrim: rgba(0,0,0,0.45);
  --print-paper: #FFFFFF; --print-ink: #000000; --print-rule: #CCCCCC;
  --qr-paper: #FFFFFF; --qr-ink: #000000;   /* S2 (v10.1419.0): a QR stays dark-on-light in EVERY theme — never redefine these under a dark scheme */
  /* RC-4 (v10.1223.0) — THE CHART THEME. Charts name a role through lib/chart_theme.js (`var(--ct-<role>, <print>)`);
     these tokens resolve the role on screen, light and dark. The print (report) values live in chart_theme.js. Light
     keeps the Gen 9 hues; dark gets its own. Lines/areas are non-text (3:1); tick/label text reads text tokens. */
  --ct-bg: var(--card); --ct-on-bar: #FFFFFF;
  --ct-ink: var(--text-1); --ct-ink-2: var(--text-2); --ct-tick: var(--text-3);
  --ct-axis: #8E8E93; --ct-grid: var(--hair-strong); --ct-mark: var(--control-off);
  --ct-up: #007AFF; --ct-up-soft: #378ADD; --ct-down: #FF3B30; --ct-down-soft: #E24B4A;
  --ct-zero: #FF3B30; --ct-zero-ink: var(--kill-text);
  --ct-pos: var(--proceed); --ct-neg: var(--kill); --ct-pos-ink: var(--proceed-text); --ct-neg-ink: var(--kill-text); --ct-ring: var(--fill-2);
  --ct-callout-ink: var(--proceed-text); --ct-callout-bg: var(--proceed-bg); --ct-callout-hair: var(--proceed-hair);
  --ct-surface: var(--fill); --ct-caution: var(--caution); --ct-caution-ink: var(--caution-text); --ct-caution-bg: var(--caution-bg); --ct-neg-bg: var(--kill-bg);
  --ct-launch-hl: #1D5C63;   /* UR-27 F2: the highlighted launch year (bar, count, year label) — 7.6:1 on the card */
  /* RC-2 part 4 (v10.1214.0) — semantic TEXT on a tint, the method the user chose 2026-09-16 (component-level text
     tokens). The fill hues stay; text on a -bg tint reads the -text token. Measured before: badge/notice/danger text
     3.98 (green) / 4.41 (amber) / 4.47 (red) in light, red 3.45 in dark; ghost button 3.82 dark, 2.95 dark on hover. */
  --caution-text: #8A5C00;
  --kill-text: #B3261E;

  /* semantic signal — meaning only */
  --proceed: #248A3D;  --proceed-bg: #EAF7EE;  --proceed-hair: #BFE6C8;
  --caution: #9A6700;  --caution-bg: #FBF3E2;  --caution-hair: #EBD9A6;
  --kill:    #C9372C;  --kill-bg:    #FBEBE9;  --kill-hair:    #F2C5BF;
  /* UR-5 F1: a status badge's ground — the -bg tint in Light (already opaque); Dark gives its own opaque values. */
  --proceed-badge-bg: #EAF7EE; --caution-badge-bg: #FBF3E2; --kill-badge-bg: #FBEBE9;

  /* depth — one soft shadow */
  --shadow: 0 1px 2px rgba(0,0,0,0.04), 0 1px 8px rgba(0,0,0,0.04);
  --shadow-raised: 0 2px 4px rgba(0,0,0,0.05), 0 4px 16px rgba(0,0,0,0.06);
  --shadow-modal: 0 12px 48px rgba(0,0,0,0.22);

  /* radii */
  --radius-card: 14px;
  --radius-ctl: 10px;
  --radius-pill: 999px;
  /* M1 (design system D12, user decision 2026-09-25): the small-radius steps D12 locked at RC-1 but that never
     landed. 2-6px -> sm, 8 -> md, 10 -> --radius-ctl, 12 -> lg; a circle is --radius-round. */
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-round: 50%;
  --hair-w: 0.5px;   /* the hairline border width, for calc() that must add a border to a spacing step */

  /* type ladder (fluid clamp: phone floor → 4K ceiling) */
  /* RC-1a (v10.1214+1): the 11px allowance belongs to EYEBROWS ONLY (design system §2 D42; UI review Q1b), so it
     gets its own name. `--fs-2xs` is now a DEPRECATED ALIAS of `--fs-xs` — the ~330 non-eyebrow uses that sat at
     11.58px (below the 12px floor) render at the floor step instead, without 330 edits; tools/check_ui_rules_scan.js
     counts every remaining use so the name can be retired. `--fs-3xs` (9px floor) is RETIRED — nothing may go that
     small. An `--fs-eyebrow` use in a rule that is not uppercase is itself counted (C2-fs-eyebrow-not-eyebrow). */
  --fs-eyebrow: clamp(0.6875rem, 0.66rem + 0.08vw, 0.75rem);
  /* RC-1b (v10.1216.0): every src/ use of --fs-2xs is renamed to --fs-xs (321 uses, zero pixel change). The alias
     stays ONLY because the web tier's pages (web/public/, out of Master/Hosted scope) still use the name; any new use
     in src/ or public/ is counted by tools/check_ui_rules_scan.js (C2-fs-2xs-non-eyebrow). */
  --fs-2xs: var(--fs-xs);
  --fs-xs:  clamp(0.75rem,  0.71rem + 0.12vw, 0.8125rem);
  --fs-sm:  clamp(0.8125rem,0.77rem + 0.15vw, 0.875rem);
  --fs-base:clamp(0.9375rem,0.89rem + 0.18vw, 1rem);
  --fs-md:  clamp(1.0625rem,1rem   + 0.22vw, 1.1875rem);
  --fs-lg:  clamp(1.375rem, 1.27rem + 0.4vw, 1.625rem);
  --fs-xl:  clamp(1.75rem,  1.55rem + 0.7vw, 2.125rem);
  --fs-2xl: clamp(2.125rem, 1.85rem + 1vw,   2.75rem);

  /* SCORE-BREAKDOWN LADDER (v10.633.0) — ONE definition of the tier sizes, consumed by BOTH the
     Project Viewer and the BC Visualizer. Previously each surface declared its own values in its
     own <style>, they drifted (BC had no sub override at all), and correcting the inversion took
     three ships. Declared on the ROW so .score-name AND .score-val both inherit — BC set it on
     .score-name only, which is why its axis SCORE stayed small while the label grew.
     INVARIANT: axis > sub. See CLAUDE.md § UI RULES. */
  --fs-sb-axis: 18px;
  --fs-sb-sub:  16px;
  --fs-sb-crit: 14px;
  --fs-sb-q:    11px;

  /* s1506 (decision 4, UR-40 F2): the widest a report picture is laid out for = chart_theme.js PICTURE_MAX_W (738 units)
     + its 1+1 px border. A wider preview column places the picture at this width instead of stretching it
     (tests/test_s1506c_picture_range R4 keeps the two equal). */
  --pic-max-w: 740px;

  /* score-bar track — ONE width for the criterion score bar. Viewer was 5.5rem and BC 9rem
     for the same component with no reason (v10.622.0). PM + Minerva Web deliberately stay
     FLUID (flex:1) — filling the column is a different, legitimate layout mode; the report
     SVG has its own fixed canvas. */
  --score-track: 10rem;

  /* width roles */
  --w-reading: clamp(20rem, 92vw, 52rem);
  --w-app:     clamp(20rem, 94vw, 80rem);
  --w-canvas:  clamp(20rem, 96vw, 150rem);
  /* active-area side gutter — the BC norm (issue 12); single source consumed by
     .app-main AND the BC bespoke wrappers, so the left/right active-area edge is
     identical across every surface. */
  --gutter:    clamp(20px, 3vw, 40px);

  /* spacing scale */
  --sp-1: 0.25rem; --sp-2: 0.5rem; --sp-3: 0.75rem; --sp-4: 1rem;
  --sp-5: 1.25rem; --sp-6: 1.5rem; --sp-7: 2rem; --sp-8: 2.5rem; --sp-9: 3rem;

  /* density-driven (default: Comfortable) */
  --card-pad: 1.5rem;
  --section-gap: var(--sp-5);
  --row-gap: var(--sp-3);
  --ctl-h: 2.25rem;
  --ctl-h-sm: 2rem;

  /* per-tool wayfinding accents — DISJOINT from the signal hues (no green/amber/red) */
  --tool: var(--accent);
  --tool-weak: var(--accent-weak);

  /* fonts — ONE stack, the high-legibility system sans (SF Pro on macOS, Segoe UI on Windows; CSP-clean, no
     fetch). Ship F (v10.1178.0) reverted the 2026-06-12 "serif headings + sans body/data" lock: the design system's
     rule is the system stack (docs/ui_design_system_gen10.md:55, confirmed by the user 2026-09-15, UI rules Q5a).
     --font-head is kept as a NAME so the §4 selector list and every `var(--font-head)` keep working, and it points
     at the body stack so a heading cannot drift. tools/check_no_serif_fonts.js §A pins this line. */
  --font-head: var(--font-body);
  --font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  /* ⚠ ADDED v10.1162.0, AND IT IS A THIRD STACK, NOT A NEW ONE. Three surfaces had each written
     their own monospace list by hand and no two agreed — `ui-monospace, Menlo, monospace`
     (Versioning `.mv-path`), `ui-monospace, 'SF Mono', Menlo, monospace` (Viewer `code`),
     `"SF Mono", "Courier New", monospace` and a bare `monospace` (Config Editor). A literal font
     stack is the same class of drift as a literal hex, and the rendered-type check in
     tools/browser_smoke/run.js can only name the legal families if they are tokens. */
  --font-mono: ui-monospace, 'SF Mono', Menlo, 'Courier New', monospace;
}

/* ── 2. Tokens (dark) — follows the OS appearance (macOS Light/Dark) ─────────── */
/* Wins the cascade because migrated surfaces carry no per-surface inline :root.
   This is exactly the structural fix the old (inert) dark block lacked. */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1C1C1E;
    --card: #2C2C2E;
    --seg-on: #636366;   /* the macOS dark selected-segment grey: --card is darker than the track in dark (read as a hole) */
    /* Dark mode: the full neutral text ramp is white (user-directed 2026-06-05) —
       hierarchy comes from weight + size, not from dimming. Semantic signal colours
       (--proceed/--caution/--kill) + --accent keep their meaning/affordance hues. */
    --text-1: #FFFFFF;
    --text-2: #FFFFFF;
    --text-3: #FFFFFF;
    --text-4: #FFFFFF;
    --hair: rgba(255,255,255,0.10);
    --hair-strong: #38383A;
    --fill: rgba(120,120,128,0.22);
    --fill-2: rgba(120,120,128,0.34);

    --accent: #0A84FF;
    --accent-weak: rgba(10,132,255,0.22);
    --accent-hover: #3D9BFF;
    --info-fg: #9EC2FF;
    /* Ship U — see the light block. Filled controls #0A6CFF / hover #0A5FE0 (decision 3); text tokens lighter. */
    --accent-fill: #0A6CFF;
    --accent-fill-hover: #0A5FE0;
    --accent-text: #5EADFF;
    --proceed-text: #30D158;
    --on-caution: #1C1C1E;
    --on-kill: #1C1C1E;
    --proceed-fill: #30D158;
    --on-proceed: #1C1C1E;
    --control-off: #7C7C80;
    --control-border: #7C7C80;
    --notice-edge: var(--hair-strong);   /* UR-22 F11 class: Dark's notice edge is unchanged */
    --badge-edge: transparent;   /* UR-22 re-check O3: Dark's badges already differ from their notices — unchanged */
    --text-disabled: #6C6C70;
    --text-placeholder: #A1A1A6;   /* Kit-1: 5.42:1 on the Dark card, 6.61:1 on the page (the browser grey was 3.02:1) */
    --tooltip-bg: #48484A;
    /* RC-4: chart hues for a dark card (the text / hair / signal roles follow their own dark tokens). */
    --ct-up: #0A84FF; --ct-up-soft: #409CFF; --ct-down: #FF453A; --ct-down-soft: #FF6961; --ct-zero: #FF453A;
    --ct-launch-hl: #5AC8D2;   /* UR-27 F2: 7.05:1 on the Dark card, 6.27:1 on the column tint (was #1D5C63, 1.83:1) */
    --caution-text: #F0B429;
    --kill-text: #FF7B72;
    /* s1501 decision 18 (FU-v10.1498-dark-report-tile-red-value-contrast): a report picture's red value sits on the picture's
       tile fill (--ct-surface = --fill, #3D3D40 in Dark), where --kill-text was 4.31:1; this red is 5.09:1 there (6.55:1 on
       the page). Dark only; Light and the print copy keep #B3261E. */
    --ct-neg-ink: #FF948C;

    --proceed: #30D158; --proceed-bg: rgba(48,209,88,0.16); --proceed-hair: rgba(48,209,88,0.35);
    --caution: #F0B429; --caution-bg: rgba(240,180,41,0.16); --caution-hair: rgba(240,180,41,0.35);
    --kill:    #FF453A; --kill-bg:    rgba(255,69,58,0.16);  --kill-hair:    rgba(255,69,58,0.35);
    /* UR-5 F1: a status badge's ground, OPAQUE in Dark — the 16% tints composited over a --fill row fell to 3.8–4.3:1. These are
       the tints as they land on the card (same look there), so a badge reads the same on a card or on a row. */
    --proceed-badge-bg: #2D4635; --caution-badge-bg: #4B422D; --kill-badge-bg: #4E3030;

    --shadow: 0 1px 2px rgba(0,0,0,0.45), 0 1px 12px rgba(0,0,0,0.4);
    --shadow-raised: 0 2px 6px rgba(0,0,0,0.5), 0 6px 20px rgba(0,0,0,0.45);
    --shadow-modal: 0 16px 56px rgba(0,0,0,0.6);
  }
}

/* ── 3. Density ─────────────────────────────────────────────────────────────── */
/* Apply .density-compact to a surface root for data-dense tools (BC/TL/CE tables). */
.density-compact {
  --card-pad: 1rem;
  --section-gap: var(--sp-4);
  --row-gap: var(--sp-2);
  --ctl-h: 2rem;
}

/* ── 4. Reset + base + accessibility ────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

/* ⚠ `hidden` DID NOT HIDE ANYTHING WITH A `display` OF ITS OWN. Found on the rendered surface on
   2026-09-14, not read off the source: `.field { display: flex }` (§9) is an AUTHOR rule and the
   `[hidden] { display: none }` it was relying on is in the USER-AGENT sheet, which every author
   rule outranks. So `el.hidden = true` on a `.field` did nothing at all — measured, both panels:
   Minerva Web's "Configuration file" picker (#wcCfgFileWrap, `hidden` in the markup since it
   shipped) was on screen while "Seed the Minerva Web standard" was selected, and the hosted
   console's showed the same way. A field offering to replace the framework, visible on a panel
   that is not going to read it.
   `!important` is the point rather than an escape hatch: `hidden` must beat whatever display a
   component sets, or the attribute means nothing on exactly the elements that need it most. The
   `.modal-backdrop[hidden]`, once the only other `[hidden]` rule here, went with the dormant `.modal` shell (RC-5).
   `tools/browser_smoke/run.js` fails the build if any element carrying `hidden` renders a box. */
[hidden] { display: none !important; }

/* Ship U (UI review I19, decision 8): a number box holding an out-of-range entry. The typed value stays, the border
   turns --kill with a --kill-bg ring, and the message under it is normal text colour (dark red measured 4.09:1).
   Values as the approved mock-up (docs/mockup_ce_product_life.html:112,117). Set by ce_ui.bindValidatedNumber;
   `!important` because the boxes carry inline or component borders. flex-basis puts the message on its own line in the
   flex cells (Zombie). */
.is-invalid { border-color: var(--kill) !important; box-shadow: 0 0 0 3px var(--kill-bg); }
/* UR-4 F2 (user decision 2026-10-03, D29l refined): the focus sign on a field in error is a solid accent ring outside
   the red edge (a 1px card gap, then 2px of --accent, >=3:1 on the card) - the 10%-alpha halo measured 1.15:1 / 1.29:1. */
.is-invalid:focus { border-color: var(--kill) !important; box-shadow: 0 0 0 1px var(--card), 0 0 0 3px var(--accent); }   /* Kit-2 */
.inp.is-invalid:focus { border-color: var(--kill) !important; box-shadow: 0 0 0 1px var(--card), 0 0 0 3px var(--accent); }   /* UR-5 F2: outranks .inp:focus (0,3,0 > 0,2,0) */
.field-invalid-msg { font-size: var(--fs-xs); color: var(--text-1); font-weight: 600; margin-top: var(--sp-1); line-height: 1.35; text-align: left; flex-basis: 100%; }   /* UR-6 F5 (decided 2026-10-03): weight 600, so an error never reads as the hint under it */

/* ── PLM S5b (K42, K46) — "No financial results", where the figures would be ──────────────────
   One block for all three reason kinds, under ONE heading (user, 2026-09-17, mock-up sign-off K46;
   the "yet" variant was dropped). Tokens only, no new colours: the caution rail carries the signal,
   the text carries the sentence. `.calc-date` is the "Calculated {YYYY-MM-DD}" line every surface
   showing figures carries (K25). `.q-advice` is the note that blocks NOTHING — the fit warning
   (mock-up S5), the Bubble Chart detail card and the portfolio totals note. Values copied from
   docs/mockup_plm_s5b_screens.html, the signed-off mock-up, not re-derived. */
.fin-none { background: var(--caution-bg); border-radius: var(--radius-ctl); border: 0.5px solid var(--caution-hair);   /* D29h: the shared caution notice, no rail (was a 3px rail, S5b mock-up) */
            padding: var(--sp-4) var(--sp-5); display: flex; gap: var(--sp-3); align-items: flex-start;
            /* RC-8 (v10.1241.0): the Viewer appends this block INSIDE its auto-fit metrics grid, where
               it became one 8.5rem cell and wrapped a word or two per line. Span the whole row. Inert
               outside a grid. */
            grid-column: 1 / -1; }
.fin-none-ic { font-size: var(--fs-base); line-height: 1.3; color: var(--caution); }
.fin-none-t  { font-size: var(--fs-sm); font-weight: 700; color: var(--text-1); margin-bottom: var(--sp-1); }
.fin-none-b  { font-size: var(--fs-sm); color: var(--text-2); line-height: 1.5; }
.fin-none-b ul { margin: var(--sp-2) 0 0 var(--sp-4); }
.fin-none-b li { margin-bottom: var(--sp-1); }
.fin-none-b p  { margin: 0 0 var(--sp-2); }
.fin-none-b p:last-child { margin-bottom: 0; }
.calc-date { font-size: var(--fs-xs); color: var(--text-3); margin-top: var(--sp-2); }
.q-advice { font-size: var(--fs-xs); color: var(--text-2); background: var(--caution-bg); border-radius: var(--radius-md);
            padding: var(--sp-3) var(--sp-3); margin-top: var(--sp-3); line-height: 1.45; }

/* Ship U (UI review I14): the question card's range sliders are 6px tall with native thumbs. On a phone or any coarse
   pointer the hit area is 44px (D152); the drawn track is unchanged. */
@media (pointer: coarse), (max-width: 600px) { .qc-range { min-height: 44px; } }

/* ⚠ A FORM CONTROL DOES NOT INHERIT THE PAGE'S FONT, AND THAT HAS BITTEN THIS REPO TWICE. The UA
   gives `button`, `input`, `select` and `textarea` their OWN font-family — Arial on this engine —
   so any control styled without `font: inherit` silently leaves the Minerva stack no matter what
   `body` says. It is invisible in the file: every rule reads correct on its own.
     · 2026-09-11 (S2, Versioning UI audit): the head-font selector list named `.btn` and
       `.seg button`, and both rendered SANS anyway — the `font: inherit` those rules carry comes
       later in the cascade. The list had claimed serif since June.
     · 2026-09-14: `.mv-modal-close` — the shared dialog's × — measured at **Arial 26px** on every
       panel of the Versioning page. It is a <button> and nobody gave it `font: inherit`.
   Per-rule `font: inherit` fixes one control and leaves the class alive, which is why it bit a
   second time. This is the root: EVERY control inherits the family, once, before any component
   rule runs, so a new control cannot be born off-stack. A component that wants a different face
   still says so and wins on the cascade.
   FAMILY ONLY, DELIBERATELY. `font-size: inherit` here would resize every control that does not
   declare a size — a layout change on every surface, which is not what this is for. Sizes are
   already held by the `--fs-*` ladder and by the rendered-type check in tools/browser_smoke/run.js,
   which fails the build on any family or size off the system. */
button, input, select, textarea, optgroup { font-family: inherit; }

/* Same reasoning one element further out: a bare `<code>` takes the user agent's monospace at the
   user agent's size, so it lands off both the stack and the ladder wherever nobody wrote a rule.
   Three surfaces had each written their own; the Versioning `.mv-path` chip keeps its own padding
   and background on top of this. Found by the rendered-type check on the launcher (v10.1162.0). */
code, kbd, samp { font-family: var(--font-mono); font-size: var(--fs-xs); }
html, body { margin: 0; }
html { min-height: 100dvh; }
body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text-1);
  font-size: var(--fs-base);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}
h1, h2, h3, h4, p { margin: 0; }
/* s1505 decision 21 + s1506 decision 1 (rule 2, R-C1/R-C2): a subscript or superscript draws ONE ladder step below its text —
   the largest general step (--fs-xs … --fs-xl) under the text's size, never under --fs-xs (the 12px floor), never larger than
   the text (under 11px eyebrow text it keeps the text's size). ONE rule computes it from the parent's size (1em in font-size):
   each clamp() term is its step when the text is larger than that step (by 0.02px), else 0; max() keeps the largest and the
   floor; min(1em, …) caps it at the text. No context names its own step: a per-context list missed the Configurator (UR-40 F1).
   Measured equal to the old list at 1280/1920 in Chromium and WebKit (s1506 runs/proto/wk.log); tests/test_s1505j_sub_size S5
   evaluates it from the tokens; ui_class B10 judges it in every state a person reaches. */
sub, sup { line-height: 0; font-size: min(1em, max(var(--fs-xs),
  clamp(0px, (1em - var(--fs-sm)   - 0.02px) * 10000, var(--fs-sm)),
  clamp(0px, (1em - var(--fs-base) - 0.02px) * 10000, var(--fs-base)),
  clamp(0px, (1em - var(--fs-md)   - 0.02px) * 10000, var(--fs-md)),
  clamp(0px, (1em - var(--fs-lg)   - 0.02px) * 10000, var(--fs-lg)),
  clamp(0px, (1em - var(--fs-xl)   - 0.02px) * 10000, var(--fs-xl)))); }
img, svg, video, canvas { max-width: 100%; }

/* Headings, titles, and labels use --font-head, which since ship F is the body stack (see the token comment).
   Includes the known surface label/title classes (sec-label, metric-*, dash-*) so the
   rule covers every migrated surface; extended per surface as BC/TL/PM/CE migrate. */
h1, h2, h3, h4,
.page-title, .section-h, .eyebrow, .card-title,
.app-header h1, .app-header .glyph,
.tile .t-name, .stat .stat-v, .stat .eyebrow,
/* `.btn` and `.seg button` are NOT here (user decision 2026-09-11, Versioning UI audit S2). They
   were listed from 2026-06-05, but both rules below carry `font: inherit`, which comes later in the
   sheet at the same specificity and wins — so every button in the suite has rendered in the BODY
   sans since then, measured with getComputedStyle, while this list claimed serif. The user chose
   to keep what they have been looking at ("serif headings + sans body/data": a control is UI, not
   a heading) and make the code say so. Since ship F every entry here renders in the body sans too. */
.badge, .chip, .tag, .count,
.action-bar .bar-label, .field > label, table.tbl thead th,
.onboarding-box h2, .ob-step-text strong, .state h3,
.sec-label, .metric-label, .metric-val, .mc-col-title, .npv-table th, .score-group-row,
#viewer-header .project-name,
.dash-greeting, .dash-h, .quick-item .label, .im-menu-item,
/* BC (Portfolio Visualizer) titles/labels/values */
.panel-title, .chart-title, .chart-subtitle, .stat-value, .stat-label,
.detail-name, .detail-metric-val, .detail-metric-label, .detail-section-title,
.score-name, .tooltip-name, .tooltip-label, .bc-cfg-name,
.lt-label, .chart-metric-btn, .sort-btn,
.signal-label, .quad-item, .detail-badge, .cgr-stage-tab,
/* ALL stat / KPI / metric / result number values → head font (the body sans since ship F), unified
   2026-06-10 so every numeric readout across surfaces matches the BC stat figures. */
.kpi-v, .ckpi-val, .hero-stat-val, .mr-stat-num, .npv-result-val, .result-metric-val,
.score-val, .cgr-crit-val, .tt-num, .zoom-range-val, .q-num, .ob-step-num {
  font-family: var(--font-head);
}

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* s98 lane C item c (F16 leftover, rail keyboard): the Evaluator's section rail item is as wide as its rail, which clips sideways
   (overflow-y: auto), so its ring is drawn INSIDE the item (precedent: versioning .stage-hd). Here, not in the page <style>,
   because page style blocks may only shrink (check_page_style_dead_css §B, RC-6). */
.pif-nav-item:focus-visible { outline-offset: -2px; }
/* F49, the SVG half (s1453; in the kit sheet, not the Visualizer page <style>, which may only shrink — RC-6): a bubble or a launch-year column reached by the keyboard shows the focus ring. The shared
   :focus-visible outline is kept, and the ring is ALSO drawn as the shape's own accent stroke, because an outline on an
   SVG shape is not painted by every engine (assumed for WebKit, not measured). A faded (non-selected) bubble is shown at
   full strength while it has focus, so the ring is never drawn at 18% opacity. */
#bubbleChart [data-bubble-id][tabindex]:focus-visible,
#bcRiskRewardChart [data-bubble-id][tabindex]:focus-visible,
#bcTimeSeriesChart .ts-launch-hit[tabindex]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
  stroke: var(--accent); stroke-width: 3px; stroke-opacity: 1; stroke-dasharray: none; opacity: 1;
}
button:focus-visible, .btn:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--accent-weak);
}
.sr-only {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ── 5. Scaffold + tool accents ─────────────────────────────────────────────── */
.app-header {
  position: sticky; top: 0; z-index: 100;
  height: 3.25rem; display: flex; align-items: center; gap: var(--sp-4);
  padding: 0 var(--sp-6);
  background: color-mix(in srgb, var(--card) 78%, transparent);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border-bottom: 0.5px solid var(--hair-strong);
}
.app-header .home {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  height: var(--ctl-h-sm); padding: 0 var(--sp-3); border-radius: var(--radius-ctl);
  background: var(--fill); color: var(--text-2);
  font-size: var(--fs-sm); font-weight: 500; cursor: pointer; border: 0;
  text-decoration: none; white-space: nowrap; transition: background 0.14s ease;
}
.app-header .home:hover { background: var(--fill-2); }
.app-header .glyph {
  width: 1.6rem; height: 1.6rem; border-radius: var(--radius-md); display: grid; place-items: center;
  background: var(--tool-weak); color: var(--tool); font-size: var(--fs-base); flex-shrink: 0;
}
.app-header .titles { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.app-header h1 {
  font-size: var(--fs-md); font-weight: 600; letter-spacing: -0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.app-header .sub {
  font-size: var(--fs-xs); color: var(--text-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.app-header .spacer { flex: 1; }
.app-header .status { font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }

.app-main { margin: 0 auto; padding: var(--sp-7) var(--gutter) var(--sp-9); max-width: var(--w-app); width: 100%; }
.app-main.w-reading { max-width: var(--w-reading); }
.app-main.w-app { max-width: var(--w-app); }
.app-main.w-canvas { max-width: var(--w-canvas); }

.page-title { font-size: var(--fs-xl); font-weight: 700; letter-spacing: -0.02em; margin-bottom: var(--sp-1); }
.page-lede { font-size: var(--fs-base); color: var(--text-3); margin-bottom: var(--sp-6); }
.eyebrow { font-size: var(--fs-eyebrow); font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-3); }
/* Section headings are one step below the page title: --fs-lg (DS §5; user decision F19, UR-1, 2026-09-30 — supersedes the
   2026-06-10 lock that made them --fs-xl, which put "Tools" and "Quick actions" at the size of the page title). */
.section-h { font-size: var(--fs-lg); font-weight: 700; letter-spacing: -0.02em; margin: var(--sp-6) 0 var(--sp-3); }

/* tool wayfinding — set the tool accent on a surface root; the header/tile glyphs pick it up */
.tool-ce     { --tool: #2E6FF2; --tool-weak: rgba(46,111,242,0.12); }
.tool-pm     { --tool: #5856D6; --tool-weak: rgba(88,86,214,0.12); }
.tool-bc     { --tool: #0E7490; --tool-weak: rgba(14,116,144,0.12); }
.tool-tl     { --tool: #7C3AED; --tool-weak: rgba(124,58,237,0.12); }
.tool-viewer { --tool: #B06A2E; --tool-weak: rgba(176,106,46,0.12); }
.tool-versioning { --tool: #475569; --tool-weak: rgba(71,85,105,0.12); }
.tool-reporting { --tool: #A0527A; --tool-weak: rgba(160,82,122,0.12); }   /* D29j: plum, was amber (read as caution) */
.tool-chat   { --tool: #0284C7; --tool-weak: rgba(2,132,199,0.12); }   /* D29j: sky, was green (read as proceed) */
/* M9 left-out items (v10.1271.0, user decision 2026-09-27): the header / tile glyph is a monochrome kit icon INKED in the
   tint (D4), so in Dark each tint takes a lighter step of its own hue — each measures >= 3:1 on its own 18% wash over the
   dark card, bg and fill (was 2.08-2.9:1 for Versioning, Evaluator, Visualizer and Timeline). check_m9_surface_rules §N. */
@media (prefers-color-scheme: dark) {
  .tool-ce { --tool: #719DF6; --tool-weak: rgba(113,157,246,0.18); }
  .tool-pm { --tool: #9B9AE6; --tool-weak: rgba(155,154,230,0.18); }
  .tool-bc { --tool: #6AA9BA; --tool-weak: rgba(106,169,186,0.18); }
  .tool-tl { --tool: #B38DF5; --tool-weak: rgba(179,141,245,0.18); }
  .tool-viewer { --tool: #C69469; --tool-weak: rgba(198,148,105,0.18); }
  .tool-versioning { --tool: #98A0AB; --tool-weak: rgba(152,160,171,0.18); }
}
@media (prefers-color-scheme: dark) { .tool-reporting { --tool: #D68AB3; --tool-weak: rgba(214,138,179,0.18); } .tool-chat { --tool: #38BDF8; --tool-weak: rgba(56,189,248,0.18); } }
.tool-im     { --tool: var(--accent); --tool-weak: var(--accent-weak); }

/* stacking helper — vertical card stacks space via container gap, never sibling margins */
.stack { display: flex; flex-direction: column; gap: var(--section-gap); }

/* ── Card / tile / stat ─────────────────────────────────────────────────────── */
.card { background: var(--card); border-radius: var(--radius-card); padding: var(--card-pad); box-shadow: var(--shadow); }
.card-title { font-size: var(--fs-md); font-weight: 700; margin-bottom: var(--sp-1); }
.subcard-title { font-size: var(--fs-base); font-weight: 600; color: var(--text-1); }   /* D23: the inner-card tier (Configurator criterion names, report cards, sub-sections) */
.card-sub { font-size: var(--fs-sm); color: var(--text-3); }

/* tile + stat = content centred both axes (§6a rule 1) */
.tile, .stat {
  background: var(--card); border-radius: var(--radius-card); padding: var(--card-pad); box-shadow: var(--shadow);
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; min-width: 0;
}
/* tiles are often <a> links — reset the default link colour/underline so their text
   uses the neutral heading colour (white in dark), never the accent-blue link colour. */
/* tiles top-align vertically (icons line up across the row regardless of name/desc length);
   horizontal centring is kept from the rule above. Overrides §6a vertical-centre for .tile only — .stat stays centred. */
.tile { min-height: 9.75rem; cursor: pointer; border: 0.5px solid transparent; transition: box-shadow 0.15s ease; color: var(--text-1); text-decoration: none; justify-content: flex-start; }

/* Header watermark — "Chemstrat Consultants" (line 1) + version (line 2), centred, sitting
   where the CONFIDENTIAL badge used to (right side of the app-header). Replaces the old
   fixed bottom-right mark. Shared so every migrated surface is identical + CE inherits it
   when it migrates. Two child <span>s stack via flex-column. */
/* M8 (v10.949.0): --text-4 (#AEAEB2) is 1.98:1 on --bg in light theme — below WCAG AA. This is the
   one genuine READABLE-text consumer of the token in src/ (the footer brand + version), so it moves
   to --text-3 (#6D6D72, 4.61:1). --text-4 stays for the icon/border/disabled/placeholder uses, which
   are WCAG-exempt. Guarded by tools/check_text4_not_readable_text.js. Dark theme unaffected (both
   tokens are #FFFFFF there). User-approved 2026-08-22. */
.chemstrat-mark { display: inline-flex; flex-direction: column; align-items: center; text-align: center; line-height: 1.2; font-size: var(--fs-xs); font-weight: 500; color: var(--text-3); white-space: nowrap; }
.tile:hover { box-shadow: var(--shadow-raised); }
/* A TILE USED AS A SELECTOR, not a launcher (v10.1161.0 — the Versioning channel/source tiles).
   A launcher tile navigates away and has no state; a selector tile is one of a set and exactly one
   is current, so it needs the same "this is the chosen one" treatment `.radio-card.sel` already
   carries: the accent edge plus the weak tint, and nothing else. Keyed on `aria-pressed` rather
   than a class so the visual state cannot drift from the state screen readers are told — the same
   reason `.seg button[aria-pressed="true"]` is keyed that way. Selector tiles are <button>s; a
   launcher <a> never carries aria-pressed and is untouched by this rule. */
.tile[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-weak); }
.tile .t-glyph {
  width: 2.25rem; height: 2.25rem; border-radius: var(--radius-lg); display: grid; place-items: center;
  background: var(--tool-weak); color: var(--tool); font-size: var(--fs-md); margin-bottom: var(--sp-3); flex-shrink: 0;
}
.tile .t-name { font-size: var(--fs-md); font-weight: 700; margin-bottom: var(--sp-1); max-width: 100%; overflow-wrap: anywhere; }
.tile .t-sub { font-size: var(--fs-sm); color: var(--text-3); line-height: 1.35; max-width: 100%; overflow-wrap: anywhere; }
/* s1501 decision 18 (ui_class B2): no fixed .stat height. It was min-height 7rem (112px) where Home's KPI tiles — the only .stat
   on any page — need 95–96px, so each tile was stretched below its words. The tile is now as tall as its content. */
.stat .eyebrow { margin-bottom: var(--sp-2); max-width: 100%; overflow-wrap: anywhere; }
.stat .stat-v { font-size: var(--fs-2xl); font-weight: 700; letter-spacing: -0.02em; }
.stat .stat-s { font-size: var(--fs-xs); color: var(--text-3); margin-top: var(--sp-1); }

/* ── 6. Buttons ─────────────────────────────────────────────────────────────── */
.btn {
  font: inherit; font-size: var(--fs-sm); font-weight: 600;
  height: var(--ctl-h); padding: 0 var(--sp-4); border-radius: var(--radius-ctl);
  border: 0; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  transition: background 0.14s ease; user-select: none; white-space: nowrap; text-decoration: none;
}
.btn-sm { height: var(--ctl-h-sm); font-size: var(--fs-xs); padding: 0 var(--sp-3); }
/* RC-3 (v10.1218.0): a button that fills its grid / flex cell (the merge-review Prev / Overview / Next row) —
   replaces the per-button inline width:100%;min-width:0;justify-content:center. */
.btn-block { width: 100%; min-width: 0; }
.btn-primary { background: var(--accent-fill); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-fill-hover); }
.btn-secondary { background: var(--fill); color: var(--text-2); border: 0.5px solid var(--hair-strong); }
.btn-secondary:hover { background: var(--fill-2); }
.btn-ghost { background: transparent; color: var(--accent-text); }
.btn-ghost:hover { background: var(--accent-weak); }
/* UR-9 F4 (user, 2026-10-05): a ghost button that reads as a link INSIDE a sentence or a meta line — no control height,
   no side padding, caption size (every use sits in a --fs-xs sentence or meta line); the kit's focus ring is unchanged. (.btn-xs is the 24px ICON button, D15.) */
.btn.btn-inline { display: inline; height: auto; padding: 0; font-size: var(--fs-xs); vertical-align: baseline; }
.btn.btn-inline.acc-cond-link { text-decoration: underline; text-underline-offset: 2px; }   /* UR-11 F6: a link beside plain text, told apart by more than colour (WCAG 1.4.1) */
/* v10.1353.0 — the Configurator's Importance column row cells (subaxis_weighting.tierRowCells): the "Not Scored" badge takes
   the column's width (= TIER_COL_W, 11rem; test_asked_last §E pins both), and the Critical '*' slot is 1rem on every row. */
.q-opts-badge.q-notscored { width: 11rem; box-sizing: border-box; align-self: stretch; display: flex; align-items: center; justify-content: center; text-align: center; background: var(--caution-bg); color: var(--caution-text); border: 1px solid var(--caution-hair); }
.q-star-slot { width: 1rem; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; }
.q-pct.q-pct-hidden { visibility: hidden; }
.q-opts-badge.q-notscored.q-notscored-hidden { visibility: hidden; }   /* s1493 L2: a capability follow-up says "Not scored" under its text (mockup v7 p-04); the box keeps the column's width */
/* Conditional questions (v10.1354.0, user decisions 2026-10-06, mock-up A): each condition is a section — an icon, a
   .section-h title (D23 amended), its facts as plain words, one line on when it is asked — and the condition's card,
   "If Yes, ask:" and its criteria sit indented under the icon. "Who answers it" is one tile per role. */
.cond-head-row { display: flex; align-items: center; gap: var(--sp-3); margin: var(--sp-6) 0 var(--sp-1); }
.cond-head-row .section-h, .section-h-row .section-h { margin: 0; }
.cond-ico { width: var(--ctl-h); height: var(--ctl-h); border-radius: var(--radius-ctl); background: var(--fill); color: var(--text-1); display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-md); flex-shrink: 0; }   /* D19: neutral tile (UR-12 F8) */
.cond-facts { margin-left: auto; font-size: var(--fs-xs); color: var(--text-2); }
.cond-purpose { font-size: var(--fs-sm); color: var(--text-2); margin: 0 0 var(--sp-3); }
.cond-purpose, .cond-indent { margin-left: calc(var(--ctl-h) + var(--sp-3)); }
.cond-lead { display: flex; align-items: center; margin-top: var(--sp-4); margin-bottom: var(--sp-2); font-size: var(--fs-sm); font-weight: 700; color: var(--text-1); }
.cond-lbl { font-size: var(--fs-xs); font-weight: 600; color: var(--text-2); margin: var(--sp-3) 0 var(--sp-2); }
.cond-note { font-size: var(--fs-xs); color: var(--text-3); margin-top: var(--sp-2); }
/* s1493 L3 (Ashwin, s1493 decision 472): a field's caption UNDER its control — Create Input Form's "No start date yet" under
   an empty date box (ui_kit.syncDateValue hides it once a date is set). The question's own help-line style. */
.field-note { font-size: var(--fs-xs); color: var(--text-3); margin-top: var(--sp-1); line-height: 1.4; }
/* D20 (amended 2026-10-06, UR-12 F9): the kit's ONE-LINE empty state, for a group inside a tab — what is missing, then its add button. */
.kit-empty-line { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; font-size: var(--fs-sm); color: var(--text-3); margin-top: var(--sp-1); margin-bottom: var(--sp-2); }
.cond-slot-xs { width: 1.5rem; flex-shrink: 0; }   /* UR-12 F4/F5: the place of a 24px icon button (.btn-icon.btn-xs) the other sections show */
.role-tiles { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.role-tile { flex: 1; min-width: 9rem; min-height: 3.25rem; box-sizing: border-box; background: var(--card); border: 1px solid var(--hair-strong); border-radius: var(--radius-ctl); padding: var(--sp-1) var(--sp-3); display: grid; grid-template-columns: 1fr auto; align-items: center; gap: var(--sp-2); }
.role-tile .toggle { margin: 0; }
.role-tile .rt-name { font-size: var(--fs-sm); font-weight: 600; color: var(--text-1); }
.role-tile .rt-sub { font-size: var(--fs-xs); color: var(--text-3); }
.btn-danger { background: var(--kill-bg); color: var(--kill-text); border: 0.5px solid var(--kill-hair); }
.btn-danger:hover { background: color-mix(in srgb, var(--kill-bg) 80%, var(--kill)); }
.btn-icon { width: var(--ctl-h); height: var(--ctl-h); padding: 0; background: var(--fill); color: var(--text-2); }
/* D29c (2026-09-27): DISABLED = SOLID MUTED, never a fade — the same rule as the Configurator's off rows (2026-09-24). A
   neutral surface and --text-disabled words; no role colour, no border; the focus ring is untouched. */
/* D37c (v10.1277.0): a landing card that cannot act here (Hosted "Collect Responses" with no project open) — D29c solid
   muted, never a fade; its caption says why. The card itself is the Evaluator's page rule (.landing-card).
   F4 (UR-1, v10.1311.0; D29c refined): the caption that says WHY keeps the normal caption ink (the page's own
   .landing-card .lc-desc, --text-3: 4.57:1 Light, 10.87:1 Dark). Muted it read 1.96:1 / 2.08:1. Title and icon stay muted. */
/* B4-6 (v10.1373.0, S1-remainder N13; user 2026-10-08): the page colour, not --fill. --fill is a translucent grey: over the
   Dark page it composites to about #303031, LIGHTER than an enabled card (#2C2C2E), so the disabled card read as the
   selected one. --bg sits below --card in both themes (Light #F2F2F7 < #FFFFFF, Dark #1C1C1E < #2C2C2E). */
.landing-card.is-disabled, .landing-card.is-disabled:hover { background: var(--bg); box-shadow: none; cursor: not-allowed; }
.landing-card.is-disabled .lc-title, .landing-card.is-disabled .lc-icon { color: var(--text-disabled); }
.btn:disabled, .btn:disabled:hover { background: var(--fill); color: var(--text-disabled); border-color: transparent; cursor: not-allowed; }
/* RC-3 part 2 (v10.1219.0): the icon-button sizes and roles that replace hand-styled inline buttons.
   .btn-xs = the compact 24px icon button (user decision 2026-09-23, lock D15) — move-up/down arrows and
   row delete ×. 24px is the WCAG 2.5.8 target-size minimum; nothing smaller is allowed. */
.btn-icon.btn-sm { width: var(--ctl-h-sm); height: var(--ctl-h-sm); }
.btn-icon.btn-xs { width: 1.5rem; height: 1.5rem; padding: 0; font-size: var(--fs-xs); }
.btn-icon.btn-ghost { background: transparent; color: var(--accent-text); }
.btn-icon.btn-ghost:hover { background: var(--accent-weak); }
.btn-icon.btn-ghost:disabled, .btn-icon.btn-ghost:disabled:hover { background: transparent; color: var(--text-disabled); }   /* UR-11 F3: .btn-icon.btn-ghost (above) outranked .btn:disabled by source order, so a disabled ▲▼ looked live */
.btn-icon.btn-danger { background: var(--kill-bg); color: var(--kill-text); }
/* The segmented option (CE_UI._makeRadioBtn: Project-type tabs, strategies, report formats) — one card-like
   choice in a row of choices. Moved out of inline style in v10.1219.0; the colours are Ship U's (I8, 2026-09-16 a
   and 2026-09-17 b). Spacing/radius now use tokens (was 7px 14px / 8px). */
.seg-opt {
  font: inherit; font-size: var(--fs-xs); font-weight: 500; line-height: 1.2; text-align: center; white-space: nowrap;
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-ctl); border: 1px solid var(--hair-strong);
  background: var(--card); color: var(--text-1); cursor: pointer; box-sizing: border-box;
  min-width: 4.625rem; min-height: 3.5rem; display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.seg-opt.is-active { font-weight: 700; background: var(--accent-fill); border-color: var(--accent-fill); color: var(--on-accent); }
.seg-opt-caution.is-active { background: var(--caution); border-color: var(--caution); color: var(--on-caution); }
.seg-opt-sub { font-size: var(--fs-xs); font-weight: 400; opacity: 0.85; } /* was margin-top:1px — dropped (D1) */
.seg-opt-extra { font-weight: 600; }
.seg-opt.is-active .seg-opt-sub { opacity: 1; }
/* Layout helpers that replace inline margin-left:auto / margin-top on buttons. */
.u-push-end { margin-left: auto; }
.u-mt-2 { margin-top: var(--sp-2); }
.u-mb-2 { margin-bottom: var(--sp-2); }
.u-mb-3 { margin-bottom: var(--sp-3); }
.u-mb-4 { margin-bottom: var(--sp-4); }
.u-mb-5 { margin-bottom: var(--sp-5); }
/* C1-25 (v10.1248.0): a control that cannot be edited here is SOLID MUTED, not faded (user decision 2026-09-24, v10.1225.0 —
   a faded row read at 2.4–2.9:1). !important because the Configurator controls carry their own page/inline ground. */
.ctl-muted { color: var(--text-2) !important; background: var(--fill) !important; cursor: not-allowed; }
.u-my-4 { margin-top: var(--sp-4); margin-bottom: var(--sp-4); }
.u-stack-2 { display: flex; flex-direction: column; gap: var(--sp-2); }
.u-row-end { display: flex; gap: var(--sp-3); justify-content: flex-end; align-items: center; }
/* v10.1411.0 (look finding): the invitation buttons, on their own line under the person, wrapping on a narrow dialog.
   (The respondent code's .inv-pin went with the code, user decision 2026-10-13.) */
.inv-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
/* s1495 (UR-32 F4, decision 522; moved here from the Visualizer page's <style>, which may only shrink — check_page_style_dead_css):
   the "What is missing (<area>) — …" lines inside the declined-partner row's one box on the Visualizer project card. */
.detail-gov-missing { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-2); padding-left: var(--sp-3); }
.u-grow { flex: 1; min-width: 0; }
/* D23 (user, 2026-10-07): pin a small control to the top-right corner of its box (the Configurator's project-type switch). */
.u-anchor { position: relative; }
/* S1 (2026-10-07): a page exactly the window's height — its own columns scroll, the page never does (the Evaluator). */
.u-fill-screen { height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
.u-corner-tr { position: absolute; top: var(--sp-2); right: var(--sp-2); display: flex; align-items: center; }
/* D24: the content starts below the corner control (its top offset + its 1.5rem height + one step), so they do not share a line. */
.u-anchor.u-corner-pad { padding-top: calc(var(--sp-6) + 2 * var(--sp-2)); }   /* --sp-6 = the compact switch's 1.5rem height */
.u-align-center, .notice.u-align-center { align-items: center; }
/* A switch with its visible label (MINERVA_UI.switchLabelEl) — e.g. "Hide empty criteria" (lock D15). */
.switch-label { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-xs); font-weight: 600; color: var(--text-2); cursor: pointer; }

/* ── 7. Badges / pills / chips / tags ───────────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: var(--sp-1); height: 1.5rem; padding: 0 var(--sp-3);
  border-radius: var(--radius-pill); font-size: var(--fs-xs); font-weight: 600;
  background: var(--fill); color: var(--text-2); white-space: nowrap;
}
.badge.proceed { background: var(--proceed-badge-bg); color: var(--proceed-text); }
/* B4-37 (UR-2 F35; user 2026-10-09): a menu's status line is information, not a control — plain text, not a pill
   with the toolbar buttons' fill and radius. Applies to the three menus' status lines (framework, account, folder). */
/* B4-41 (N33/N34): the Home menus sit inside .action-bar, whose `.action-bar .badge { padding: 0 var(--sp-3) }` (later
   in this sheet, same weight) kept the 12px inset and made "Signed in · administrator" wrap. Named under .action-bar too. */
.im-menu-status .badge, .action-bar .im-menu-status .badge { background: none; height: auto; padding: 0; border-radius: 0; font-weight: 400; color: var(--text-2); white-space: normal; }
.badge.caution { background: var(--caution-badge-bg); color: var(--caution-text); }
.badge.kill { background: var(--kill-badge-bg); color: var(--kill-text); }
.badge .dot { width: 0.45rem; height: 0.45rem; border-radius: var(--radius-round); background: currentColor; }
/* FU-f-cfg-chip-no-ellipsis (2026-09-25): the capped cfg: chips (the cap itself lives on each page) are block containers,
   because an inline-flex box never draws text-overflow's "…" — a long name was cut mid-letter. Same 1.5rem box as .badge. */
.badge.viewer-cfg-name, .badge.pm-cfg-name, .badge.bc-cfg-name { display: inline-block; line-height: 1.5rem; vertical-align: middle; }
.count {
  display: inline-flex; align-items: center; justify-content: center; min-width: 1.3rem; height: 1.3rem; padding: 0 var(--sp-1);
  border-radius: var(--radius-pill); font-size: var(--fs-xs); font-weight: 700; background: var(--accent-fill); color: var(--on-accent);
}
.chip {
  display: inline-flex; align-items: center; gap: var(--sp-2); height: 2rem; padding: 0 var(--sp-3);
  border-radius: var(--radius-pill); border: 0.5px solid var(--control-border); background: var(--card);
  color: var(--text-2); font-size: var(--fs-sm); font-weight: 500; cursor: pointer; transition: background 0.12s ease;
}
.chip:hover { background: var(--fill); }
.chip.active { background: var(--accent-fill); color: var(--on-accent); border-color: var(--accent-fill); }
.tag {
  display: inline-flex; align-items: center; gap: var(--sp-2); height: 1.75rem; padding: 0 var(--sp-1) 0 var(--sp-3);
  border-radius: var(--radius-pill); background: var(--accent-weak); color: var(--accent-text); font-size: var(--fs-xs); font-weight: 600;
}
/* M3c (v10.1255.0): the × was 0.9rem (14.4px, off the type ladder — the browser gate's TYPE OFF LADDER) and faded to 70%
   (off = solid muted). A ladder step, --text-2 at rest, the tag's own ink on hover. */
.tag button { border: 0; background: transparent; color: var(--text-2); cursor: pointer; font-size: var(--fs-sm); line-height: 1; padding: var(--sp-1); }
.tag button:hover { color: inherit; }

/* ── 8. List rows / table ───────────────────────────────────────────────────── */
.list { background: var(--card); border-radius: var(--radius-card); box-shadow: var(--shadow); overflow: hidden; }
.row {
  display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--card-pad);
  border-bottom: 0.5px solid var(--hair); cursor: pointer; transition: background 0.12s ease;
  color: var(--text-1); text-decoration: none; /* reset link styling when a row is an <a> */
}
.row:last-child { border-bottom: 0; }
.row:hover { background: var(--fill); }
.row.sel { background: var(--accent-weak); }
/* M7 (render gate): a selected row's muted line reads --text-2 — --text-3 on the accent tint measured 4.49:1 (the Viewer's
   picker carried this as a page rule, RC-7 v10.1224.0; on the kit row it belongs to every list). */
.row.sel .main .m { color: var(--text-2); }
.row .g { width: 1.9rem; height: 1.9rem; border-radius: var(--radius-ctl); background: var(--fill); display: grid; place-items: center; flex-shrink: 0; font-size: var(--fs-base); }
.row .main { flex: 1; min-width: 0; padding: 0; height: auto; overflow: visible; background: transparent; }   /* M5: a page's own .main (the Evaluator's layout column) must not reach into a row */
/* M5 (v10.1261.0, lock D28d): a row that only DISPLAYS (an evaluator, a saved contact) — no pointer, no hover wash. */
.row.static { cursor: default; }
.row.static:hover { background: transparent; }
/* M9 (D32c): a row may be a <button> (Reclassify's lists) — it takes the row's look, not the browser's button face. */
button.row { width: 100%; font: inherit; text-align: left; background: transparent; border: 0; border-bottom: 0.5px solid var(--hair); }
.row .main .t, .row .main .m { display: block; }
.row .main .t { font-size: var(--fs-sm); font-weight: 600; overflow-wrap: anywhere; }
.row .main .m { font-size: var(--fs-xs); color: var(--text-3); overflow-wrap: anywhere; }
/* s1493 L3 (UR-31 F7, Ashwin s1493 decision 472, option B): a list row's title that wraps to at most two lines, then "…"
   (Merge Responses project names; the full name is in the row's hover tip). */
.row .main .t.t-clamp2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
.row .meta { flex-shrink: 0; font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }
/* M4 (v10.1258.0, lock D27g / D27e) — the Project Evaluator's review-before-submit answers are a LEFT-aligned
   label / value list (was the value pushed to the right edge, D1-20); the critical-question * is neutral bold (D1-11). */
.pm-review-row { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: var(--sp-4); padding: var(--sp-1) 0; border-top: 0.5px solid var(--hair-strong); font-size: var(--fs-sm); text-align: left; }
.pm-review-row .pm-rv-label { color: var(--text-2); overflow-wrap: anywhere; }
.pm-review-row .pm-rv-val { color: var(--text-1); overflow-wrap: anywhere; }
.pm-crit-star { font-weight: 700; color: var(--text-1); margin-left: var(--sp-1); }
.pm-plabel { text-transform: none; }   /* the (P_Tech) / (P_Mark) line under a caps probability axis row keeps its case */
.u-keep-case { text-transform: none; }   /* s1490 item 19 (decisions 408-409, mockup version B): an abbreviation with a lower-case letter ("rNPV") keeps its case inside an upper-cased label (the Visualizer's summary tiles and Compact strip) */

/* ── HORIZONTAL SCROLL AFFORDANCE FOR WIDE TABLES (v10.986.0, Ashwin-directed) ────────────
   A PERSISTENT, VISIBLE horizontal scrollbar on the cash-flow tables.

   WHY IT IS NEEDED, MEASURED: the after-tax model took the Viewer's cash-flow table from 8
   columns to 12 and the PM's from 10 year-columns to as many as the product's life. The
   wrapper has always been `overflow-x: auto`, so nothing broke and the page never scrolled
   sideways — but on macOS the default is OVERLAY scrollbars, which are invisible until you
   already know to scroll. Found by looking at a booted surface: the columns hidden at a
   1017px viewport were Net CF, DCF and Cum. NPV — the three that answer "what did this
   project earn" — with nothing on screen to say more existed.

   The alternative was to narrow the headers or drop the ($M) suffixes to make 12 columns fit.
   That was rejected as a false economy: it degrades every reader's table to accommodate a
   narrow window. Show the scrollbar instead and keep the columns.

   `::-webkit-scrollbar` with an explicit height is what forces a CLASSIC, always-present bar
   in WebKit/Blink — which is both Chrome and the Tauri WKWebView the desktop app runs in —
   overriding the OS "show scroll bars: when scrolling" preference. `scrollbar-width` /
   `scrollbar-color` are the standards-track equivalents for engines that honour them. Both
   are declared; whichever the engine supports wins, and neither breaks the other.

   It lives HERE rather than in the three surfaces that draw these tables because all eleven
   desktop pages AND web/public/index.html load this file — one definition, not three copies
   drifting apart (the mcSliderDefaults class this repo has already paid for).

   `overflow-x: auto` is deliberately NOT `scroll`: a table that fits gets no bar and no dead
   strip of track under it. */
.npv-table-wrap { overflow-x: auto; scrollbar-width: thin; scrollbar-color: var(--text-3) var(--fill); }
.npv-table-wrap::-webkit-scrollbar { height: 10px; }
.npv-table-wrap::-webkit-scrollbar-track { background: var(--fill); border-radius: var(--radius-sm); }
/* THUMB COLOUR IS `--text-3`, NOT `--hair-strong`, AND THAT WAS MEASURED RATHER THAN EYEBALLED.
   `--hair-strong` is a HAIRLINE token: #E5E5EA in light, #38383A in dark. Against the `--fill`
   track it computes to 1.11:1 in light and 1.08:1 in dark — and in dark the thumb is DARKER than
   its own track, i.e. an inverted, invisible control. `--text-3` is the muted-FOREGROUND token
   (#6D6D72 / #FFFFFF) and gives 4.56:1 light, 10.83:1 dark against the same track.
   A scrollbar exists to be noticed; a hairline token is the wrong family for it. */
.npv-table-wrap::-webkit-scrollbar-thumb { background: var(--text-3); border-radius: var(--radius-sm); box-shadow: inset 0 0 0 2px var(--fill); }
.npv-table-wrap::-webkit-scrollbar-thumb:hover { background: var(--text-2); }
/* M7 (lock D30g, 2026-09-27): THE cash-flow table — one spec for the Viewer and the Evaluator (they had two: eyebrow
   caps header + hairline rows here, a filled header + boxed cells there). Numbers right, row heads left in normal case. */
.npv-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.npv-table th { font-size: var(--fs-eyebrow); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-3); text-align: right; padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--hair-strong); white-space: nowrap; }
.npv-table th:first-child { text-align: left; }
.npv-table tbody th { font-size: var(--fs-xs); font-weight: 600; text-transform: none; letter-spacing: 0; color: var(--text-2); text-align: left; border-bottom: 1px solid var(--hair); }
.npv-table td { padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--hair); text-align: right; }
.npv-table td:first-child { text-align: left; font-weight: 500; color: var(--text-2); }
.npv-table td.pos { color: var(--text-1); }
.npv-table td.neg { color: var(--kill-text); }
/* s1502 decision 31 (Ashwin 2026-11-04): the Evaluator's results cash-flow table's line-item labels are left-aligned, as the
   Viewer's (R-F7's declared "Viewer row labels" exception, extended to this table; the Viewer keeps its own fuller rule in
   Project_Viewer.html). `!important` because the kit centres every table cell with `!important` (below); scoped to the
   screen table (.npv-table-years), not the print blocks (OF2 holds those in print). Here, not in Project_Manager.html,
   because page style blocks only shrink (RC-6, check_page_style_dead_css). */
.npv-table-years th.npv-rowhead { text-align: left !important; white-space: nowrap; }
.npv-table tfoot .total-row td { font-weight: 700; border-top: 1px solid var(--hair-strong); border-bottom: none; padding-top: var(--sp-3); font-size: var(--fs-sm); }
.npv-table tfoot .total-row td:first-child { text-transform: uppercase; font-size: var(--fs-eyebrow); letter-spacing: 0.03em; color: var(--text-3); font-weight: 700; }
/* UR-22 N1 (v10.1441.0): the cash-flow table's PRINT copy (viewer_econ.renderCashFlowPrintHTML) — every year in blocks
   that fit the page, and the total. Never on screen; the print block below swaps it in for the scrolling table. */
.npv-print { display: none; }

table.tbl { width: 100%; border-collapse: collapse; background: var(--card); border-radius: var(--radius-card); overflow: hidden; box-shadow: var(--shadow); }
.tbl thead th {
  font-size: var(--fs-eyebrow); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-3); font-weight: 700;
  text-align: center; padding: var(--sp-3) var(--card-pad); border-bottom: 0.5px solid var(--hair-strong); background: var(--fill);
}
.tbl tbody td { font-size: var(--fs-sm); padding: var(--sp-3) var(--card-pad); border-bottom: 0.5px solid var(--hair); color: var(--text-1); text-align: center; }
.tbl tbody tr:last-child td { border-bottom: 0; }
/* A date in a table cell is one token and never breaks at its hyphens (P3 look, v10.1284.0: the
   Versioning roster's Last seen column wrapped '2026-08-24' onto two lines at 1024px). */
.tbl .t-date { white-space: nowrap; }
/* A roster too wide for its card (v10.1295.0, user decision 2026-09-29). The nine-column Minerva Hosted
   customers table needed 960px in an 874px card at a 1024px window, so Access scrolled out of sight.
   `.density-tight` puts its cells one spacing step tighter than .density-compact; `.btn-wraps` lets a
   name button wrap instead of widening its column. Measured in Chromium on this CSS: 802px with the
   live names, 798px with a 26-letter one. Applied from code (versioning.js _clHostedTable), not a page. */
.tbl-wrap.density-tight { --card-pad: var(--sp-2); }
.tbl .btn.btn-wraps { white-space: normal; overflow-wrap: anywhere; height: auto; min-height: var(--ctl-h-sm); padding-block: var(--sp-1); }
/* s1506 decision 3 (FU-v10.1503-chat-raw-markdown): a Chat answer drawn from the safe Markdown subset (src/chat/chat_markdown.js).
   The bubble keeps the page's own size (--fs-sm) and line height; a heading is one or two ladder steps up, never past --fs-md
   (the bubble sits under the page's section level), and a table is the kit's table.tbl, flat inside the bubble. Here, not in
   Chat_With_Minerva.html, because page style blocks only shrink (RC-6, check_page_style_dead_css). */
.ch-bubble.md-body { white-space: normal; }
.md-doc > * + * { margin-top: var(--sp-2); }
.md-doc .md-p { white-space: pre-wrap; }
.md-doc .md-h { font-family: var(--font-head); font-weight: 600; color: var(--text-1); }
.md-doc .md-h1 { font-size: var(--fs-md); }
.md-doc .md-h2 { font-size: var(--fs-base); }
.md-doc .md-h3 { font-size: var(--fs-sm); font-weight: 700; }
.md-doc .md-list { margin-block: 0; padding-inline-start: var(--sp-5); }
.md-doc .md-table-wrap { overflow-x: auto; }
.md-doc table.md-table { box-shadow: none; border: 1px solid var(--hair); }
/* Numeric columns are CENTRE-aligned like every other cell (user-locked) — by the global rule
   below, not by a class of their own.
   ⚠ `.tbl .num, .tbl th.num` (tabular figures) lived here until v10.1155.0 and was REMOVED as
   dead: ✅measured, nothing puts a bare class="num" inside a `.tbl`. `_table()` in versioning.js
   builds every td and th without it, no HTML carries one, and the 22 `"num"` emissions in
   web/server/report.js are `.rpt-tbl` cells that file styles itself. It therefore never applied
   its tabular-nums to anything, and removing it changes no pixel.
   ⚠ `.inp.num` (further down) is ALIVE and must stay: versioning.js:1089 builds `'inp num'` for
   number-type questions. v10.1153.0's changelog called BOTH rules dead — that was wrong about
   this one, and wrong because the grep that "measured" it excluded every line containing
   "number", which is the very word that call site uses. */

/* B4-42 (N36): the report preview's free-text columns (report_model textCols → td.rpt-txt) are left-aligned. v10.1386.0
   put this rule only in render_html's print stylesheet, which the exported HTML document uses but the in-app preview does
   not; here the global centred-cell rule below won. More specific + !important, so it wins over that rule. */
.rg-preview .rpt-table td.rpt-txt { text-align: left !important; }
/* B4-43 (UR-2 F28, reproduced on rebuilt v10.1388.0): cards that follow each other in the Viewer's results and on
   Portfolio Evolution touched (0–2px). They space by --section-gap, as the Viewer's right column already does. */
#viewer-results:not([hidden]) { display: flex; flex-direction: column; gap: var(--section-gap); }
#snapshot-pills-card { margin-bottom: var(--section-gap); }
#dashboard { display: flex; flex-direction: column; gap: var(--section-gap); }
/* B4-51 (UR-3 Obs 3): the report preview fades its bottom edge while more content is below (Report_Generator.js toggles
   .rg-more on render and scroll). The mask is on the scroll box, so it sits at the visible bottom edge, not the content's. */
.rg-preview.rg-more { -webkit-mask-image: linear-gradient(to bottom, currentColor calc(100% - 3rem), transparent); mask-image: linear-gradient(to bottom, currentColor calc(100% - 3rem), transparent); }   /* a mask reads only alpha: any opaque colour (E5: no literal) */
/* ── Global table rule (user-locked 2026-06-10) ─────────────────────────────────
   Every table cell — header AND body, including numeric/money columns — is centre-
   aligned across ALL surfaces. !important so it wins over surfaces that set their own
   per-table text-align (e.g. TL .summary-table / .traj-table, Viewer .npv-table). */
table th, table td { text-align: center !important; }
/* M1 (v10.1242.0): the Configurator GRID tables (ce_ui.gridTable; the .cfg-table base rules live in Config_Editor.html).
   A row-name <th scope="row"> reads like a body cell (left, not an eyebrow), a value cell centres its own contents,
   and the old zebra rows stay. Here, not in the page <style>, because page blocks may only shrink (RC-6). */
.cfg-table th.cfg-rowhead { padding: var(--sp-3) var(--sp-4); font-size: inherit; font-weight: inherit; color: inherit; text-transform: none;
  letter-spacing: normal; background: none; white-space: normal; border-bottom: 0.5px solid var(--hair); vertical-align: top; text-align: left !important; }
.cfg-table tr:last-child th.cfg-rowhead { border-bottom: none; }
.cfg-table tr.cfg-grid-zebra td, .cfg-table tr.cfg-grid-zebra th.cfg-rowhead { background: var(--fill); }
.cfg-cell-c { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--sp-2); }
.cfg-cell-l { justify-content: flex-start; }
/* M1b (v10.1243.0), lock D20: THE empty state (ui_kit.emptyState). Left-aligned, no icon, one spacing. */
.kit-empty { padding: var(--sp-4) 0; text-align: left; }
.kit-empty .ke-title { font-size: var(--fs-sm); font-weight: 600; color: var(--text-1); }
.kit-empty .ke-body { margin-top: var(--sp-1); font-size: var(--fs-xs); line-height: 1.5; color: var(--text-2); max-width: var(--w-reading); }
.kit-empty .ke-action { margin-top: var(--sp-3); }
.kit-empty .ke-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-3); }
.kit-empty .ke-actions .ke-action { margin-top: 0; }
.cfg-table tr.cfg-grid-rule td, .cfg-table tr.cfg-grid-rule th.cfg-rowhead { border-top: 1px solid var(--hair-strong); }
.cfg-grid-wrap { border: 0.5px solid var(--hair-strong); border-radius: var(--radius-ctl); overflow: hidden; }
.cfg-table.cfg-grid { min-width: 0; }


/* ── Score-breakdown Execute/Sell sub-group heading (shared: BC detail / PM results /
   Viewer) — emitted by viewer_scores.js _awSubHeaderHtml. Base size --fs-xs is correct
   for BC/PM (criterion rows --fs-2xs < sub --fs-xs < axis --fs-sm). The Viewer runs a
   larger scale (rows --fs-sm→--fs-xs, axis --fs-base) and overrides the size to --fs-sm
   so the three tiers stay strictly ordered (axis › Execute/Sell › criterion). The size
   lives in CSS (not the inline emit) so each surface can set its own tier. */
.aw-group-subheading { font-size: var(--fs-xs); font-weight: 700; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.5px; margin: var(--sp-3) 0 var(--sp-1); padding-bottom: var(--sp-1); border-bottom: 1px solid var(--hair); }

/* ── 9. Form controls — all text LEFT-aligned + vertically centred (§6a rule 4) ── */
.field { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.field > label { font-size: var(--fs-xs); font-weight: 600; color: var(--text-2); }
.inp {
  height: var(--ctl-h); border: 0.5px solid var(--control-border); border-radius: var(--radius-ctl);   /* D29e: >= 3:1 edge */
  background: var(--card); color: var(--text-1); font: inherit; font-size: var(--fs-sm);
  padding: 0 var(--sp-3); width: 100%; min-width: 0; text-align: left;
}
.inp:focus { outline: 0; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-weak); }
/* RC-7a (v10.1236.0, user decision 2026-09-24): the kit input's focus, for a text box that is not an .inp
   (its own padding / border stay). Replaces 13 `outline:none` declarations that showed no focus at all.
   ⚠ !important on the border colour, measured: most of these boxes set their border in an inline style, which beats
   a class — without it the focus was the 10%-alpha halo alone (render-verify, 2026-09-24), too faint to find. */
.focus-halo:focus { outline: 0; border-color: var(--accent) !important; box-shadow: 0 0 0 3px var(--accent-weak); }
/* Kit-2, slice 9 render check (2026-10-03): a .focus-halo box in error (the evidence-note box) kept the halo rule's
   blue edge when focused — that rule carries !important at the same weight as .is-invalid:focus and comes later. */
.focus-halo.is-invalid:focus { border-color: var(--kill) !important; box-shadow: 0 0 0 1px var(--card), 0 0 0 3px var(--accent); }
/* D29c: a disabled control never shows the pointer (kit-wide, so no page re-declares it). */
:disabled { cursor: not-allowed; }
/* D29c: an entry point that is unavailable on this seat (Home tiles / quick actions; set by Innovation_Manager
   _applyFrameworkGate) — the same solid muted look, on the kit so no page re-declares it. */
.is-unavailable, .is-unavailable:hover { background: var(--fill) !important; box-shadow: none; cursor: not-allowed; }
.is-unavailable, .is-unavailable * { color: var(--text-disabled) !important; }
.is-unavailable .t-glyph { background: var(--fill-2); }
.inp:disabled { background: var(--fill); color: var(--text-disabled); border-color: var(--hair-strong); cursor: not-allowed; }   /* D29c */
.inp.invalid { border-color: var(--kill); box-shadow: 0 0 0 3px var(--kill-bg); }
/* Kit-2 (user decision 2026-10-02): focused while in error, the red edge stays and the halo is the focus colour, so a
   keyboard user can see where they are (it was the same red halo focused or not). */
.inp.invalid:focus { border-color: var(--kill); box-shadow: 0 0 0 1px var(--card), 0 0 0 3px var(--accent); }
/* number input: LEFT-aligned (not right), sized so digits never clip */
.inp.num { text-align: left; width: 6rem; min-width: 6rem; font-variant-numeric: tabular-nums; }
/* The COMPACT input (M3c, v10.1255.0, user decision 2026-09-26, E1-21): one input spec, two sizes. For a dense rail or a
   chart-card toolbar — the Visualizer's Custom-factor min/max boxes and its three chart selects. */
.inp.inp-sm { height: var(--ctl-h-sm); font-size: var(--fs-xs); padding: 0 var(--sp-2); border-radius: var(--radius-md); }
.inp.inp-sm.num { width: 3.25rem; min-width: 3.25rem; padding: 0 var(--sp-1); }
select.inp.inp-sm { width: auto; max-width: 16rem; padding-right: var(--sp-7); }
.field .err { font-size: var(--fs-xs); color: var(--text-1); font-weight: 600; margin-top: var(--sp-1); line-height: 1.35; }   /* D29d: a field's own error = the locked field style (Ship U decision 8) */

/* ── SIGN-IN / ACCESS MODAL MESSAGES ── `.si-err` (v10.1017.0, whose history is in SHIP_LOG / CHANGELOG) was a second
   error style; see D29d below. */
/* D29d (M6): `.si-err` retired — a sign-in / access refusal is about the whole form, so it is the shared kill notice. */

/* RECOVERY (v10.1139.0) — the lost-password route on the sign-in screen. A <button> that reads
   as a link: there is nowhere to navigate to, and an <a> with no href is not keyboard-reachable,
   so the affordance is styled rather than structural. Quiet enough not to compete with the
   primary action in the footer. Its focus ring is the kit's accent ring (A-28: this comment used to
   say `currentColor` because "this stylesheet has no focus token" — false; the rule below uses --accent). */
.si-link {
  display: inline-block;
  margin-top: var(--sp-2);
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--fs-xs);
  color: var(--text-3);
  text-decoration: underline;
  cursor: pointer;
}
.si-link:hover { color: var(--text-2); }
.si-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The quiet counterpart: standing help text and the good-news notice after an invitation is
   accepted. Muted and small so it reads as secondary — and so it cannot be mistaken for the
   error above, which was exactly the failure. `--text-3`, not `--text-4`: the 2026-08-23
   decision put genuinely readable text on the AA-compliant token. */
/* B-06 (D33d, v10.1272.0): the sign-in / team-access dialogs emitted .mv-modal-lede, .si-lbl, .si-inp, .si-qr and
   .si-secret with no style anywhere — labels ran inline against their fields and the lede sat flush on the first label.
   The label is the kit field label; the lede is the dialog's opening sentence. */
.mv-modal-lede { margin: 0 0 var(--sp-2); color: var(--text-2); }
.si-lbl { display: block; margin: var(--sp-3) 0 var(--sp-1); font-size: var(--fs-xs); font-weight: 600; color: var(--text-2); }
.si-inp { width: 100%; }
.si-qr { display: grid; place-items: center; margin: var(--sp-3) 0; }
.si-qr svg, .si-qr img { width: 10rem; height: 10rem; box-sizing: content-box; padding: var(--sp-4); background: var(--qr-paper); color: var(--qr-ink); border-radius: var(--radius-sm); }
.si-secret { margin-top: var(--sp-3); }
.si-secret .si-lbl { margin-top: 0; }
.si-secret code { overflow-wrap: anywhere; user-select: all; }
.mv-modal-note {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--text-3);
}
textarea.inp { height: auto; min-height: 4.5rem; padding: var(--sp-2) var(--sp-3); resize: vertical; line-height: 1.4; text-align: left; }
select.inp {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-3) 50%), linear-gradient(135deg, var(--text-3) 50%, transparent 50%);
  background-position: calc(100% - 1.1rem) 55%, calc(100% - 0.75rem) 55%;
  background-size: 0.35rem 0.35rem, 0.35rem 0.35rem; background-repeat: no-repeat; padding-right: var(--sp-7);
}
.toggle { position: relative; width: 2.7rem; height: 1.6rem; border-radius: var(--radius-pill); background: var(--control-off); border: 0; padding: 0; cursor: pointer; transition: background 0.15s ease; flex-shrink: 0; }
.toggle::after { content: ""; position: absolute; top: 0.18rem; left: 0.18rem; width: 1.24rem; height: 1.24rem; border-radius: var(--radius-round); background: var(--on-accent); box-shadow: var(--shadow-knob); }
.toggle[aria-checked="true"] { background: var(--accent-fill); }
.toggle[aria-checked="true"]::after { left: auto; right: 0.18rem; }
/* D24 (user, 2026-10-07): the compact switch — the Configurator's project-type cards, where the full switch out-weighed the
   card's 18px dot. 40 × 24px: the 24px floor of a small control (lock D15), the kit's proportions (track 1.67:1, knob 0.77). */
.toggle.toggle-compact { width: 2.5rem; height: 1.5rem; }
.toggle.toggle-compact::after { top: 0.17rem; left: 0.17rem; width: 1.16rem; height: 1.16rem; }
.toggle.toggle-compact[aria-checked="true"]::after { left: auto; right: 0.17rem; }
/* RC-3 (v10.1217.0): THE switch — built only by lib/ui_kit.js (MINERVA_UI.switchHtml/switchEl), a real
   <button role="switch" aria-checked>. Was unused and keyed on aria-pressed; now the one switch every surface
   uses. ON = --accent-fill (user decision 2026-09-23: on/off is a selection, blue; green means proceed). OFF =
   --control-off (never a --text-* token: those are #FFFFFF in dark, V-2). The knob MOVES WITHOUT A SLIDE — only
   the background fades (design system motion rule: opacity/background fades only, no transform). Measured:
   knob/track 4.56 ON, 5.15 / 4.16 OFF (light / dark); track vs card >= 3.05 (check_ui_contrast_tokens). */
.radio-card {
  display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-3); border: 0.5px solid var(--control-border);
  border-radius: var(--radius-ctl); cursor: pointer; font-size: var(--fs-sm); min-width: 0; transition: background 0.12s ease, border-color 0.12s ease;
}
.radio-card:hover { background: var(--fill); }
.radio-card .dot { width: 1.15rem; height: 1.15rem; border-radius: var(--radius-round); border: 1.5px solid var(--text-3); flex-shrink: 0; }
.radio-card.sel { border-color: var(--accent); background: var(--accent-weak); }
.radio-card.sel .dot { border-color: var(--accent); box-shadow: inset 0 0 0 3px var(--accent), inset 0 0 0 5px var(--card); }
/* UR-19 F1 (v10.1436.0, user 2026-10-15): a question's answer radio (.radio-opt, the Evaluator and the respondent page) is DRAWN.
   The app's WebKit view ignores accent-color in Dark and drew a checked radio as a bare white dot with no ring (~1:1 against
   the card), read as a bullet; Chromium honoured it, so the two pages differed. One spec, the radio-card dot's tokens. */
.radio-opt input[type="radio"] { -webkit-appearance: none; appearance: none; width: 1.05rem; height: 1.05rem; margin: var(--sp-1) 0 0; flex-shrink: 0; border-radius: var(--radius-round); border: 1.5px solid var(--text-3); background: var(--card); cursor: pointer; }
.radio-opt input[type="radio"]:checked { border-color: var(--accent); box-shadow: inset 0 0 0 3px var(--card), inset 0 0 0 9px var(--accent); }
.radio-opt input[type="radio"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* FU-v10.1484-insufficient-row-wash-is-inline (s1487): a picked "I don't have sufficient information" row takes the caution wash
   from its STATE (the `selected` class the live handlers toggle), not from an inline colour fixed when the card was drawn.
   More specific than either page's own `.radio-opt.selected` blue (Project_Manager.html, respond.html). */
.radio-opt.selected[data-oi="insufficient_info"] { background: var(--caution-bg); }
/* UR-19 F9: a word in a sentence that is a button (it takes the sentence's text, underlined). */
button.link-inline { background: none; border: 0; padding: 0; font: inherit; font-weight: 600; color: inherit; text-decoration: underline; cursor: pointer; }
.radio-card .lab { flex: 1; min-width: 0; text-align: left; overflow-wrap: anywhere; }
.radio-card .sc { flex-shrink: 0; font-size: var(--fs-xs); color: var(--text-3); font-weight: 600; }
/* ── A RADIO-CARD MAY CARRY A CAPTION UNDER ITS TITLE (v10.1162.0) ─────────────────────────────
   The component had `.lab` and nothing else, so a surface that wanted a description put one inside
   `.lab` as a bare <span>. A span is INLINE: `margin-top` is discarded, and the caption renders in
   the same line box as the title. Measured on the rendered page 2026-09-14, not read off the file:
   `Seed the Minerva Web standardA full snapshot as v1...` and `One customerChosen from the
   customers...`. This is the same failure `.mv-path` had ("a short command trailed the step text"),
   and the same fix — declare the box, do not rely on the author reaching for a block element.
   §5 wants one component of each kind, so the caption belongs HERE, not re-invented per surface. */
.radio-card .rc-name { display: block; font-weight: 600; }
.radio-card .rc-sub { display: block; margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--text-3); }
/* On the SELECTED card the ground is --accent-weak, where --text-3 measures 4.49:1 — under the
   4.5:1 AA floor by a hair (measured 2026-09-14, composited, not eyeballed). One step darker
   clears it without making unselected captions heavier than the design intends. */
.radio-card.sel .rc-sub { color: var(--text-2); }
/* A two-line label must not leave the dot floating at the vertical centre of a tall card. */
.radio-card:has(.rc-sub) { align-items: flex-start; }
.radio-card:has(.rc-sub) .dot { margin-top: 0; }
/* D32i (M9, 2026-09-27): the QUICK-ACTION tile — a named kit variant of the tile: compact and left-aligned (an action,
   not a destination), no icon square. Moved here from index.html's own <style> (B-24). */
.quick-item { padding: var(--sp-4); border: 0.5px solid var(--hair-strong); border-radius: var(--radius-ctl); cursor: pointer; transition: background 0.12s ease; background: var(--card); display: flex; flex-direction: column; gap: var(--sp-1); text-decoration: none; color: inherit; min-width: 0; }
.quick-item:hover { background: var(--fill); }
.quick-item .icon { font-size: var(--fs-md); }
.quick-item .label { font-size: var(--fs-md); font-weight: 700; }
.quick-item .desc { font-size: var(--fs-xs); color: var(--text-3); overflow-wrap: anywhere; }
.check { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); cursor: pointer; }
/* M8 (v10.1267.0, lock D31g): a radio-card or a check may be a <button> (the Reports report type / chart options) —
   it takes the text and colour of its surroundings, not the browser's button face. .radio-card.sel still wins (0,2,0). */
button.radio-card, button.check { font: inherit; font-size: var(--fs-sm); color: var(--text-1); background: transparent; text-align: left; }
button.radio-card { width: 100%; }
button.check { border: 0; padding: 0; }
/* M9 (D32c): an unavailable card is solid muted (D29c) and does not react to hover. */
button.radio-card:disabled { cursor: not-allowed; }
button.radio-card:disabled:hover { background: transparent; }
button.radio-card:disabled .rc-name, button.radio-card:disabled .rc-sub { color: var(--text-disabled); }
.check .box { width: 1.2rem; height: 1.2rem; border-radius: var(--radius-sm); border: 1.5px solid var(--text-3); display: grid; place-items: center; flex-shrink: 0; color: var(--on-accent); font-size: var(--fs-xs); }
.check.on .box { background: var(--accent-fill); border-color: var(--accent); }
.seg { display: inline-flex; background: var(--fill); border-radius: var(--radius-ctl); padding: var(--sp-1); }
.seg button { border: 0; background: transparent; font: inherit; font-size: var(--fs-xs); font-weight: 600; color: var(--text-2); padding: var(--sp-1) var(--sp-3); border-radius: var(--radius-md); cursor: pointer; white-space: nowrap; transition: background 0.15s, color 0.15s; }
/* D22 (user decision 2026-09-25; applied to the ONE .seg 2026-09-26, M3c): the selected segment is the neutral raised
   --seg-on above the --fill track in both themes (was --card, which reads as a hole in dark). */
.seg button[aria-pressed="true"] { background: var(--seg-on); color: var(--text-1); box-shadow: var(--shadow); }
.seg button:hover:not([aria-pressed="true"]) { color: var(--text-1); }
.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* M8 (D29c on the .seg, now that Chat's mode is one): an unavailable segment is SOLID MUTED — no fade. */
.seg button:disabled { color: var(--text-disabled); }
/* ── FORM CONTROLS INHERIT THE NEUTRAL TEXT COLOUR (v10.1124.0) ────────────────────────────────
   A control that is given a background but no `color` falls back to the user agent's default text
   colour, which does not follow this file's dark palette — so on a dark card the value the person
   typed renders at almost no contrast against the placeholder beside it. Measured at v10.1123.0:
   ELEVEN controls in `src/**` were in that state (7 inline styles that set `background` and no
   `color`, plus 4 fields carrying `class="input"`, a class defined in NO stylesheet in this repo).

   ⚠ **THIS RULE IS NOT NEW — IT WAS WRITTEN ONCE AND NEVER TRAVELLED.** `Config_Editor.html`
   carried exactly these two lines, with exactly this diagnosis ("predate dark mode and set no base
   color -> UA default = invisible on dark"), applied to ONE of the eleven surfaces. Every surface
   loads THIS file, so this is the one place the rule can live without eleven copies to drift.
   The CE copy was removed in the same commit; a second definition is how the first one got lost.

   `color-scheme` is the other half and is deliberate: it is what makes browser-DRAWN chrome — the
   select arrow, number spinners, the date picker, scrollbars — follow the theme. A control whose
   text this rule fixes still gets a light-mode arrow drawn on a dark field without it.

   Specificity is deliberately the lowest possible (one element selector), so any control that
   states its own colour — `.text-input`, `.crit-weight-input`, an inline `color:` — still wins. */
html { color-scheme: light dark; }
input, select, textarea { color: var(--text-1); }
/* Kit-1 (user decision 2026-10-02, found fixing UR-3 F3): no placeholder had a colour of its own, so every one was the
   browser's grey (#757575), 3.02:1 on the Dark card. One rule, from one token; solid, because Firefox fades it by default. */
::placeholder { color: var(--text-placeholder); opacity: 1; }
/* UR-4 F12 (user decision 2026-10-03): an EMPTY date field's "mm/dd/yyyy" is a format hint, not a value, so it takes the
   placeholder token too (it was full text ink, so an empty date looked filled in). "Empty" is the value attribute, which
   ui_kit.js keeps in step with what the person types or picks (syncDateValue). */
input[type=date]:not([value])::-webkit-datetime-edit, input[type=date][value=""]::-webkit-datetime-edit { color: var(--text-placeholder); }
/* UR-18 F4 (v10.1431.0, user decision 2026-10-15): the rule above reaches the separators only — WebKit (the app's engine) draws
   an empty field's month/day/year in its own grey. Measured in Safari 2026-10-15 on #FFFFFF: digits 2.40:1 before, 5.69:1 with
   these (the placeholder token's own 5.82:1 in the same picture). Solid, as ::placeholder is. */
input[type=date]:not([value])::-webkit-datetime-edit-month-field, input[type=date]:not([value])::-webkit-datetime-edit-day-field,
input[type=date]:not([value])::-webkit-datetime-edit-year-field, input[type=date]:not([value])::-webkit-datetime-edit-text,
input[type=date][value=""]::-webkit-datetime-edit-month-field, input[type=date][value=""]::-webkit-datetime-edit-day-field,
input[type=date][value=""]::-webkit-datetime-edit-year-field, input[type=date][value=""]::-webkit-datetime-edit-text { color: var(--text-placeholder); -webkit-text-fill-color: var(--text-placeholder); opacity: 1; }

input[type=range] { -webkit-appearance: none; appearance: none; width: 100%; height: 0.35rem; border-radius: var(--radius-pill); background: var(--fill-2); }
input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 1.2rem; height: 1.2rem; border-radius: var(--radius-round); background: var(--on-accent); border: 0; box-shadow: var(--shadow-knob), 0 0 0 0.5px var(--control-border); cursor: pointer; }   /* D29: shadow only (the edge is part of the shadow) */
input[type=range]::-moz-range-thumb { width: 1.2rem; height: 1.2rem; border-radius: var(--radius-round); background: var(--on-accent); border: 0; box-shadow: var(--shadow-knob), 0 0 0 0.5px var(--control-border); cursor: pointer; }

/* ── 10. Action bar / states / banners / toast / tooltip / modal ────────────── */
.action-bar {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  background: var(--card); border-radius: var(--radius-card); box-shadow: var(--shadow); padding: var(--sp-3) var(--card-pad);
}
/* M9 render check (v10.1270.0): a bar whose controls are drawn later (Analytics, before any project) is not an empty strip. */
.action-bar:empty { display: none; }
.action-bar .grp { display: flex; align-items: center; gap: var(--sp-2); }
.action-bar .bar-label { font-size: var(--fs-eyebrow); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-3); }
.action-bar .bar-divider { width: 1px; height: 1.4rem; background: var(--hair-strong); margin: 0 var(--sp-1); }
.action-bar .bar-spacer { flex: 1; }
/* Option 1 — toolbar control group: every status badge AND button in an action bar shares
   one height (--ctl-h-sm) + one corner (--radius-ctl), so the row reads as one even control
   band (was: 24px fully-round badges next to 36px buttons → uneven). Badges keep their colour;
   badges OUTSIDE a toolbar stay the small 24px pill. */
.action-bar .badge, .pm-action-bar .badge, .app-header .badge,
.action-bar .btn,   .pm-action-bar .btn {
  height: var(--ctl-h-sm); border-radius: var(--radius-ctl);
}
.action-bar .badge, .pm-action-bar .badge, .app-header .badge { font-size: var(--fs-sm); padding: 0 var(--sp-3); }
.action-bar .btn-icon, .pm-action-bar .btn-icon { width: var(--ctl-h-sm); }

.state {
  background: var(--card); border-radius: var(--radius-card); box-shadow: var(--shadow); padding: var(--sp-8) var(--card-pad);
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--sp-2);
}
.state .state-ic { width: 2.75rem; height: 2.75rem; border-radius: var(--radius-card); background: var(--fill); display: grid; place-items: center; font-size: var(--fs-md); color: var(--text-3); margin-bottom: var(--sp-1); }
.state h3 { font-size: var(--fs-md); font-weight: 600; }
.state p { font-size: var(--fs-sm); color: var(--text-3); max-width: 28rem; line-height: 1.5; }

.notice {
  display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-ctl);
  font-size: var(--fs-sm); border: 0.5px solid var(--notice-edge); background: var(--fill);
}
.notice .nic { flex-shrink: 0; font-size: var(--fs-base); line-height: 1.3; }
.notice .ntx { min-width: 0; overflow-wrap: anywhere; color: var(--text-1); }
/* ⚠ THE NOTICE'S WORDS ARE IN THE TEXT COLOUR; THE MEANING IS IN THE TINT, BORDER AND ICON (user
   decision 2026-09-11, Versioning UI audit S1). The variants below set `color` to the signal hue,
   and as TEXT on their own tints those measured below WCAG AA's 4.5:1 — proceed 3.98, caution 4.41,
   kill 4.47 (light). The `.nic` icon still inherits the hue (a graphic needs 3:1, which all pass);
   only the sentence moves to --text-1. Darkening the tokens was the alternative and was declined:
   it would shift the signal colours in every chart and verdict tile as well. */
.notice .ntx strong { font-weight: 600; }
.notice.info { background: var(--accent-weak); border-color: color-mix(in srgb, var(--accent) 25%, transparent); color: var(--info-fg); }
.notice.proceed { background: var(--proceed-bg); border-color: var(--proceed-hair); color: var(--proceed-text); }
.notice.caution { background: var(--caution-bg); border-color: var(--caution-hair); color: var(--caution-text); }
.notice.kill { background: var(--kill-bg); border-color: var(--kill-hair); color: var(--kill-text); }
/* The "page could not load" panel (M3a, v10.1251.0): one copy, was pasted into six pages' <style>. Its top/bottom
   margin was 5rem (80px, off the spacing scale) — now the largest step, --sp-9 (48px). Project_Manager keeps its own
   variant for M4. */
/* D29f (2026-09-27): the "page could not load" panel is the shared caution NOTICE — tint, hairline, the kit warning icon,
   words in the text colour (the heading was amber text). One definition; no page re-declares it. */
/* D32f (2026-09-27, supersedes D29f for this panel): "Starting Minerva…" is a LOADING state, so it is the shared INFO
   notice (accent tint, matching hairline, the kit info icon) — amber said "something is wrong" while nothing was. */
.js-warn { max-width: var(--w-reading); margin: var(--sp-9) auto; padding: var(--sp-3) var(--sp-4); display: flex; align-items: flex-start; gap: var(--sp-3); background: var(--accent-weak); border: 0.5px solid color-mix(in srgb, var(--accent) 25%, transparent); border-radius: var(--radius-ctl); color: var(--text-1); font-size: var(--fs-sm); }
.js-warn .ic { flex-shrink: 0; font-size: var(--fs-base); line-height: 1.3; color: var(--info-fg); }
.js-warn h2 { font-size: var(--fs-sm); font-weight: 700; margin: 0 0 var(--sp-1); }
.js-warn p { font-size: var(--fs-sm); line-height: 1.5; margin: 0; color: var(--text-1); }
/* A stage's colour as a small dot before its name (M3b, v10.1252.0 — replaced the Visualizer's 3px stage rail, R-D4).
   The colour arrives as --dot; a filtered-out (dimmed) chip's dot is solid muted, like its text (off = solid muted). */
.stage-dot { display: inline-block; width: 0.5rem; height: 0.5rem; border-radius: var(--radius-round); background: var(--dot, var(--hair-strong)); margin-right: var(--sp-1); vertical-align: 0.0625rem; }
.dimmed .stage-dot { background: var(--hair-strong); }
/* RC-3 notices (v10.1220.0): a title line over the body, inside .ntx (icon | title / body). */
.notice .notice-title { font-weight: 700; margin-bottom: var(--sp-1); }
/* UR-22 F9/F13 (v10.1441.0): a notice's inner row (text + action), its secondary line (a date, "frozen for audit") and a
   quoted note. Secondary text is --text-2, AA on every notice fill (--text-3 measured 4.45:1 on the Light .notice.kill fill).
   Moved here from Project_Manager.html (was .pm-banner-row/-meta/-note) — generic notice parts, one definition. */
.notice .ntx:has(> .notice-row) { flex: 1; }   /* the row spans the notice, so its action sits at the right (was .notice.pm-banner > .ntx) */
.notice-row { display: flex; align-items: center; gap: var(--sp-3); }
.notice-row.wrap { flex-wrap: wrap; }
.notice-meta { font-size: var(--fs-xs); color: var(--text-2); }
/* s1496 decision 539 (mockup s1496_mockup_banner_reason_v1): a notice's second line, in its own block under the first —
   same size and colour as the first line, a small step above it (Home's mixed-version banner: why projects are behind). */
.notice .ntx .notice-more { display: block; margin-top: var(--sp-2); }
/* UR-30 F5 (s1489): the Merge complete screen's "set aside" line (merge_dialog_panel _mergeRenderComplete) — was an inline style.
   In the kit, not the Evaluator's own <style>: page style blocks only shrink (check_page_style_dead_css §B, RC-6). */
.pm-merge-set-aside { font-size: var(--fs-xs); color: var(--caution-text); margin-top: var(--sp-1); }
.notice-note { font-size: var(--fs-sm); color: var(--text-2); margin-top: var(--sp-2); line-height: 1.5; }
/* Debt D U5 (2026-10-18): the score card's one-line "Held down most by: …" under an axis heading (Viewer and Visualizer).
   Plain secondary text, --text-2 (AA on the card in Light and Dark); --fs-xs, the 12px floor. */
.sb-holddown { font-size: var(--fs-xs); color: var(--text-2); line-height: 1.4; overflow-wrap: anywhere; }
/* s1489 finding E (E1, decision 391/400): the Viewer's NPV figure when the ring is not drawn (NPV <= 0) — the tile value's
   size and weight, red below zero. Its own rules here, not grouped onto .metric-card .metric-val: two checks find that rule by
   its exact selector (check_no_serif_fonts §D, check_not_calculated_says_so §E), and page style blocks may only shrink (RC-6). */
.ecv-noring-npv { font-size: var(--fs-lg); font-weight: 700; letter-spacing: -0.02em; color: var(--text-1); margin-bottom: var(--sp-1); line-height: 1.1; white-space: nowrap; }
.ecv-noring-npv.ecv-noring-neg { color: var(--kill-text); }

/* `.ipf-toast` is the class the controllers (showToast) emit suite-wide; `.toast` is the
   short alias used in mockups. Both styled identically. Types: success/error/warning/info. */
.toast-container, .ipf-toast-container { position: fixed; bottom: 1.5rem; left: 50%; transform: translateX(-50%); z-index: 9999; display: flex; flex-direction: column; gap: var(--sp-2); pointer-events: none; }
/* F3 (S1, 2026-10-07): while a dialog is open, toasts move to the TOP so they never cover its buttons (the review's
   Submit sits at the bottom of the dialog). The backdrop exists only while a dialog is open (modal.js removes it). */
body:has(.mv-modal-backdrop) .toast-container, body:has(.mv-modal-backdrop) .ipf-toast-container { bottom: auto; top: var(--sp-4); }
/* UR-19 F2 (v10.1436.0, user 2026-10-15): on a wizard step (.q-nav) toasts sit at the top, so an error never covers the step's buttons. */
body:has(.q-nav) .ipf-toast-container { bottom: auto; top: 7rem; }
/* UR-20 F5 (v10.1438.0, user 2026-10-16): 7rem clears the Evaluator header and toolbar (Save, Project Lead, the title) — the
   UR-19 top: var(--sp-4) sat over them. Still clear of the step's buttons at the bottom (UR-19 F2). */
.toast, .ipf-toast { pointer-events: auto; display: inline-flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-lg); font-size: var(--fs-sm); font-weight: 500; line-height: 1.4; background: var(--accent-fill); color: var(--on-accent); box-shadow: var(--shadow-raised); max-width: 30rem; animation: mv-toast-fade 0.15s ease both; }
/* D29a (2026-09-27): a toast FADES in (opacity only, 150ms — no slide) and takes its fill + ink from the signal tokens, so
   Dark gets its own pair (the four hex fills were light-only, A-37). REFINED 2026-10-23 (s1480 decision 226, F-22): the fade
   is a keyframe animation, not an @starting-style transition. In the app's web view the @starting-style transition left the
   pause/resume toast at 10% opacity, or never started it, so the toast was in the page for its 4 s and not seen (debug build,
   s1480 P1-P4); the keyframe fade was seen. Same look: opacity 0 to 1 in 150ms. */
@keyframes mv-toast-fade { from { opacity: 0; } to { opacity: 1; } }
.toast.success, .ipf-toast.success { background: var(--proceed-fill); color: var(--on-proceed); }
.toast.error, .ipf-toast.error { background: var(--kill); color: var(--on-kill); }
.toast.warn, .toast.warning, .ipf-toast.warn, .ipf-toast.warning { background: var(--caution); color: var(--on-caution); }
.toast.info, .ipf-toast.info { background: var(--accent-fill); color: var(--on-accent); }
/* M4 render check (v10.1259.0): a sticky toast's × is the kit ghost icon button (D27l) — on the toast it takes the toast's
   own white text, not the accent blue (which read blue-on-amber, found on screen). */
.ipf-toast .btn-icon.btn-ghost { color: inherit; }
/* s1490 L4 (decision 426 (i)): a toast that leaves while the pointer is over the stack keeps its box, unseen, until the
   pointer leaves (Project_Manager _toastGone), so the stack does not move under the pointer. */
.ipf-toast.ipf-toast-held { visibility: hidden; }
/* Visualizer series labels (lock D28j): "Bubble type" / "Metric" never break mid-label. */
.bc-metric-row > label, .bc-ts-row > label { white-space: nowrap; }
/* UR-30 F5 (s1489): the Visualizer's Innovation Balance column note "Leaves out N projects below zero (…)" — a kit class, not an inline style string (UI rule 4; a page <style> may only shrink, RC-6). */
.bc-ib-below-zero { font-size: var(--fs-xs); color: var(--text-3); text-align: right; }

/* ── Onboarding / first-launch overlay (IM, cross-cutting) ──────────────────── */
/* RC-5 (v10.1222.0): `.onboarding-overlay` retired — the Home setup dialogs are the shared dialog (MINERVA_MODAL),
   which hosts each `.onboarding-box` as its body. Their holders in index.html are `.setup-holder` (always hidden). */
.setup-holder { display: none; }
.onboarding-box { width: 100%; }
.mv-modal.mv-setup .mv-modal-body { padding: var(--sp-6) var(--sp-8); }
.onboarding-box h2 { font-size: var(--fs-lg); font-weight: 700; margin-bottom: var(--sp-2); letter-spacing: -0.02em; }
.onboarding-box .ob-subtitle { font-size: var(--fs-sm); color: var(--text-2); margin-bottom: var(--sp-6); line-height: 1.5; }
.onboarding-steps { display: flex; flex-direction: column; gap: var(--sp-4); margin-bottom: var(--sp-7); }
.onboarding-step { display: flex; align-items: flex-start; gap: var(--sp-4); }
.ob-step-num { width: 1.75rem; height: 1.75rem; border-radius: var(--radius-round); background: var(--accent-fill); color: var(--on-accent); font-size: var(--fs-sm); font-weight: 700; display: grid; place-items: center; flex-shrink: 0; }
.ob-step-text strong { font-size: var(--fs-sm); font-weight: 600; display: block; margin-bottom: var(--sp-1); }
.ob-step-text span { font-size: var(--fs-sm); color: var(--text-2); line-height: 1.4; }
/* The setup dialogs' main action is the canonical primary button, full width (was the bespoke .ob-dismiss-btn,
   white on --accent: 3.65:1 in dark). `.ob-go` adds only the space above it. */
.ob-go { margin-top: var(--sp-2); }

/* A-09 (M6): the dormant `.tooltip-bubble` and `.skel` (+ its shimmer) rules were removed — no markup anywhere used them.
   The one tooltip is lib/fast_tooltip.js (#fast-tip), coloured by --tooltip-bg / --tooltip-text. */

/* RC-5 (v10.1222.0): the dormant `.modal-backdrop` / `.modal` / `.modal-head|-body|-foot` shell was deleted — no
   markup anywhere (src, public, web, hosted) used it; MINERVA_MODAL (§10b) is the one dialog shell (design system D7). */

/* ── 10b. Shared modal substrate (lib/modal.js / MINERVA_MODAL) ──────────────── */
/* JS-instantiated, focus-trapped dialog host shared by every surface. Chrome only;
   content classes (e.g. .viewer-modal-q-*) are styled by the consuming surface. */
.mv-modal-backdrop { position: fixed; inset: 0; z-index: 1000; background: var(--scrim); display: flex; align-items: center; justify-content: center; padding: var(--sp-4); }
/* RC-5 (v10.1222.0): a dialog that must sit above every other layer (the hosted upgrade wall). */
.mv-modal-backdrop.mv-top { z-index: 2147483647; }
/* The licence gate (lib/license_gate.js) — an OPAQUE full-page cover, deliberately NOT the shared dialog: the app
   must not show behind an unlicensed prompt (user decision 2026-09-24, design system D16). Was an injected <style>
   of literals (RC-5, v10.1222.0). */
.license-gate { position: fixed; inset: 0; z-index: 2147483646; display: flex; align-items: center; justify-content: center; background: var(--bg); padding: var(--sp-4); }
.license-gate .lg-card { width: min(30rem, 100%); background: var(--card); border-radius: var(--radius-card); box-shadow: var(--shadow-modal); padding: var(--sp-8) var(--sp-7); text-align: center; }
.license-gate .lg-brand { font-size: var(--fs-lg); font-weight: 700; color: var(--text-1); margin-bottom: var(--sp-1); }
.license-gate .lg-cust { font-size: var(--fs-base); color: var(--text-2); margin-bottom: var(--sp-4); }
.license-gate .lg-status { font-size: var(--fs-sm); margin-bottom: var(--sp-5); }
.license-gate .lg-ok { color: var(--proceed-text); }
.license-gate .lg-bad { color: var(--kill-text); font-weight: 600; }
.license-gate .lg-box { margin-top: var(--sp-2); }
.license-gate textarea.inp { width: 100%; height: auto; min-height: 4.5rem; resize: vertical; }
.license-gate .lg-actions { display: flex; gap: var(--sp-2); justify-content: center; margin-top: var(--sp-3); }
.license-gate .lg-err { color: var(--kill-text); font-size: var(--fs-xs); margin-top: var(--sp-2); min-height: 1rem; }
.license-gate .lg-hint { color: var(--text-2); font-size: var(--fs-xs); margin-top: var(--sp-4); }
/* ── Monte Carlo charts (PDF | CDF | Tornado) — Viewer and Project Evaluator, ONE definition (RC-4, v10.1223.0; the two
   pages each had a copy). User decision 2026-09-24 "wrap to fit": each chart gets at least its natural width (480 +
   card padding) so its 11-13px text is DRAWN at 11-13px — side by side at any width had squeezed it to ~5-7px
   (measured). Three in a row only on a wide window; otherwise they wrap. The chart never grows past 1:1. */
.mc-charts-row { display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: stretch; }
.mc-col { flex: 1 1 31.5rem; min-width: 0; display: flex; flex-direction: column; }
.mc-col-title { font-size: var(--fs-eyebrow); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-1); text-align: center; margin-bottom: var(--sp-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; height: 1.125rem; line-height: 1.125rem; }
.mc-chart-card { background: var(--card); border-radius: var(--radius-card); border: 0.5px solid var(--hair-strong); overflow: hidden; flex: 1; padding: var(--sp-3); }
.mc-chart-card svg { max-width: 30rem; margin: 0 auto; }
.mc-chart-svg, .tornado-svg { width: 100%; height: auto; display: block; }
.mc-col-caption { font-size: var(--fs-xs); font-weight: 500; color: var(--text-2); text-align: center; margin-top: var(--sp-2); }
.tornado-empty { padding: var(--sp-5) var(--sp-3); font-size: var(--fs-sm); color: var(--text-2); text-align: center; line-height: 1.45; }
/* A dialog drawn as a left side sheet (the Visualizer compact-view filters, RC-5). */
.mv-modal-backdrop.mv-drawer-host { justify-content: flex-start; align-items: stretch; padding: 0; }
.mv-modal.mv-drawer { width: min(27.5rem, 92vw); max-height: none; height: 100dvh; border-radius: 0; border-width: 0 0.5px 0 0; }
.mv-modal { position: relative; background: var(--card); border-radius: var(--radius-lg); box-shadow: var(--shadow-modal);   /* D29b: shadow only */ width: min(36rem, calc(100vw - 2rem)); max-height: 85vh; display: flex; flex-direction: column; }
/* RC-7a (v10.1236.0): the dialog is focused itself only when it holds no control (modal.js). No ring for that
   programmatic focus after a click; a keyboard-opened dialog keeps the app-wide :focus-visible ring. */
.mv-modal:focus:not(:focus-visible) { outline: none; }
.mv-modal[data-size="sm"] { width: min(26rem, calc(100vw - 2rem)); }
.mv-modal[data-size="lg"] { width: min(52rem, calc(100vw - 2rem)); }
/* M8 (D31e): a report slide opened at its own size — the whole window, the body scrolling when the slide is wider. */
.mv-modal[data-size="full"] { width: calc(100vw - 2rem); }
.mv-modal-header { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-4) var(--sp-5); border-bottom: 0.5px solid var(--hair); }
.mv-modal-title { font-size: var(--fs-md); font-weight: 700; margin: 0; color: var(--text-1); }
.mv-modal-close { background: none; border: none; cursor: pointer; font-size: var(--fs-lg); line-height: 1; color: var(--text-3); padding: 0 var(--sp-1); }
.mv-modal-close:hover { color: var(--text-1); }
/* D29g: a dismissible dialog with no title still gets the shared ×, in the corner (modal.js). */
/* M9 render check (v10.1270.0): right at --sp-4, not --sp-2 — a long setup dialog's body scrollbar runs down the right
   edge and sat on top of the × (Connectors, found on screen). */
.mv-modal-close.is-floating { position: absolute; top: var(--sp-2); right: var(--sp-4); z-index: 1; }
.mv-modal-body { padding: var(--sp-4) var(--sp-5) var(--sp-5); overflow-y: auto; font-size: var(--fs-sm); color: var(--text-1); line-height: 1.5; }
.mv-modal-footer { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--sp-2); padding: var(--sp-3) var(--sp-5); border-top: 0.5px solid var(--hair); background: var(--fill); }
/* Modal body content helpers (confirm message + prompt field) */
.mv-modal-body:empty { display: none; }
.mv-modal-msg { color: var(--text-2); white-space: pre-wrap; }
.mv-modal-msg + .inp { margin-top: var(--sp-4); }
.ce-existing-pick { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-2); margin-top: var(--sp-4); }   /* FU-966 picker (B2) */
.ce-existing-pick .btn { white-space: normal; text-align: left; }   /* UR-29 F2: a long question wraps instead of running past the dialog's edge */
/* CE-1 (s1487 T11): a wrapped choice keeps its background round every line — the kit's .btn-sm is one control-height tall
   with no top/bottom padding, so the second line sat on the pill's bottom edge. Grows with its text; a one-line choice stays
   about the control height. */
.ce-existing-pick .btn.btn-sm { height: auto; min-height: var(--ctl-h-sm); padding-top: var(--sp-2); padding-bottom: var(--sp-2); }

/* ── Team access panel (SEATS T2) ──────────────────────────────────────────────
   Built from the SHARED `.list` / `.row` / `.badge` vocabulary above rather than a private one,
   so it inherits the suite's card, hairline, spacing and dark-mode tokens for free. These four
   rules are the only genuine deltas.

   ⚠ THE `cursor` OVERRIDE IS THE LOAD-BEARING ONE. `.row` ships `cursor: pointer` and a hover
   background because every other list in the suite is clickable. A roster row is NOT: releasing a
   seat is T3 and does not exist yet, so a pointer here promises an action that would do nothing —
   which is exactly the kind of dead affordance `check_hosted_dead_affordances` exists to stop. */
.ax-list .ax-row { cursor: default; }
.ax-list .ax-row:hover { background: transparent; }
/* A released seat is shown for the record, so it is present but visibly past-tense. */
/* B4-53 (v10.1393.0, N42; user 2026-10-09): was `color: var(--text-3)`, which is #FFFFFF in Dark (a contrast choice), so a
   released row read exactly like an active one there. The row's words are dimmed instead, in both themes; the badge on the
   right keeps full strength and says the state. */
.ax-list .ax-released .main { opacity: 0.6; }
/* "you" — a quiet marker, not a badge competing with the Active/Released one on the right. */
.ax-you { font-size: var(--fs-xs); font-weight: 600; color: var(--text-3); }
.ax-note { margin-top: var(--sp-3); font-size: var(--fs-xs); }
/* T3 — the Release control sits beside the Active/Released badge in `.row .meta`, which is a bare
   flex-shrink:0 box with no internal spacing of its own. */
.ax-list .ax-row .meta { display: inline-flex; align-items: center; gap: var(--sp-2); }
/* T4a — an outstanding invitation is shown against the ceiling but is NOT access anyone holds, so
   it reads as pending rather than present. Same de-emphasis as a released row, different badge. */
.ax-list .ax-invited .main { opacity: 0.6; }   /* B4-53 (N42): as a released row */

/* ── 11. Responsive + print + reduced-motion ────────────────────────────────── */
@media (max-width: 48em) {
  .app-header { height: auto; min-height: 3.25rem; flex-wrap: wrap; padding: var(--sp-2) var(--sp-4); }
  .app-main { padding: var(--sp-5) 1rem var(--sp-8); }
  .action-bar { padding: var(--sp-2) var(--sp-3); }
}
@media (max-width: 30em) {
  /* The 44px touch-target floor (design system §7). `.seg button` and `.mv-modal-close` joined the
     `.btn` rule 2026-09-11 (Versioning UI audit S3/S4): measured at 420px, the dialog's × was a 22px
     target and the segmented control's buttons 28px.

     `.inp` joined 2026-09-12. MEASURED, not reasoned: web/public/index.html at 420px drew its
     access-token field #tok at 36px with the Sign in button beside it at 44px — the one control you
     must hit to type was the short one. `.inp` carries `height: var(--ctl-h)` (2.25rem = 36px,
     :121) and NO media query redefines that token, so the floor has to come from `min-height`
     here rather than from the token: `.density-compact` overrides `--ctl-h` to 2rem on its own
     subtree (:179) and would win over any :root redefinition inside this block, whereas
     `min-height` beats a fixed `height` in every case.

     ⚠ THIS CANNOT AFFECT MINERVA MASTER. src-tauri/tauri.conf.json:20 pins the window at
     minWidth 1024, so 30em (480px) is a width the desktop app cannot be dragged to. The only
     surface this rule reaches is the web tier, which loads a byte-identical mirror of this file
     (web/_engine/src/lib/minerva-v2.css) and runs on real phones.

     GUARDED BY `tools/check_tap_target_floor.js`, WRITTEN IN THE SAME SHIP. Until it existed,
     `grep -rln '2.75rem|44px|tap.target|touch.target' tools/` returned nothing: §7 was a
     convention, so a later edit could drop a selector from this list and all 297 smokes stayed
     green — FU-v10.1142 (guard scope is never declared) with a worked example. That gate now
     carries the covered selector list as a DECLARED scope (§B), pins the floor value (§A), keeps
     an inventory of every control class this repo ships (§C), fails if Master's window floor ever
     drops below this breakpoint and makes the paragraph above false (§D), and compares the rule
     against every mirrored copy of this stylesheet (§E). Deliberate-regression TTF-1 removes
     `.inp` from the list below and TTF-2 makes §E vacuous; both are detected.

     ⚠ IT IS NOT A MEASUREMENT. A rule that exists can still be beaten by something more specific,
     and the file said `min-height: 2.75rem` for `.btn` the entire time `.inp` was drawn at 36px.
     The rendered check is the render-verify at 420px; the gate keeps the rule from losing a
     selector between those. */
  /* M8 (v10.1267.0): `.chip` joined when Chat's suggestions became the kit chip (32px drawn below the floor). */
  .btn, .seg button, .mv-modal-close, .inp, .chip { min-height: 2.75rem; }
  .mv-modal-close { min-width: 2.75rem; }
  /* v10.1219.0: an icon button stays square at the floor (the 24px .btn-xs would otherwise be 24 wide x 44 tall). */
  .btn-icon { min-width: 2.75rem; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}
@media print {
  /* UR-22 F1 (v10.1441.0): A PRINT IS ALWAYS LIGHT. Paper is white, so a print taken in Dark mode drew dark cards with
     black titles (1.51:1) and white text on pale notices (1.13:1). Every token the Dark block (§2) redefines is restated
     here at its Light (§1) value; this block comes after §2, so it wins whatever the OS scheme. tests/test_ur22a_fixes.js
     §F1 fails if a token added to §2 is not restated here, or is restated with another value. */
  :root {
    --bg: #F2F2F7; --card: #FFFFFF; --seg-on: #FFFFFF; --text-1: #1C1C1E; --text-2: #3C3C43; --text-3: #6D6D72;
    --text-4: #AEAEB2; --hair: rgba(60,60,67,0.10); --hair-strong: #E5E5EA; --fill: rgba(118,118,128,0.10);
    --fill-2: rgba(118,118,128,0.16); --accent: #0A6CFF; --accent-weak: rgba(10,108,255,0.10);
    --accent-hover: #0A5FE0; --info-fg: #0A3E8C; --accent-fill: var(--accent);
    --accent-fill-hover: var(--accent-hover); --accent-text: #0055CC; --proceed-text: #1B6E2F; --on-caution: #FFFFFF;
    --on-kill: #FFFFFF; --proceed-fill: #1B6E2F; --on-proceed: #FFFFFF; --control-off: #6D6D72; --control-border: #86868B;
    --notice-edge: var(--control-border); --badge-edge: currentColor; --text-disabled: #AEAEB2; --text-placeholder: #6D6D72; --tooltip-bg: #1C1C1E;
    --ct-up: #007AFF; --ct-up-soft: #378ADD; --ct-down: #FF3B30; --ct-down-soft: #E24B4A; --ct-zero: #FF3B30; --ct-launch-hl: #1D5C63;
    --caution-text: #8A5C00; --kill-text: #B3261E; --ct-neg-ink: var(--kill-text); --proceed: #248A3D; --proceed-bg: #EAF7EE; --proceed-hair: #BFE6C8;
    --caution: #9A6700; --caution-bg: #FBF3E2; --caution-hair: #EBD9A6; --kill: #C9372C; --kill-bg: #FBEBE9;
    --kill-hair: #F2C5BF; --proceed-badge-bg: #EAF7EE; --caution-badge-bg: #FBF3E2; --kill-badge-bg: #FBEBE9;
    --shadow: 0 1px 2px rgba(0,0,0,0.04), 0 1px 8px rgba(0,0,0,0.04);
    --shadow-raised: 0 2px 4px rgba(0,0,0,0.05), 0 4px 16px rgba(0,0,0,0.06);
    --shadow-modal: 0 12px 48px rgba(0,0,0,0.22);
  }
  html { color-scheme: light; }
  .app-header, .action-bar, .footer, .btn, button { display: none !important; }
  body { background: var(--print-paper) !important; color: var(--print-ink) !important; }
  /* UR-22 F2 (user decision 2026-10-17): a page gutter, so a card's right border and corner are not cut at the paper edge. */
  .app-main { max-width: 100% !important; padding: 0 var(--sp-4) !important; }
  /* UR-22 F2: a print carries the whole record — a closed <details> prints its content. This is the CSS path, for an
     engine with ::details-content; Project_Viewer.js also opens every closed <details> on beforeprint and restores
     them on afterprint, which covers every engine. */
  details::details-content { content-visibility: visible !important; }
  /* UR-22 N1 (v10.1441.0): paper cannot scroll, so the sideways-scrolling cash-flow table printed ten of 21 years and cut
     its total off. Where the page carries the print copy (the Viewer), the copy prints instead: blocks of at most six
     years, each naming its line items, the total under the last (its grid and total row: final round NF1, below).
     tests/test_recheck_fixes.js §N1. */
  .npv-table-wrap:has(+ .npv-print) { display: none !important; }
  .npv-print { display: block; }
  .npv-print .npv-print-block { break-inside: avoid; }
  .npv-print .npv-print-block + .npv-print-block { margin-top: var(--sp-4); }
  /* Final round NF1 (Ashwin 2026-10-17): ONE column grid for every block. cashflow_labels.js gives each block the same
     <colgroup> (the line-item column + six year columns); laid out fixed, a 5-year block's years sit under a 6-year
     block's and the spare column stays empty. A line item may wrap rather than widen its column. The total is its own
     row from the line-item column across (viewer_econ.js printFoot), its figure in the cell's ink, red when negative. */
  .npv-print .npv-print-block { table-layout: fixed; }
  .npv-print .npv-print-block col.npv-print-col-label { width: 12rem; }
  /* OF1 + OF2 (Ashwin 2026-10-17): in print every row header of a block - each line item, and the Viewer's total, which is
     one cell (scope="row") - is LEFT-aligned with no left padding, so the labels and the total's label start at one x
     (the total kept the header's 12px). OF2 is a declared, PRINT-ONLY exception to the centre-every-cell rule above
     (docs/ui_rules.md R-F7); check_table_cell_centring fails it anywhere else. tests/test_recheck_fixes.js §OF1/§OF2. */
  .npv-print .npv-print-block th[scope="row"] { white-space: normal; text-align: left !important; padding-left: 0; }
  .npv-print .npv-print-total th { border-top: 1px solid var(--hair-strong); border-bottom: none; padding-top: var(--sp-3); }
  .npv-print .npv-print-total-val { margin-left: var(--sp-3); font-size: var(--fs-sm); text-transform: none; letter-spacing: 0; color: var(--text-1); }
  .npv-print .npv-print-total-val.neg { color: var(--kill-text); }
  /* The Evaluator (Ashwin 2026-10-17: Cmd+P prints the whole record, as the Viewer does). Its window-height shell
     (.u-fill-screen) and scroll columns printed one screenful — measured: 898px of a 4,174px Results page; on paper the
     record flows from page to page. The module rail, the toolbar and the progress strip are controls, not the record, so
     they stay off the paper (the Viewer's header and action bar already do, above). The column's grey page ground
     (--bg) gives way to the paper, as the body's does. tests/test_recheck_fixes.js §P. */
  .u-fill-screen, .u-fill-screen .layout-wrapper, .u-fill-screen .layout-wrapper > .main { height: auto !important; overflow: visible !important; display: block !important; background: var(--print-paper) !important; }
  .u-fill-screen .module-nav, .pm-action-bar, .progress-bar { display: none !important; }
  .card, .tile, .stat, .list, table.tbl { box-shadow: none !important; border: 0.5px solid var(--print-rule) !important; break-inside: avoid; }
  /* Decision 276 (2026-10-23): ON PAPER THE CHARTS FLOW AS BLOCKS, ONE WHOLE CHART AT A TIME. A chart is an <svg>, which
     cannot be split across pages, so at a page break Chromium pushes it to the next page. Inside a FLEX container the item
     around it keeps the height the flex layout gave it, so the pushed chart spilled out of its card and the next card was
     drawn over it (the Evaluator's CDF over the histogram; the Viewer's Governance Flags over the tornado, the tornado over
     the CDF). In block flow a box grows by what it pushes. The Evaluator's Results page is ONE .card several pages tall,
     and "never break inside a card" sent it whole to page 2, leaving page 1 with the title alone. A heading stays with
     what it heads. tests/test_print_flow.js. */
  .mc-charts-row, .mc-col, .viewer-col, #viewer-results:not([hidden]) { display: block !important; }
  .mc-col + .mc-col { margin-top: var(--sp-4); }
  #viewer-results > * + * { margin-top: var(--section-gap); }
  .mc-col { break-inside: avoid; }
  .gov-section { break-inside: avoid; }
  .card.pm-card { break-inside: auto; }
  .mc-col-title, .results-collapse-header, details.panel > summary, .card-title { break-after: avoid; }
  /* s1489 B4 (decision 387, the sitting's Viewer print): THE RECORD ALONE, PAGE-WIDTH, BREAKING BETWEEN ROWS. The app prints
     in WebKit, which lays the page out wider than the paper (assumed: its 1.25x minimum shrink factor), so the Viewer's
     two-column grid stayed on paper — the Projects card (a control, not the record) printed beside the project and squeezed
     it. `.card { break-inside: avoid }` above then sent the tall Score Breakdown card whole to the next page, leaving the page
     under the NPV gauge blank; and a score label kept its screen ellipsis ("OPPORTUNIT…"). The Viewer's own <style> may only
     shrink (check_page_style_dead_css §B), so its print rules live here, beside .viewer-col's. tests/test_s89b4_viewer_print.js. */
  .viewer-layout { display: block !important; }
  .viewer-layout > .card:first-child { display: none !important; }
  #viewer-results > .card { break-inside: auto; }
  .metric-card, .ecv-block, .score-row, .score-group-row { break-inside: avoid; }
  #viewer-results > .card > .sec-label { break-after: avoid; }
  .score-name { white-space: normal; overflow: visible; text-overflow: clip; }
  /* s1506 decision 2 (FU-v10.1500-print-heading-orphans): `break-after: avoid` on a heading did not hold — "Monte Carlo
     distribution", "Governance flags" and "NPV cash flow" printed alone at a page foot. lib/print_keep.js puts a copy of
     each heading and the first whole unit after it into ONE group before a print (and takes it out after); the group may
     not split, and the original heading stays off paper. tests/test_s1506c_print_keep.js prints and reads the pages. */
  .print-keep { break-inside: avoid; }
  .print-keep-copy { display: block; }
  .print-keep-src { display: none !important; }
  /* s1506 (lead finding on Master 10.1500.0, WebKit 1920x961: "Page 1 of 1", the Deep Dive cut after the Project background
     grid): the Reports preview is a scroll box on screen (.rg-preview max-height 75vh, overflow-y auto, a fade mask at its
     foot), and nothing released it on paper, so a print got one box-full. On paper it is the record, whole: the box opens
     to its full height without the mask, the layout is block flow, and the report controls (a control, not the record, as
     the Viewer's Projects card above) and the "Preview" eyebrow stay off paper. tests/test_s1506c_reports_print.js prints it. */
  .rg-layout { display: block !important; }
  .rg-layout > section:first-child, .rg-layout > section > .eyebrow { display: none !important; }
  .rg-preview { max-height: none !important; overflow: visible !important; -webkit-mask-image: none !important; mask-image: none !important; border: 0 !important; padding: 0 !important; }
  /* s1506 decision 36 (UR-41 F1): a picture is kept whole on paper, its title with it — as the published document keeps it
     (render_html _PRINT_CSS `.rpt-chart{page-break-inside:avoid}`). Without it "SENSITIVITY", "NPV CUMULATIVE" and "NPV
     DISTRIBUTION" printed alone at a page foot, their picture on the next page (10 times in 8 prints of the sample projects).
     tests/test_s1506c_reports_print.js RP4 prints it. */
  .rg-preview .rpt-chart { break-inside: avoid; }
  /* s1506 decision 38: a picture in pieces (a governance panel, render_html._pictureHtml) parts between its pieces — kept whole it
     was pushed to a new page and still sliced there (BC-SAMPLE-04, Chromium: 8 A4 pages with half of page 3 empty; 7 with this
     rule). A piece is a picture, which paper never breaks. test_s1506c_reports_print RP5. */
  .rg-preview .rpt-chart.rpt-chart-pieces { break-inside: auto; }
  /* s1506 decision 25(a) (FU-v10.1506-no-generic-scroll-box-print-check; ui_class B13 reads every surface as it prints):
     Portfolio Analytics clipped on paper — its bar labels end in an ellipsis on one line ("Customer Development → High…",
     246 of 200px shown) and a breakdown table wider than its block was cut at the block's edge (418 of 386px; its cells do
     not wrap). Paper has no hover and no scroll, so on paper a label wraps whole and a table's cells wrap to fit the block.
     !important: the page's own <style> (Portfolio_Analytics.html, after this sheet) sets the same properties at the same
     specificity and would win (measured: the plain rules left all 7 cuts in place). Even wrapped, a breakdown table's
     columns need more than a block's grid column at a 1024 window (413 of 301px), so on paper the blocks stand one per row,
     page-width (the record alone, page-width, as the Viewer: s1489 B4).
     The bar track's own overflow:hidden (rounded ends) clips nothing it holds and is not touched. */
  .al-bar-lbl { white-space: normal !important; overflow: visible !important; text-overflow: clip !important; }
  .al-tbl th, .al-tbl td { white-space: normal !important; }
  .al-grid, .al-stage-grid { grid-template-columns: 1fr !important; }
}
/* the group exists only between beforeprint and afterprint; on screen it never shows (a print preview that is cancelled
   still fires afterprint) */
@media screen { .print-keep-copy { display: none; } }

/* ── 12. Icon system (X4): unified monochrome line icons via CSS mask-image.
   currentColor-tinted (inherits text colour / semantic tokens), CSP-safe (CSS, no
   script). Usage in HTML or JS-rendered markup: <span class='mv-ic mv-ic-NAME'></span>.
   Replaces the bright-emoji-vs-monochrome-glyph mix on chrome headings + buttons. */
.mv-ic { --mv-ic: none; display: inline-block; width: 1em; height: 1em; vertical-align: -0.135em;
  background-color: currentColor; flex-shrink: 0;
  -webkit-mask: var(--mv-ic) no-repeat center / contain; mask: var(--mv-ic) no-repeat center / contain; }
.mv-ic-governance { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3v18'/%3E%3Cpath d='M6 4h11l-2 4 2 4H6'/%3E%3C/svg%3E"); }
.mv-ic-eye { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E"); }
.mv-ic-stalled { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3h12M6 21h12'/%3E%3Cpath d='M8 3c0 4 4 5 4 9M16 3c0 4-4 5-4 9M8 21c0-4 4-5 4-9M16 21c0-4-4-5-4-9'/%3E%3C/svg%3E"); }
.mv-ic-bars { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 21h18'/%3E%3Cpath d='M6.5 18v-7M12 18V5M17.5 18v-4'/%3E%3C/svg%3E"); }
.mv-ic-trend { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 17l6-6 4 4 8-8'/%3E%3Cpath d='M15 7h6v6'/%3E%3C/svg%3E"); }
/* ── E1 (lock D35, v10.1274.0): THE CONFIG ICON SET. Committee-picked icons (criteria, roles, the picker) are kit icons
   now; lib/icons.js maps every emoji a saved config may still hold to one of these at draw time (no migration). Each
   drawn for this set on the kit's 24-unit, 2-stroke line grid, so they sit with the X4 icons above. */
.mv-ic-star { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3l2.8 5.7 6.2.9-4.5 4.4 1.1 6.2L12 17.3 6.4 20.2l1.1-6.2L3 9.6l6.2-.9z'/%3E%3C/svg%3E"); }
.mv-ic-versus { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 5l14 14M19 5L5 19'/%3E%3Cpath d='M3 16l5 5M16 21l5-5'/%3E%3C/svg%3E"); }
.mv-ic-puzzle { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 7h3a2 2 0 1 1 4 0h3v3a2 2 0 1 1 0 4v3h-3a2 2 0 1 0-4 0H5v-3a2 2 0 1 0 0-4z'/%3E%3C/svg%3E"); }
.mv-ic-mountain { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 20l6-10 4 6 3-4 5 8z'/%3E%3C/svg%3E"); }
.mv-ic-handshake { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 7l4-1 5 3 5-3 4 1'/%3E%3Cpath d='M3 7v7l6 5a1.5 1.5 0 0 0 2-2'/%3E%3Cpath d='M21 7v7l-6 5a1.5 1.5 0 0 1-2-2'/%3E%3Cpath d='M9 12l3 3 3-3'/%3E%3C/svg%3E"); }
.mv-ic-castle { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 21V8h2V5h2v3h2V5h4v3h2V5h2v3h2v13z'/%3E%3Cpath d='M10 21v-4a2 2 0 0 1 4 0v4'/%3E%3C/svg%3E"); }
.mv-ic-clock { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='13' r='8'/%3E%3Cpath d='M12 9v4l3 2M5 3L2 6M19 3l3 3'/%3E%3C/svg%3E"); }
.mv-ic-leaf { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 19c0-8 6-14 15-14 0 9-6 15-14 15'/%3E%3Cpath d='M5 19l8-8'/%3E%3C/svg%3E"); }
.mv-ic-diamond { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3l9 9-9 9-9-9z'/%3E%3C/svg%3E"); }
.mv-ic-beaker { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 3h6M10 3v6L5 19a1.5 1.5 0 0 0 1.3 2h11.4a1.5 1.5 0 0 0 1.3-2L14 9V3'/%3E%3Cpath d='M7.5 14h9'/%3E%3C/svg%3E"); }
.mv-ic-microscope { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 21h12M9 17h6M11 17V9M8 3h6l-1 6h-4z'/%3E%3Cpath d='M15 12a4 4 0 0 1 0 8'/%3E%3C/svg%3E"); }
.mv-ic-briefcase { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='7' width='18' height='13' rx='2'/%3E%3Cpath d='M9 7V5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2M3 13h18'/%3E%3C/svg%3E"); }
.mv-ic-person { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4 21a8 8 0 0 1 16 0'/%3E%3C/svg%3E"); }
.mv-ic-factory { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 21V11l6 3v-3l6 3V6h6v15z'/%3E%3Cpath d='M7 18h2M12 18h2M17 18h2'/%3E%3C/svg%3E"); }
.mv-ic-gem { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3h12l4 6-10 12L2 9z'/%3E%3Cpath d='M2 9h20M9 3l3 18 3-18'/%3E%3C/svg%3E"); }
.mv-ic-globe { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18'/%3E%3C/svg%3E"); }
.mv-ic-battery { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='7' width='17' height='10' rx='2'/%3E%3Cpath d='M22 11v2M6 10v4M10 10v4'/%3E%3C/svg%3E"); }
.mv-ic-sprout { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21v-9'/%3E%3Cpath d='M12 12c0-4-3-6-8-6 0 4 3 6 8 6zM12 10c0-4 3-6 8-6 0 4-3 6-8 6z'/%3E%3C/svg%3E"); }
.mv-ic-palette { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3a9 9 0 1 0 0 18c1.5 0 2-1 2-2s-1-2 0-3 3 0 4-1 3-2 3-4c0-4.5-4-8-9-8z'/%3E%3Ccircle cx='7.5' cy='11' r='1'/%3E%3Ccircle cx='10' cy='7' r='1'/%3E%3Ccircle cx='15' cy='7.5' r='1'/%3E%3C/svg%3E"); }
.mv-ic-signal { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12a7 7 0 0 1 14 0M8.5 12a3.5 3.5 0 0 1 7 0'/%3E%3Cpath d='M12 12v9M9 21h6'/%3E%3C/svg%3E"); }
.mv-ic-dna { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 3c0 5 10 5 10 9s-10 4-10 9'/%3E%3Cpath d='M17 3c0 5-10 5-10 9s10 4 10 9'/%3E%3Cpath d='M9 6.5h6M9 17.5h6'/%3E%3C/svg%3E"); }
.mv-ic-bolt { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13 2L4 14h7l-1 8 9-12h-7z'/%3E%3C/svg%3E"); }
.mv-ic-traffic { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='8' y='2' width='8' height='20' rx='3'/%3E%3Ccircle cx='12' cy='7' r='1.5'/%3E%3Ccircle cx='12' cy='12' r='1.5'/%3E%3Ccircle cx='12' cy='17' r='1.5'/%3E%3C/svg%3E"); }
.mv-ic-cap { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 9l10-5 10 5-10 5z'/%3E%3Cpath d='M6 11v5c3 2 9 2 12 0v-5M22 9v6'/%3E%3C/svg%3E"); }
.mv-ic-calendar { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E"); }
.mv-ic-crane { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 21V3L3 7h18'/%3E%3Cpath d='M6 3l14 4'/%3E%3Cpath d='M18 7v4'/%3E%3Cpath d='M16 11h4v3h-4z'/%3E%3Cpath d='M3 21h7'/%3E%3C/svg%3E"); }
.mv-ic-megaphone { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 10v4h4l9 5V5L7 10z'/%3E%3Cpath d='M19 9a4 4 0 0 1 0 6'/%3E%3C/svg%3E"); }
.mv-ic-link { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 14a4 4 0 0 0 6 0l3-3a4 4 0 0 0-6-6l-1 1'/%3E%3Cpath d='M14 10a4 4 0 0 0-6 0l-3 3a4 4 0 0 0 6 6l1-1'/%3E%3C/svg%3E"); }
.mv-ic-dot { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='4'/%3E%3C/svg%3E"); }
.mv-ic-thermometer { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 4a2 2 0 0 1 4 0v10a4 4 0 1 1-4 0z'/%3E%3Cpath d='M12 10v7'/%3E%3C/svg%3E"); }
.mv-ic-archive { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='5' rx='1'/%3E%3Cpath d='M5 9v11h14V9M10 13h4'/%3E%3C/svg%3E"); }
.mv-ic-lock { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5' y='11' width='14' height='10' rx='2'/%3E%3Cpath d='M8 11V7a4 4 0 0 1 8 0v4'/%3E%3C/svg%3E"); }
.mv-ic-hand { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 13V5a1.5 1.5 0 0 1 3 0v6M11 11V4a1.5 1.5 0 0 1 3 0v7M14 11V5.5a1.5 1.5 0 0 1 3 0V14a7 7 0 0 1-7 7 6 6 0 0 1-5-3l-2-4a1.5 1.5 0 0 1 2.6-1.5L8 15'/%3E%3C/svg%3E"); }
.mv-ic-zoom { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M21 21l-4.3-4.3M8 11h6M11 8v6'/%3E%3C/svg%3E"); }
/* E1 (D35b): the icon picker — the config icon set as a grid of kit icon buttons; the current pick is the selected look. */
.icon-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(2.75rem, 1fr)); gap: var(--sp-2); }
.icon-opt .mv-ic { font-size: var(--fs-md); }
.icon-opt.sel { border-color: var(--accent); background: var(--accent-weak); color: var(--accent-text); }
/* M9 left-out items (v10.1271.0): two tool glyphs the kit lacked — the Report Generator (a page) and Versioning (a rocket). */
.mv-ic-doc { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z'/%3E%3Cpath d='M14 3v6h6'/%3E%3Cpath d='M8 13h8M8 17h5'/%3E%3C/svg%3E"); }
.mv-ic-rocket { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 15l-3-3c1.5-4.5 5-8 12-9 0 7-4.5 10.5-9 12z'/%3E%3Ccircle cx='14.5' cy='9.5' r='1.5'/%3E%3Cpath d='M6 12H3l2.5-3.5H9M12 18v3l3.5-2.5V15'/%3E%3Cpath d='M5.5 16.5C4 18 4 20 4 20s2 0 3.5-1.5'/%3E%3C/svg%3E"); }
.mv-ic-search { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M21 21l-4.3-4.3'/%3E%3C/svg%3E"); }
.mv-ic-money { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v10'/%3E%3Cpath d='M14.6 9.2c-.6-.8-1.6-1.2-2.6-1.2-1.4 0-2.5.9-2.5 2s1.1 1.8 2.5 2 2.5.9 2.5 2-1.1 2-2.5 2c-1 0-2-.4-2.6-1.2'/%3E%3C/svg%3E"); }
.mv-ic-target { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Ccircle cx='12' cy='12' r='5'/%3E%3Ccircle cx='12' cy='12' r='1.5'/%3E%3C/svg%3E"); }
.mv-ic-gear { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3.2'/%3E%3Cpath d='M12 2.5v3M12 18.5v3M2.5 12h3M18.5 12h3M5.2 5.2l2.1 2.1M16.7 16.7l2.1 2.1M18.8 5.2l-2.1 2.1M7.3 16.7l-2.1 2.1'/%3E%3C/svg%3E"); }
.mv-ic-wrench { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 4.5a5 5 0 0 1-6.6 6.6L5 20.5 3.5 19l9.4-9.4A5 5 0 0 1 19.5 3z'/%3E%3C/svg%3E"); }
.mv-ic-clipboard { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5' y='4' width='14' height='17' rx='2'/%3E%3Cpath d='M9 4.5V3.5h6v1'/%3E%3Cpath d='M9 10h6M9 14h6M9 18h4'/%3E%3C/svg%3E"); }
.mv-ic-camera { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='7' width='18' height='13' rx='2'/%3E%3Ccircle cx='12' cy='13.5' r='3.8'/%3E%3Cpath d='M8.5 7l1.3-3h4.4L15.5 7'/%3E%3C/svg%3E"); }
.mv-ic-merge { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 21V9a6 6 0 0 0 6 6h6'/%3E%3Ccircle cx='6' cy='6' r='2.2'/%3E%3Ccircle cx='18' cy='15' r='2.2'/%3E%3C/svg%3E"); }
.mv-ic-box { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 8l-9-5-9 5 9 5 9-5z'/%3E%3Cpath d='M3 8v8l9 5 9-5V8'/%3E%3Cpath d='M12 13v8'/%3E%3C/svg%3E"); }
.mv-ic-save { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z'/%3E%3Cpath d='M17 21v-7H7v7M7 3v4h7'/%3E%3C/svg%3E"); }
.mv-ic-folder { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z'/%3E%3C/svg%3E"); }
.mv-ic-plug { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 2v6M15 2v6'/%3E%3Cpath d='M7 8h10v3a5 5 0 0 1-10 0z'/%3E%3Cpath d='M12 16v6'/%3E%3C/svg%3E"); }
.mv-ic-finish { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 3v18'/%3E%3Cpath d='M5 4h14v9H5z'/%3E%3Cpath d='M9 4v4.5M13 8.5V13M5 8.5h4M13 4h4M9 4h4v4.5M9 8.5h4'/%3E%3C/svg%3E"); }
.mv-ic-close { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M5.6 5.6l12.8 12.8'/%3E%3C/svg%3E"); }
.mv-ic-rotate { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 12a8 8 0 1 1-2.3-5.6'/%3E%3Cpath d='M20 4v4h-4'/%3E%3C/svg%3E"); }
.mv-ic-partner { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8.5' cy='8' r='3'/%3E%3Ccircle cx='17' cy='9' r='2.6'/%3E%3Cpath d='M3 20a5.5 5.5 0 0 1 11 0M14.5 20a4.5 4.5 0 0 1 6.5-4'/%3E%3C/svg%3E"); }
.mv-ic-compass { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M15.6 8.4l-2 5.2-5.2 2 2-5.2z'/%3E%3C/svg%3E"); }
.mv-ic-scale { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3v18M5 21h14M6.5 6.5h11'/%3E%3Cpath d='M6.5 6.5l-3 6a3 3 0 0 0 6 0zM17.5 6.5l-3 6a3 3 0 0 0 6 0z'/%3E%3C/svg%3E"); }
.mv-ic-carry { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 14l-4-4 4-4'/%3E%3Cpath d='M5 10h9a5 5 0 0 1 5 5v3'/%3E%3C/svg%3E"); }
.mv-ic-add { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E"); }
.mv-ic-check { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 13l4 4 10-10'/%3E%3C/svg%3E"); }
.mv-ic-warn { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3 2 20h20L12 3z'/%3E%3Cpath d='M12 10v5'/%3E%3Cpath d='M12 17.5h.01'/%3E%3C/svg%3E"); }
.mv-ic-info { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v6'/%3E%3Cpath d='M12 7.5h.01'/%3E%3C/svg%3E"); }
.mv-ic-download { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3v12'/%3E%3Cpath d='M7 11l5 5 5-5'/%3E%3Cpath d='M4 20h16'/%3E%3C/svg%3E"); }
.mv-ic-filter { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 5h18l-7 8.5V20l-4-2.2v-4.3z'/%3E%3C/svg%3E"); }
.mv-ic-bubble { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='7.5' cy='15' r='3'/%3E%3Ccircle cx='15' cy='8.5' r='4'/%3E%3Ccircle cx='17.5' cy='17' r='2'/%3E%3C/svg%3E"); }
.mv-ic-table { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='14' rx='2'/%3E%3Cpath d='M3 10h18M9.5 5v14'/%3E%3C/svg%3E"); }
.mv-ic-dice { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='3'/%3E%3Ccircle cx='8.5' cy='8.5' r='1.1' fill='%23000' stroke='none'/%3E%3Ccircle cx='15.5' cy='8.5' r='1.1' fill='%23000' stroke='none'/%3E%3Ccircle cx='12' cy='12' r='1.1' fill='%23000' stroke='none'/%3E%3Ccircle cx='8.5' cy='15.5' r='1.1' fill='%23000' stroke='none'/%3E%3Ccircle cx='15.5' cy='15.5' r='1.1' fill='%23000' stroke='none'/%3E%3C/svg%3E"); }
.mv-ic-trophy { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 21h8M12 17v4M7 4h10v5a5 5 0 0 1-10 0z'/%3E%3Cpath d='M7 5H4v2a3 3 0 0 0 3 3M17 5h3v2a3 3 0 0 1-3 3'/%3E%3C/svg%3E"); }
.mv-ic-shield { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3l8 3v5c0 5-3.4 8.5-8 10-4.6-1.5-8-5-8-10V6z'/%3E%3C/svg%3E"); }
.mv-ic-trash { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13M10 11v6M14 11v6'/%3E%3C/svg%3E"); }
.mv-ic-chat { --mv-ic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z'/%3E%3C/svg%3E"); }

/* FU-v10.925 (user-directed) — surfaces the hosted cut does not want shown: the M365 / "Save reports
   to OneDrive" target (IM Maintenance menu) and the AI-report module toggles — AI-Assist Narrative +
   MCP Query (CE Modules → Reporting). The AI toggles run on the LLM egress the hosted editions strip
   (stripAiEgress). The OneDrive target does NOT — m365_publish.js writes local files only, no network
   (check_publish_no_network) — and this line used to claim it did. It stays hidden on Hosted by a
   separate user decision (X3, lock D38, 2026-09-28): a product choice not to send a Hosted customer's
   portfolio report and its JSON/CSV sidecar into a OneDrive-synced folder. Marked `data-hide-hosted` in the markup; hidden here on any hosted edition
   (edition_chrome.js adds `body.edition-<id>`). One rule covers both pages (both load this sheet). */
/* The `body.edition-hosted-admin` half of this selector went at v10.1119.0 (WG-12): the class is
   written by `edition_chrome.js applyBodyClass` as `edition-<ED.edition>`, and `hosted-admin` has
   not been a producible edition since v10.767.0 — `build_edition.js:460` throws a named error for
   it — so the selector could never match. `check_hosted_hidden_surfaces` §3 accepts the one-selector
   form on its first alternative; the second alternative, which pinned the pair, is retired with it. */
body.edition-hosted [data-hide-hosted] { display: none !important; }

/* ── THE FRAMEWORK-VERSION BANNER — ONE DEFINITION, BOTH SURFACES (F2, v10.1098.0) ─────────────
   Moved here from the inline <style> in src/index.html. It carries TWO sibling facts:
     • Home  — "n projects were scored under an earlier version" (im_data.mixedVersionBannerHTML)
     • Home + Framework Configurator — "this FRAMEWORK is older than this build, publish to update"
       (src/lib/framework_currency.js)
   The Configurator loads THIS stylesheet and never loaded index.html's inline block, so the second
   surface could only have been served by duplicating the rules there — the BC-03 drift v10.951.0
   fixed by consolidating (guarded by check_viewer_modal_shared.js). Tokens only. */
/* D29f: the mixed-framework banner is the shared caution notice (`notice caution`); this class only spaces it. */
.mixed-fw-banner { margin-bottom: var(--sp-5); }

/* Slice 11 (A7-e): Innovation Manager's "Publish framework" dialog — the reason field, full width (its label is the IM
   dialogs' .si-lbl, UR-6 F3). */
.im-pub-reason { width: 100%; font-family: inherit; }
/* Slice 12 (A8-g/h/i; mock-up docs/mockup_audit_pack.html §4–§5): Master Clients — "Records kept", Export / Delete. */
.cl-ret { display: inline-flex; flex-direction: column; align-items: center; gap: var(--sp-1); }
.cl-ed { display: inline-flex; gap: var(--sp-1); }
.cl-ret-row { display: flex; align-items: center; gap: var(--sp-2); }
.cl-steps { font-size: var(--fs-sm); padding-left: var(--sp-5); margin-top: var(--sp-3); }
.btn-apart { margin-left: var(--sp-5); }   /* UR-5 F6: a destructive action set apart from its neighbours (design system §6) */

/* B4-10 (v10.1377.0 → v10.1378.0, N3): in the Visualizer's COMPACT detail an AXIS row puts its name on its own line and
   its bar + score on the next, right-aligned so the bar starts where every criterion bar below it starts (bar and score
   are fixed widths at the row end). v10.1377.0 only let the name wrap, and the fixed-width bar (--score-track) then ran
   over "OPPORTUNITY" — the column was narrower than that one word (seen on rebuilt v10.1377.0; user chose this layout
   2026-10-09). Default view is wide enough and is untouched. Here, not in the page <style>: page blocks only shrink (RC-6). */
body.compact-view .detail-scores .score-group-row:not(.sb-sub) { flex-wrap: wrap; }
body.compact-view .detail-scores .score-group-row:not(.sb-sub) > .score-name { flex: 1 0 100%; white-space: normal; overflow: visible; }
body.compact-view .detail-scores .score-group-row:not(.sb-sub) > .score-bar-bg { margin-left: auto; }
/* B4-29 (v10.1384.0, F20): the Update-required wall holds no control, so focus rests on its box; no ring there (it
   drew a 2-3px accent outline in both themes with nothing to act on). Every other dialog keeps the RC-7a rule. */
#minerva-upgrade-wall:focus { outline: none; }
/* B4-30 (v10.1385.0, F15; user 2026-10-09: the Evaluator matches the other pages). Its landing sat INSIDE .main's
   gutter with the full --w-app, so its content ran ~26px wider each side than .app-main pages, whose --w-app
   includes the gutter (border-box). Moved here from the page <style> (RC-6: page styles only shrink). */
/* B4-40 (v10.1389.0, N35 — seen on rebuilt v10.1388.0): the canvas pages (Visualizer, Viewer, Evolution, Reports) size
   against --w-canvas (96vw), not --w-app (94vw), so v10.1385.0's rule left the Evaluator 13px narrower each side at 1280px.
   Same token as theirs now; the browser gate measures both edges. */
#app.main > .landing-wrap { max-width: calc(var(--w-canvas) - 2 * var(--gutter)); margin: 0 auto; }
/* B4-31 (F21): the Hosted Home lede's Sign in button sits after its sentence. */
.page-lede .lede-action { margin-left: var(--sp-2); vertical-align: middle; }
/* B4-32 (F23): the setup dialogs' heading lives in the shared header bar now; the box's own h2 stays in the
   holder (for the no-dialog fallback) but is not drawn twice. */
.mv-modal.mv-setup .onboarding-box > h2 { display: none; }

/* B4-59 (v10.1395.0, F8 rest): the controls now say words ("Development cost", "Expected commercial value"), so they are
   wider. Evolution: a chart's title keeps its width and the metric buttons drop below it when the column is too narrow (the
   title was squeezed to one word per line). Visualizer: the Bubble type menu keeps its old width (a native menu sizes to its
   longest choice); the chosen name is in its tooltip. */
.chart-col-head { flex-wrap: wrap; row-gap: var(--sp-2); }
.chart-col-head > :first-child { flex: 1 0 auto; }
#bc-metric-select { max-width: 9rem; }
/* …and side by side, the two Evolution charts start level (user 2026-10-09, "Align the charts first"): each column's
   title sits on top and its buttons under it, and the two heads share one row height (subgrid), so the charts below line up. */
.charts-row { grid-template-rows: auto auto auto; }
.charts-row > .charts-col { display: grid; grid-row: span 3; grid-template-rows: subgrid; align-content: start; }
.charts-row .chart-col-head { flex-direction: column; align-items: flex-start; justify-content: space-between; }

/* N50 (v10.1396.0) / s1501 decision 4: report preview KPI cards are a column, the VALUE first and the label below it, so every
   value starts level at the top of its card whatever its label wraps to. The label no longer takes the spare height
   (`flex: 1 0 auto`, P1: it pushed the values to the bottom of a row as tall as its tallest card). */
.rg-preview .rpt-kpi { display: flex; flex-direction: column; }
/* s1501 decision 7 (Q6): a Deep Dive picture fills the preview's section width; the Executive Summary's slides stay
   29rem thumbnails (Report_Generator.html .rg-preview .rpt-chart; 32rem until s1506 decision 26). Here, not in the page's <style>: page style blocks only shrink (RC-6). */
.rg-preview .rpt-chart.rpt-chart-wide { max-width: var(--pic-max-w); }   /* s1506: drawn at most at its design maximum, placed at its width (chart_theme PICTURE_W) */
.rg-preview .rpt-chart.rpt-chart-own > svg { width: auto; max-width: 100%; }   /* s1506 decision 25 (b): a picture with no producer for a width is drawn 1:1, at its own width (render_html._pictureHtml) */
/* s1506 decision 38: a picture drawn in pieces (a governance panel: render_html._pictureHtml) — the wrapper draws the picture's
   border (Report_Generator.html .rg-preview .rpt-chart > svg), the pieces stack edge to edge into the one picture. */
.rg-preview .rpt-chart > .rpt-pieces { border: 1px solid var(--hair); border-radius: var(--radius-ctl); overflow: hidden; }
.rg-preview .rpt-pieces > svg { width: 100%; height: auto; display: block; }
/* s1501 decision 3: a closed / paused project's state, one line under the report preview's Background grid. */
.rg-preview .rpt-bg-status { font-size: var(--fs-sm); font-weight: 600; color: var(--caution-text); margin: 0 0 var(--sp-4); }

/* N51 (v10.1398.0; user 2026-10-10 "Scroll sideways when narrow"): the vendor console's Hosted clients table has ten
   columns; at 1024 px the squeezed names broke one or two letters a line. In this table a name wraps only between words,
   so the table keeps its width and `.tbl-wrap` (overflow-x: auto) scrolls sideways instead. */
.tbl-wrap.density-tight .tbl .btn.btn-wraps { overflow-wrap: normal; word-break: normal; }

/* Part A slice G3 — Home's "Modify project classification" panel: Close is the one danger card, set apart and last
   (decision (e), design system §6). And the Home line counting paused projects past their review date (decision (b)). */
.rc-actions .rc-action.rc-danger { margin-top: var(--sp-3); }
.rc-actions .rc-action.rc-danger .rc-name { color: var(--kill-text); }
/* UR-14 (user decisions 2026-10-11). F2: the Close card's title in --kill-text (--kill was 4.09:1 in Dark) and the card's own
   --control-border edge (the --kill-hair edge was 1.55:1). F1 (D29c refined): an unavailable card takes the --fill surface
   without the control edge, its title muted, and the line that says why it is off keeps caption ink (data-disabled-reason). */
.rc-actions .rc-action:disabled, .rc-actions .rc-action:disabled:hover { background: var(--fill); border-color: transparent; }
.rc-actions .rc-action:disabled .rc-sub { color: var(--text-3); }
/* F5: a frozen record's typed fields are read-only in normal ink (not disabled); a menu keeps the same look though disabled. */
.inp.is-readonly, .inp.is-readonly:disabled { background: var(--fill); color: var(--text-1); border-color: var(--hair-strong); cursor: default; }
/* F17: when one KPI tile has a line under its number, an empty line in the other keeps the same height, so the two numbers
   sit level (with no line in either, nothing is reserved and the tiles keep their size). */
.kpi-row:has(.kpi-delta:not(:empty)) .kpi-delta:empty::before { content: '\00a0'; }
/* s1501 decision 18 (ui_class B2, measured on the Mac): with no line under either number, the empty .kpi-delta still added its
   margin-top (--sp-1) below the eyebrow's margin-bottom (--sp-2) — 12px of empty tile under the words (108px tile, 96px of
   content once the 7rem floor went). Neither is drawn then; a tile with a line keeps both (and F17 above keeps the pair level). */
.kpi-row:not(:has(.kpi-delta:not(:empty))) .kpi-delta:empty { display: none; }
.kpi-row:not(:has(.kpi-delta:not(:empty))) .stat .eyebrow { margin-bottom: 0; }
/* F18: a caution badge on a caution notice gets the card fill (in Light the two fills were the same #FBF3E2). */
.notice.caution .badge.caution { background: var(--card); }
/* UR-22 re-check O3 (v10.1441.0): the same class for kill and proceed, Light only (--badge-edge is transparent in Dark, whose
   badge fills already differ). An inset edge, so nothing moves. tests/test_recheck_fixes.js §O3. */
.notice.kill .badge.kill, .notice.proceed .badge.proceed { box-shadow: inset 0 0 0 0.5px var(--badge-edge); }
.kpi-delta.is-caution { color: var(--caution-text); }
/* UR-13 F11 (user decision 2026-10-11): an empty choice in a picker reads as an instruction, in the placeholder colour. */
select.inp.is-empty { color: var(--text-placeholder); }

/* RV-v10.1387 look (v10.1408.0, harness 2026-10-11): the Account menu's items keep to one line. Once the gates
   composed, a signed-in administrator's menu showed "Export account audit pack…", which wrapped at the menu's
   13rem minimum; the menu now grows to its longest item instead (the B4 release 18 "one line" rule). */
.im-menu-list .im-menu-item { white-space: nowrap; }   /* v10.1409.0: every Home menu (File settings wrapped "Save reports to OneDrive…") */

/* ── Configurator answer rows (s1483 decisions 297/298) — moved out of src/ce/Config_Editor.html's <style>, which only
   shrinks (RC-6, check_page_style_dead_css §B). Only the Configurator draws .q-row / .q-detail / .q-opt-*. ── */
/* s1483 decisions 297/298: the question row and its detail panel share these four measurements, so an answer row can
   put its × under the question's ×, its answer box under the question box and its score under the Importance menu
   (browser smoke "CE ANSWER ROWS"). Change one only by changing it here. */
body { --ce-qnum-w: 108px; --ce-qrow-pad-x: var(--sp-5); --ce-qdet-pad-l: 2.875rem; --ce-qdet-pad-r: 1.125rem; }
/* s1483 decisions 297/298 — an answer row mirrors its question row's three columns: the id/× column (its × centred
   where the question's × is), the question box, and an invisible copy of the question's right-hand cluster whose
   Importance slot holds the score. The detail panel is wider than the question row's content by
   (--ce-qdet-pad-l − --ce-qrow-pad-x) on the left and (--ce-qrow-pad-x − --ce-qdet-pad-r) on the right. */
/* The side edges are drawn as inset shadows, not borders: a 0.5px border lays out as 1px at 1x and 0.5px at 2x, which
   would move every column by a different amount per screen. */
.answer-row.q-opt-row { gap: 0; padding: var(--sp-2) 0; border-left-width: 0; border-right-width: 0; box-shadow: inset 0.5px 0 0 var(--hair-strong), inset -0.5px 0 0 var(--hair-strong); }
.q-opt-xcol { flex: 0 0 var(--ce-qnum-w); width: var(--ce-qnum-w); display: flex; justify-content: center; align-items: center;
  margin-left: calc(var(--ce-qrow-pad-x) - var(--ce-qdet-pad-l)); }
.q-opt-row .opt-text-input { flex: 1; min-width: 0; }
.q-right.q-opt-right { padding-bottom: 0; align-self: center; margin-right: calc(var(--ce-qrow-pad-x) - var(--ce-qdet-pad-r)); }
.q-right.q-opt-right > * { visibility: hidden; max-height: 0; overflow: hidden; }   /* keeps each cell's WIDTH, takes no height */
.q-right.q-opt-right > .q-score-slot { visibility: visible; max-height: none; overflow: visible; align-self: center; display: flex; justify-content: center; align-items: center; border-width: 0; background: transparent; padding: 0; }
.q-opt-desc-row { display: flex; align-items: flex-start; }
.q-opt-desc-row .q-opt-gutter { flex: 0 0 calc(var(--ce-qnum-w) + var(--ce-qrow-pad-x) - var(--ce-qdet-pad-l)); }
.q-opt-desc-row .q-right.q-opt-right { align-self: stretch; margin-right: calc(var(--ce-qrow-pad-x) - var(--ce-qdet-pad-r)); }

/* Portfolio Visualizer — moved here from the page <style> (RC-6: page style blocks only shrink; check_page_style_dead_css §B). */
/* s1506 decision 27: the numbered markers' key under the chart — number then full name, two columns where they fit. */
.bc-name-key[hidden] { display:none; }
.bc-name-key-list { list-style:none; margin:var(--sp-2) auto 0; padding:0; max-width:min(var(--bc-chart-max), 98cqw); display:grid; grid-template-columns:repeat(auto-fill, minmax(16rem, 1fr)); gap:var(--sp-1) var(--sp-4); font-size:var(--fs-xs); color:var(--text-2); }
.bc-name-key-list li { display:flex; gap:var(--sp-2); align-items:baseline; min-width:0; }
.bc-name-key-num { font-weight:700; color:var(--text-1); min-width:2ch; text-align:right; font-variant-numeric:tabular-nums; }
