/* ==========================================================================
   What's My R? v5 "Warm Clarity" — styles.css (OWNER: styles.css only, no JS/HTML here)
   Source of truth: docs/DESIGN-SYSTEM.md — §1 palette, §2 type, §3 spacing/radius/
   elevation/borders, §4 components, §8 motion, §9 iOS. Supersedes BUILD-SPEC §4
   (tokens/classes) for everything DESIGN-SYSTEM.md names; §4 remains authoritative
   for any class this file does not mention.

   NO class name emitted by a screen changes. Every selector that existed in v4
   still exists here; only its declarations moved onto the token system.

   Every contrast ratio quoted in a comment below was RECOMPUTED in this pass with a
   WCAG 2.x relative-luminance implementation over the FINAL surfaces in this file.
   Ratios measured against the old (inverted) --card2 are stale and were not carried
   forward. Recompute before changing any value.
   ========================================================================== */

/* ---- 1. Tokens (DESIGN-SYSTEM §1.1 / §2.1 / §2.3 / §3.1 / §3.3 / §3.4) -------- */
:root{
  /* surfaces — three routine, one raised (§1.2).
     card/bg 1.18:1 · card2/card 1.15:1 · card2/bg 1.36:1 · bg2/card2 1.17:1
     --card2 is INVERTED from v4: it is now one step LIGHTER than --card, so every
     field, well, table stripe and :hover reads as an inset rather than a hole. */
  --bg:#100E0B; --card:#241F1A; --card2:#2F2A24; --bg2:#3B352E;

  /* ink — four steps (§1.3). on bg / card / card2 / bg2:
     --ink      17.11 / 14.50 / 12.62 / 10.75
     --ink2     11.91 / 10.10 /  8.79 /  7.48
     --muted     7.90 /  6.70 /  5.83 /  4.96
     --tertiary  6.51 /  5.52 /  4.80 /  4.09  ← FORBIDDEN on --bg2 (4.09 fails AA).
     --bg2 is a non-routine raised surface (toast/popover/modal) and bears --ink only.
     This was a comment and nothing else until now: --tertiary has 50-plus call sites and
     nothing stopped one landing on a toast. It is now ENFORCED by
     tests/styles-tokens.test.mjs, which fails if any rule setting background:var(--bg2)
     also sets, or is an ancestor selector of a rule that sets, color:var(--tertiary) or
     color:var(--ink3). --ink3 is an alias of the same hex and carries the same ban.
     The three --bg2 sites today (.toast, .seg-btn/.unit-toggle :hover, .btn:hover) all
     bear --ink; the one non-ink descendant, .toast-action, is --gold at 5.70:1. */
  --ink:#F4F1EC; --ink2:#CFCBC3; --muted:#ACA59B; --tertiary:#9C958D; --ink3:#9C958D;

  /* rules — two tiers, enforced (§1.4).
     --line  decorative divider INSIDE an already-bounded region. Never a sole boundary.
     --line2 EVERY meaningful boundary: 4.21 on bg, 3.56 on card, 3.10 on card2. */
  --line:#3A342E; --line2:#7D746B;

  /* tone scale — a ranked LUMINANCE ladder, red < blue < gold < green (§1.5).
     Greyscale: #979797 → #A4A4A4 → #B2B2B2 → #C1C1C1, monotonic at ~1.18x per rung,
     so the scale still orders with the hue removed entirely.
     Every tone clears 4.5:1 on all three routine surfaces and all four tints. */
  --red:#CC856F; --blue:#8BA6C8; --gold:#CBAF6C; --green:#96CCB7;
  --violet:#B992C5;                       /* outside the scale; 6th categorical, mark-only */

  /* ink on accent fills (§1.5). --ink on an accent fill is FORBIDDEN: 1.60–2.61:1. */
  --ink-on-red:#34150D;                   /* 5.68:1 on --red  */
  --ink-on-blue:#0E1E37;                  /* 6.66:1 on --blue */
  --ink-on-gold:#261D0A;                  /* 7.83:1 on --gold */
  --ink-on-green:#09231A;                 /* 9.17:1 on --green */

  /* categorical series ladder (§5.4). Greyscale #747474 → #8D8D8D → #C6C6C6 → #E8E8E8. */
  --s-tax:#B05D48; --s-salary:#BB8046; --s-ss:#9FCFCC; --s-other:#E4EBDB;

  /* type — stacks (§2.1). -apple-system must LEAD or an Apple device with Segoe
     installed loses SF. "Segoe UI Variable Text" must precede "Segoe UI" or Win 11
     pins the old static face. ui-serif is deliberately ABSENT: on Windows Chromium it
     resolves to Times New Roman and would beat Palatino Linotype. */
  --font-sans:-apple-system,BlinkMacSystemFont,"Segoe UI Variable Text","Segoe UI",
    system-ui,Roboto,"Helvetica Neue",Arial,sans-serif;
  --font-serif:"Iowan Old Style","Palatino Linotype",Charter,Constantia,Georgia,"Noto Serif",serif;
  --font-mono:ui-monospace,"SF Mono","Cascadia Mono",Consolas,"Roboto Mono",monospace;
  --font-num:var(--font-sans);            /* a numeral NEVER takes the serif (§2.6 rule 5) */

  /* type — scale (§2.3). Base is 16px: the reader is in their fifties and is being
     asked to trust the number. --t-xs 12px is the floor; nothing renders below it. */
  --t-xs:12px; --t-sm:14px; --t-base:16px; --t-md:18px;
  --t-lg:21px; --t-xl:27px; --t-2xl:34px; --t-3xl:clamp(34px,6.2vw,56px);
  /* numeral steps — all four are sans 700 + tabular-nums, letter-spacing 0 */
  --t-num-sm:15px; --t-num-md:20px; --t-num-lg:27px; --t-num-xl:clamp(34px,8vw,46px);

  /* spacing — 4px base, nine steps, and no value outside them (§3.1) */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px;
  --sp-6:32px; --sp-7:48px; --sp-8:64px; --sp-9:96px;

  /* radius — four values, nine legacy names kept alive so every var(--r13) across
     41 files snaps to the system with zero edits (§3.3). --r-full is permitted on
     round things and bar ends ONLY, never on a text container. */
  --r-sm:4px; --r-md:8px; --r-lg:12px; --r-full:999px;
  --r9:4px; --r10:4px; --r11:4px; --r12:8px; --r13:8px; --r14:8px; --r16:12px; --r22:12px; --r999:999px;

  /* elevation — three levels, exactly one of which casts a shadow (§3.4).
     At most ONE Floating element per view. Cards separate by the bg→card value step
     plus a --line2 hairline, never by shadow. */
  --e-flush:none;
  --e-bounded:none;
  --e-floating:0 8px 24px rgba(0,0,0,.55), 0 1px 2px rgba(0,0,0,.4);
  --shadow:none;                          /* was 0 18px 50px rgba(0,0,0,.35) — V8 */

  /* tinted fills — V10. Declared ONCE, never re-mixed inline. Because the mix names
     var(--card) as its base, light mode re-mixes for free when the tokens re-point.
     Computed: #352923 / #2E2D2B / #352D22 / #2F302A.
     Accent on its own tint: red 4.79 · blue 5.49 · gold 6.38 · green 7.37.
     Every ink step incl. --tertiary is >=4.50:1 on all four.
     --line2 sinks to 2.91–3.07 on them, which is why a tinted component's boundary
     is a 3px accent LEFT RULE and never a tinted border. */
  --tint-red:color-mix(in srgb, var(--red) 10%, var(--card));
  --tint-blue:color-mix(in srgb, var(--blue) 10%, var(--card));
  --tint-gold:color-mix(in srgb, var(--gold) 10%, var(--card));
  --tint-green:color-mix(in srgb, var(--green) 10%, var(--card));

  /* motion + focus (§1.7, §8.1). Two rings, because one ring cannot clear a saturated
     fill: wherever the outer gold ring fails, the inner --bg ring is >=6.56:1, and the
     two rings are 9.07:1 against each other — a SHAPE change as well as a colour one. */
  --ease:cubic-bezier(.22,1,.36,1);
  --focus-ring:0 0 0 2px var(--bg), 0 0 0 4px var(--gold);

  /* iOS safe areas (§9.2). These resolve to 0 until index.html carries
     viewport-fit=cover — see the F1 report, integrator item I-5. */
  --safe-t:env(safe-area-inset-top,0px);  --safe-b:env(safe-area-inset-bottom,0px);
  --safe-l:env(safe-area-inset-left,0px); --safe-r:env(safe-area-inset-right,0px);
}

/* THE WEIGHT LADDER (§2.2, V2). Three weights, all real faces:
     400 — body, captions, prose, ALL serif headings        (Segoe UI Regular)
     600 — labels, buttons, table headers, nav, row labels  (Segoe UI Semibold)
     700 — the single most important word or figure in a view, and every numeral
   500, 650, 750, 800, 850 and 900 are DELETED. On Windows 800/850/900 all land on
   Segoe UI Black, a display face that was setting 11px uppercase micro-labels.
   Lint gate (§11.3 #25):
     grep -rhoE "font-weight:\s*[0-9]+" styles.css app/ | grep -vE ":(400|600|700)$"
   must return nothing. */

/* ---- 2. Reset + base ------------------------------------------------------ */
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}   /* rotation must not resize type */
body{
  background:var(--bg);                   /* V8: both radial-gradient()s deleted, incl. the #15224a literal */
  color:var(--ink);
  font-family:var(--font-sans);
  font-size:var(--t-base);line-height:1.6;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  -webkit-tap-highlight-color:transparent;               /* the grey iOS flash reads as a rendering bug */
  overscroll-behavior:none;                              /* no white rubber-band behind a dark app */
  min-height:100vh;min-height:100dvh;                    /* dvh: the keyboard-open fix (§9.3) */
}
input,select,button,textarea{font-family:inherit}
::selection{background:var(--gold);color:var(--ink-on-gold)}   /* 7.83:1 — was rgba(70,211,154,.35) */
img{max-width:100%}
[hidden]{display:none!important}
svg{display:block}

/* touch behaviour a Capacitor shell needs from the CSS (§9.4). The killed tap
   highlight and the :active state always ship together, or a button feels dead. */
button,a,.row[role="button"],.chip,.pill.btn,.tier-card[role="button"],.lt-marker{
  touch-action:manipulation;-webkit-user-select:none;user-select:none}
.num,.table,p,li,.rc-code{-webkit-user-select:text;user-select:text}
button:active,.chip:active,.row[role="button"]:active{background:var(--card2)}

/* ---- 3. Accessibility utilities ------------------------------------------ */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.skip{position:absolute;left:var(--sp-3);top:-60px;z-index:200;display:inline-flex;align-items:center;
  background:var(--green);color:var(--ink-on-green);        /* 9.17:1 */
  font-weight:600;font-size:var(--t-sm);padding:0 var(--sp-4);min-height:44px;
  border-radius:var(--r-md);transition:top .18s var(--ease)}
.skip:focus{top:var(--sp-3)}

a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,
[tabindex]:focus-visible,summary:focus-visible{
  outline:none;box-shadow:var(--focus-ring);border-radius:var(--r-sm);
}

/* ---- 4. Chrome: header/brand, toast, nav --------------------------------- */
header{text-align:center;padding:calc(var(--sp-6) + var(--safe-t)) var(--sp-5) var(--sp-2)}
.brand{display:inline-flex;align-items:center;gap:var(--sp-3)}

/* V6: the wordmark was a gradient clipped to text, so `color` computed to transparent
   and it could never be a favicon or an App Store icon. It is now ONE FLAT INK SERIF
   with the trailing R in --gold — the existing .dot hook. Works at 16px, works in
   light mode, survives greyscale. */
.brand h1{font-family:var(--font-serif);font-size:20px;font-weight:400;letter-spacing:-.01em;
  background:none;-webkit-background-clip:border-box;background-clip:border-box;
  color:var(--ink);-webkit-text-fill-color:var(--ink)}
.brand .dot{color:var(--gold);-webkit-text-fill-color:var(--gold)}
.monogram{width:44px;height:44px;border-radius:var(--r-md);display:grid;place-items:center;
  font-family:var(--font-serif);font-weight:700;font-size:22px;
  color:var(--ink-on-gold);background:var(--gold);box-shadow:none}   /* the green glow is deleted */
.monogram.sm{width:32px;height:32px;font-size:16px}
.tag{color:var(--ink2);margin-top:var(--sp-3);font-size:var(--t-md);line-height:1.5;
  max-width:34ch;margin-left:auto;margin-right:auto;text-wrap:balance}
.tag b{color:var(--ink);font-weight:600}

/* toast (app.js builds div.toast(.good|.warn|.bad) > span.toast-text + button.toast-action? + button.toast-x)
   The ONE Floating element in the system (§3.4/§4.23): --bg2 ground, --line2 edge,
   --e-floating. Tone is a 3px left rule, never a tinted border. --bg2 bears --ink only. */
#toast-root{position:fixed;z-index:300;display:flex;flex-direction:column;gap:var(--sp-2);
  align-items:center;pointer-events:none;
  left:calc(var(--sp-4) + var(--safe-l));right:calc(var(--sp-4) + var(--safe-r));
  bottom:calc(var(--sp-4) + var(--safe-b))}
@media(min-width:768px){#toast-root{left:auto;right:calc(var(--sp-5) + var(--safe-r));
  bottom:calc(var(--sp-5) + var(--safe-b));align-items:flex-end}}
.toast{pointer-events:auto;display:flex;align-items:center;gap:var(--sp-3);max-width:420px;width:100%;
  background:var(--bg2);border:1px solid var(--line2);border-left:3px solid var(--line2);
  border-radius:var(--r-md);box-shadow:var(--e-floating);
  padding:var(--sp-3) var(--sp-4);font-size:var(--t-sm);line-height:1.5;color:var(--ink)}
.toast.good{border-left-color:var(--green)}
.toast.warn{border-left-color:var(--gold)}
.toast.bad{border-left-color:var(--red)}
.toast-text{flex:1;min-width:0;line-height:1.5}
.toast-action{flex:none;background:none;border:none;color:var(--gold);font-weight:600;font-size:var(--t-sm);
  cursor:pointer;padding:0 var(--sp-2);min-width:44px;min-height:44px}
.toast-x{flex:none;background:none;border:none;color:var(--muted);font-size:var(--t-md);line-height:1;
  cursor:pointer;width:44px;height:44px;min-width:44px;min-height:44px;display:grid;place-items:center;
  border-radius:var(--r-sm)}
.toast-x:hover{color:var(--ink);background:var(--card2)}

/* nav (app.js updateNav → #nav > div.topnav|div.bottomnav > button.topnav-item|bottomnav-item(.on) >
   span.nav-icon + span.nav-label + span.nav-dot.tone-<x>)
   §3.6: the top nav takes over at 768px, not 920 — at 768x1024 an iPad in portrait was
   rendering the phone tab bar. Six items at >=768px measure ~600px total and fit. */
.topnav{display:none}
.bottomnav{display:flex}
@media(min-width:768px){ .topnav{display:flex} .bottomnav{display:none} }

.topnav{max-width:1180px;margin:0 auto;gap:var(--sp-1);justify-content:center;
  padding:calc(var(--sp-2) + var(--safe-t)) var(--sp-4) var(--sp-2);border-bottom:1px solid var(--line)}
.topnav-item{display:inline-flex;align-items:center;gap:var(--sp-2);background:none;
  border:1px solid transparent;border-radius:var(--r-md);color:var(--muted);
  font-size:var(--t-sm);font-weight:600;padding:0 var(--sp-3);
  min-height:44px;cursor:pointer;transition:.12s var(--ease);position:relative}
.topnav-item:hover{color:var(--ink);background:var(--card2)}
.topnav-item.on{color:var(--ink);background:var(--card2);border-color:var(--line2)}
.topnav-item .nav-icon{width:18px;height:18px;flex:none;font-size:var(--t-base);line-height:1}

.bottomnav{position:fixed;left:0;right:0;bottom:0;z-index:100;justify-content:space-around;
  background:var(--card);border-top:1px solid var(--line2);box-shadow:none;
  padding:var(--sp-1) calc(var(--sp-1) + var(--safe-l)) calc(var(--sp-1) + var(--safe-b)) calc(var(--sp-1) + var(--safe-r))}
.bottomnav-item{flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:2px;background:none;border:none;color:var(--muted);
  font-size:var(--t-xs);font-weight:600;padding:var(--sp-1) 2px;
  min-height:44px;border-radius:var(--r-sm);cursor:pointer;position:relative}
.bottomnav-item .nav-icon{width:20px;height:20px;flex:none;font-size:var(--t-md);line-height:1}
/* the label ellipses rather than overflowing: six items at 320px leaves ~53px each, and a
   12px floor (§11.3 #26) costs more than that for "Withdraw". Ellipsis keeps #31 true. */
.bottomnav-item .nav-label{line-height:1.1;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bottomnav-item.on{color:var(--ink)}
/* selected is a SHAPE change as well as a colour one, so it survives greyscale */
.bottomnav-item.on::after{content:"";position:absolute;top:0;left:22%;right:22%;height:2px;
  border-radius:var(--r-full);background:var(--gold)}
.nav-dot{position:absolute;top:2px;right:calc(50% - 16px);width:7px;height:7px;border-radius:50%;background:currentColor}
.topnav-item .nav-dot{top:6px;right:var(--sp-2)}

/* ---- 5. Layout ------------------------------------------------------------ */
/* §3.6: the two-column split moves from 920 to 1100. At 920 each column was ~430px —
   too narrow for the hero meter plus the tier trio. */
.layout{max-width:1080px;margin:var(--sp-5) auto 0;display:grid;grid-template-columns:1fr;gap:var(--sp-5);
  padding-left:calc(var(--sp-4) + var(--safe-l));padding-right:calc(var(--sp-4) + var(--safe-r));
  padding-bottom:calc(var(--sp-9) + var(--safe-b))}
@media(min-width:768px){.layout{padding-bottom:var(--sp-8)}}
@media(min-width:1100px){.layout{grid-template-columns:1.12fr .88fr;align-items:start}}
@media(min-width:1440px){.layout{max-width:1180px;gap:var(--sp-6)}}

.screen{display:block}
/* .s-<id> — screen root hook only; each screen module injects its own scoped
   `.s-<id> ...` rules via ui.style() per §3.1/§3.2. No shared rules live here. */

.col-result{order:-1}
@media(min-width:1100px){.col-result{position:sticky;top:var(--sp-5);order:0}}

/* ---- 6. Cards -------------------------------------------------------------- */
/* Bounded (§3.4): a 1px --line2 edge at 3.56:1 and NO shadow. A card may not contain
   a card (§3.5) — an inner block is --card2 at --r-md with no border and no shadow. */
.card{background:var(--card);border:1px solid var(--line2);border-radius:var(--r-lg);box-shadow:var(--e-bounded)}
.card-pad{padding:var(--sp-5)}
@media(max-width:599.98px){.card-pad{padding:var(--sp-4)}}
.card.raised{box-shadow:var(--e-floating)}
/* a tint is a 3px accent LEFT RULE at full token value on an untinted ground
   (5.49–7.37:1); the three linear-gradient washes are deleted */
.card.tint-gold,.card.tint-green,.card.tint-blue{background:var(--card);border-left-width:3px}
.card.tint-gold{border-left-color:var(--gold)}
.card.tint-green{border-left-color:var(--green)}
.card.tint-blue{border-left-color:var(--blue)}

/* §4.2 — .step stops being a filled green pill and becomes a gold eyebrow ABOVE the
   title: a step number is wayfinding, not a call to action, and a saturated fill in
   front of a heading was outranking the heading. Markup is unchanged. */
.sec-h{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--sp-1) var(--sp-3);margin-bottom:var(--sp-1)}
.sec-h .step{flex:0 0 100%;font-size:var(--t-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--gold);background:none;border:none;padding:0}
.sec-h h2{font-family:var(--font-serif);font-size:var(--t-lg);font-weight:400;letter-spacing:-.01em;
  line-height:1.35;text-wrap:balance}
.sec-h .sec-h-action{margin-left:auto}
.sec-sub{color:var(--muted);font-size:var(--t-sm);margin:var(--sp-2) 0 var(--sp-4);max-width:62ch}

.group-label{display:flex;align-items:center;gap:var(--sp-2);margin:var(--sp-5) 0 var(--sp-2);
  font-size:var(--t-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.group-label::after{content:"";flex:1;height:1px;background:var(--line)}
.group-label .gi{width:16px;height:16px;flex:none;line-height:1}

.divider{height:1px;background:var(--line);margin:var(--sp-4) 0;border:none}

/* ---- 7. Rows & fields ------------------------------------------------------ */
/* §4.4 — THE DEAD-RULE FIX. v4 styled `.row-txt label`; ui.js:428 emits `span.lbl`,
   so every row label across all 24 screens rendered as unstyled inline body text,
   distinguishable from its own hint only by colour. One retargeted selector. */
.row{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-3);
  border:1px solid transparent;border-radius:var(--r-md);min-height:44px;transition:.12s var(--ease)}
.row:hover{background:var(--card2);border-color:var(--line)}
.row-main{display:flex;align-items:center;gap:var(--sp-3);min-width:0;flex:1}
.row .ic{width:40px;height:40px;flex:none;border-radius:var(--r-sm);display:grid;place-items:center;
  font-size:var(--t-md);background:var(--card2);border:1px solid var(--line);color:var(--muted)}
.row-txt{min-width:0}
.row-txt .lbl{display:block;font-size:var(--t-md);font-weight:600;color:var(--ink);line-height:1.35;cursor:pointer}
.row-sub{display:block;font-size:var(--t-sm);color:var(--muted);margin-top:2px}
/* a bolded fragment inside a hint is emphasis, not a funded state — colour is for facts */
.row-sub b{color:var(--ink2);font-weight:600}
.row-tags{display:flex;gap:var(--sp-2);flex-wrap:wrap;margin-top:var(--sp-2)}
.row-tags:empty{display:none}

.row-input{display:flex;align-items:center;gap:var(--sp-2);flex:none}
/* V7: 16px on an <input> is NOT a style choice — under 16px iOS Safari zooms the page
   on focus and does not zoom back. Focus moves green → gold so it is one colour
   everywhere and never collides with the funded state. */
/* 46px = a 44px INPUT plus the field's own two 1px borders. The tap-target rule is
   measured on the <input>, not on its wrapper, so the input stretches to fill the
   field and the field grows by exactly its border. */
.field{display:flex;align-items:center;background:var(--card2);border:1px solid var(--line2);
  border-radius:var(--r-md);padding:0 var(--sp-3);min-height:46px;width:124px;transition:.12s var(--ease)}
.field.full{width:100%}
.field:focus-within,.field.focus{border-color:var(--gold);box-shadow:var(--focus-ring)}
.field .cur{color:var(--tertiary);font-size:.7em;font-weight:400}
.field input{background:none;border:none;outline:none;color:var(--ink);
  font-family:var(--font-num);font-size:16px;font-weight:600;font-variant-numeric:tabular-nums;
  width:100%;text-align:right;align-self:stretch;min-height:44px;padding:0 0 0 var(--sp-1)}
.field input::placeholder{color:var(--tertiary);font-weight:400}   /* 4.80:1 on --card2 — was 2.92:1 */

.frow{display:flex;gap:var(--sp-3);margin-top:var(--sp-3);flex-wrap:wrap}
.ffield{flex:1;min-width:140px}
.ffield label{display:block;font-size:var(--t-sm);font-weight:600;color:var(--ink2);margin-bottom:var(--sp-2)}
.ffield label small,.ffield small{color:var(--muted);font-weight:400}
.ffield .field{width:100%}

/* the data: URI chevron cannot read a custom property, so its literal is updated to
   --muted's new value. This is ONE of exactly two sanctioned hard-coded hex values in
   the product (the other is index.html's theme-color). muted/card2 = 5.83:1. */
.select{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:100%;
  background:var(--card2) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23ACA59B' fill='none' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right var(--sp-3) center;
  border:1px solid var(--line2);border-radius:var(--r-md);color:var(--ink);
  font-size:16px;font-weight:600;padding:11px 32px 11px var(--sp-3);
  min-height:44px;cursor:pointer;transition:.12s var(--ease)}
.select:hover{border-color:var(--ink2)}

/* ---- 8. Controls ------------------------------------------------------------ */
/* §4.13 — the selected segment goes INK-ON-GROUND (--ink2 on --bg, 11.91:1). A
   segmented control is a view switch, not a value, and it was spending two of the
   five accent colours on furniture. */
.seg,.unit-toggle{display:inline-flex;background:var(--card2);border:1px solid var(--line2);
  border-radius:var(--r-md);overflow:hidden;flex:none}
.seg-btn,.unit-toggle button{background:none;border:none;color:var(--muted);
  font-size:var(--t-xs);font-weight:600;padding:0 var(--sp-3);min-height:44px;cursor:pointer;
  transition:.12s var(--ease)}
.seg-btn.on,.unit-toggle button.on{background:var(--ink2);color:var(--bg)}
.seg-btn:not(.on):hover,.unit-toggle button:not(.on):hover{color:var(--ink);background:var(--bg2)}

/* §4.8 — the pill is an OUTLINE TAG, not a filled lozenge. Six hand-mixed literals
   (#bcd4ff #e3c9ff #a9eccf #f7d38f #f3a9ad) are deleted; --r-full is dropped because a
   999px radius around a word reads as consumer candy and 4px reads as a tag on a
   document. Accent-on-card is 5.56–9.03:1.
   TAP TARGET: span.pill is a LABEL and is exempt from 44px. button.pill.btn is a
   CONTROL and is not. */
.pill{display:inline-flex;align-items:center;gap:var(--sp-1);font-size:var(--t-xs);font-weight:600;
  padding:3px var(--sp-2);border-radius:var(--r-sm);border:1px solid var(--line2);
  background:none;color:var(--muted);white-space:nowrap;line-height:1.4}
.pill.ord{color:var(--blue);border-color:var(--blue)}
.pill.inv{color:var(--violet);border-color:var(--violet)}
.pill.free,.pill.grow{color:var(--green);border-color:var(--green)}
.pill.ss{color:var(--gold);border-color:var(--gold)}
.pill.fixed{color:var(--red);border-color:var(--red)}
.pill.btn{cursor:pointer;min-height:44px;padding:0 var(--sp-3);transition:.12s var(--ease)}
.pill.btn:hover{background:var(--card2)}     /* was filter:brightness(1.18) — an unmeasurable colour */

/* §4.15 — selected is a DOUBLED GOLD BORDER, not a blue fill: a shape change as well
   as a colour change, so aria-pressed has a visual twin that survives greyscale.
   ui.js:486's inline el.style.borderColor override still applies and is unaffected. */
.chips,.chip-row{display:flex;gap:var(--sp-2);flex-wrap:wrap;margin-top:var(--sp-4)}
.chip{background:var(--card2);border:1px solid var(--line2);color:var(--ink2);
  font-size:var(--t-sm);font-weight:600;padding:0 var(--sp-4);min-height:44px;
  display:inline-flex;align-items:center;border-radius:var(--r-md);cursor:pointer;transition:.12s var(--ease)}
.chip:hover{color:var(--ink);border-color:var(--ink2)}
.chip.on{background:none;border-color:var(--gold);color:var(--gold);box-shadow:inset 0 0 0 1px var(--gold)}

/* inner block (§3.5): --card2, --r-md, no border of its own inside a bordered card */
.adj{margin-top:var(--sp-4);background:var(--card2);border:1px solid var(--line);
  border-radius:var(--r-md);padding:var(--sp-4)}
.adj-head{display:flex;align-items:center;gap:var(--sp-3);min-height:44px}
.adj-head .ti{width:18px;height:18px;flex:none;font-size:var(--t-base);line-height:1;color:var(--muted)}
.adj-head .tl{font-size:var(--t-base);font-weight:600}
.adj-head .sum{margin-left:auto;font-size:var(--t-xs);color:var(--muted);font-weight:600}
.adj-body{margin-top:var(--sp-4);transition:.18s var(--ease)}
.adj.off .adj-body{opacity:.35;pointer-events:none;filter:grayscale(.5)}
.adj-note{color:var(--muted);font-size:var(--t-sm);line-height:1.6;margin-top:var(--sp-3);max-width:62ch}
.adj-note b{color:var(--ink);font-weight:600}

/* §4.14 — the off state gains a --line2 border (3.10:1) so the track is visible as a
   control and not just a darker smudge; the knob moves off the #fff literal. The
   ::before inset is the SANCTIONED pattern for a 62x44 target without a 62x44 visual. */
.switch{position:relative;width:44px;height:26px;flex:none;cursor:pointer;display:inline-block}
.switch::before{content:"";position:absolute;inset:-9px}
.switch input{opacity:0;width:0;height:0;position:absolute}
.switch .track{position:absolute;inset:0;background:var(--card2);border:1px solid var(--line2);
  border-radius:var(--r-full);transition:.18s var(--ease)}
.switch .knob{position:absolute;top:4px;left:4px;width:18px;height:18px;border-radius:50%;
  background:var(--muted);transition:.18s var(--ease)}
.switch input:checked ~ .track{background:var(--green);border-color:var(--green)}
.switch input:checked ~ .knob{transform:translateX(18px);background:var(--ink-on-green)}
.switch input:focus-visible ~ .track{box-shadow:var(--focus-ring)}

.slider-row{display:flex;flex-direction:column;gap:var(--sp-2);margin-top:var(--sp-3)}
.slider-row:first-child{margin-top:0}
.slider-val{font-family:var(--font-num);font-weight:700;color:var(--gold);flex:none;
  font-size:var(--t-num-sm);font-variant-numeric:tabular-nums;letter-spacing:0}
/* the green→gold→red track was the only rainbow left in the kit and it encoded nothing:
   a slider track is furniture. Neutral track, single-token variants.

   §9.5 — THE CONTROL IS 44px TALL; THE VISIBLE TRACK STAYS 6px. Three things changed:

   1. The selector. `ui.sliderRow` (ui.js:960-970) emits a BARE <input type=range>, or
      `input.cool|.gold|.plain` when tinted — it never emits the `.range` class. So the
      old `.range` / `.range.cool` rules matched NOTHING the app renders, which is why six
      screen css blocks re-declare the whole slider from scratch. The base now hangs off
      `input[type=range]`, where the markup actually is; `.range` is kept as an alias so
      any hand-written markup carrying it still styles.
   2. `min-height`, not `height`, is the load-bearing word. min-height clamps used height
      whatever the specificity of the rule that set `height`, so a screen block's
      `input[type=range]{height:40px}` (plan-assumptions, plan-people, four more) can no
      longer shrink the tap target. Only another `min-height` can, which is what the lint
      gate is for.
   3. `height:6px` moves off the input and onto the track pseudo-element, so growing the
      control to 44px does not grow the furniture. Thumb margin-top:-6px centres a 20px
      thumb on the 8px track box (6px content + 2 x 1px border): (8 - 20) / 2. */
input[type=range],.range{-webkit-appearance:none;appearance:none;width:100%;
  height:44px;min-height:44px;padding:0;background:none;border:0;
  border-radius:var(--r-full);outline:none;cursor:pointer;
  --range-fill:var(--card2);--range-edge:var(--line2)}
input[type=range].cool,.range.cool{--range-fill:var(--blue);--range-edge:var(--blue)}
input[type=range].gold,.range.gold{--range-fill:var(--gold);--range-edge:var(--gold)}
input[type=range].plain,.range.plain{--range-fill:var(--line2);--range-edge:var(--line2)}
input[type=range]::-webkit-slider-runnable-track,.range::-webkit-slider-runnable-track{
  height:6px;border-radius:var(--r-full);background:var(--range-fill);border:1px solid var(--range-edge)}
input[type=range]::-moz-range-track,.range::-moz-range-track{
  height:6px;border-radius:var(--r-full);background:var(--range-fill);border:1px solid var(--range-edge)}
input[type=range]::-webkit-slider-thumb,.range::-webkit-slider-thumb{-webkit-appearance:none;
  width:20px;height:20px;border-radius:50%;background:var(--ink);border:3px solid var(--card);
  box-shadow:none;cursor:pointer;margin-top:-6px}
input[type=range]::-moz-range-thumb,.range::-moz-range-thumb{width:20px;height:20px;border-radius:50%;
  background:var(--ink);border:3px solid var(--card);cursor:pointer}
input[type=range]:focus-visible,.range:focus-visible{box-shadow:var(--focus-ring)}

/* §4.9 — three fixes: .btn:hover{color:#fff} deleted (a literal, and 1.60:1 on a
   .primary fill); .btn.danger:hover's literal #2a0c0e becomes var(--ink-on-red); and
   filter:brightness(1.08) deleted — brightening a token is an unmeasurable colour.
   .btn.sm stops being 36px: a small button is NARROWER, never shorter.
   §9.5 is 44x44, TWO AXES. min-height alone left a short label under target on the
   cross axis: button.btn.sm labelled "Fix" measured 40.8 x 44 at 390px (four of them
   on /survivor). min-width:44px is the same pattern .toast-action and .toast-x
   already use above. .btn.full still wins the main axis with width:100%. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  border:1px solid var(--line2);background:var(--card2);color:var(--ink);
  padding:0 var(--sp-4);min-height:44px;min-width:44px;border-radius:var(--r-md);
  font-family:var(--font-sans);font-weight:600;font-size:var(--t-sm);cursor:pointer;
  transition:.12s var(--ease)}
.btn:hover{border-color:var(--ink2);background:var(--bg2)}
.btn.primary{background:var(--green);border-color:var(--green);color:var(--ink-on-green)}
.btn.primary:hover{background:var(--green);border-color:var(--ink-on-green);color:var(--ink-on-green);filter:none}
.btn.ghost{background:none}
.btn.ghost:hover{border-color:var(--ink2)}
.btn.danger{background:none;border-color:var(--red);color:var(--red)}
.btn.danger:hover{background:var(--red);border-color:var(--red);color:var(--ink-on-red)}   /* 5.68:1 */
.btn.sm{padding:0 var(--sp-3);font-size:var(--t-xs);min-height:44px}
.btn.full{width:100%}
.btn:disabled{opacity:.45;pointer-events:none}

/* §4.19 — no fill; the 3px blue left rule already says "aside". .tip b moves off --blue. */
.tip{display:flex;gap:var(--sp-3);margin-top:var(--sp-4);padding:var(--sp-3) var(--sp-4);
  border-radius:var(--r-md);background:none;border:1px solid var(--line);border-left:3px solid var(--blue);
  color:var(--ink2);font-size:var(--t-sm);line-height:1.6}
.tip > span:first-child{flex:none;width:18px;height:18px;line-height:1;color:var(--blue)}
.tip b{color:var(--ink);font-weight:600}

/* ---- 9. Result column ------------------------------------------------------- */
.verdict{text-align:center;padding:var(--sp-5)}

/* §4.10a — V4, FIXED AT THE SOURCE. v4 rotated `.ring` -90deg, but ui.js:601 returns
   div.ring > svg, so .ring-center (the percentage text) rotated too — and home.js:470
   and survivor.js:437 each carried a private un-rotate. The rotation now lives on the
   SVG, where it belongs. BOTH LOCAL PATCHES MUST BE DELETED IN THE SAME COMMIT
   (§10.5 coupling A): with this file alone those screens double-rotate.
   210px fixed → fluid max-width:180px (at 390px the fixed ring ate ~40% of the first
   screen); stroke 15→10; linecap round→butt, because a rounded cap at 0% still draws a
   visible dot, which reads as progress that does not exist. */
.ringwrap{position:relative;width:100%;max-width:180px;aspect-ratio:1;margin:0 auto var(--sp-2)}
.ring{position:relative;width:100%;max-width:180px;aspect-ratio:1;margin-inline:auto;transform:none}
.ring > svg{display:block;width:100%;height:auto;transform:rotate(-90deg)}
.ring-track{fill:none;stroke:var(--card2);stroke-width:10}
.ring-fill{fill:none;stroke:var(--green);stroke-width:10;stroke-linecap:butt;
  transition:stroke-dashoffset .7s var(--ease),stroke .4s}
.ring-center{position:absolute;inset:0;display:grid;place-content:center;text-align:center}
.ring-pct{font-family:var(--font-num);font-size:var(--t-num-xl);font-weight:700;letter-spacing:0;
  font-variant-numeric:tabular-nums;line-height:1}
.ring-pct .sfx{font-size:.55em;color:var(--tertiary)}
.ring-cap{font-size:var(--t-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);margin-top:var(--sp-2)}

/* §4.10b — ui.rail, the V1 fix. The gate is a FIXED 75% of the track at every value and
   on every screen, the scale is linear, and the fill never lies about being full.
   pos(v) = min(v / 1.3333, 1) is written by ui.js to --rail-pos. */
.rail{display:flex;flex-direction:column;gap:var(--sp-2)}
.rail-pct{font-family:var(--font-num);font-size:var(--t-num-xl);font-weight:700;letter-spacing:0;
  font-variant-numeric:tabular-nums;color:var(--ink);line-height:1}
.rail-pct .sfx{font-size:.55em;color:var(--tertiary)}
/* An empty reading is an em dash, and "—%" is not a reading — it is a unit with nothing in
   front of it, which is what /_kitchen's pending ring printed. `num()` already marks the empty
   case with `.num-empty` (ui.js:354), so the suffix hides itself off that class rather than off
   `pending`: EVERY empty value loses the stray %, not just the ones a caller remembered to flag.
   `display:none` removes it from the accessibility tree too, so a screen reader hears "—" and
   not "dash percent" — the same result as not emitting the span, without editing the frozen
   ring/rail markup that tests/ui.test.mjs pins. */
.ring-pct .num-empty + .sfx,.rail-pct .num-empty + .sfx{display:none}
.rail-cap{font-size:var(--t-sm);color:var(--muted)}
.rail-track{--rail-gate:75%;position:relative;height:12px;border-radius:var(--r-full);
  background:var(--card2);border:1px solid var(--line2)}
.rail-fill{display:block;height:100%;width:var(--rail-pos,0%);background:var(--green);
  border-radius:var(--r-full);transition:width .7s var(--ease)}
/* the 100% gate — a fixed constant, taller than the track so it reads as a threshold */
.rail-track::after{content:"";position:absolute;top:-4px;bottom:-4px;left:var(--rail-gate);
  width:1px;background:var(--ink2)}
/* beyond the scale the cap changes SHAPE: a flat end means "this is the value", a
   notched end means "the axis stops here, the number does not" */
.rail-fill.over-max{clip-path:polygon(0 0,calc(100% - 6px) 0,100% 50%,calc(100% - 6px) 100%,0 100%)}

.v-title{font-family:var(--font-serif);font-size:var(--t-xl);font-weight:400;letter-spacing:-.01em;
  line-height:1.25;margin-top:var(--sp-2);text-wrap:balance}
.v-msg{color:var(--ink2);font-size:var(--t-base);margin:var(--sp-2) auto 0;max-width:42ch;line-height:1.6}
.v-msg b{color:var(--ink);font-weight:600}
.v-msg .good{color:var(--green)}
.v-msg .bad{color:var(--red)}

/* inner block (§3.5). The headline figure is --ink: the tone lives in the meter beside
   it and in the words, never in a neon numeral (§1.6 — colour is the third channel). */
.rbig{margin:var(--sp-5) var(--sp-3) var(--sp-1);padding:var(--sp-4);border-radius:var(--r-md);
  background:var(--card2);border:1px solid var(--line);
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);text-align:left}
.rbig-label{font-size:var(--t-sm);font-weight:400;letter-spacing:0;text-transform:none;color:var(--muted)}
.rbig-amt{font-family:var(--font-num);font-size:var(--t-num-lg);font-weight:700;letter-spacing:0;
  font-variant-numeric:tabular-nums;color:var(--ink);line-height:1.1;margin-top:var(--sp-1)}
.rbig-sub{font-size:var(--t-sm);color:var(--muted);margin-top:2px}

.flow{margin:var(--sp-4) var(--sp-3) 0;display:flex;align-items:stretch;gap:var(--sp-2)}
.flow .stp{flex:1;min-width:0;background:var(--card2);border:1px solid var(--line);
  border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3);text-align:left}
.flow .stp .k{font-size:var(--t-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);white-space:nowrap}
.flow .stp .v{font-family:var(--font-num);font-size:var(--t-num-md);font-weight:700;letter-spacing:0;
  margin-top:2px;font-variant-numeric:tabular-nums}
.flow .stp.gross .v{color:var(--muted)}
.flow .stp.net .v{color:var(--green)}
.flow .arr{display:grid;place-items:center;color:var(--muted);font-size:var(--t-base);flex:none}

/* tinted component (§1.5): --tint-gold fill + a 3px gold left rule. --line2 sinks to
   2.96:1 on this fill, which is exactly why the boundary is the rule and not a border. */
.fire{margin:var(--sp-4) var(--sp-3) 0;padding:var(--sp-4);background:var(--tint-gold);
  border:1px solid var(--line);border-left:3px solid var(--gold);border-radius:var(--r-md)}
.fire-top{display:flex;justify-content:space-between;align-items:center;font-size:var(--t-xs);
  font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--gold)}
.fire-top .r25{color:var(--muted);font-weight:600}
.fire-num{font-family:var(--font-num);font-size:var(--t-num-lg);font-weight:700;letter-spacing:0;
  font-variant-numeric:tabular-nums;margin-top:var(--sp-2);color:var(--ink);line-height:1.1}
.fire-sub{font-size:var(--t-sm);color:var(--muted);margin-top:var(--sp-1)}
.fire-bar{height:8px;border-radius:var(--r-full);background:var(--card);overflow:hidden;
  margin-top:var(--sp-3);border:1px solid var(--line)}
.fire-fill{height:100%;border-radius:var(--r-full);background:var(--gold);transition:width .7s var(--ease)}
.fire-prog{font-size:var(--t-xs);margin-top:var(--sp-2);display:flex;justify-content:space-between;color:var(--muted)}
.fire-prog b{color:var(--ink);font-weight:600}

/* phase timeline (v3 .tl/.tl-h/.bdg/.tseg .age/.mid/.track/.meta/.cov, flattened per §4) */
.tl{margin:var(--sp-4) var(--sp-3) 0;padding:var(--sp-4);background:var(--card2);
  border:1px solid var(--line);border-radius:var(--r-md)}
.tl-head{display:flex;justify-content:space-between;align-items:center;gap:var(--sp-2);
  font-size:var(--t-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.tl-badge{font-weight:600}
.tseg{display:flex;gap:var(--sp-3);padding:var(--sp-3) 0;border-bottom:1px solid var(--line)}
.tseg:last-child{border-bottom:none}
.tseg-age{flex:none;width:54px;text-align:center}
.tseg-age .rng{font-family:var(--font-num);font-size:var(--t-num-sm);font-weight:700;letter-spacing:0;
  font-variant-numeric:tabular-nums}
.tseg-age .ic{font-size:var(--t-base);margin-top:2px;line-height:1}
.tseg-body{flex:1;min-width:0}
.tseg-body .lbl{font-size:var(--t-sm);color:var(--ink);line-height:1.5}
.tseg-track{height:8px;border-radius:var(--r-full);background:var(--card);overflow:hidden;margin-top:var(--sp-2)}
.tseg-track .fill{height:100%;border-radius:var(--r-full);transition:width .7s var(--ease)}
.tseg-meta{font-size:var(--t-xs);color:var(--muted);margin-top:var(--sp-1)}
.tseg-pct{flex:none;width:46px;text-align:right;font-family:var(--font-num);font-size:var(--t-num-sm);
  font-weight:700;letter-spacing:0;font-variant-numeric:tabular-nums}
.tl-note{color:var(--muted);font-size:var(--t-sm);margin-top:var(--sp-3);line-height:1.6;max-width:62ch}
.tl-note b{color:var(--ink);font-weight:600}

/* breakdown bar (v3 .bd/.bar/.legend → .bd/.bd-bar/.bd-legend) */
.bd{padding:var(--sp-4) var(--sp-5) var(--sp-5);border-top:1px solid var(--line)}
.bd-h{font-size:var(--t-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);
  margin-bottom:var(--sp-3);display:flex;justify-content:space-between;gap:var(--sp-2)}
.bd-h .sub{font-weight:400;text-transform:none;letter-spacing:0;color:var(--tertiary)}  /* 5.52:1 — was 2.68:1 */
.bd-bar{display:flex;height:16px;border-radius:var(--r-full);overflow:hidden;background:var(--card2);
  border:1px solid var(--line2)}
.bd-bar i{display:block;height:100%;transition:width .7s var(--ease)}
.bd-bar .empty{flex:1;display:grid;place-items:center;font-size:var(--t-xs);color:var(--tertiary)}
.bd-legend{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-2) var(--sp-4);margin-top:var(--sp-4)}
@media(max-width:420px){.bd-legend{grid-template-columns:1fr}}
.bd-legend .leg{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--t-sm)}
.bd-legend .leg .sw{width:10px;height:10px;border-radius:var(--r-sm);flex:none}
.bd-legend .leg .ln{color:var(--muted);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bd-legend .leg .lv{font-family:var(--font-num);font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:0}
.bd-legend .leg .lp{color:var(--muted);font-size:var(--t-xs);width:38px;text-align:right;font-variant-numeric:tabular-nums}
/* charts.js:560 emits a bare i.legend-dot inside .bd-legend .leg — a different shape
   under the same class as ui.legendDot's span (§4.16) */
.bd-legend .leg .legend-dot{width:10px;height:10px;border-radius:var(--r-sm);flex:none;display:block}

/* the semantic pair (§1.6). 1.25:1 under tritanopia is NOT a pass and this file does
   not claim one: surplus and shortfall are never distinguished by colour. Every
   instance carries a sign (fmt.signedMoney), a word ("to spare" / "short") and a
   direction. Colour is the third channel, never the first. */
.gap{margin-top:var(--sp-4);padding:var(--sp-4);border-radius:var(--r-md);
  font-size:var(--t-base);line-height:1.6;border:1px solid var(--line);border-left:3px solid var(--line2)}
.gap.need{background:var(--tint-red);border-left-color:var(--red)}
.gap.spare{background:var(--tint-green);border-left-color:var(--green)}
.gap .gv{font-weight:700;font-variant-numeric:tabular-nums}
.gap.need .gv{color:var(--red)}      /* 4.79:1 on --tint-red */
.gap.spare .gv{color:var(--green)}   /* 7.37:1 on --tint-green */

.sscmp{display:flex;gap:var(--sp-2);margin-top:var(--sp-3)}
.sscell{flex:1;min-width:0;text-align:center;background:var(--card2);border:1px solid var(--line);
  border-radius:var(--r-md);padding:var(--sp-2) var(--sp-1);transition:.12s var(--ease)}
/* selected is a doubled border — a shape change, so it survives greyscale */
.sscell.on{border-color:var(--green);box-shadow:inset 0 0 0 1px var(--green);background:var(--tint-green)}
.sscell .sa{font-size:var(--t-xs);font-weight:600;color:var(--muted)}
.sscell .sm{font-family:var(--font-num);font-size:var(--t-num-sm);font-weight:700;letter-spacing:0;
  margin-top:2px;font-variant-numeric:tabular-nums}
.sscell .sp{font-size:var(--t-xs);font-weight:600;margin-top:2px}
.sscell .sp.up{color:var(--green)}
.sscell .sp.dn{color:var(--red)}
.sscell .sp.fra{color:var(--blue)}
.ss-hint{font-size:var(--t-sm);color:var(--muted)}

.proj{margin-top:var(--sp-3);padding:var(--sp-3) var(--sp-4);border-radius:var(--r-md);
  background:var(--tint-green);border:1px solid var(--line);border-left:3px solid var(--green);
  font-size:var(--t-sm);line-height:1.6}
.proj .pn{font-family:var(--font-num);font-size:var(--t-num-md);font-weight:700;letter-spacing:0;
  font-variant-numeric:tabular-nums;color:var(--ink)}
.proj small{color:var(--muted)}

/* ---- 10. New components (v4 only) -------------------------------------------- */

/* §4.5 — THE KPI STOPS BEING A TILE AND BECOMES A DEFINITION ROW. Four tiles 109px
   wide and 222px tall, with 11px/800 uppercase labels wrapping to 3–4 lines and one
   tile height forced by the worst case: tuning the tile could not fix that, removing
   the tile could. Label left in sentence case, figure right, caption under both, a
   hairline between rows. The class names and the markup are untouched, so
   home.js:310's el.querySelectorAll('.kpi')[2] still resolves. */
.kpi{background:none;border:none;border-top:1px solid var(--line);border-radius:0;
  padding:var(--sp-3) 0;display:grid;grid-template-columns:1fr auto;column-gap:var(--sp-4);align-items:baseline}
.kpi-label{grid-column:1;font-size:var(--t-sm);font-weight:400;letter-spacing:0;text-transform:none;color:var(--muted)}
.kpi-value{grid-column:2;grid-row:1;justify-self:end;display:block;font-family:var(--font-num);
  font-size:var(--t-num-md);font-weight:700;letter-spacing:0;font-variant-numeric:tabular-nums;color:var(--ink)}
.kpi-cap{grid-column:1/-1;font-size:var(--t-xs);color:var(--tertiary);margin-top:2px;max-width:48ch}

/* callout — inline feedback card (tone: good/warn/bad/neutral).
   §4.6 — the tone TITLE stops being coloured: a coloured heading plus a coloured rule
   plus a coloured icon is three signals for one fact. The rule and the icon carry it. */
.callout{display:flex;gap:var(--sp-3);padding:var(--sp-4);border-radius:var(--r-md);
  font-size:var(--t-base);line-height:1.6;background:var(--card2);border:1px solid var(--line)}
.callout-title{font-family:var(--font-serif);font-size:var(--t-lg);font-weight:400;color:var(--ink)}
.callout-body{color:var(--ink2);margin-top:var(--sp-1)}
.callout.good,.callout.warn,.callout.bad{background:var(--card);border:1px solid var(--line);border-left:3px solid}
.callout.good{border-left-color:var(--green)}
.callout.warn{border-left-color:var(--gold)}
.callout.bad{border-left-color:var(--red)}
.callout.neutral{background:var(--card2);border-color:var(--line)}

/* banner — dismissible notice strip. Same tone treatment as .callout. */
.banner{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);
  border-radius:var(--r-md);background:var(--card2);border:1px solid var(--line);margin-bottom:var(--sp-4)}
.banner-title{font-weight:600;font-size:var(--t-base)}
.banner-body{color:var(--ink2);font-size:var(--t-sm);margin-top:2px}
.banner.good,.banner.warn,.banner.bad{background:var(--card);border-left:3px solid}
.banner.good{border-left-color:var(--green)}
.banner.warn{border-left-color:var(--gold)}
.banner.bad{border-left-color:var(--red)}
.banner-x{flex:none;background:none;border:none;color:var(--muted);font-size:var(--t-md);line-height:1;
  cursor:pointer;width:44px;height:44px;min-width:44px;min-height:44px;display:grid;place-items:center;
  border-radius:var(--r-sm);margin-left:auto}
.banner-x:hover{color:var(--ink);background:var(--card2)}

/* empty state — one of only two places centred text is permitted (§2.5) */
.empty{text-align:center;padding:var(--sp-7) var(--sp-5);color:var(--muted)}
.empty-icon{width:24px;height:24px;margin:0 auto;font-size:var(--t-lg);line-height:1;color:var(--tertiary)}
.empty-title{font-family:var(--font-serif);font-size:var(--t-lg);font-weight:400;color:var(--ink);margin-top:var(--sp-3)}
.empty-body{font-size:var(--t-base);margin-top:var(--sp-2);max-width:42ch;margin-left:auto;margin-right:auto;line-height:1.6}

/* table — .table-wrap{overflow-x:auto} IS the no-sideways-scroll guarantee for wide
   tables (§11.4 #31) and may not be removed */
.table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}
.table{width:100%;border-collapse:collapse;font-size:var(--t-num-sm);white-space:nowrap}
.table th{text-align:left;font-size:var(--t-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);padding:var(--sp-2) var(--sp-3);border-bottom:1px solid var(--line2)}
.table th[data-align="right"],.table td[data-align="right"]{text-align:right}
.table td{padding:var(--sp-3);border-bottom:1px solid var(--line);font-variant-numeric:tabular-nums;color:var(--ink2)}
.table tr:last-child td{border-bottom:none}
.table tr:hover td{background:var(--card2)}
.table tr.dead td{color:var(--tertiary)}          /* 5.52:1 — was 2.68:1 */
.table tr.dead td.age{color:var(--muted);text-decoration:line-through}

/* accordion */
.accordion{display:flex;flex-direction:column;gap:var(--sp-2)}
.acc-item{background:var(--card);border:1px solid var(--line2);border-radius:var(--r-lg);overflow:hidden}
.acc-head{width:100%;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  background:none;border:none;color:var(--ink);text-align:left;padding:var(--sp-4) var(--sp-5);
  min-height:44px;cursor:pointer}
.acc-head:hover{background:var(--card2)}
.acc-head-title{font-family:var(--font-serif);font-size:var(--t-lg);font-weight:400}
.acc-head-caption{font-size:var(--t-sm);color:var(--muted);font-weight:400;margin-top:2px}
.acc-head-chevron{width:14px;height:14px;flex:none;color:var(--muted);transition:transform .18s var(--ease)}
.acc-item.open .acc-head-chevron{transform:rotate(180deg)}
.acc-body{display:none;padding:0 var(--sp-5) var(--sp-5)}
.acc-item.open .acc-body{display:block}
.acc-pinned{margin-bottom:var(--sp-4)}

/* stale — data from a prior computation shown while a fresher one loads (§8.4).
   grayscale(.3) is deleted: on a warm palette desaturating destroys the product's
   whole temperature and reads as BROKEN rather than as one moment out of date. */
.stale{opacity:.5;filter:none}
.stale-label{font-family:var(--font-mono);font-size:var(--t-xs);letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted)}

/* badge — small tier tag, tone words match .tone-*. A LABEL (a span), so it is exempt
   from the 44px rule. --r-full is dropped per §3.3. Accent on its own tint: 4.79–7.37. */
.badge{display:inline-flex;align-items:center;font-size:var(--t-xs);font-weight:600;
  padding:3px var(--sp-2);border-radius:var(--r-sm);border:1px solid var(--line);line-height:1.4}
.badge.green{color:var(--green);background:var(--tint-green)}
.badge.gold{color:var(--gold);background:var(--tint-gold)}
.badge.blue{color:var(--blue);background:var(--tint-blue)}
.badge.red{color:var(--red);background:var(--tint-red)}
.badge.muted{color:var(--muted);background:var(--card2)}

/* fan — MC fan-chart wrapper (chart + trial-count chips); an inner block (§3.5) */
.fan{margin-top:var(--sp-4);padding:var(--sp-4);background:var(--card2);border:1px solid var(--line);
  border-radius:var(--r-md)}

/* compare — ranked horizontal bars (charts.compareBars) */
.compare{display:flex;flex-direction:column;gap:var(--sp-3);margin-top:var(--sp-2)}
.compare-row{display:flex;flex-direction:column;gap:var(--sp-1)}
.compare-row-top{display:flex;justify-content:space-between;gap:var(--sp-2);font-size:var(--t-sm)}
.compare-row-top .lab{color:var(--ink);font-weight:600}
.compare-row-top .val{font-family:var(--font-num);font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:0}
.compare-bar{height:10px;border-radius:var(--r-full);background:var(--card2);border:1px solid var(--line);overflow:hidden}
.compare-bar i{display:block;height:100%;border-radius:var(--r-full);background:var(--blue);transition:width .7s var(--ease)}
.compare-row.best .compare-bar i{background:var(--green)}
.compare-row.highlight .compare-row-top .lab{color:var(--gold)}

/* hatched — diagonal-stripe overlay for a stacked-bar/segment (V9's second channel;
   --s-tax is hatched in EVERY chart) */
.hatched{position:relative}
.hatched::before{content:"";position:absolute;inset:0;
  background-image:repeating-linear-gradient(135deg,currentColor 0 3px,transparent 3px 6px);opacity:.6}

/* num — every formatted numeric value (ui.num). UNTOUCHED in JS: ui.js:342 throws
   without src and every figure keeps data-src/data-kind. Styling only. */
.num{font-family:var(--font-num);font-variant-numeric:tabular-nums;letter-spacing:0}
.num.nominal{border-bottom:1px dotted var(--tertiary);cursor:help}
.num-empty{color:var(--tertiary)}

/* chart — generic SVG chart wrapper (charts.js builders) */
.chart{width:100%}
.chart svg{display:block;width:100%;height:auto}
.chart title{pointer-events:none}

/* legend — generic colour-key row (donut/lineChart/fanChart), distinct from .bd-legend.
   §4.16 MARKUP MISMATCH FIX: v4 styled the SPAN as the 9px swatch while ui.js:646 puts
   the colour on the <i>, so the swatch was the whole label row. Style the <i>.
   plan-accounts.js's local .legend-dot workaround must be DELETED IN THE SAME COMMIT
   (§10.5 coupling B). */
.legend{display:flex;flex-wrap:wrap;gap:var(--sp-2) var(--sp-4);margin-top:var(--sp-3)}
.legend-item{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--t-sm);color:var(--muted)}
.legend-dot{display:inline-flex;align-items:center;gap:var(--sp-2);font-size:var(--t-sm);color:var(--muted);
  width:auto;height:auto;border-radius:0;background:none}
.legend-dot > i{width:10px;height:10px;flex:none;border-radius:var(--r-sm);display:block}

/* spinner */
.spinner{width:18px;height:18px;border-radius:50%;border:2px solid var(--line2);border-top-color:var(--gold);
  animation:wmr-spin .8s linear infinite;flex:none}
.spinner-row{display:inline-flex;align-items:center;gap:var(--sp-2);font-size:var(--t-sm);color:var(--muted)}
@keyframes wmr-spin{to{transform:rotate(360deg)}}

/* stepper — onboarding step-of-4 indicator. Progress is wayfinding, not a funded
   state, so the dot is gold (§4.2's argument, applied consistently). */
.stepper{display:flex;align-items:center;justify-content:center;gap:var(--sp-3);margin-bottom:var(--sp-4);
  font-size:var(--t-xs);color:var(--muted);font-weight:600}
.step-dots{display:flex;gap:var(--sp-2)}
.step-dots span{width:7px;height:7px;border-radius:50%;background:var(--line2);transition:.18s var(--ease)}
.step-dots span.on{background:var(--gold);width:20px;border-radius:var(--r-full)}

/* ---- 11. Tone mapping (one place; capacity/fundedShare ≥1 green, ≥.8 gold, ≥.5 blue, else red)
   D-23: this is a RANKED SEVERITY SCALE, so §1.5 orders it by luminance. It survives
   greyscale (#979797 → #A4A4A4 → #B2B2B2 → #C1C1C1) and every CVD. ------------------ */
.tone-green{color:var(--green)}
.tone-gold{color:var(--gold)}
.tone-blue{color:var(--blue)}
.tone-red{color:var(--red)}
.tone-muted{color:var(--muted)}

/* ---- 12. Responsive (§3.6 — five tiers, was one) -------------------------------- */
/* Phone            < 600  single column, bottom tab bar, card pad 16, section gap 48
   Phone L / small  >= 600 KPI list 2-up, .frow side by side, card pad 24
   Tablet portrait  >= 768 top nav replaces the bottom bar
   Two-column       >=1100 .layout 1.12fr .88fr, .col-result sticky
   Desktop          >=1440 content capped at 1180, section gap 64 */
@media(max-width:767.98px){
  main#app{padding-bottom:var(--sp-2)}
}

/* ---- 13. Reduced motion (§8.2). DO NOT SIMPLIFY: .01ms rather than `none` keeps
   transitionend firing for any JS that awaits it. ---------------------------------- */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    scroll-behavior:auto!important;
  }
  /* a frozen spinner is a bug, not a reduced animation: the global kill snaps it to
     360deg and leaves a static ring that reads as a broken control */
  .spinner{animation:none!important;border-color:var(--line2);border-top-color:var(--gold);opacity:1}
  /* every bar arrives at its final width with no sweep */
  .rail-fill,.tier-bar i,.fire-fill,.tseg-track .fill,.bd-bar i,.compare-bar i,
  .ring-fill,.gauge-fill{transition:none!important}
}

/* ==== v4.1 product layer (integrator; PRODUCT-DECISIONS P1/P2/P10/P13) ==================
   Everything below is additive. Screen agents file class requests here via the integrator. */

/* header brand is now a link to the homepage; the trailing R of "WhatsMyR" is the brand mark */
/* `color` is not decoration here: without it the link inherits the UA's default link colour
   (Chrome's dark-mode rgb(158,158,255)), the only element in the app computing one. Nothing
   paints purple today only because both children set their own colour — any bare text node,
   ::before or future sibling inside the brand link would, in the header, on every screen. */
a.brand{text-decoration:none;cursor:pointer;color:var(--ink)}
a.brand:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:var(--r-sm)}

/* disclaimer footer (P10/P13) — rendered by the shell on every screen */
footer.disclaimer{max-width:1080px;margin:var(--sp-6) auto var(--sp-9);text-align:center;
  color:var(--muted);font-size:var(--t-sm);line-height:1.6;
  padding-left:calc(var(--sp-5) + var(--safe-l));padding-right:calc(var(--sp-5) + var(--safe-r))}
footer.disclaimer p{margin:0 auto;max-width:62ch}
footer.disclaimer .disclaimer-sub{margin-top:var(--sp-2);font-size:var(--t-xs);color:var(--tertiary)}
@media(min-width:768px){footer.disclaimer{margin-bottom:var(--sp-7)}}

/* Phase-0 mismatch fix: ui.js emits short class names (.ci/.ct/.cb callout,
   .bt/.bb/.ba banner, .ei/.et/.eb empty, .at/.ac/.av accordion head) while the rules
   above were written for the long names. Alias the short names here.
   DEC-2: these spans keep their markup byte-for-byte; only their CONTENT changes from a
   text node to an inline <svg>, which is why every rule below sizes a box and sets
   `color` rather than `font-size` alone — ui.icon paints in currentColor. */
.callout .ct{font-family:var(--font-serif);font-size:var(--t-lg);font-weight:400;color:var(--ink)}
.callout .cb{color:var(--ink2);margin-top:var(--sp-1)}
.callout .ci{flex:none;width:18px;height:18px;margin-top:2px;font-size:var(--t-base);line-height:1.1;color:var(--muted)}
.callout.good .ci{color:var(--green)}
.callout.warn .ci{color:var(--gold)}
.callout.bad .ci{color:var(--red)}
.banner .bt{font-weight:600;font-size:var(--t-base)}
.banner .bb{color:var(--ink2);font-size:var(--t-sm);margin-top:2px}
.banner .ba{margin-left:var(--sp-2);background:none;border:1px solid var(--line2);color:var(--ink);
  border-radius:var(--r-sm);padding:0 var(--sp-3);min-height:44px;font-size:var(--t-sm);font-weight:600;cursor:pointer}
.banner .ba:hover{background:var(--card2);border-color:var(--ink2)}
.empty .ei{width:24px;height:24px;margin:0 auto;font-size:var(--t-lg);line-height:1;color:var(--tertiary)}
.empty .et{font-family:var(--font-serif);font-size:var(--t-lg);font-weight:400;color:var(--ink);margin-top:var(--sp-3)}
.empty .eb{font-size:var(--t-base);margin-top:var(--sp-2);max-width:42ch;margin-left:auto;margin-right:auto;line-height:1.6}
.empty .btn{margin-top:var(--sp-4)}
.acc-head .at{font-family:var(--font-serif);font-size:var(--t-lg);font-weight:400}
.acc-head .ac{display:block;font-size:var(--t-sm);color:var(--muted);font-weight:400;margin-top:2px}
.acc-head .av{width:14px;height:14px;flex:none;color:var(--muted);transition:transform .18s var(--ease)}
.acc-item.open .acc-head .av{transform:rotate(180deg)}

/* ---- §7.4 shared classes the module agents' own `css` exports do NOT emit -------------
   widgets.js and charts.js each export a `css` string that app.js injects once at boot
   (boot(): ui.style('widgets'|'charts', mod.css)), and between them they already ship every
   other row of the §7.4 table: .tier-* / .snap-strip / .qcard* / .explain* / .gap-row +
   .gap-sev / .gap-fix / .evt-* / .sample-tag / .perm / .convo* (widgets.js) and the whole
   .lt-* life timeline incl. its half of the narrow-screen scroll (charts.js). Those stay in
   their own modules; only what neither emits is typed here.
   `.tier-trio.stale` deliberately has no rule of its own — §7.4 asks for "the §4 `.stale`
   treatment" and §4's `.stale` (opacity, above) already matches the trio's own element,
   so a duplicate here would be a second source for one look. */

/* decision grid (§4.4): ten question cards. The 700px breakpoint joins the tier list at
   600px (§3.6). The answer card itself (.qcard / .qcard-verdict / .qcard-metrics /
   .qcard-chip) comes from widgets.js. */
.q-grid{display:grid;grid-template-columns:1fr;gap:var(--sp-2);margin-top:var(--sp-4)}
@media(min-width:600px){.q-grid{grid-template-columns:repeat(2,1fr)}}
.q-ask{display:block;font-size:var(--t-sm);color:var(--muted);margin-top:2px;line-height:1.5;white-space:normal}

/* gap severity accent (§7.4): the dot itself is widgets.js's `.gap-sev.badge.<tone>`; the row's
   own high/medium/low rule is the integrator's half, so the two never drift apart. */
.gap-row.high{border-left:3px solid var(--gold)}
.gap-row.medium{border-left:3px solid var(--blue)}
.gap-row.low{border-left:3px solid var(--line2)}

/* ==== v4.2 file layer (integrator; FILE-SPEC §7.5) =====================================
   Additive, and the only shared rule this work needs: every other class the four new screens
   emit is scoped under `.s-open` / `.s-update` / `.pf-file` / `.s-start` and ships inside that
   screen's own `css` export, which app.js and plan.js inject as `screen-<id>`.

   Four states, one per §5.4 row. `saved` is the bare class and carries no modifier, because the
   quiet state is the resting state. `never` and `problem` are dashed and gold-bordered
   respectively — never red, never a dot, never animated: a status that shouts is a nag.

   §4.22 — BOTH CHIPS ARE CONTROLS (they are <button>s), so both take the 44px minimum. They
   were 28px and ~26px. They still ship centred here; moving them into a right-aligned utility
   row inside the top nav is §7.1's job and needs app.js's data-screen line first.

   The border is --line2, NOT the --line the §4.22 code block prints. §4.22's snippet contradicts
   §1.4, and §1.4 is the rule that is stated as absolute: "if a boundary tells you where to click,
   it is --line2". These two chips are unhoused — background:none, no container — so their 1px
   border is their ONLY boundary, and --line #3A342E over that ground is 1.33:1 on --card and
   1.57:1 on --bg: below the 3:1 UI floor and, in practice, invisible. --line2 #7D746B measures
   4.21:1 on --bg (the ground they actually sit on, under the header) and 3.56:1 on --card, so
   both clear the floor on either surface. --line stays what its own token comment says it is:
   a decorative divider inside an already-bounded region, never a sole boundary.

   Hover therefore cannot stay --line2 any more (it would now be a no-op against the resting
   state). It goes to --ink2, which is exactly what every other bounded control in this file
   already does on hover: .chip, .btn, .btn.ghost, .select, .synccard-choice, .banner .ba. */
.filechip,.authchip{display:inline-flex;align-items:center;min-height:44px;padding:0 var(--sp-3);
  border-radius:var(--r-sm);border:1px solid var(--line2);background:none;color:var(--muted);
  font-size:var(--t-xs);font-weight:600;cursor:pointer;transition:.12s var(--ease);
  max-width:calc(100% - 32px);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.filechip{margin:var(--sp-2) auto 0}
.authchip{margin:0 auto var(--sp-2)}
.filechip:hover,.authchip:hover{color:var(--ink);border-color:var(--ink2)}
.filechip.dirty{color:var(--ink)}
.filechip.never,.filechip.unsaved,.authchip.signedOut{border-style:dashed}
.filechip.problem,.authchip.offline{color:var(--gold);border-color:var(--gold)}
.authchip.syncing{color:var(--ink2)}
.filechip:focus-visible,.authchip:focus-visible{outline:none;box-shadow:var(--focus-ring)}
@media (max-width:600px){.filechip,.authchip{margin-bottom:var(--sp-2)}}

/* ==== v4.4 account layer (ACCOUNTS-SPEC §8.6, §6.5, §9.4) ================================= */

/* The two unskippable cards (§6.5's conflict, §9.4's claim). Three choices, equal visual weight,
   none preselected — F27's rule made visible: nothing here reads as "the one you were going to
   press anyway", because the whole point is that only the person can answer it. */
.synccard-host:empty{display:none}
.synccard{max-width:640px;margin:0 auto var(--sp-4);border-left-width:3px;border-left-color:var(--gold)}
.synccard-t{margin:0 0 var(--sp-2);font-family:var(--font-serif);font-size:var(--t-lg);font-weight:400}
.synccard-b{margin:0 0 var(--sp-1);color:var(--ink2);line-height:1.6;max-width:62ch}
.synccard-d{margin:0 0 var(--sp-3);color:var(--ink3);font-size:var(--t-xs)}
.synccard-choice{display:block;width:100%;margin-top:var(--sp-2);padding:var(--sp-3) var(--sp-4);
  text-align:left;min-height:44px;border:1px solid var(--line2);border-radius:var(--r-md);
  background:var(--card2);cursor:pointer;transition:.12s var(--ease)}
.synccard-choice:hover{border-color:var(--ink2)}
.synccard-choice:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.synccard-choice-t{display:block;color:var(--ink);font-weight:600;font-size:var(--t-sm)}
.synccard-choice-h{display:block;margin-top:2px;color:var(--ink3);font-size:var(--t-xs);line-height:1.5}

/* The account screen's rows. */
.s-account .acct-wrap{max-width:620px;margin:0 auto;padding:0 var(--sp-1)}
.s-account .acct-card{margin-bottom:var(--sp-4)}
.s-account .acct-h{margin:0 0 var(--sp-2);font-family:var(--font-serif);font-size:var(--t-lg);font-weight:400}
.s-account .acct-p{margin:0 0 var(--sp-3);color:var(--ink2);font-size:var(--t-sm);line-height:1.6;max-width:62ch}
.s-account .acct-meta{margin:0 0 var(--sp-3);color:var(--ink3);font-size:var(--t-xs)}
.s-account .acct-list{margin:0;padding:0;list-style:none}
.s-account .acct-row{display:flex;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-2) 0;
  border-top:1px solid var(--line);font-size:var(--t-sm)}
.s-account .acct-row:first-child{border-top:0}
.s-account .acct-new{color:var(--gold)}
.s-account .acct-error:empty{display:none}
.s-account .rc-code{display:block;margin:var(--sp-3) 0;padding:var(--sp-4) var(--sp-3);
  border:1px solid var(--line2);border-radius:var(--r-md);background:var(--card2);
  font-family:var(--font-mono);font-size:var(--t-md);letter-spacing:.06em;text-align:center;
  word-break:break-all;user-select:all}

/* ==== §6.5 / §7.1 — THE GLOBAL HEADER, gated on body[data-screen] ==========================
   Added by the SHELL pass (index.html + app/app.js), because these three rules and app.js's
   `document.body.dataset.screen = screen.id` are one change: neither half does anything alone,
   and the CSS block at line ~877 above says in as many words that this work "needs app.js's
   data-screen line first". If the styles pass also lands §6.5 verbatim, these rules are the
   identical declarations and either copy may be deleted — but only one of them.

   THE NEGATION IS THE POINT. `body:not([data-screen="landing"])` matches a <body> that carries no
   attribute at all, which is every body before the first `renderRoute()` has run. So the
   marketing header is hidden from the FIRST PAINT rather than from the first frame after boot,
   and a cold load of #/home never flashes 165px of monogram-wordmark-tagline above the user's own
   portfolio. Measured signed-in chrome above the content: 287px (header 165 + #nav 122) → 56px.

   On the landing the same element becomes the 56px utility bar of §6.6's wireframe: flush left,
   centred in the same 1180px measure as .topnav, no longer a centred marketing block. The
   tagline is not hidden here because it is no longer in the markup — index.html deleted it, so it
   is said once, by the hero, on the one page that sells (V6). */
body:not([data-screen="landing"]) header{display:none}
body[data-screen="landing"] header{display:flex;align-items:center;justify-content:space-between;
  max-width:1180px;margin-inline:auto;text-align:left;min-height:56px;
  padding:calc(var(--sp-2) + var(--safe-t)) calc(var(--sp-4) + var(--safe-r))
          var(--sp-2) calc(var(--sp-4) + var(--safe-l))}
body[data-screen="landing"] .monogram{width:32px;height:32px;font-size:16px}

/* §9.2 — the footer's own bottom inset. Every other full-bleed container already spends
   --safe-b (.layout:247, .bottomnav:226, #toast-root:188); the disclaimer, which is a SIBLING of
   <main> and therefore outside .layout's padding, spent none — so on a notched iPhone its last
   line sat under the home indicator. It needs the inset at both breakpoints: below 768 the fixed
   tab bar is over it, and above 768 the only iPhones that wide are in landscape, where the home
   indicator is still there. */
footer.disclaimer{margin-bottom:calc(var(--sp-9) + var(--safe-b))}
@media(min-width:768px){footer.disclaimer{margin-bottom:calc(var(--sp-7) + var(--safe-b))}}
