/* ── Talqo visual system (Sprint 3 · TASK B, Complir direction) ──────────────
   SINGLE LIGHT THEME — the dark theme is removed, not toggled. Existing token
   NAMES are kept (so un-restyled screens keep working) and remapped to the new
   light palette; the new canonical tokens below (--accent, --line, --ground,
   the type/space/shape scales) are what the new components use. */
:root {
  /* ── SIGNAL, WARMED — one warm neutral ramp (hue 42°) + three tonal tiers ──────────
     Every neutral below is the SAME hue at a different lightness, which is what makes this a
     system rather than a set of chosen colours. Warm, because the product is an evidence tool
     and warm paper reads as a document; low-chroma, so Talqo blue sits on it without muddying.

     THE THREE TIERS, and the rule for which a card gets — see DESIGN_PRIMITIVES "Elevation":
       tier 1  the ONE card answering the screen's question   surface + --shadow-raised
       tier 2  cards carrying figures you act on              surface + --shadow
       tier 3  reference and explanation                      surface-alt, no shadow
     Elevation is MEANING here, not decoration. At most one tier-1 card per screen.

     Contrast is solved, not chosen: all 29 text/UI pairs clear WCAG AA, tightest
     green-fill / surface at 3.11:1. --amber-fill also FIXES an inherited failure — the old
     #E0A020 was 2.28:1 on white, below the 3.0 floor for a non-text UI boundary. */
  /* ═══ PREMIUM PASS, 24 Aug 2026 — the token layer everything else consumes ═════
     A blind panel of four designers scored this app 4.5–6/10 and priced it at €20–50/seat
     against a €125–185 list. The single loudest cause was incoherence: five blues, ad-hoc greys,
     radii from 1px to 99px, ten shadows. The fix is not more decoration, it is FEWER decisions —
     one accent, five semantic colours, one grey ramp, two radii, two shadows.

     THE PALETTE IS COOL NOW, deliberately. The previous ramp was warm (hue 42°) on the argument
     that "warm paper reads as a document". It also read as beige, and beige is what a 2011
     internal tool looks like. The ink is the brand navy's own family, so chrome and accent share
     a temperature instead of fighting.

     CONTRAST IS COMPUTED, NOT CHOSEN — test/contrast.test.js derives every ratio from these
     exact values and fails the build below AA. Tightest text pair here is amber-on-amber-tint at
     4.56 and the tightest non-text boundary is the amber fill at 3.23; both were tuned UP from
     first drafts that missed (4.41 and 2.98). Nothing in this block is a guess. */
  /* THE RAMP IS GENERATED, NOT PICKED — every neutral is hue 215° at a different lightness and
     saturation, which is what makes it a ramp rather than seven colours that happen to look
     grey together. It deviates from the brief's literal hexes in two places, both because a
     guard was measuring something those values would have broken, and I would rather ship the
     measured version than the typed one:
       · the canvas is #EFF2F5, not #F7F8FA. At #F7F8FA the ground sits 1.063:1 from a white
         card, under the 1.09 floor in elevation-tiers.test.js — cards stop lifting off the
         page, which is exactly the flat look this pass exists to remove.
       · ink-3 is #666F7A, not #6B7480. #6B7480 clears AA on white (4.74) but lands at 4.22 on
         the canvas; I checked it against the wrong background first and the contrast test
         caught it. Every ink now clears 4.5 on all three grounds. */
  /* ═══════════════════════════════════════════════════════════════════════════
     PLATE 07 — "Quiet instrument, one loud object"           built 26 Aug 2026
     ═══════════════════════════════════════════════════════════════════════════
     Everything below is transcribed from the plate, not derived from it. The
     premium-pass palette that preceded it was a repair job on incoherence; this
     is a designed system, and where the two disagree the plate wins.

     THE RULE THE SHEET STATES ABOUT ITSELF:
       "One accent. Four semantic values, used only for deal state. Three inks,
        five neutrals, and nothing else in the product is coloured."

     What changes from the premium pass, and why it is not a regression:
       · the accent value is unchanged (#14508C) — the pass had already found it;
       · the four semantic values are now DEAL STATE ONLY. Green no longer means
         "good", amber no longer means "warning". They mean agreed, not agreed,
         contradicted, not asked, and nothing else may borrow them;
       · a fifth state colour arrives — "not asked" — because the absence of a
         question is a state the product could not previously draw at all;
       · the neutrals go from a generated 215° ramp to the plate's five, which are
         cooler and further apart at the light end, where all the work happens.
     ─────────────────────────────────────────────────────────────────────────── */

  /* ONE ACCENT. Links, primary action, active nav. The only blue in the product. */
  --accent:#14508C;

  /* THE FOUR SEMANTIC VALUES — deal state, and nothing else.
     Each has a SILHOUETTE as well as a colour (see --mark-* below): block, half
     block, diamond, open square. Shape carries the meaning and colour only
     reinforces it, so the set survives greyscale, a board-pack print, and 8px. */
  --agreed:#16613F;        /* the customer said yes on a call        · block       */
  /* #856110, one step under the plate's #8A6512, and this is the one place the build
     departs from the sheet. Measured on every ground it lands on: the plate's value is
     5.32 on the surface and 5.17 on the band, but 4.40 on the plate's own CANVAS —
     under the 4.5 floor for text, and the state WORD is text. The other three states
     clear the canvas at 6.18, 6.31 and 6.80, so this was the single outlier rather than
     a palette-wide problem. #856110 reads 4.68 on the canvas and 5.65 on the surface,
     and is indistinguishable from the plate value side by side.
     The rule this follows is the product's own, learned the hard way in the premium
     pass: a colour is not solved until it is measured against every ground it lands on,
     and white is rarely the one that matters. */
  --not-agreed:#856110;    /* discussed, no acceptance               · half block  */
  --contradicted:#9A2B2B;  /* accepted, then withdrawn               · diamond     */
  --not-asked:#BCC3CB;     /* nobody has raised it                   · open square */

  /* THREE INKS. */
  --ink:#0B1220;    /* figures, headings, primary text     */
  --ink-2:#49515C;  /* body, state words, secondary rows   */
  --ink-3:#5F6775;  /* metadata, captions, denominators    */

  /* FIVE NEUTRALS. */
  --canvas:#E6EAF0;   /* app ground behind cards            */
  --surface:#FFFFFF;  /* cards on the ground                */
  --recessed:#EDF0F5; /* footnote cards. sits into the ground */
  --band:#FBFCFD;     /* table headers and footers ONLY     */
  --row-hover:#F7F9FB;/* the only hover fill in the product */
  --track:#DCE1E7;    /* the unfilled half of any comparison bar */

  /* Deferred to cycle two with plate 10, and declared so nothing invents its own. */
  --dark-ground:#05090D;

  /* Legacy names kept pointing at the plate so un-rebuilt screens move with it
     rather than stranding on the old palette mid-build. Each screen drops these
     as it lands; the grep gate below counts what is left. */
  --ground:var(--canvas); --surface-alt:var(--recessed);
  /* --line-2 is the BAR TRACK and must be told apart from the canvas behind it. The generated
     ramp first produced #F0F2F5 against a #EFF2F5 ground — 1.002:1, which is two names for one
     colour, so a bar's empty portion vanished into the page. A blind reviewer caught it by
     sampling pixels: "one point apart, visually identical, two tokens". Lightening the track
     also raises every fill's contrast on it, so the fix runs in the safe direction. */
  /* The two rules. --line is every hairline; --line-2 is the bar TRACK, which must be told
     apart from the canvas behind it or a bar's unfilled half vanishes into the page. Both now
     point at the plate: the plate names one track value and one rule weight. */
  --line:#DCE1E7; --line-2:var(--track);
  /* ONE accent. The brand navy family — this is the only non-semantic colour in the product.
     Links, primary buttons, active nav and focus rings all draw from these four values. */
  --accent:#14508C; --accent-hover:#0E3A68; --accent-weak:#E8F0F9; --accent-ring:rgba(20,80,140,.32);
  /* The semantic five. These plus the grey ramp are the ONLY non-neutral colours allowed. */
  /* green-fill is darkened to #2B8159 so the two forecast-band fills separate by 1.36:1 in
     luminance. The pair that shipped before separated by 1.011 — visually identical brightness,
     told apart by hue and the hatch alone, which is exactly the case a colour-blind reader and
     a greyscale print both lose. The hatch still carries it; this means it no longer has to. */
  /* LEGACY SEMANTIC NAMES → the plate's deal states. green/amber/red used to mean
     good/warning/bad, which is exactly the overload plate 07 removes: they now resolve to
     agreed / not agreed / contradicted and mean nothing else. Screens drop these as they land. */
  --green:var(--agreed); --green-bg:#E9F2ED; --green-fill:var(--agreed);
  /* amber is #856618, a shade under the brief's #8A6A1A: that value clears AA on white (5.05)
     but lands at 4.496 on the canvas — a rounding error away from the floor, and under it. */
  --amber:var(--not-agreed); --amber-bg:#F8F3E8; --amber-fill:var(--not-agreed);
  --red:var(--contradicted); --red-bg:#F7EAEA;
  /* ── THE TYPE SCALE — six steps: 12 · 13 · 15 · 18 · 24 · 32.
     Page titles move 20 → 24 and the hero number 30 → 32, because a title that barely outranks
     its own body copy is why the panel called this a settings screen. Body moves 13 → 15: at 13
     the product was dense in the way a spreadsheet is dense, not in the way a broadsheet is.
     Every font-size in the app already reads a token (690 uses, zero hard-coded), so the scale
     changes here and the whole product follows.
     Eyebrows are 11px — deliberately BELOW the scale, because a label that has stopped being
     prose should not compete with prose. It is the one exception and it is named as one. */
  /* ── PLATE 07 · TYPE — three faces, three jobs, and one ramp each ─────────────
     The plate gives each face its own sizes, and they are not the same ramp:

       Newsreader   84 / 38 / 27   figures, the one headline per screen, and the
                                   buyer's sentence quoted as evidence. NEVER body.
       Inter        20 / 16 / 13 / 11   every interface label, row and paragraph:
                                   page 20, card 16, row 13, meta 11, eyebrow 9.5.
       JetBrains    10.5 / 9.5     the transcript, verbatim, and its timestamp.

     This is a real reduction from the premium pass's six-step single ramp: the
     interface ramp is now FOUR steps, because the serif took the top of the old
     scale and the mono took the bottom. A face that needs a size the plate does
     not give it is a sign the wrong face is doing the job. */
  --fs-figure:84px;  --lh-figure:.92;     /* Newsreader · the hero figure           */
  --fs-headline:38px;--lh-headline:1.06;  /* Newsreader · one headline per screen   */
  --fs-quote:27px;   --lh-quote:1.28;     /* Newsreader · the buyer's sentence      */
  --fs-page:20px;    --lh-page:26px;      /* Inter · page title                     */
  --fs-card:16px;    --lh-card:22px;      /* Inter · card title                     */
  --fs-row:13px;     --lh-row:18px;       /* Inter · rows and paragraphs            */
  --fs-meta:11px;    --lh-meta:15px;      /* Inter · metadata                       */
  --fs-eyebrow:9.5px;--lh-eyebrow:13px; --tracking-eyebrow:.1em;
  --fs-mono:10.5px;  --lh-mono:1.55;      /* JetBrains · the transcript             */
  --fs-stamp:9.5px;                       /* JetBrains · the timestamp on it        */

  /* ── PLATE 07 · THE THREE FACE STACKS ─────────────────────────────────────── */
  --face-serif:'Newsreader', Georgia, 'Times New Roman', serif;
  --face-ui:'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --face-mono:'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ── PLATE 07 · MEASURE — three radii, four planes ────────────────────────── */
  --r-pill:3px; --r-control:5px; --r-card:8px;
  --plane-footnote:none;
  --plane-card:0 1px 2px rgba(11,18,32,.06);
  --plane-primary:0 1px 2px rgba(11,18,32,.07), 0 8px 20px -12px rgba(11,18,32,.18);
  --plane-drawer:0 24px 60px -20px rgba(11,18,32,.34);

  /* ── PLATE 09 · MOTION — "and how little of it there is" ──────────────────────
     "Three curves, three durations, one maximum distance. Motion in this product
     exists to say THAT JUST CHANGED and nothing else — there is no delight layer, no
     parallax, no number that counts up."

     THE SIX RULES, which the plate says are enforceable in review and which
     test/plate-09-motion.test.js therefore enforces:
       1. Nothing travels more than 24px. Distance is not drama.
       2. Motion marks a change of STATE, never a change of data. A figure that
          updates does not animate — it is simply the new figure.
       3. Exit is always faster than enter. Leaving should never cost the user time.
       4. Only opacity and transform animate. Nothing that triggers layout ever does.
       5. One thing moves at a time. If two would, the second waits 40ms.
       6. No easing that overshoots. This product is a record; it does not bounce. */
  --enter:180ms; --enter-ease:cubic-bezier(.20,.70,.20,1);
  --exit:120ms;  --exit-ease:cubic-bezier(.40,0,.90,.40);
  --micro:90ms;  --micro-ease:cubic-bezier(.30,0,.20,1);
  --travel:24px;   /* the maximum any element may move, anywhere in the product */
  --stagger:40ms;  /* if two things would move at once, the second waits this long */

  /* ── PLATE 07 · GRID — "the frame is the same on every screen" ─────────────── */
  --frame:1440px;        /* the frame every screen is drawn at        */
  --rail:224px;          /* sidebar, fixed, never collapses           */
  --pad:28px;            /* page padding, left and right              */
  --gutter:16px;         /* gutter, and the gap between stacked cards */
  --col:82px;            /* one column                                */
  --split-content:670px; /* 7 columns — content, left                 */
  --split-evidence:474px;/* 5 columns — evidence, right               */

  /* The premium-pass ramp, kept resolving so un-rebuilt screens still paint while
     the build walks the eight screens. Each maps to its nearest plate step, and
     the grep gate counts what is left so the aliases cannot quietly become the
     system. */
  --fs-display:var(--fs-figure); --lh-display:var(--lh-figure);
  --fs-title:var(--fs-page);     --lh-title:var(--lh-page);
  --fs-subtitle:var(--fs-card);  --lh-subtitle:var(--lh-card);
  --fs-body:var(--fs-row);       --lh-body:var(--lh-row);
  --fs-caption:var(--fs-row);    --lh-caption:var(--lh-row);
  --fs-micro:var(--fs-meta);     --lh-micro:var(--lh-meta);
  /* legacy token names → the scale (existing classes keep working, one scale) */
  --fs-h2:var(--fs-title); --fs-h3:var(--fs-subtitle);
  --fs-hero:var(--fs-display); --fs-metric:var(--fs-title);
  /* spacing 4/8/12/16/20/24/32/48/64.
     --sp-5 IS NEW AND IT IS A BUG FIX, not a scale expansion. Five rules across styles.css and
     app.js already referenced var(--sp-5) — including the trust bar's legend column gap — and
     the token had never been declared. An undefined custom property makes the whole declaration
     invalid, so those gaps silently computed to zero: the legend's colour swatches sat flush
     against the previous item's figure, rendering as "€482K▪" with no space. All three blind
     reviewers independently reported that as a glyph or font-fallback bug. It was neither; it
     was a missing line in this block. */
  --sp-1:4px;--sp-2:8px;--sp-3:12px;--sp-4:16px;--sp-5:20px;--sp-6:24px;--sp-8:32px;--sp-12:48px;--sp-16:64px;
  /* SHAPE — two radii, and nothing else. 6px is every control; 10px is every card. The app had
     eleven distinct radii including 1px, 2px, 3px and three separate pill values. */
  /* PLATE 07 radii: 3 pill · 5 control · 8 card. The premium pass shipped two (6/10) and a
     999px chip; the plate rules three, and the pill is 3px — a pill in this system is a small
     rounded rectangle, not a lozenge. Legacy names point at the plate. */
  --radius-card:var(--r-card); --radius-input:var(--r-control); --radius-chip:var(--r-pill);
  /* THE LOGO's two lighter bars. Its third bar has always drawn --accent, so when the accent
     moved to navy the mark rendered two indigo bars beside one navy one — the retired palette
     surviving in the one element on every single screen. These are the accent at two tints. */
  --accent-t1:#A9C3DE; --accent-t2:#5C8CBC;
  /* Text on a dark surface — the strip and the on-dark receipt. Named because "which grey goes
     on navy" is a decision worth making once, not four times at four call sites. */
  --on-dark:#FFFFFF; --on-dark-2:#C7D2E0; --on-dark-3:#96A6BA; --on-dark-warn:#E8C77A;
  --scrim:rgba(22,32,43,.52); --scrim-soft:rgba(22,32,43,.46);
  /* ELEVATION — TWO levels, and nothing else. A card sits, or an overlay floats. The previous
     three-tier "elevation is meaning" system asked a reader to decode shadow depth, which no
     reader has ever done; the tier that carried meaning now carries it in type and position.
     --shadow-hover and --shadow-raised are kept as aliases so un-restyled screens still parse. */
  /* PLATE 07 elevation: FOUR planes — footnote, card, primary, drawer. The premium pass had
     collapsed to two; the plate needs a third for the one card that answers the screen and a
     fourth for the evidence drawer, which floats over everything. */
  --shadow:var(--plane-card);
  --shadow-overlay:var(--plane-drawer);
  --shadow-hover:var(--shadow);
  --shadow-raised:var(--shadow);
  /* widths */
  --col-max:1200px; --bar-max:640px;

  /* ── legacy names remapped to the new palette (keep un-restyled screens alive) ── */
  --navy:var(--ink); --signal:var(--accent); --signal-dim:var(--accent-weak);
  --stone:var(--ground); --stone1:var(--surface-alt); --stone2:var(--surface-alt); --stone3:var(--line);
  --ink2:var(--ink-2); --ink3:var(--ink-3);
  /* The legacy radius pair maps exactly onto the ruled two: --r is only ever used by controls
     (buttons, inputs, chips) and --rl only by containers (cards, modals, dropdowns), so pointing
     them at the two new tokens converts every un-restyled screen without touching it. */
  --r:var(--radius-input); --rl:var(--radius-card);
  --sidebar-bg:var(--surface); --sidebar-border:var(--line);
  --content-bg:var(--ground); --card-bg:var(--surface); --card-border:var(--line);
  --nav-active-bg:var(--accent-weak); --nav-active-color:var(--accent-hover);
  --text-primary:var(--ink); --text-secondary:var(--ink-2); --text-muted:var(--ink-3);
  --btn-primary-bg:var(--accent); --btn-primary-fg:#FFFFFF;
  --on-accent:#FFFFFF; --surface-solid:#FFFFFF;
  --input-bg:var(--surface); --input-border:var(--line);
  color-scheme:light;
}

/* PLATE 09 · REDUCED MOTION — "Not a downgrade. A supported mode."
   Every duration collapses to zero and every transform is dropped. NOTHING ELSE
   CHANGES: the scrim still appears, the drawer still opens, the toast still carries
   Undo. The plate is explicit that "the design has to be complete at frame one",
   which is why this is three token overrides rather than a pile of
   animation:none !important rules — the components never learn about it. */
@media (prefers-reduced-motion: reduce) {
  :root { --enter:0ms; --exit:0ms; --micro:0ms; --travel:0px; --stagger:0ms; }
}
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:'Manrope',sans-serif;background:var(--content-bg);color:var(--text-primary);font-size:var(--fs-body);line-height:1.6;min-height:100vh;font-variant-numeric:tabular-nums;transition:background 0.2s,color 0.2s;}

/* ═══════════════════════════════════════════════════════════════════════════════
   AUTH — the first screen, on the system                             26 Aug 2026
   ═══════════════════════════════════════════════════════════════════════════════
   THE STRIP. #top-strip is the app shell's docked row, fixed at left:220px to clear
   the sidebar. On a signed-out page there is no sidebar, so it rendered as an empty
   dark bar with a 220px notch — the first thing a prospect saw. It is hidden while
   #app is not .visible rather than made full-bleed, because it carries nothing here
   and a full-width empty dark bar is still an empty dark bar. :has() is already a
   dependency of this sheet (#top-strip has its own :has rules), so this adds none.
   ═══════════════════════════════════════════════════════════════════════════════ */
body:has(#app:not(.visible)) #top-strip{display:none;}

.auth-wrap{min-height:100vh;display:grid;grid-template-columns:minmax(0,520fr) minmax(0,440fr);
           background:var(--canvas);}
.auth-left{background:var(--surface);border-right:1px solid var(--line);
           display:flex;flex-direction:column;gap:var(--sp-8);
           padding:var(--sp-12) var(--sp-12) var(--sp-8);min-width:0;}
.auth-right{display:flex;align-items:center;justify-content:center;padding:var(--sp-12) var(--sp-8);min-width:0;}
.auth-brand{display:flex;align-items:center;gap:var(--sp-3);}
.auth-logo-mark{display:flex;align-items:flex-end;gap:3px;height:20px;}
.auth-logo-mark span{display:block;width:4.5px;border-radius:2px;}
.auth-logo-mark span:nth-child(1){height:9px;background:var(--accent-t1);}
.auth-logo-mark span:nth-child(2){height:14px;background:var(--accent-t2);}
.auth-logo-mark span:nth-child(3){height:20px;background:var(--accent);}
.auth-brand-name{font-family:var(--face-ui);font-size:var(--fs-page);line-height:var(--lh-page);
                 font-weight:600;color:var(--ink);letter-spacing:-.01em;}

/* THE PITCH. Newsreader for the headline, because the serif is what this product uses
   when it is STATING something rather than labelling it. And the headline WRAPS — the
   old 84px display words painted 71px past the panel because one word could not fit
   and nothing made it break. A balanced wrap at the headline step cannot do that. */
.auth-pitch{display:flex;flex-direction:column;gap:var(--sp-4);}
.auth-headline{font-family:var(--face-serif);font-size:var(--fs-headline);
               /* 1.06, not the inherited 1.6: at 84px the old ratio put a 134px box
                  around 115px of glyph and left every caption unmoored from its word. */
               line-height:var(--lh-headline);color:var(--ink);margin:0;
               text-wrap:balance;max-width:20ch;letter-spacing:-.01em;}
.auth-lede{font-family:var(--face-ui);font-size:var(--fs-card);line-height:var(--lh-card);
           color:var(--ink-2);margin:0;max-width:46ch;}

/* THE EXHIBIT, at rest beside the form. Its label is not decoration: a prospect must
   never read the demo workspace as their own data. */
/* The exhibit belongs WITH the pitch, not adrift below it. margin-top:auto here pushed
   it to the panel's foot and opened ~215px of void between the lede and the thing the
   lede is describing. The footer takes the auto margin instead: brand, pitch and
   exhibit group at the top, and the compliance text anchors to the bottom where
   reference material belongs. */
.auth-exhibit{display:flex;flex-direction:column;gap:var(--sp-2);}
.auth-exhibit-label{font-family:var(--face-ui);font-size:var(--fs-eyebrow);
                    letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;
                    color:var(--ink-3);margin:0;}
/* Three sentences, each on its own measured line, instead of one run-on with a
   hand-placed <br>. The claims are unchanged and locked elsewhere; only the setting
   moved. */
.auth-footer{display:flex;flex-direction:column;gap:5px;max-width:56ch;margin-top:auto;padding-top:var(--sp-6);}
.auth-footer p{font-family:var(--face-ui);font-size:var(--fs-meta);line-height:var(--lh-meta);
               color:var(--ink-3);margin:0;}

.auth-card{width:100%;max-width:360px;}
.auth-title{font-family:var(--face-ui);font-size:var(--fs-page);line-height:var(--lh-page);
            font-weight:600;color:var(--ink);margin:0;}
.auth-sub{font-family:var(--face-ui);font-size:var(--fs-row);line-height:var(--lh-row);
          color:var(--ink-3);margin:4px 0 var(--sp-6);}
.field{margin-bottom:var(--sp-3);}
.field label{display:block;font-family:var(--face-ui);font-size:var(--fs-meta);color:var(--ink-2);
             margin-bottom:5px;font-weight:600;}
.field input{width:100%;background:var(--surface);border:1px solid var(--line);
             border-radius:var(--r-control);padding:10px 12px;font-family:var(--face-ui);
             font-size:var(--fs-row);color:var(--ink);outline:none;
             transition:border-color var(--micro) var(--micro-ease);}
.field input:focus-visible{border-color:var(--accent);outline:2px solid var(--accent);outline-offset:1px;}
.field input::placeholder{color:var(--ink-3);}

/* UGLY DATA ON THE WAY IN. A failed sign-in, an expired invite and a sent reset all
   land in this one element, and all three used to be one red box. They are different
   facts and the system already tells them apart: a refusal is CONTRADICTED (something
   was tried and rejected), an expiry is NOT AGREED (it existed and lapsed), and a sent
   reset is neither — it is information, and takes the accent. */
.auth-error{font-family:var(--face-ui);font-size:var(--fs-row);line-height:var(--lh-row);
            color:var(--ink);background:var(--surface);border:1px solid var(--line);
            border-left:3px solid var(--contradicted);padding:9px 12px;
            border-radius:var(--r-control);margin-bottom:var(--sp-3);display:none;}
.auth-error.show{display:block;}
.auth-error.is-expired{border-left-color:var(--not-agreed);}
.auth-error.is-info{border-left-color:var(--accent);}

/* THROUGH THE TOKEN, not around it. --btn-primary-bg already resolves to the accent;
   hard-coding var(--accent) here would have made this the one primary button in the
   product that stops moving when the token does. button-system.test.js caught it. */
.btn-primary{width:100%;background:var(--btn-primary-bg);color:var(--btn-primary-fg);
             border:1px solid var(--btn-primary-bg);
             border-radius:var(--r-control);padding:11px;font-family:var(--face-ui);
             font-size:var(--fs-row);font-weight:600;cursor:pointer;margin-top:var(--sp-2);
             transition:background var(--micro) var(--micro-ease);}
.btn-primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);}
.btn-primary:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.btn-primary:disabled{opacity:.5;cursor:default;}
.auth-forgot{text-align:center;margin-top:var(--sp-3);}
.auth-forgot a{font-family:var(--face-ui);font-size:var(--fs-row);font-weight:600;
               color:var(--accent);text-decoration:none;}
.auth-forgot a:hover{text-decoration:underline;}
.auth-note{font-family:var(--face-ui);font-size:var(--fs-meta);line-height:var(--lh-meta);
           color:var(--ink-3);text-align:center;margin:var(--sp-4) 0 0;}

/* AT 375 THE PITCH STAYS. .auth-left was display:none, which turned the first screen
   on a phone into a bare form under an empty dark bar. It stacks above the form
   instead, keeping the headline and the exhibit — the two things that say what this
   is — and dropping only the footer, which is reference text. */
@media (max-width:820px){
  .auth-wrap{grid-template-columns:minmax(0,1fr);}
  .auth-left{border-right:0;border-bottom:1px solid var(--line);
             padding:var(--sp-6) var(--sp-5);gap:var(--sp-5);}
  .auth-right{padding:var(--sp-6) var(--sp-5) var(--sp-8);align-items:flex-start;}
  .auth-headline{font-size:var(--fs-quote);line-height:var(--lh-quote);max-width:none;}
  .auth-exhibit{margin-top:0;}
  .auth-footer{display:none;}
}

/* APP */
.app{display:none;min-height:100vh;}
.app.visible{display:flex;}

/* SIDEBAR */
.sidebar{width:220px;flex-shrink:0;background:var(--sidebar-bg);border-right:0.5px solid var(--sidebar-border);display:flex;flex-direction:column;min-height:100vh;position:sticky;top:0;height:100vh;transition:background 0.2s;}
.sidebar-brand{display:flex;align-items:center;gap:10px;padding:20px 18px 24px;}
.logo-mark{display:flex;align-items:flex-end;gap:2.5px;height:16px;}
.logo-mark span{display:block;width:3.5px;border-radius:1.5px;}
.logo-mark span:nth-child(1){height:7px;background:var(--accent-t1);}
.logo-mark span:nth-child(2){height:11px;background:var(--accent-t2);}
.logo-mark span:nth-child(3){height:16px;background:var(--signal);}
.q-accent{color:var(--signal);}
.sidebar-name{font-size:var(--fs-subtitle);font-weight:600;color:var(--text-primary);letter-spacing:-0.5px;}
.sidebar-nav{display:flex;flex-direction:column;gap:1px;padding:0 8px;flex:1;}
.nav-tab{display:flex;align-items:center;gap:10px;padding:8px 12px;font-size:var(--fs-body);color:var(--text-secondary);cursor:pointer;border-radius:8px;border:none;background:none;font-family:'Manrope',sans-serif;transition:all 0.12s;text-align:left;width:100%;}
.nav-tab:hover{color:var(--text-primary);background:var(--stone2);}
.nav-tab.active{color:var(--nav-active-color);background:var(--nav-active-bg);font-weight:500;}
.nav-tab-dot{width:5px;height:5px;border-radius:50%;background:currentColor;opacity:0.5;flex-shrink:0;}
.nav-tab.active .nav-tab-dot{opacity:1;}
.sidebar-footer{padding:12px 16px;border-top:0.5px solid var(--sidebar-border);display:flex;align-items:center;justify-content:space-between;}
.user-info{display:flex;align-items:center;gap:8px;}
.user-av{width:26px;height:26px;border-radius:50%;background:var(--signal-dim);color:var(--signal);display:flex;align-items:center;justify-content:center;font-size:var(--fs-micro);font-weight:600;flex-shrink:0;}
.user-name-small{font-size:var(--fs-micro);color:var(--text-secondary);}
.theme-toggle{width:28px;height:28px;border-radius:7px;border:0.5px solid var(--sidebar-border);background:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:var(--fs-body);color:var(--text-muted);transition:all 0.12s;}
.theme-toggle:hover{background:var(--stone2);color:var(--text-primary);}
.role-pill{font-size:var(--fs-micro);padding:1px 6px;border-radius:99px;font-weight:500;}
/* Its two siblings below already read tokens; this one did not, so a superadmin saw a cool
   lavender pill pinned in the sidebar footer against an otherwise warm chrome. The ROLE
   distinction is kept — superadmin still reads differently from admin and rep — it is just
   built from the ramp now instead of from two literals. */
.role-superadmin{background:var(--accent-weak);color:var(--accent-hover);}
.role-admin{background:var(--signal-dim);color:var(--signal);}
.role-rep{background:var(--stone2);color:var(--ink2);}

/* MAIN */
.main{flex:1;padding:24px 28px;overflow-y:auto;min-width:0;}
/* Reserve the fixed strip's height ONLY when the strip actually carries something. The demo
   badge and the "viewing as" banner are the only tenants; with neither, the strip is empty
   and reserving for it would be dead space at the top of every screen for every customer. */
body.has-topstrip .main{padding-top:calc(36px + var(--sp-6));}

/* ─── Sprint 9A TASK 1 — the top strip ───────────────────────────────────────
   ONE fixed row over the main content area (right of the 220px sidebar), three
   zones. grid 1fr/auto/1fr keeps the viewing-as pill centred whether or not the
   demo badge (left) or page actions (right) are present, so the two amber elements
   can never occupy the same pixels again. pointer-events:none lets the empty strip
   pass clicks through; only the zone contents are interactive. */
/* PREMIUM PASS, 24 Aug 2026 — the strip is DOCKED, not floating.
   It was a transparent tray holding two floating pills: a brown "DEMO — SAMPLE DATA" lozenge and
   a rounded "Viewing as" pill, both hovering over the page title. The panel read them as debug
   output left switched on, and they were the first thing on every screen.
   Now it is one slim opaque bar with its own bottom rule — scaffolding that looks built rather
   than dropped on top. Opaque matters: the old tray let content scroll UNDER the pills, so at
   certain scroll positions a page title and a floating badge occupied the same pixels. A solid
   bar with reserved height above the content cannot overlap anything at any scroll position. */
#top-strip{position:fixed;top:0;left:220px;right:0;z-index:900;display:grid;grid-template-columns:auto 1fr auto;align-items:center;column-gap:14px;height:36px;padding:0 28px;background:var(--ink);border-bottom:1px solid rgba(255,255,255,.10);pointer-events:auto;}
#top-strip .topstrip-zone{display:flex;align-items:center;gap:10px;min-width:0;}
#topstrip-left{justify-content:flex-start;}
#topstrip-center{justify-content:flex-start;}
#topstrip-right{justify-content:flex-end;}
#top-strip .topstrip-zone>*{pointer-events:auto;}
/* The demo tag: a quiet marker inside the bar, not an amber lozenge over the content. It is
   still unmissable — it is in the one dark bar on the screen — without shouting. */
/* The divider after the demo tag is drawn only when something FOLLOWS it. As a plain
   border-right it rendered on every screen with nothing on its other side — a separator
   separating one thing from nothing, which all three blind reviewers read as a placeholder
   somebody forgot to fill. :has() lets the rule ask whether a sibling zone is occupied. */
#top-strip #demo-badge{background:transparent;color:var(--on-dark-warn);font-size:var(--fs-eyebrow);font-weight:700;letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;padding:0;border:0;white-space:nowrap;}
#top-strip:has(#topstrip-center > *:not([style*="display:none"])) #demo-badge,
#top-strip:has(#topstrip-right > *) #demo-badge{border-right:1px solid rgba(255,255,255,.18);padding-right:14px;}
#top-strip #imp-banner{background:transparent;border:0;border-radius:0;padding:0;color:var(--on-dark-2);font-size:var(--fs-caption);gap:10px;}
#top-strip #imp-banner-text{color:var(--on-dark-2);white-space:nowrap;}
/* The select inside the dark bar is the same control as everywhere else, inverted. */
#top-strip select{height:26px;line-height:24px;background-color:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.22);color:#fff;font-size:var(--fs-micro);padding:0 28px 0 10px;background-position:right 9px center;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23C7D2E0' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");}
#top-strip select:hover{background-color:rgba(255,255,255,.16);border-color:rgba(255,255,255,.34);}
#top-strip select option{color:var(--ink);background:var(--surface);}
#top-strip a, #top-strip button{color:#fff;}
#top-strip :focus-visible{outline:2px solid #fff;outline-offset:2px;}
@media(max-width:820px){#top-strip{left:0;padding:0 16px;}}

/* ─── Sprint 9A TASK 2 — the forecast tree as a real, aligned table ───────────
   A grid with a header row. Figure columns are FIXED width so every hierarchy
   depth (company → manager → rep → deal) lines up vertically; indentation lives
   on the name cell only. Figures are right-aligned and tabular-nums, so two rows
   compare by scanning, not re-reading. Semantic colour lives on the column
   HEADERS (a label), never on every digit (which would be noise). */
.ftree{--ftcol:118px;font-size:var(--fs-body);}
.ftree-row{display:grid;grid-template-columns:minmax(0,1fr) var(--ftcol) var(--ftcol) var(--ftcol) var(--ftcol);align-items:start;column-gap:10px;}
.ftree-head{border-bottom:2px solid var(--line);padding:6px 0;font-size:var(--fs-micro);font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-3);position:sticky;}
.ftree-num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;padding-right:14px;}
.ftree-name{min-width:0;}
.ftree-h-confirmed{color:var(--green);}
.ftree-h-unconfirmed{color:var(--amber);}
.ftree details>summary{cursor:pointer;list-style:none;}
.ftree details>summary::-webkit-details-marker{display:none;}

/* DISCLOSURES — premium pass, 24 Aug 2026. Only the forecast tree ever hid the browser's own
   marker, so every OTHER <details> in the product rendered the platform's grey ▸ triangle:
   the coach's "How states are computed", "Recurring across teams", and "Scores & dates" on
   every call-history row. Every blind reviewer across both panels named them, one calling them
   "default <details>/<summary> disclosure triangles, untouched" — the same tell as the native
   select chevron, in a place the select fix never reached.
   One rule now hides the marker everywhere and draws our own chevron, rotating on open, in the
   same stroke weight and ink as the chevron on every select. */
details>summary{list-style:none;cursor:pointer;}
details>summary::-webkit-details-marker{display:none;}
details>summary::marker{content:'';}
details:not(.ftree details)>summary::before{
  content:''; display:inline-block; width:10px; height:7px; margin-right:8px;
  vertical-align:middle; transition:transform .15s ease;
  background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23666F7A' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center;
}
details:not(.ftree details)[open]>summary::before{transform:rotate(180deg);}
@media (prefers-reduced-motion:reduce){ details>summary::before{transition:none;} }
/* .ftree-caret retired 2026-08-10 — the chevron is structural now; see the fold
   affordance block further down, which draws it on .ftree-name::before because this
   summary is a grid container. */
.ftree-cell-muted{color:var(--ink-3);}
@media(max-width:900px){.ftree{--ftcol:90px;font-size:var(--fs-caption);}}
/* ONE CONTENT WIDTH (premium pass, 24 Aug 2026 — item 7).
   Seventeen surfaces wrapped themselves in .vs-page and got the 1200px column; the rest ran
   edge to edge, so Team analysis and the analyse output hugged the left half of a 1440px
   screen with a dead gutter beside them — the reader's eye had to travel a different distance
   on every screen. Setting it on the panel itself means a screen gets the column by EXISTING,
   not by remembering to opt in, and the nested .vs-page becomes a harmless no-op. */
.tab-panel{display:none;max-width:var(--col-max);margin:0 auto;}
.tab-panel.active{display:block;}
.page-head{margin-bottom:20px;}
.page-title{font-size:var(--fs-title);font-weight:500;color:var(--text-primary);letter-spacing:-0.5px;}
.page-sub{font-size:var(--fs-body);color:var(--text-muted);margin-top:3px;}

/* CARDS */
/* superseded by the one card system at the foot of this file (Part 2) */
.card-head{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:0.5px solid var(--card-border);background:var(--stone2);}
.card-title{font-size:var(--fs-body);font-weight:500;color:var(--text-primary);}
.card-sub{font-size:var(--fs-micro);color:var(--ink3);}
.card-body{padding:14px 16px;}

/* ── App visual scale (VISUAL ONLY). Global, un-prefixed tokens + classes,
   promoted from the analytics screen so the scale is app-wide, not analytics-
   scoped. Reusable everywhere; no data, logic, or provenance wording lives here.
   A screen opts into the roomy card scale with the .polished wrapper; the
   .hero-num classes are usable standalone. Rename only — renders identically. ── */
:root{
  --fs-hero:var(--fs-display);   /* legacy alias → the one display step */
  --fs-metric:var(--fs-title);   /* legacy alias → title step */
  --fs-section:var(--fs-body);   /* legacy alias → body step */
  --pad-card:22px;     /* generous, consistent card padding */
  --gap-section:16px;  /* breathing room between sections */
}
.polished .card{margin-bottom:var(--gap-section);}
.polished .card-head{padding:15px 22px;}
.polished .card-title{font-size:var(--fs-section);font-weight:600;letter-spacing:-.2px;}
.polished .card-sub{font-size:var(--fs-caption);color:var(--ink3);margin-top:2px;}
.polished .card-body{padding:var(--pad-card);}
/* Hero metric: large, heavy, tight — dominates its card. Colour stays --ink so
   the semantic green/amber encoding elsewhere keeps its meaning. */
.hero-num{font-family:'Manrope',sans-serif;font-size:var(--fs-hero);font-weight:700;letter-spacing:-1px;line-height:1.03;color:var(--ink);}
.hero-num-sm{font-family:'Manrope',sans-serif;font-size:var(--fs-metric);font-weight:700;letter-spacing:-.5px;color:var(--ink);}
/* ── ROI section redesign (visual pass, VISUAL ONLY) ─────────────────────────
   Two committed colour ZONES + contained anchor CARDS replace the washed-out
   pastel fills and the flat text stack. The semantic system is unchanged and
   made CRISPER: green = measured/from-your-data, amber = your-assumption —
   carried by a solid 4px edge, a directional colour wash, a crisp tinted
   border and a solid kicker, in BOTH themes. Depth comes from layered washes
   and soft shadows in light mode; dark mode keeps borders and drops shadows. */
:root{
  --roi-shadow:var(--shadow-raised);
  --roi-measured-border:#B7D8BE;
  --roi-measured-wash:linear-gradient(105deg,rgba(42,122,59,.11),rgba(42,122,59,.035) 52%,rgba(42,122,59,0) 78%);
  --roi-assumption-border:#E6CDA0;
  --roi-assumption-wash:linear-gradient(105deg,rgba(138,90,0,.11),rgba(138,90,0,.035) 52%,rgba(138,90,0,0) 78%);
}
.roi-zone{position:relative;background:var(--card-bg);border:1px solid var(--card-border);border-radius:14px;padding:18px 20px;box-shadow:var(--roi-shadow);overflow:hidden;}
.roi-zone::before{content:'';position:absolute;inset:0;pointer-events:none;}
.roi-zone>*{position:relative;}
.roi-zone-measured{border-color:var(--roi-measured-border);border-left:4px solid var(--green);}
.roi-zone-measured::before{background:var(--roi-measured-wash);}
.roi-zone-assumption{border-color:var(--roi-assumption-border);border-left:4px solid var(--amber);}
.roi-zone-assumption::before{background:var(--roi-assumption-wash);}
/* The four-anchor breakdown: real contained cards in a responsive grid, each
   top-edged by its NATURE — green (measured) or amber (assumption-driven). */
.roi-anchors{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:14px;margin-top:10px;}
.roi-anchor{background:var(--card-bg);border:1px solid var(--card-border);border-radius:12px;padding:14px 16px;box-shadow:var(--roi-shadow);min-width:0;}
.roi-anchor-measured{border-top:3px solid var(--green);}
.roi-anchor-assumption{border-top:3px solid var(--amber);}
.roi-kicker{font-family:'Manrope',sans-serif;font-size:var(--fs-micro);letter-spacing:.09em;text-transform:uppercase;font-weight:700;}
/* Answer-first type ladder (refinement pass): the eye hits the NUMBER first.
   34px measured hero → 24px projection result → 21px anchor leads (hero-num-sm)
   → 10px mono labels → 11-12px caveats. Manrope figures + labels (tabular). The
   34px hero deliberately supersedes the "21px step" rule for THIS section, per
   ruling: the measured answer is the section's dominant element. */
.roi-hero-num{font-family:'Manrope',sans-serif;font-size:var(--fs-display);font-weight:700;letter-spacing:-1.2px;line-height:1.05;color:var(--ink);}
.roi-result{font-family:'Manrope',sans-serif;font-size:var(--fs-display);font-weight:700;letter-spacing:-.6px;line-height:1.1;color:var(--ink);}

/* KPI stat tiles inside a .polished screen adopt the same hero treatment as the
   analytics scan strip (Manrope, heavy, tabular). Reusable by any .polished screen. */
/* Dashboard declutter (design audit PR 2): the KPI tiles are the SCAN STRIP,
   not rivals to the cockpit hero — 19px, one visual step below the 28px hero.
   Labels take the mono-kicker treatment so tiles and cockpit vitals read as
   one system. DECORATIVE spines removed (the blanket blue on tiles 1/3 and on
   every dash card carried no meaning); the SEMANTIC ones stay — amber on
   overdue, green on score. Color = meaning only. */
.polished .kpi-val{font-family:'Manrope',sans-serif;font-size:var(--fs-title);font-weight:600;letter-spacing:-.4px;font-variant-numeric:tabular-nums;}
.polished .kpi-label{font-family:'Manrope',sans-serif;letter-spacing:.08em;}
.polished .kpi-grid .kpi:nth-child(2){border-left:3px solid var(--amber);}
.polished .kpi-grid .kpi:nth-child(4){border-left:3px solid var(--green);}
/* Forecast adoption: signal-blue spine on its cards. Scoped to #tab-forecast so it
   never reaches the analytics example cards (which carry a dashed border, no spine). */
#tab-forecast .card{border-left:3px solid var(--signal);}
/* Call analysis result cards (.rcard) adopt the card system on a .polished screen:
   signal-blue spine, heavier heading, roomier body. The receipt-gated approve block
   uses NONE of these classes (only .fl/.personal-quote/.btn), so it stays untouched. */
.polished .rcard{border-left:3px solid var(--signal);border-radius:var(--rl);margin-bottom:var(--gap-section);}
.polished .rcard-hd{padding:12px 16px;}
.polished .rcard-title{font-size:var(--fs-body);font-weight:600;}
.polished .rcard-body{padding:16px;}
/* Settings sections (.ss) adopt the card system on a .polished screen: signal-blue
   spine, heavier section heading, roomier rows. The field-map inputs' value/placeholder
   logic (fix #5) lives in app.js and is untouched — this is presentation only. */
.polished .ss{border-left:3px solid var(--signal);margin-bottom:var(--gap-section);}
.polished .ss-hd{padding:15px 22px;font-size:var(--fs-section);font-weight:600;}
.polished .ss-row{padding:13px 22px;}

/* KPI */
.kpi-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:20px;}
.kpi{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-card);padding:15px 17px;transition:background 0.2s;}
.kpi-label{font-size:var(--fs-body);color:var(--ink-3);text-transform:uppercase;letter-spacing:0.06em;margin-bottom:5px;}
.kpi-val{font-size:var(--fs-title);font-weight:700;color:var(--text-primary);letter-spacing:-0.6px;}
.kpi-sub{font-size:var(--fs-body);color:var(--ink-3);margin-top:3px;}

/* DASH GRID */
.dash-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px;}
.dash-full{grid-column:1/-1;}

/* BANT CHART */
.bchart{display:flex;flex-direction:column;gap:10px;}
.bchart-row{display:flex;align-items:center;gap:10px;}
.bchart-lbl{font-size:var(--fs-body);color:var(--ink-2);width:88px;text-align:right;flex-shrink:0;}
.bchart-wrap{flex:1;height:8px;background:var(--line-2);border-radius:99px;overflow:hidden;}
.bchart-bar{height:100%;border-radius:99px;}
.bchart-pct{font-size:var(--fs-body);font-weight:600;color:var(--ink);width:34px;}

/* STAGE */
.stage-list{display:flex;flex-direction:column;gap:7px;}
.stage-row{display:flex;align-items:center;gap:8px;}
.stage-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;}
.stage-name{font-size:var(--fs-body);color:var(--ink-2);flex:1;}
.stage-count{font-size:var(--fs-body);font-weight:600;color:var(--text-primary);}
.stage-bw{width:70px;height:5px;background:var(--line-2);border-radius:99px;}
.stage-bf{height:100%;border-radius:99px;}

/* RECENT CALLS */
.recent-call{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--line);cursor:pointer;transition:opacity 0.12s;}
.recent-call:last-child{border-bottom:none;}
.recent-call:hover{opacity:0.75;}
.rc-av{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:var(--fs-body);font-weight:600;flex-shrink:0;background:var(--signal-dim);color:var(--signal);}
.rc-name{font-size:var(--fs-body);font-weight:500;color:var(--text-primary);}
.rc-summary{font-size:var(--fs-body);color:var(--ink-2);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:300px;}
.rc-time{font-size:var(--fs-body);color:var(--ink-3);margin-left:auto;flex-shrink:0;}

/* TAGS */
/* Sprint 9 TASK E — TAG species: OUTLINE, neutral. A tag is a neutral ATTRIBUTE
   (player-coach, you, board-safe, at-risk, new) — never a good/bad signal, so no
   tinted fill. One spec. tag-accent is the single identity accent (you/player-coach). */
/* ═══ THE PILL — ONE component, TWO variants (premium pass, 24 Aug 2026) ══════
   The product had pills in five shapes: .chip, .tag, .chip-metric, an inline-styled DEMO
   lozenge and an inline-styled gap marker, at four heights and three font sizes. Worse, a
   PRICE STATE ("confirmed") and a PROVENANCE fact ("call-confirmed") rendered as the same
   solid green pill, so the reader had to learn which column meant which — the design was
   making them do work the design should have done.

     SOLID  (.chip)          the deal's price state: confirmed · likely · not accepted ·
                             unconfirmed · contradicted. A judgement about the deal.
     OUTLINE (.tag-outline)  provenance: call-confirmed · unconfirmed · gap · under/over.
                             A fact about the evidence behind it.

   Both are 11px, both share one height and one padding, both take colour from the semantic
   tokens and nowhere else. Column semantics are unchanged — only which variant paints them. */
.tag{display:inline-flex;align-items:center;height:20px;padding:0 9px;border-radius:var(--radius-chip);font-size:var(--fs-eyebrow);font-weight:600;line-height:1;white-space:nowrap;background:transparent;border:1px solid var(--line);color:var(--ink-2);}
.tag-accent{border-color:var(--accent);color:var(--accent);}
/* Bare .tag-green/amber/red stay neutral outlines so a missed call-site degrades cleanly
   rather than emitting a stray tinted pill; the semantic colour arrives only WITH
   .tag-outline, which is the variant that means "this is provenance". */
.tag-blue{border-color:var(--accent);color:var(--accent);background:transparent;}
.tag-gray{border-color:var(--line);color:var(--ink-2);background:transparent;}
.tag-green,.tag-amber,.tag-red{border-color:var(--line);color:var(--ink-2);background:transparent;}
.tag-outline{background:transparent;font-weight:600;}
.tag-outline.tag-green{border-color:var(--green);color:var(--green);}
.tag-outline.tag-amber{border-color:var(--amber);color:var(--amber);}
.tag-outline.tag-red{border-color:var(--red);color:var(--red);}
.tag-outline.tag-neutral{border-color:var(--ink-3);color:var(--ink-2);}
/* One size for pills that sit inside a table row, replacing the inline height/font-size that
   was pasted at each call site. */
.pill-sm{height:18px;padding:0 8px;font-size:var(--fs-eyebrow);}

/* ANALYSE */
.analyse-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;align-items:start;}
.sticky-left{position:sticky;top:78px;}
.upload-zone{border:1.5px dashed var(--stone3);border-radius:var(--rl);padding:24px;text-align:center;cursor:pointer;background:var(--card-bg);transition:all 0.15s;position:relative;margin-bottom:12px;}
.upload-zone:hover{border-color:var(--signal);background:var(--signal-dim);}
.upload-zone input{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%;}
.upload-icon{width:36px;height:36px;border-radius:var(--r);background:var(--signal-dim);display:flex;align-items:center;justify-content:center;margin:0 auto 8px;}
.upload-title{font-size:var(--fs-body);font-weight:500;color:var(--text-primary);margin-bottom:2px;}
.upload-hint{font-size:var(--fs-micro);color:var(--ink3);}
.divider-or{display:flex;align-items:center;gap:10px;margin:12px 0;color:var(--ink3);font-size:var(--fs-caption);}
.divider-or::before,.divider-or::after{content:'';flex:1;height:1px;background:var(--stone3);}
textarea.tx{width:100%;background:var(--card-bg);border:0.5px solid var(--card-border);border-radius:var(--rl);padding:12px;font-size:var(--fs-caption);color:var(--ink);font-family:'Manrope',sans-serif;resize:vertical;min-height:160px;outline:none;line-height:1.65;transition:border-color 0.15s;}
textarea.tx:focus{border-color:var(--signal);}
textarea.tx::placeholder{color:var(--ink3);font-family:'Manrope',sans-serif;}
.action-row{display:flex;gap:8px;margin-top:12px;}
.btn-sec{background:var(--card-bg);border:0.5px solid var(--card-border);border-radius:var(--r);padding:8px 14px;font-size:var(--fs-caption);color:var(--ink2);cursor:pointer;font-family:'Manrope',sans-serif;transition:all 0.12s;}
.btn-sec:hover{border-color:var(--ink2);color:var(--ink);}
/* ─── Sprint 9 TASK B — the button rank ──────────────────────────────────────
   PRIMARY (accent-filled, max one per view): .btn-primary / .btn-analyse
   via --btn-primary-bg (now accent — the black fill is retired product-wide).
   SECONDARY (outline): .btn-sec. TERTIARY (text + ›): .btn-tertiary for open/see/view
   links. Green/red stay SEMANTIC (Save/confirm · Delete) — colour = meaning (ruled). */
.btn-tertiary{background:none;border:none;padding:4px 2px;font-size:var(--fs-body);font-weight:600;color:var(--accent);cursor:pointer;font-family:'Manrope',sans-serif;display:inline-flex;align-items:center;gap:3px;}
.btn-tertiary:hover{color:var(--accent-hover);text-decoration:underline;}
.btn-tertiary::after{content:'›';font-size:var(--fs-subtitle);line-height:1;}
.btn-analyse{flex:1;background:var(--btn-primary-bg);color:var(--btn-primary-fg);border:none;border-radius:var(--r);padding:9px 16px;font-size:var(--fs-body);font-weight:500;cursor:pointer;font-family:'Manrope',sans-serif;display:flex;align-items:center;justify-content:center;gap:7px;transition:opacity 0.15s;}
.btn-analyse:hover{opacity:0.88;}
.btn-analyse:disabled{opacity:0.4;cursor:not-allowed;}
.spinner{width:12px;height:12px;border:2px solid rgba(128,128,128,0.35);border-top-color:currentColor;border-radius:50%;animation:spin 0.7s linear infinite;}
@keyframes spin{to{transform:rotate(360deg);}}
.err-msg{background:var(--red-bg);border:1px solid var(--red);border-radius:var(--r);padding:9px 12px;font-size:var(--fs-caption);color:var(--red);margin-top:10px;display:none;}
.err-msg.show{display:block;}

/* RESULTS */
.results-panel{display:none;}
.results-panel.show{display:block;}
.results-hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;padding-bottom:12px;border-bottom:1px solid var(--stone3);}
.results-title{font-size:var(--fs-body);font-weight:600;color:var(--text-primary);letter-spacing:-0.3px;}
.results-ts{font-size:var(--fs-micro);color:var(--ink3);margin-top:2px;}
.sync-badge{display:flex;align-items:center;gap:4px;font-size:var(--fs-micro);color:var(--green);background:var(--green-bg);padding:3px 9px;border-radius:99px;font-weight:500;}

/* superseded by the one card system at the foot of this file (Part 2) */
.rcard-hd{display:flex;align-items:center;gap:7px;padding:10px 13px;border-bottom:1px solid var(--stone3);background:var(--stone2);}
.rcard-icon{width:22px;height:22px;border-radius:5px;display:flex;align-items:center;justify-content:center;font-size:var(--fs-body);font-weight:700;flex-shrink:0;}
.rcard-title{font-size:var(--fs-subtitle);font-weight:600;color:var(--text-primary);}
.rcard-sf{font-size:var(--fs-micro);color:var(--ink3);margin-left:auto;}
.rcard-body{padding:12px 13px;}
.fl{font-size:var(--fs-micro);color:var(--ink3);text-transform:uppercase;letter-spacing:0.07em;margin-bottom:4px;font-weight:500;}
.fv{font-size:var(--fs-body);color:var(--ink);line-height:1.6;}
.fg{margin-bottom:10px;}
.fg:last-child{margin-bottom:0;}

.bant4{display:grid;grid-template-columns:1fr 1fr;gap:7px;}
.bcell{background:var(--stone2);border-radius:var(--r);padding:9px 10px;}
.bk{font-size:var(--fs-micro);color:var(--ink3);text-transform:uppercase;letter-spacing:0.08em;margin-bottom:3px;}
.bs{font-size:var(--fs-body);font-weight:600;margin-bottom:2px;}
.bs.High{color:var(--green);}
.bs.Medium{color:var(--amber);}
.bs.Low{color:var(--red);}
.bs.Unknown{color:var(--ink3);}
.bn{font-size:var(--fs-micro);color:var(--ink2);line-height:1.4;}

.task-item{display:flex;align-items:flex-start;gap:8px;padding:7px 0;border-bottom:1px solid var(--stone3);}
.task-priority{width:8px;height:8px;border-radius:50%;margin-top:6px;flex-shrink:0;}
.priority-high{background:var(--red);}
.priority-medium{background:var(--amber);}
.priority-low{background:var(--ink3);}
.task-complete{width:14px;height:14px;border-radius:3px;border:1.5px solid var(--stone3);cursor:pointer;flex-shrink:0;margin-top:4px;display:flex;align-items:center;justify-content:center;font-size:var(--fs-micro);transition:all 0.12s;}
.task-complete.done{background:var(--green);border-color:var(--green);color:var(--on-accent);}
.gap-item{display:flex;align-items:flex-start;gap:8px;padding:6px 0;font-size:var(--fs-caption);color:var(--red);}
.gap-dot{width:5px;height:5px;border-radius:50%;background:var(--red);margin-top:6px;flex-shrink:0;}
.validated-badge{font-size:var(--fs-micro);padding:1px 6px;border-radius:99px;margin-left:4px;}
.v-yes{background:var(--green-bg);color:var(--green);}
.v-no{background:var(--red-bg);color:var(--red);}
.today-task{display:flex;align-items:flex-start;gap:10px;padding:8px 0;border-bottom:1px solid var(--stone3);}
.today-task:last-child{border-bottom:none;}
.kanban-task:hover{border-color:var(--signal) !important;box-shadow:0 1px 4px rgba(51,80,224,0.12);}
.task-col{transition:background 0.1s;}
.task-col.drag-over{background:var(--signal-dim);border-radius:6px;}
.score-trend{display:flex;align-items:flex-end;gap:3px;height:32px;}
.trend-bar{width:14px;border-radius:3px 3px 0 0;background:var(--signal);opacity:0.7;min-height:4px;}
.task-item:last-child{border-bottom:none;}
.task-dot{width:5px;height:5px;border-radius:50%;background:var(--signal);margin-top:7px;flex-shrink:0;}
.task-text{font-size:var(--fs-caption);color:var(--ink);line-height:1.4;font-weight:500;}
.task-meta{font-size:var(--fs-micro);color:var(--ink3);margin-top:2px;}

.personal-quote{border-left:2px solid var(--amber);padding-left:11px;font-size:var(--fs-body);color:var(--ink2);font-style:italic;line-height:1.6;}

/* COACHING */
.coach-rep{background:var(--signal-dim);border-radius:var(--rl);padding:14px;margin-bottom:8px;}
.coach-title{font-size:var(--fs-caption);font-weight:500;color:var(--signal);margin-bottom:10px;display:flex;align-items:center;gap:6px;}
.coach-tip{display:flex;align-items:flex-start;gap:7px;padding:5px 0;font-size:var(--fs-caption);color:var(--ink);}
.coach-tip-dot{width:5px;height:5px;border-radius:50%;background:var(--signal);margin-top:6px;flex-shrink:0;}
.coach-missed{background:var(--amber-bg);border-left:2px solid var(--amber);padding:9px 11px;border-radius:0 var(--r) var(--r) 0;font-size:var(--fs-caption);color:var(--ink2);line-height:1.5;margin-top:8px;}
.coach-score{display:flex;align-items:center;gap:8px;margin-top:10px;padding-top:10px;border-top:1px solid rgba(51,80,224,0.2);}
.score-label{font-size:var(--fs-micro);color:var(--ink2);}
.score-dots{display:flex;gap:3px;}
.score-dot{width:16px;height:6px;border-radius:3px;}

.coach-manager{background:var(--green-bg);border-radius:var(--rl);padding:14px;margin-bottom:8px;}
.coach-manager .coach-title{color:var(--green);}
/* Line icons replacing the emoji that used to sit in card titles and empty states (premium
   pass). Same 24×24 stroke set as the sidebar, so one icon language runs through the product
   instead of two — a lightbulb rendered by the OS font can never match a hand-drawn nav icon. */
.ttl-ic{width:15px;height:15px;flex:none;}
.empty-ic{width:34px;height:34px;color:var(--ink-3);margin:0 auto var(--sp-3);display:block;}
.mgr-flag{display:flex;align-items:flex-start;gap:7px;padding:5px 0;font-size:var(--fs-caption);color:var(--ink);}
.mgr-plan{background:var(--card-bg);border-radius:var(--r);padding:10px 12px;font-size:var(--fs-caption);color:var(--ink2);line-height:1.5;margin-top:8px;border:1px solid var(--stone3);}
.mgr-risk{background:var(--red-bg);border-left:2px solid var(--red);padding:8px 11px;border-radius:0 var(--r) var(--r) 0;font-size:var(--fs-caption);color:var(--red);line-height:1.5;margin-top:8px;}

/* EXPORT ROW */
.export-row{display:flex;gap:7px;margin-top:12px;padding-top:12px;border-top:1px solid var(--stone3);}
.btn-export{display:flex;align-items:center;gap:4px;padding:6px 10px;font-size:var(--fs-micro);font-weight:500;border-radius:var(--r);border:1px solid var(--stone3);background:var(--card-bg);color:var(--ink2);cursor:pointer;font-family:'Manrope',sans-serif;transition:all 0.12s;}
.btn-export:hover{border-color:var(--text-primary);color:var(--text-primary);}

/* HISTORY */
/* Brought onto the one card system. It renders as a card and was missed by the alias rule
   because it is not named .card/.rcard/.ss — a fifth system hiding behind a different noun. */
.history-item{display:flex;align-items:center;gap:12px;padding:13px 15px;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-card);box-shadow:var(--shadow);transition:box-shadow var(--dur-micro) var(--ease-out);margin-bottom:7px;cursor:pointer;transition:border-color 0.12s;}
.history-item:hover{box-shadow:var(--shadow-hover);}
.hi-av{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:var(--fs-micro);font-weight:600;flex-shrink:0;background:var(--signal-dim);color:var(--signal);}
.hi-name{font-size:var(--fs-body);font-weight:500;color:var(--text-primary);}
/* No max-width. The 340px cap clipped every summary at a third of the width the row had,
   leaving two thirds of the card blank — the row truncating its own content next to its own
   empty space. nowrap + ellipsis stay, so it still truncates, but at the point it actually
   runs out of room. */
.hi-summary{font-size:var(--fs-caption);color:var(--ink2);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.hi-tags{display:flex;gap:4px;margin-top:4px;flex-wrap:wrap;}
.hi-time{font-size:var(--fs-micro);color:var(--ink3);margin-left:auto;flex-shrink:0;}
.history-empty{text-align:center;padding:50px 20px;color:var(--ink3);font-size:var(--fs-body);line-height:1.7;}
.hi-menu{position:relative;}
.hi-dots{width:28px;height:28px;border-radius:6px;border:none;background:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:var(--fs-title);color:var(--ink3);transition:all 0.12s;flex-shrink:0;}
.hi-dots:hover{background:var(--stone2);color:var(--ink);}
.hi-dropdown{position:absolute;right:0;top:32px;background:var(--card-bg);border:0.5px solid var(--card-border);border-radius:var(--rl);box-shadow:0 4px 16px rgba(26,26,46,0.12);z-index:300;min-width:160px;overflow:hidden;display:none;}
.hi-dropdown.show{display:block;}
.hi-dropdown-item{display:flex;align-items:center;gap:8px;padding:9px 14px;font-size:var(--fs-caption);color:var(--ink);cursor:pointer;transition:background 0.1s;}
.hi-dropdown-item:hover{background:var(--stone2);}

/* MODAL */
.modal-overlay{position:fixed;inset:0;background:var(--scrim);z-index:500;display:none;align-items:flex-start;justify-content:center;padding:40px 20px;overflow-y:auto;}
.modal-overlay.show{display:flex;}
.modal{background:var(--surface-solid);border-radius:var(--rl);width:100%;max-width:680px;overflow:hidden;margin:auto;}
.modal-head{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--stone3);background:var(--stone2);}
.modal-title{font-size:var(--fs-subtitle);font-weight:600;color:var(--text-primary);letter-spacing:-0.3px;}
.modal-close{width:28px;height:28px;border-radius:6px;background:var(--stone2);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:var(--fs-subtitle);color:var(--ink2);}
/* THE MODAL'S SCROLL — premium pass, 24 Aug 2026. Every blind reviewer across both panels
   reported the same three things about this one element: content guillotined mid-card at the
   bottom edge with no fade and no peek, a chunky platform scrollbar with arrow buttons running
   down the inside, and that scrollbar sitting ~40px from the page's own — two scrollbars in one
   screenshot. One called it "somewhere around Windows 95".
   max-height goes to 78vh (a little more content per screen without touching the layout), the
   scrollbar becomes a thin ink thumb on no track, and a fade at the lower edge tells the reader
   that the cut IS a cut and there is more below — which is the part that made it read as broken
   rather than as scrollable. The fade is a pseudo-element on the container, so it never scrolls
   away with the content. */
.modal-body{padding:20px;max-height:78vh;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--ink-3) transparent;position:relative;}
.modal-body::-webkit-scrollbar{width:8px;}
.modal-body::-webkit-scrollbar-track{background:transparent;}
.modal-body::-webkit-scrollbar-thumb{background:var(--line);border-radius:99px;border:2px solid var(--surface);}
.modal-body::-webkit-scrollbar-thumb:hover{background:var(--ink-3);}
.modal-body::-webkit-scrollbar-button{display:none;height:0;}
.modal{position:relative;}
.modal::after{content:'';position:absolute;left:0;right:0;bottom:0;height:28px;pointer-events:none;
  background:linear-gradient(to bottom,rgba(255,255,255,0),var(--surface-solid));border-radius:0 0 var(--rl) var(--rl);}

/* ADMIN */
.admin-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
.input-row{display:flex;gap:8px;margin-bottom:12px;align-items:center;}
/* Field labels for the admin forms — placeholder-only inputs stop saying what they are the
   moment someone types into them, which is exactly when a half-filled form most needs to. */
.fl{flex:none;width:96px;font-size:var(--fs-eyebrow);letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;color:var(--ink-3);}
.fieldlabel{font-size:var(--fs-caption);font-weight:600;color:var(--ink-2);margin-bottom:var(--sp-3);}
/* Inline labels for the call-history date range. Every blind reviewer called these fields "raw
   browser date inputs" — the box itself is styled to match the selects beside it (36px, 6px
   radius, Manrope, our own calendar glyph), and what they were actually reading is Chrome's own
   dd/mm/yyyy placeholder, which cannot be removed from input[type=date] without replacing the
   control. Two of them named the real fixable problem underneath it: two unlabelled boxes with
   no indication which is the start and which is the end. "From … to" says it in five pixels. */
.fl-inline{font-size:var(--fs-eyebrow);letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;color:var(--ink-3);flex:none;}
.inp{background:var(--card-bg);border:1px solid var(--input-border);border-radius:var(--radius-input);padding:0 12px;height:36px;font-size:var(--fs-caption);color:var(--text-primary);font-family:'Manrope',sans-serif;outline:none;flex:1;}
.inp:focus{border-color:var(--accent);}
.inp-sm{width:140px;flex:none;}

/* ═══ FORM CONTROLS — PREMIUM PASS, 24 Aug 2026 ══════════════════════════════
   The panel was unanimous and it was right: not one control in the product had ever been
   styled. Every <select> rendered the operating system's own chevron, so the app looked
   different on every machine it was opened on and looked designed on none of them; a select
   and a text input in the same filter row disagreed on height by several pixels because each
   inherited its own platform metrics; and :focus only tinted a border, which is not a focus
   indicator a keyboard user can find.

   STYLE, DON'T REBUILD. Every control below is still the native element — real <select>, real
   <input type=checkbox> — so the keyboard, the screen reader, the mobile picker and the form
   semantics are all untouched. appearance:none removes only the paint. A rebuilt div-listbox
   would have looked the same and cost the accessibility that comes free here.

   ONE HEIGHT: 36px on every control, so a row of them shares a baseline.
   ONE RING: 2px accent at offset 2, on every focusable thing, via :focus-visible so it appears
   for keyboards and not for mouse clicks. */
.inp, select.inp, .vs-input, .ss-inp, .method-select, input[type=text].inp, input[type=email].inp,
input[type=date], input[type=number], input[type=search], textarea.inp {
  font-family:'Manrope',sans-serif;
  font-size:var(--fs-caption);
  color:var(--text-primary);
  background:var(--input-bg);
  border:1px solid var(--input-border);
  border-radius:var(--radius-input);
}
/* SELECTS — one chevron, ours, drawn in ink and never the platform's. The right padding
   reserves the chevron's lane so a long option label cannot run underneath it. */
select, select.inp, select.vs-input, select.ss-inp, select.method-select {
  -webkit-appearance:none; -moz-appearance:none; appearance:none;
  height:36px; padding:0 34px 0 12px; line-height:34px;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%236B7480' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 12px center;
  cursor:pointer;
}
select::-ms-expand{display:none;}
select:hover{border-color:var(--ink-3);}
/* DATE INPUTS — matched to the select rather than given a bespoke picker. The native picker
   is kept (it is better than anything worth hand-rolling here); only the field is restyled,
   and the calendar glyph is tinted to the same ink as our chevron. */
/* The native date field's INTERNALS were the loudest remaining mismatch — all three blind
   reviewers named it, and one called it "the single clearest we-ran-out-of-time signal in the
   whole set". Setting height and border was not enough: Chrome draws its own calendar glyph and
   its own dd/mm/yyyy text, both in the platform's colours, right beside our chevroned selects.
   The picker itself is deliberately kept — it is better than anything worth hand-rolling — so
   only the paint is replaced: our chevron-weight calendar mark, our type, and the empty state
   set in muted ink so it reads as a placeholder instead of as data. */
input[type=date]{height:36px;padding:0 8px 0 12px;font-family:'Manrope',sans-serif;font-size:var(--fs-caption);color:var(--text-primary);}
input[type=date]::-webkit-datetime-edit{font-family:'Manrope',sans-serif;color:var(--text-primary);}
input[type=date]:invalid::-webkit-datetime-edit,
input[type=date]:not(:focus):placeholder-shown::-webkit-datetime-edit{color:var(--ink-3);}
input[type=date]::-webkit-datetime-edit-fields-wrapper{padding:0;}
input[type=date]::-webkit-calendar-picker-indicator{
  width:14px;height:14px;cursor:pointer;opacity:1;
  background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 16 16'%3E%3Cg fill='none' stroke='%23666F7A' stroke-width='1.4' stroke-linecap='round'%3E%3Crect x='2.2' y='3.2' width='11.6' height='10.6' rx='2'/%3E%3Cpath d='M2.2 6.6h11.6M5.5 1.8v2.6M10.5 1.8v2.6'/%3E%3C/g%3E%3C/svg%3E") no-repeat center;
}
input[type=date]:hover::-webkit-calendar-picker-indicator{filter:brightness(.6);}
/* TEXT-LIKE INPUTS — same box as the select so a filter row lines up. */
input[type=text].inp, input[type=email].inp, input[type=password].inp, input[type=number].inp,
input[type=search].inp, .vs-input, .ss-inp {
  height:36px; padding:0 12px;
}
textarea.inp{height:auto;padding:10px 12px;line-height:var(--lh-body);}
/* CHECKBOX + RADIO — custom, accent-coloured, and still the native input underneath.
   accent-color does the whole job in every browser the product supports, which is why this is
   four lines rather than the usual pseudo-element scaffold. */
input[type=checkbox], input[type=radio]{
  -webkit-appearance:none; appearance:none;
  width:16px; height:16px; margin:0; flex:none;
  border:1.5px solid var(--ink-3); background:var(--surface);
  border-radius:4px; cursor:pointer; position:relative; vertical-align:middle;
}
input[type=radio]{border-radius:50%;}
input[type=checkbox]:checked, input[type=radio]:checked{background:var(--accent);border-color:var(--accent);}
input[type=checkbox]:checked::after{
  content:''; position:absolute; left:4.5px; top:1px; width:4px; height:8px;
  border:solid #fff; border-width:0 2px 2px 0; transform:rotate(42deg);
}
input[type=radio]:checked::after{
  content:''; position:absolute; left:4px; top:4px; width:6px; height:6px;
  border-radius:50%; background:#fff;
}
input[type=checkbox]:hover, input[type=radio]:hover{border-color:var(--accent);}
input[type=checkbox]:disabled, input[type=radio]:disabled{opacity:.5;cursor:not-allowed;}
/* THE ONE FOCUS RING. :focus-visible, so it answers the keyboard and stays out of the way of
   the mouse. Applied to every focusable control in the product from a single rule. */
.inp:focus-visible, select:focus-visible, input:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible, [tabindex]:focus-visible {
  outline:2px solid var(--accent); outline-offset:2px; border-radius:var(--radius-input);
}
.inp:focus, select:focus, textarea:focus{border-color:var(--accent);}
/* A row of controls shares one baseline — the item the panel called out by name. */
.input-row, .filter-bar, .vs-filters { align-items:center; }
.btn-green{background:var(--green);color:var(--on-accent);border:none;border-radius:var(--r);padding:8px 14px;font-size:var(--fs-caption);font-weight:500;cursor:pointer;font-family:'Manrope',sans-serif;white-space:nowrap;}
.btn-red{background:var(--red);color:var(--on-accent);border:none;border-radius:var(--r);padding:5px 10px;font-size:var(--fs-micro);cursor:pointer;font-family:'Manrope',sans-serif;}
/* ROW ACTIONS — premium pass, 24 Aug 2026.
   Every user row carried four buttons in four treatments: solid accent (Export), white outline
   (Reset password), and TWO solid reds (Delete all data, Remove). Six rows deep that is a wall
   of colour, with the two most dangerous actions given the same weight as the two most routine.
   All three blind reviewers named this screen as the single biggest drag on perceived price,
   and one put a number on it — "€30/seat on its own".
   Row actions are now one quiet treatment. Destructive ones keep red, as TEXT and border rather
   than fill, so they still read as dangerous without shouting on every row.
   THIS IS SAFE, and the reason is checked rather than assumed: both destructive actions are
   confirm-gated in code — Remove behind one confirm, Delete all data behind two, the second
   naming the account. The dialog is the guard; the button was only ever the label on it. */
/* The action cluster is pushed to the row's right edge and stays there. Rows differ in which
   actions they carry — the signed-in admin has no Deactivate, only a rep gets a manager
   picker — so left-flowing buttons started at a different x on almost every row and the list
   ragged down the page. Two reviewers measured that by eye ("~80px right of everyone else's").
   Anchoring right means the LAST button lines up on every row, which is the edge the eye reads. */
.user-row .rowact:first-of-type{margin-left:auto;}
.rowact{background:none;border:1px solid var(--line);border-radius:var(--radius-input);padding:5px 10px;font-size:var(--fs-eyebrow);font-family:'Manrope',sans-serif;color:var(--ink-2);cursor:pointer;margin-right:4px;white-space:nowrap;}
.rowact:last-child{margin-right:0;}
.rowact:hover{border-color:var(--ink-3);color:var(--ink);}
.rowact-danger{color:var(--red);border-color:var(--red-bg);}
.rowact-danger:hover{color:#fff;background:var(--red);border-color:var(--red);}
.user-row{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--stone3);}
.user-row:last-child{border-bottom:none;}
.user-av-sm{width:32px;height:32px;border-radius:50%;background:var(--signal-dim);color:var(--signal);display:flex;align-items:center;justify-content:center;font-size:var(--fs-body);font-weight:600;flex-shrink:0;}
.user-name{font-size:var(--fs-body);font-weight:500;color:var(--ink);}
.user-email{font-size:var(--fs-micro);color:var(--ink3);}
.company-row{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--stone3);}
.company-row:last-child{border-bottom:none;}
.method-select{background:var(--card-bg);border:0.5px solid var(--card-border);border-radius:var(--r);padding:4px 8px;font-size:var(--fs-caption);color:var(--text-primary);font-family:'Manrope',sans-serif;outline:none;margin-left:auto;}
select{color:var(--text-primary);background:var(--card-bg);}
/* Native <option> popups are drawn by the OS — give them an explicit solid
   background + text colour so the list is readable in both light and dark. */
select option{background:var(--surface-solid);color:var(--text-primary);}

/* SETTINGS */
/* superseded by the one card system at the foot of this file (Part 2) */
.ss-hd{padding:12px 16px;border-bottom:1px solid var(--stone3);font-size:var(--fs-body);font-weight:500;color:var(--text-primary);background:var(--stone2);}
.ss-row{display:flex;align-items:center;justify-content:space-between;padding:11px 16px;border-bottom:1px solid var(--stone3);}
.ss-row:last-child{border-bottom:none;}
.ss-label{font-size:var(--fs-body);color:var(--ink);}
.ss-sub{font-size:var(--fs-micro);color:var(--ink3);margin-top:1px;}
.ss-inp{background:var(--stone2);border:0.5px solid var(--card-border);border-radius:var(--r);padding:6px 10px;font-size:var(--fs-caption);color:var(--text-primary);font-family:'Manrope',sans-serif;outline:none;width:180px;}
.ss-inp:focus{border-color:var(--signal);background:var(--card-bg);}
.status-pill{font-size:var(--fs-micro);padding:2px 9px;border-radius:99px;font-weight:500;}
.pill-green{background:var(--green-bg);color:var(--green);}
.pill-red{background:var(--red-bg);color:var(--red);}
.task-popup{position:fixed;inset:0;background:var(--scrim-soft);z-index:600;display:none;align-items:center;justify-content:center;padding:20px;}
.task-popup.show{display:flex;}
.task-popup-card{background:var(--surface-solid);border-radius:var(--rl);width:100%;max-width:480px;overflow:hidden;box-shadow:0 8px 32px rgba(26,26,46,0.18);}
.task-popup-hd{padding:14px 18px;border-bottom:1px solid var(--stone3);background:var(--stone2);display:flex;align-items:center;justify-content:space-between;}
.task-popup-body{padding:16px 18px;}
.score-info-popup{position:fixed;inset:0;background:var(--scrim-soft);z-index:600;display:none;align-items:center;justify-content:center;padding:20px;overflow-y:auto;}
.score-info-popup.show{display:flex;}
.score-info-card{background:var(--surface-solid);border-radius:var(--rl);width:100%;max-width:560px;max-height:90vh;overflow-y:auto;}
.dim-row{display:flex;align-items:flex-start;gap:12px;padding:10px 0;border-bottom:1px solid var(--stone3);}
.dim-row:last-child{border-bottom:none;}
.dim-score{width:36px;height:36px;border-radius:var(--r);display:flex;align-items:center;justify-content:center;font-size:var(--fs-body);font-weight:600;flex-shrink:0;}
.dim-label{font-size:var(--fs-body);font-weight:500;color:var(--text-primary);margin-bottom:2px;}
.dim-what{font-size:var(--fs-caption);color:var(--ink2);line-height:1.5;margin-bottom:3px;}
.dim-how{font-size:var(--fs-micro);color:var(--signal);font-style:italic;}
.team-n-btn{background:var(--stone2);border:1px solid var(--stone3);border-radius:var(--r);padding:6px 12px;font-size:var(--fs-body);color:var(--ink2);cursor:pointer;font-family:'Manrope',sans-serif;transition:all 0.12s;}
.team-n-btn.active{background:var(--btn-primary-bg);color:var(--btn-primary-fg);border-color:var(--btn-primary-bg);}
.profile-stat{background:var(--card-bg);border:0.5px solid var(--card-border);border-radius:var(--rl);padding:16px 18px;}
.profile-stat-label{font-size:var(--fs-micro);color:var(--text-muted);text-transform:uppercase;letter-spacing:0.06em;margin-bottom:4px;}
.profile-stat-val{font-size:var(--fs-title);font-weight:600;color:var(--text-primary);letter-spacing:-0.6px;}
.profile-stat-sub{font-size:var(--fs-micro);color:var(--ink3);margin-top:2px;}
.strength-item{display:flex;align-items:flex-start;gap:8px;padding:8px 0;border-bottom:1px solid var(--stone3);font-size:var(--fs-body);color:var(--ink);}
.strength-item:last-child{border-bottom:none;}
.traj-pill{display:inline-flex;align-items:center;gap:5px;padding:4px 12px;border-radius:99px;font-size:var(--fs-caption);font-weight:500;}
.traj-up{background:var(--green-bg);color:var(--green);}
.traj-flat{background:var(--amber-bg);color:var(--amber);}
.traj-down{background:var(--red-bg);color:var(--red);}
.change-item{display:flex;align-items:flex-start;gap:10px;padding:9px 0;border-bottom:1px solid var(--stone3);}
.change-item:last-child{border-bottom:none;}
.change-dot{width:8px;height:8px;border-radius:50%;margin-top:5px;flex-shrink:0;}
.change-text{font-size:var(--fs-body);color:var(--ink);line-height:1.5;flex:1;}
.change-meta{font-size:var(--fs-micro);color:var(--ink3);margin-top:2px;}
.coach-story{background:var(--navy);border-radius:var(--rl);padding:18px 20px;margin-bottom:12px;}
.coach-story-label{font-size:var(--fs-micro);color:rgba(255,255,255,0.5);text-transform:uppercase;letter-spacing:0.07em;margin-bottom:8px;}
.coach-story-text{font-size:var(--fs-body);color:white;line-height:1.7;}
.score-sparkline{display:flex;align-items:flex-end;gap:3px;height:48px;}
.spark-bar{border-radius:2px 2px 0 0;min-height:3px;flex:1;}

/* ══════════ MOBILE (restored after refactor) ══════════ */
@media (max-width: 820px) {
  /* App shell: sidebar becomes a top bar */
  .app.visible{flex-direction:column;}
  .sidebar{width:100%;height:auto;min-height:0;position:static;border-right:none;border-bottom:0.5px solid var(--sidebar-border);}
  .sidebar-brand{padding:14px 16px 10px;}
  .sidebar-nav{flex-direction:row;overflow-x:auto;padding:0 10px 10px;gap:4px;-webkit-overflow-scrolling:touch;scrollbar-width:none;}
  .sidebar-nav::-webkit-scrollbar{display:none;}
  .nav-tab{flex-shrink:0;width:auto;padding:7px 12px;font-size:var(--fs-caption);}
  .nav-tab-dot{display:none;}
  .sidebar-footer{padding:8px 16px;}
  .main{padding:18px 14px;}

  /* Grids collapse */
  .kpi-grid{grid-template-columns:repeat(2,1fr);}
  .dash-grid,.analyse-grid,.bant4,.admin-grid{grid-template-columns:1fr;}

  /* AUTH ON A PHONE — the panel is NOT hidden any more. It was display:none here,
     which meant the first screen a prospect saw on a phone was a bare form with no
     statement of what the product is. The 820px block above stacks it instead; this
     rule only tightens the form's own padding. */
  .auth-right{padding:24px 16px;}

  /* Type scale down slightly */
  .page-title{font-size:var(--fs-title);}
  h1{font-size:var(--fs-title);}

  /* Any wide tables scroll instead of breaking layout */
  .main{overflow-x:hidden;}
  .card{max-width:100%;overflow-x:auto;}
}

@media (max-width: 480px) {
  .kpi-grid{grid-template-columns:1fr 1fr;gap:8px;}
  .main{padding:14px 10px;}
}

/* ═══════════ COCKPIT SKIN — type & component refinements (whole-site reskin) ═══════════ */
.page-title,.kpi-val,.auth-title,.results-title,.sidebar-name,.stat-val,
.profile-stat-val,.modal-title{font-family:'Manrope',sans-serif;letter-spacing:-0.5px;}
.kpi-label,.fl,.bk,.profile-stat-label,.rcard-sf{font-family:'Manrope',sans-serif;}
.kpi-val,.stat-val,.profile-stat-val,.bs,.dim-score,.stage-count,.kpi-sub .num{font-variant-numeric:tabular-nums;}
/* Cleaner, flush card headers (cockpit look) instead of a filled grey bar */
.card-head,.ss-hd,.rcard-hd{background:var(--card-bg);}
.card,.kpi{transition:border-color .12s,background .2s;}
.kpi:hover{border-color:var(--stone3);}
button:focus-visible,a:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:2px solid var(--signal);outline-offset:2px;}
@media(max-width:820px){#cockpit-hero>div{grid-template-columns:1fr !important;}}
/* Signal-spine — the recurring cue, now on history rows */
.hi-spine{width:3px;border-radius:2px;align-self:stretch;flex-shrink:0;}
.history-item{gap:12px;}

/* Sidebar — grouped nav with icons (cockpit style) */
.nav-scroll{flex:1;display:flex;flex-direction:column;padding:0 8px;overflow-y:auto;}
.nav-scroll .sidebar-nav{flex:none;padding:0;}
.nav-cap{font-family:'Manrope',sans-serif;font-size:var(--fs-micro);letter-spacing:.09em;text-transform:uppercase;color:var(--ink3);font-weight:500;padding:14px 12px 6px;}
.nav-tab svg{width:16px;height:16px;flex-shrink:0;}

/* Tier sections — expandable parents (the spine). Plan decides lock; role decides items. */
.nav-sec{margin-top:2px;}
.nav-sec-hd{display:flex;align-items:center;justify-content:space-between;width:100%;background:none;border:none;cursor:pointer;text-align:left;font-family:'Manrope',sans-serif;font-size:var(--fs-micro);letter-spacing:.09em;text-transform:uppercase;color:var(--ink3);font-weight:500;padding:14px 12px 6px;}
.nav-sec-hd:hover{color:var(--text-secondary);}
.sec-chev{font-size:var(--fs-micro);transition:transform .15s;}
.nav-sec.collapsed .sec-items{display:none;}
.nav-sec.collapsed .sec-chev{transform:rotate(-90deg);}
.sec-lock{font-size:var(--fs-micro);margin-right:5px;}
.nav-sec.locked .sec-items .nav-tab{color:var(--ink-3);}

@media(max-width:820px){
  .nav-scroll{flex-direction:row;overflow-x:auto;padding:0 10px 10px;gap:4px;align-items:center;}
  .nav-scroll .sidebar-nav{flex-direction:row;gap:4px;}
  .nav-cap{display:none;}
  .nav-tab svg{display:none;}
  /* Flatten tier sections into the horizontal strip; headers hide, lock dim persists */
  .nav-sec,.nav-sec .sec-items{display:contents;}
  .nav-sec-hd{display:none;}
  .nav-sec.collapsed .sec-items{display:contents;}
}

/* ─── Print stylesheet (Sprint 2.3) ─────────────────────────────────────────
   Tuned for the printable report surfaces — Business Analytics (#tab-biz) and
   the Overview Report (which is its own window with its own inline print CSS).
   Screen theme is untouched: these rules apply ONLY when printing. Forces an
   ink-on-paper light surface, drops the app chrome, keeps bars/fills legible in
   monochrome, and stops blocks from splitting across page breaks. */
@media print {
  /* Light surface regardless of the on-screen theme */
  :root, :root[data-theme="dark"] {
    --content-bg:#ffffff; --card-bg:#ffffff; --card-border:#C9CED6; --stone3:#C9CED6; --stone2:#F0F2F5;
    --sidebar-bg:#ffffff; --surface:#ffffff; --surface-solid:#ffffff; --input-bg:#ffffff;
    --text-primary:#171F2C; --text-secondary:#3E4856; --text-muted:#666F7A;
    --ink:#171F2C; --ink2:#3E4856; --ink3:#666F7A; --signal:#14508C;
  }
  body { background:#fff !important; color:#171F2C !important; }
  /* Keep bar fills / coloured chips when the browser would otherwise drop backgrounds */
  * { -webkit-print-color-adjust:exact !important; print-color-adjust:exact !important; }
  /* Drop the app chrome — print only the content */
  .sidebar, #imp-banner, #demo-badge, .theme-toggle, button, .btn-primary, .btn-green, .btn-red, .no-print { display:none !important; }
  .app { display:block !important; }
  .content, .main, .page-wrap { margin:0 !important; padding:0 !important; width:100% !important; max-width:100% !important; }
  /* Only the active tab is on screen — print it edge to edge */
  .tab-panel:not(.active) { display:none !important; }
  /* Blocks must not split across a page break */
  .card, .vs-card, .roi-anchor, .bizstat, table, tr, .cockpit-hero, .hero-num, .hero-num-sm { break-inside:avoid; page-break-inside:avoid; }
  /* Board pack (Sprint 7 · TASK C): projected/printed in someone else's deck — force
     the light ink-on-paper read and keep each section whole. */
  .board-pack { max-width:100% !important; }
  .board-pack .vs-card { background:#fff !important; border-color:#C9CED6 !important; }
  h1, h2, h3, .card-title, .page-title { break-after:avoid; page-break-after:avoid; }
  /* Monochrome fallback: give filled bars a hairline so they read even without colour */
  #tab-biz [style*="background:var(--signal)"] { outline:1px solid #14508C; }
}

/* ── Visual system components (Sprint 3 · TASK B) — build once, reuse ─────────
   Light, single-theme, Complir language. Hierarchy is the step change: ONE
   display element per screen, page → card → row (one nesting level), receipts
   as attached source cards. Semantic colour (green/amber) only on chips + bar
   fills + thresholds. */
.vs { font-family:'Manrope',sans-serif; color:var(--ink); }
.vs-page { max-width:var(--col-max); margin:0 auto; }
/* Sprint 12 TASK C — density inversion: tighter vertical rhythm (16px vertical
   padding vs the old 24) so more signal fits above the fold; horizontal stays
   roomy. Inter-card gap tightens to match (see .vs-stack). 68ch caption cap kept. */
.vs-card { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-card); box-shadow:var(--shadow); padding:var(--sp-4) var(--sp-6); }

/* ─── Sprint 9 TASK G — motion + skeletons ───────────────────────────────────
   ONE motion token: 150ms ease-out. Cards fade + rise 4px on entry; drillable
   rows lift on hover (shadow + cursor) — that lift IS the drill affordance.
   Skeleton rows (matching the final layout) shimmer while data loads. All of it
   yields to prefers-reduced-motion. */
@keyframes vsRise { from { opacity:0; transform:translateY(4px); } to { opacity:1; transform:none; } }
.vs-card { animation:vsRise .15s ease-out both; }
.ftree details>summary, .recent-call, .metric-row.drillable, .hover-lift { transition:box-shadow .15s ease-out, background .15s ease-out; }
.ftree details>summary:hover, .recent-call:hover, .metric-row.drillable:hover, .hover-lift:hover { background:var(--surface-alt); box-shadow:var(--shadow-hover); cursor:pointer; border-radius:8px; }
@keyframes vsShimmer { 0% { background-position:-200px 0; } 100% { background-position:200px 0; } }
.sk { background:linear-gradient(90deg,var(--line-2) 25%,var(--surface-alt) 50%,var(--line-2) 75%); background-size:400px 100%; animation:vsShimmer 1.1s linear infinite; border-radius:6px; }
.sk-row { display:flex; align-items:center; gap:12px; padding:12px 0; border-bottom:1px solid var(--line); }
.sk-line { height:12px; }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration:.001ms !important; animation-iteration-count:1 !important; transition-duration:.001ms !important; } }

/* ─── Sprint 9 TASK H (H3) — visible keyboard focus rings from the accent token.
   :focus-visible so a mouse click shows no ring but keyboard navigation does; the
   explicit second rule wins over inputs that zero their outline. */
:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
.field input:focus-visible, .inp:focus-visible, .ss-inp:focus-visible, textarea.tx:focus-visible, .method-select:focus-visible, .vs-input:focus-visible { outline:2px solid var(--accent); outline-offset:1px; border-color:var(--accent); }
.vs-stack > * + * { margin-top:var(--sp-4); } /* TASK C — denser inter-card rhythm (was sp-6) */
.tnum { font-variant-numeric:tabular-nums; }

.vs-display { font-size:var(--fs-display); line-height:var(--lh-display); font-weight:700; letter-spacing:-.02em; color:var(--ink); font-variant-numeric:tabular-nums; }
.vs-h2 { font-size:var(--fs-h2); line-height:1.35; font-weight:600; letter-spacing:-.01em; color:var(--ink); margin:0; }
.vs-h3 { font-size:var(--fs-h3); line-height:1.4; font-weight:600; color:var(--ink); margin:0; }
.vs-eyebrow { font-size:var(--fs-eyebrow); line-height:1.2; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-3); }
/* Sprint 9 TASK F1 — explainer/caption paragraphs cap at 68ch for readability. */
.vs-caption { font-size:var(--fs-caption); line-height:1.4; color:var(--ink-3); max-width:68ch; }
/* TASK F2 — a question is a card TITLE, sentence-case, never an ALL-CAPS eyebrow. */
.hero-kpi .hk-eyebrow.is-question { text-transform:none; letter-spacing:normal; font-size:var(--fs-h3); font-weight:600; color:var(--ink); }
.vs-sub { font-size:var(--fs-body); line-height:1.5; color:var(--ink-2); }

/* ═══ PLATE 07 · THE FOUR WORDS AND THEIR MARKS ════════════════════════════════
   "Four silhouettes, not four fills — block, half block, diamond, open square. The
   same four appear in the bar, the legend and every table. Shape carries the meaning
   and colour only reinforces it, so the set survives greyscale, a board-pack print,
   and 8px."

   The mark is an inline SVG in currentColor, so ONE class sets both the shape's tone
   and the word's, and the pair can never disagree. There is no background fill: the
   plate's states are a mark plus a word on the card's own ground, not a lozenge —
   which is also why they sit correctly inside a table row without changing its pitch.
   Markup comes from public/state-vocabulary.js; nothing hand-writes these. */
.st { display:inline-flex; align-items:baseline; gap:6px; white-space:nowrap;
      font-family:var(--face-ui); font-size:var(--fs-row); line-height:var(--lh-row); color:var(--ink-2); }
.st .mk { flex:none; align-self:center; }
.st-word { font-weight:500; }
.st-detail { color:var(--ink-3); font-size:var(--fs-meta); }
.st-agreed       { color:var(--agreed); }
.st-not-agreed   { color:var(--not-agreed); }
.st-contradicted { color:var(--contradicted); }
/* "not asked" is the faintest object in the set by design — the plate flags this as its
   own open question. It is deliberately quiet because an unasked question is not a
   failure, but it still has to be findable, so the WORD takes ink-3 while only the mark
   takes the pale value. A state nobody can see is a state nobody acts on. */
.st-not-asked    { color:var(--ink-3); }
.st-not-asked .mk { color:var(--not-asked); }

/* PROVENANCE · OUTLINE CHIP · NEVER A STATE. Where a figure came from, not what the
   buyer did. Outline, never filled, and it carries no state silhouette — that
   separation is the whole reason the old table could not be learned. */
.pv { display:inline-flex; align-items:center; height:18px; padding:0 7px;
      border:1px solid var(--line); border-radius:var(--r-pill);
      font-family:var(--face-ui); font-size:var(--fs-meta); line-height:1;
      color:var(--ink-2); background:transparent; white-space:nowrap; }
.pv-agreed       { border-color:var(--agreed); color:var(--agreed); }
.pv-contradicted { border-color:var(--contradicted); color:var(--contradicted); }
.pv-neutral      { border-color:var(--line); color:var(--ink-2); }

/* status chip — the SOLID variant of the one Pill (see "THE PILL" above). 11px and 20px tall,
   matching the outline variant exactly, so the two read as one component in two states rather
   than as two components. */
.chip { display:inline-flex; align-items:center; gap:6px; height:20px; padding:0 9px; border-radius:var(--radius-chip); font-size:var(--fs-eyebrow); font-weight:600; line-height:1; white-space:nowrap; }
.chip::before { content:''; width:6px; height:6px; border-radius:50%; background:currentColor; flex-shrink:0; }
.chip-green { background:var(--green-bg); color:var(--green); }
.chip-amber { background:var(--amber-bg); color:var(--amber); }
.chip-neutral { background:var(--line-2); color:var(--ink-2); }
.chip-neutral::before { background:var(--ink-3); }
.chip-red { background:var(--red-bg); color:var(--red); }
.chip-plain::before { display:none; }
/* Sprint 9 TASK E — METRIC species: mono, neutral. A bare multiple (1x, 2x) is a
   number, not a status: no dot, no state colour. STATE (.chip above) stays tinted. */
.chip-metric { display:inline-flex; align-items:center; height:20px; padding:0 8px; border-radius:6px; font-family:'Manrope',sans-serif; font-size:var(--fs-caption); font-weight:600; line-height:1; white-space:nowrap; background:var(--surface-alt); color:var(--ink); font-variant-numeric:tabular-nums; }

/* hero KPI */
.hero-kpi .hk-eyebrow { font-size:var(--fs-eyebrow); font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-3); }
.hero-kpi .hk-num { font-size:var(--fs-display); line-height:var(--lh-display); font-weight:700; letter-spacing:-.02em; color:var(--ink); font-variant-numeric:tabular-nums; margin-top:var(--sp-1); }
.hero-kpi .hk-passport { font-size:var(--fs-caption); color:var(--ink-3); margin-top:var(--sp-2); }

/* source / receipt card — the signature */
.source-card { background:var(--surface-alt); border:1px solid var(--line); border-radius:var(--radius-input); padding:var(--sp-3) var(--sp-4); }
.source-card .sc-eyebrow { font-size:var(--fs-eyebrow); font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-3); }
.source-card .sc-quote { font-size:var(--fs-body); line-height:1.5; color:var(--ink-2); margin-top:var(--sp-1); }
.source-card .sc-origin { font-size:var(--fs-caption); color:var(--ink-3); margin-top:var(--sp-1); }
.source-card--accent { border-left:2px solid var(--accent); }

/* ── THE RECEIPT (Sprint 12 · TASK B; device: grey) — the evidence atom ───────
   The verbatim thing a buyer said, rendered as ONE designed object at every
   altitude (rep deal · coach row · tree drill · board pack · director hero) so a
   reader watches the same proof travel upward. The DEVICE is deliberately GREY:
   colour in Talqo means a STATE (green=measured, amber=assumption, red=past the
   threshold, green/red=commit/destroy). A receipt is not a state, so it carries
   no colour. --ink-3 is used as a border colour nowhere else, so the grey left
   rule is the unique tell (accent never was — 3px accent is on ~5 other spines).
   The rule stays a BORDER, not a tint, because receipts print and tints wash out.
   The device = 3px ink-3 rule + tinted grey ground + the ❝ mark; hierarchy is the
   dominant subtitle quote over caption-scale, muted metadata. Order fixed in
   receipt(): quote → attribution → claim → link. */
.receipt { position:relative; background:var(--surface-alt); border-left:3px solid var(--ink-3);
  border-radius:0 var(--radius-input) var(--radius-input) 0; padding:12px 16px 12px 20px;
  margin-top:var(--sp-2); font-variant-numeric:tabular-nums; break-inside:avoid; page-break-inside:avoid; }
.receipt::before { content:"\201C"; position:absolute; left:8px; top:5px; font-size:var(--fs-title); line-height:1;
  font-weight:700; color:var(--ink-3); opacity:.32; pointer-events:none; }
.receipt .receipt-quote { font-size:var(--fs-subtitle); line-height:var(--lh-subtitle); color:var(--ink); font-weight:500; }
.receipt .receipt-quote.is-absent { font-size:var(--fs-body); line-height:var(--lh-body); color:var(--ink-2); font-weight:400; }
.receipt .receipt-attr { font-size:var(--fs-caption); line-height:var(--lh-caption); color:var(--ink-3); margin-top:6px; }
.receipt .receipt-claim { display:inline-block; font-size:var(--fs-caption); line-height:var(--lh-caption);
  text-transform:uppercase; letter-spacing:.05em; color:var(--ink-2); font-weight:600; margin-top:6px; }
.receipt .receipt-link { margin-top:8px; }
.receipt .receipt-link .btn-tertiary { font-size:var(--fs-caption); padding:0; }
/* dark-surface variant — for a receipt sitting on a dark hero card */
.receipt.on-dark { background:rgba(255,255,255,.07); border-left-color:rgba(255,255,255,.55); }
.receipt.on-dark::before { color:#fff; opacity:.3; }
.receipt.on-dark .receipt-quote { color:#fff; }
.receipt.on-dark .receipt-attr { color:var(--on-dark-3); }
.receipt.on-dark .receipt-claim { color:var(--on-dark-2); }

/* defensible bar (≤640) */
.dbar { max-width:var(--bar-max); }
.dbar-head { display:flex; justify-content:space-between; align-items:baseline; gap:var(--sp-3); }
.dbar-label { font-size:var(--fs-body); font-weight:500; color:var(--ink-2); }
.dbar-value { font-size:var(--fs-h3); font-weight:600; color:var(--ink); font-variant-numeric:tabular-nums; }
.dbar-track { position:relative; height:10px; background:var(--line-2); border-radius:var(--radius-chip); margin-top:var(--sp-2); }
.dbar-fill { height:100%; background:var(--green-fill); border-radius:var(--radius-chip); }
.dbar-fill.amber { background:var(--amber-fill); }
.dbar-tick { position:absolute; top:-3px; bottom:-3px; width:2px; background:var(--amber-fill); border-radius:2px; }
.dbar-receipt { font-size:var(--fs-caption); color:var(--ink-3); margin-top:var(--sp-2); }

/* metric row (fixes orphaned-metadata rows) */
.metric-row { display:flex; align-items:flex-start; gap:var(--sp-4); padding:var(--sp-3) 0; border-bottom:1px solid var(--line); }
.metric-row:last-child { border-bottom:none; }
.metric-main { flex:1; min-width:0; }
.metric-name { display:flex; align-items:center; gap:var(--sp-2); font-size:var(--fs-body); font-weight:500; color:var(--ink); }
.metric-receipt { font-size:var(--fs-caption); color:var(--ink-3); margin-top:2px; }
.metric-figs { display:flex; align-items:baseline; gap:var(--sp-3); flex-shrink:0; font-variant-numeric:tabular-nums; }
.metric-figs .metric-val { font-size:var(--fs-h3); font-weight:600; color:var(--ink); }

/* empty state */
.vs-empty { font-size:var(--fs-body); color:var(--ink-3); }

/* number input */
.vs-input { font-family:'Manrope',sans-serif; font-size:var(--fs-body); font-variant-numeric:tabular-nums; padding:8px 12px; border:1px solid var(--input-border); border-radius:var(--radius-input); background:var(--surface); color:var(--ink); outline:none; }
.vs-input:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-ring); }

.unit-btn{border:none;background:none;cursor:pointer;font-family:'Manrope',sans-serif;font-size:var(--fs-body);font-weight:600;color:var(--ink-2);padding:6px 16px;border-radius:99px;transition:all .12s;}
.unit-btn.active{background:var(--surface);color:var(--ink);box-shadow:0 1px 3px rgba(0,0,0,0.08);}

/* TAL-024 — visually hidden, still announced. Used by the #a11y-status live region and
   by labels that would be visual noise where the placeholder already reads as the label. */
.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;}

/* ── ADMIN REDESIGN (B1) — reorganisation only, no new behaviour ─────────────
   Health strip is always visible above the tabs: the five facts an operator
   checks before anything else. The tabs quarantine Danger last and give the
   pending queue a badge, so a non-empty queue is visible without opening it. */
.adm-health{display:flex;flex-wrap:wrap;gap:0;border:1px solid var(--line);border-radius:var(--radius-input);background:var(--surface);margin-bottom:var(--sp-4);overflow:hidden;}
.adm-h{flex:1 1 0;min-width:150px;padding:11px 16px;border-right:1px solid var(--line);}
.adm-h:last-child{border-right:none;}
.adm-h-label{font-size:var(--fs-micro);color:var(--ink-3);text-transform:uppercase;letter-spacing:.07em;font-weight:600;}
.adm-h-val{font-size:var(--fs-subtitle);font-weight:700;color:var(--ink);margin-top:3px;letter-spacing:-.3px;}
.adm-h-sub{font-size:var(--fs-micro);color:var(--ink-3);margin-top:2px;}
.adm-h-val.warn{color:var(--amber);}

.adm-tabs{display:flex;gap:2px;border-bottom:1px solid var(--line);margin-bottom:var(--sp-4);flex-wrap:wrap;}
.adm-tab{background:none;border:none;border-bottom:2px solid transparent;padding:9px 15px;font-family:Manrope,sans-serif;font-size:var(--fs-body);font-weight:600;color:var(--ink-3);cursor:pointer;display:flex;align-items:center;gap:7px;}
.adm-tab:hover{color:var(--ink);}
.adm-tab.active{color:var(--ink);border-bottom-color:var(--accent);}
.adm-tab-danger{margin-left:auto;color:var(--red);}
.adm-tab-danger:hover{color:var(--red);}
.adm-tab-danger.active{color:var(--red);border-bottom-color:var(--red);}
.adm-badge{background:var(--amber);color:#fff;font-size:var(--fs-micro);font-weight:700;border-radius:99px;padding:1px 7px;min-width:18px;text-align:center;}
.adm-panel{display:none;}
.adm-panel.active{display:block;}

/* ── FOLD AFFORDANCE — STRUCTURAL, NEVER OPT-IN (RULED 2026-08-10) ────────────
   A fold may not exist without the affordance. The rule is keyed on <details> itself,
   so a new fold CANNOT omit it — there is no markup to remember and no span to forget.

   Why it had to become structural: the -webkit-details-marker reset below suppresses
   the native triangle GLOBALLY, and replacing it was opt-in markup. Predictably, four
   of the seven folds forgot — the director drill (rep row, team row, "How this is
   counted") and the coach explainer all rendered as plain headings that happened to be
   clickable. Two span mechanisms existed to do the same job (.adm-chev on one fold,
   .ftree-caret on two); both are retired here, because the fix for "three mechanisms
   and four omissions" is one mechanism nobody has to invoke.

   NOT retired, deliberately: `.expandable .chev` below. Those are div+role=button
   drawers, not <details> — no selector reaches both, and .chev is the working,
   documented primitive for that half. Two element types, two rules, zero opt-in each.
   The sidebar's .sec-chev is also left alone: its rotation sense is inverted (it turns
   when COLLAPSED, because nav sections ship open) and it is a nav control, not a fold.
   The glyph is pseudo-element content, matching .btn-tertiary::after — decorative, and
   <summary> already announces its own expanded state to assistive tech. */
summary::-webkit-details-marker{display:none;}
details>summary::before{content:'\25B8';display:inline-block;width:11px;margin-right:6px;color:var(--ink-3);font-size:.9em;transition:transform .15s;}
details[open]>summary::before{transform:rotate(90deg);}
/* The forecast tree's <summary> IS the grid container (.ftree-row, five columns), so a
   ::before on it becomes a GRID ITEM and steals the first column — which is exactly why
   an earlier blanket attempt was reverted rather than shipped. The chevron rides the
   first CELL instead, so it also sits inside that row's depth indent and keeps the
   hierarchy readable. */
.ftree details>summary::before{content:none;}
.ftree details>summary .ftree-name::before{content:'\25B8';display:inline-block;width:11px;margin-right:4px;color:var(--ink-3);font-size:var(--fs-micro);transition:transform .12s;}
.ftree details[open]>summary .ftree-name::before{transform:rotate(90deg);}

/* ── EXPAND AFFORDANCE PRIMITIVE (B2) ────────────────────────────────────────
   A surface that expands must LOOK like it expands. One class, used everywhere:
   pointer cursor, a chevron that rotates when open, and a hover tint so the
   target reads as interactive before the click, not after it. */
.expandable{cursor:pointer;transition:background .12s;}
.expandable:hover{background:var(--surface-alt);}
.expandable .chev{display:inline-block;transition:transform .15s;color:var(--ink-3);margin-right:6px;font-size:.9em;}
.expandable.open .chev{transform:rotate(90deg);}
.expandable:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;}

/* ── Period picker (calendar-style, ruled) ────────────────────────────────────
   A financial period is a calendar object: a year, a quarter inside it, a month
   inside that. The old flat <select> made the reader do that arithmetic. Quarters
   and months are laid out as they actually sit in the year, so picking one is a
   spatial act. Writes through the same setPeriod() the <select> used. */
/* .vs-card animates with fill-mode "both", so EVERY card keeps a transform after the
   animation ends and becomes its own stacking context. A popover inside a card therefore
   cannot paint above anything outside it, however high its own z-index: the card that hosts
   the picker has to be raised, not the popover. Without this the picker opens correctly and
   the next card down the page covers the quarter cells — visible, but not clickable. */
.popover-host{position:relative;z-index:60;}
.periodpick{position:relative;display:inline-block;}
.periodpick-btn{display:inline-flex;align-items:center;gap:8px;min-width:150px;justify-content:space-between;cursor:pointer;text-align:left;}
/* The period picker is a <button>, not a <select>, so appearance:none never reached it and it
   kept drawing a text "▾" beside four real selects wearing the SVG chevron — one control in
   every filter row that didn't match its neighbours. font-size:0 hides the glyph (it stays in
   the DOM, and it was already aria-hidden) and the same SVG is painted in its place, so the
   picker and the selects are now indistinguishable. Both period pickers use this. */
.periodpick-chev{color:var(--ink-3);font-size:0;width:12px;height:8px;flex:none;
  background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23666F7A' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center;}
.periodpick-btn{height:36px;}

/* THE COVERAGE CELL (premium pass, 24 Aug 2026 — item 7).
   This cell ran four things along one line: a bar, "0.9x", "/ 3.2x", and the words "under the
   line" — four competing elements at three sizes on one row, so the eye had nowhere to land and
   the qualifier read as if it were part of the number. Now the bar and the figure share the top
   line, because those are the two things a manager is comparing, and the qualifier drops to an
   11px sub-line, which is what it actually is: a reading of the figure, not a second figure. */
.covcell{display:flex;flex-direction:column;gap:1px;min-width:0;}
.covcell-fig{display:flex;align-items:baseline;gap:6px;white-space:nowrap;}
.covcell-mult{font-weight:700;font-variant-numeric:tabular-nums;font-size:var(--fs-caption);}
.covcell-mult.under{color:var(--amber);}
.covcell-line{font-size:var(--fs-micro);color:var(--ink-3);font-variant-numeric:tabular-nums;}
.covcell-qual{font-size:var(--fs-eyebrow);color:var(--ink-3);white-space:nowrap;}
.covcell-qual.under{color:var(--amber);font-weight:600;}
.periodpick-pop{position:absolute;z-index:60;top:calc(100% + 6px);left:0;min-width:268px;
  background:var(--surface);border:0.5px solid var(--line);border-radius:var(--radius-input);
  box-shadow:0 10px 30px rgba(0,0,0,.16);padding:12px;}
.periodpick-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;}
.periodpick-year{font-weight:700;font-size:var(--fs-subtitle);color:var(--ink);}
.periodpick-nav{background:transparent;border:0.5px solid var(--line);border-radius:var(--radius-input);
  color:var(--ink);cursor:pointer;padding:2px 10px;font-family:inherit;font-size:var(--fs-subtitle);line-height:1.4;}
.periodpick-nav:hover:not(:disabled){background:var(--surface-alt);}
.periodpick-nav:disabled{opacity:.35;cursor:default;}
.periodpick-label{font-size:var(--fs-micro);color:var(--ink-2);font-weight:600;margin:10px 0 5px;}
.periodpick-row{display:flex;}
.periodpick-grid{display:grid;gap:5px;}
.periodpick-grid.q{grid-template-columns:repeat(4,1fr);}
.periodpick-grid.m{grid-template-columns:repeat(4,1fr);}
.periodpick-cell{background:var(--surface-alt);border:0.5px solid transparent;border-radius:var(--radius-input);
  color:var(--ink);cursor:pointer;padding:7px 4px;font-family:inherit;font-size:var(--fs-caption);font-weight:600;
  transition:background .12s,border-color .12s;}
.periodpick-cell.wide{width:100%;}
.periodpick-cell:hover:not(:disabled){background:var(--surface);border-color:var(--line);}
.periodpick-cell:disabled{opacity:.3;cursor:default;}
.periodpick-cell.on{background:var(--accent);color:var(--on-accent);border-color:var(--accent);}
.periodpick-cell:focus-visible{outline:2px solid var(--accent);outline-offset:1px;}

/* Company & pipeline rows — the three per-company dropdowns used to sit bare in a row with
   no visible labels at all. Currency in particular read as an unlabelled "EUR" chip and
   operators could not find it. A header row names the columns; each cell repeats its own
   label for when the row wraps and the header is no longer above the right control. */
.company-row-head{font-size:var(--fs-micro);color:var(--ink3);font-weight:600;letter-spacing:.02em;text-transform:uppercase;padding-bottom:6px;}
.company-row-head .company-col{padding:0;}
.company-col{display:flex;flex-direction:column;gap:3px;min-width:104px;}
.company-col-lbl{font-size:var(--fs-micro);color:var(--ink3);font-weight:500;}
/* The header already names the columns on a wide row; the per-cell labels are the fallback
   for wrapped rows, so they hide when the header is doing the job. */
@media (min-width:760px){ .company-row:not(.company-row-head) .company-col-lbl{display:none;} }

/* The tenant a configuration write will land on, stated on the surfaces where the page-head
   company FILTER is hidden — so no admin sub-tab ever shows two company controls at once. */
.adm-scope-chip{display:inline-flex;align-items:center;gap:6px;background:var(--input-bg);border:0.5px solid var(--input-border);border-radius:var(--r);padding:7px 12px;font-size:var(--fs-caption);color:var(--ink2);white-space:nowrap;}

/* ── TRUST BAR — one 4-segment stacked bar that PARTITIONS open pipeline ───────────────
   Replaces a single-segment fill plus six numbers of prose. The segments are a partition,
   not a ranking: they sum to open pipeline exactly, which is the claim the bar is making
   and the thing tested. Colour runs confident → unknown left to right so the eye reads the
   trust gradient without needing the legend. */
.tbar { max-width:var(--bar-max); margin-top:var(--sp-5); }
.tbar-head { font-size:var(--fs-h3); font-weight:600; color:var(--ink); font-variant-numeric:tabular-nums; line-height:1.4; }
.tbar-track { display:flex; height:14px; border-radius:var(--radius-chip); overflow:hidden; margin-top:var(--sp-3); background:var(--line-2); }
.tbar-seg { height:100%; }
/* A segment can be a rounding sliver; keep it visible rather than letting it vanish. */
/* ── THE TRUST BAR — told apart by PATTERN (ruled 2026-08-11) ─────────────────
   This was the LAST accent-as-severity site and the only one where bands actually
   TOUCH: four segments, direct flex children of .tbar-track, no gap. It was parked
   out of the recolour slice deliberately, because its fix is a pattern design and
   not a colour swap, and it is the most prominent bar in the product.

   What was wrong, measured rather than asserted:
     --green-fill <-> --accent      2.01
     --accent     <-> --amber-fill  1.99
     --green-fill <-> --amber-fill  1.01   ← whenever the middle segment is ZERO,
       which trustBarHTML() produces by omitting zero-value segments and which
       test/trust-bar.test.js deliberately exercises. That is the exact pair
       DESIGN_PRIMITIVES calls "close to invisible for everyone else".
   The only thing separating any of them was a 1px white hairline. A hairline is
   not a pattern.

   The fix follows forecastBar (the house's own solved version of this problem):
   solid / hatched at 135deg, on the DARKER semantic tokens, never the -fill pair.
   Each band gets a treatment that does NOT depend on which neighbour it happens to
   have, so the partition survives any segment dropping to zero:

     s1 validated ................ SOLID  --green   6.21 on the track
     s2 price agreed, qual open .. HATCH  --green   6.21 (dark stripe)
     s3 analysed, not confirmed .. SOLID  --amber   6.01
     s4 unassessed ............... HATCH  --ink-3   4.59

   Hatching s2 in the SAME green as s1 is the point, not a compromise: that money
   IS price-validated, it is only the qualification that is open, so it belongs to
   green and the pattern says "partial". Blue said nothing at all.

   NOT copied from forecastBar: its unconfirmed band uses --line, which measures
   1.16 against this track. There it is the remainder; here s4 is a named band
   carrying real money, so it keeps --ink-3 and stays legible.
   Floors: 3.0 for a non-text boundary. All four clear it against the track.
   The divider is widened to 2px so a boundary is guaranteed structurally even
   where two neighbours are close in luminance. */
.tbar-seg + .tbar-seg { box-shadow:inset 2px 0 0 var(--surface); }
.tbar-s1 { background:var(--green); }
.tbar-s2 { background:repeating-linear-gradient(135deg,
             var(--green) 0 4px, color-mix(in srgb, var(--green) 55%, var(--surface)) 4px 8px); }
.tbar-s3 { background:var(--amber); }
.tbar-s4 { background:repeating-linear-gradient(135deg,
             var(--ink-3) 0 4px, color-mix(in srgb, var(--ink-3) 55%, var(--surface)) 4px 8px); }
/* The key mirrors the bar: a solid band gets a solid dot, a hatched band a hatched
   dot. A legend that flattens the pattern makes the reader learn two vocabularies. */
.tbar-dot.tbar-s2, .tbar-dot.tbar-s4 { background-size:6px 6px; }
.tbar-key { display:flex; flex-wrap:wrap; gap:var(--sp-2) var(--sp-5); margin-top:var(--sp-3); }
.tbar-item { display:flex; align-items:baseline; gap:7px; min-width:0; }
.tbar-dot { width:9px; height:9px; border-radius:3px; flex-shrink:0; position:relative; top:1px; }
.tbar-name { font-size:var(--fs-caption); color:var(--ink-2); }
.tbar-amt { font-size:var(--fs-caption); font-weight:600; color:var(--ink); font-variant-numeric:tabular-nums; }
.tbar-note { font-size:var(--fs-caption); color:var(--ink-3); margin-top:var(--sp-3); max-width:62ch; }
/* The evidence quote sits BELOW the bar and subordinate to it: the bar makes the claim,
   the quote proves one segment of it. */
.tbar-proof { margin-top:var(--sp-3); padding-left:var(--sp-3); border-left:2px solid var(--accent); }
.tbar-proof-lead { font-size:var(--fs-micro); color:var(--ink-3); text-transform:uppercase; letter-spacing:.04em; margin-bottom:5px; }
@media (max-width:520px){ .tbar-key { gap:var(--sp-2) var(--sp-3); } }

/* A deactivated account is STATED, not hidden: the row stays, the person stays attributed
   to their history, and the chip says why they can no longer sign in. */
.user-status-off { font-size:var(--fs-micro); font-weight:600; color:var(--ink-3); background:var(--line-2);
  border-radius:var(--radius-chip); padding:2px 8px; margin-right:8px; white-space:nowrap; }
.user-row.is-inactive .user-name { color:var(--ink-3); }

/* ── TARGET SETTING ────────────────────────────────────────────────────────────────────
   Allocation reads down: org line → team → the people in it. Mismatch is stated in colour
   but never blocks a save — an admin mid-reallocation is legitimately over-allocated for a
   few minutes, and a form that refuses to save is a form people work around. */
.tgt-org { display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--sp-2) var(--sp-3);
  padding:var(--sp-3) 0 var(--sp-4); border-bottom:1px solid var(--line); margin-bottom:var(--sp-2); }
.tgt-org-fig { font-size:var(--fs-h2); font-weight:700; color:var(--ink); font-variant-numeric:tabular-nums; }
.tgt-org-lab { font-size:var(--fs-caption); color:var(--ink-3); }
.tgt-org-state { flex-basis:100%; font-size:var(--fs-caption); color:var(--ink-2); }
.tgt-team { padding:var(--sp-3) 0; border-bottom:1px solid var(--line-2); }
.tgt-row { display:flex; align-items:center; gap:var(--sp-3); padding:3px 0; flex-wrap:wrap; }
.tgt-name { flex:1; min-width:160px; font-size:var(--fs-body); color:var(--ink); }
.tgt-row-team .tgt-name { font-weight:600; }
.tgt-row-rep .tgt-name { padding-left:var(--sp-4); color:var(--ink-2); }
.tgt-row-sum .tgt-name { padding-left:var(--sp-4); color:var(--ink-3); font-size:var(--fs-caption); }
.tgt-input { display:flex; align-items:center; gap:5px; min-width:200px; }
.tgt-in { width:130px; font-weight:600; font-variant-numeric:tabular-nums; }
.tgt-sum { font-weight:600; font-variant-numeric:tabular-nums; color:var(--ink-2); }
.tgt-yr { font-size:var(--fs-micro); color:var(--ink-3); }
.tgt-state { min-width:170px; font-size:var(--fs-caption); }
.tgt-ok    { color:var(--green); font-weight:600; }
.tgt-under { color:var(--amber); font-weight:600; }
.tgt-over  { color:var(--red); font-weight:600; }
.tgt-unset { color:var(--ink-3); }
.tgt-actions { display:flex; align-items:center; gap:var(--sp-3); margin-top:var(--sp-4); flex-wrap:wrap; }
@media (max-width:640px){ .tgt-state { min-width:0; flex-basis:100%; } }

/* "All dates" clears the deals close-date filter — full width above the year nav, because it
   is a way OUT of the filter rather than another period to pick. */
.periodpick-clear { display:block; width:100%; margin-bottom:8px; }

/* ITEM G — the gap and coverage-risk figures read as understated next to the numbers they
   qualify. Weight and colour only: no size jump, no layout change, so a loud figure cannot
   reflow the row it sits in. */
.fig-loud { font-weight:700; color:var(--amber); }
.fig-loud b { font-weight:700; }

/* Deals summary strip — one row of totals for exactly the rows listed below it. */
.dstrip { display:flex; flex-wrap:wrap; gap:var(--sp-3) var(--sp-6); align-items:baseline;
  padding:var(--sp-3) var(--sp-6); margin-bottom:var(--sp-3); }
.dstrip-cell { min-width:96px; }
.dstrip-lab { font-size:var(--fs-micro); color:var(--ink-3); text-transform:uppercase; letter-spacing:.04em; }
.dstrip-val { font-size:var(--fs-subtitle); font-weight:700; color:var(--ink); font-variant-numeric:tabular-nums; }
.dstrip-note { flex-basis:100%; font-size:var(--fs-caption); color:var(--ink-3); }

/* ── CALL HISTORY — a list, not a scroll ───────────────────────────────────────────────
   285 rows rendered as one uniform 9,751px column with no landmarks. The day is the thing
   people navigate by, so it became a heading; the per-row detail moved behind the house
   .expandable affordance so a row is one line until asked. */
/* The day-break should be FELT, not squinted at. 4px under the label made it read as part of
   the card beneath it rather than as a boundary between groups. --sp-8 above opens the break,
   --sp-3 below sets the label apart from the first card of its day. The rule under the label
   stays: it earns its place in the STUCK state, where the header sits over content scrolling
   beneath it and the air above has scrolled away. :first-child keeps the list from opening
   with 32px of nothing. */
.hist-day { position:sticky; top:0; z-index:2; background:var(--surface);
  font-size:var(--fs-micro); font-weight:700; color:var(--ink-3); text-transform:uppercase;
  letter-spacing:.04em; padding:var(--sp-2) 0; margin:var(--sp-8) 0 var(--sp-3);
  border-bottom:1px solid var(--line-2); }
.hist-day:first-child { margin-top:0; }
/* One line. The full summary is one click away in the call itself — a paragraph per row is
   what made 285 rows unreadable. */
.hi-oneline { display:-webkit-box; -webkit-line-clamp:1; -webkit-box-orient:vertical;
  overflow:hidden; text-overflow:ellipsis; }
.hist-more { display:inline-flex; align-items:center; gap:5px; margin-top:4px;
  font-size:var(--fs-micro); color:var(--ink-3); border-radius:var(--radius-chip); padding:2px 6px; }
.hist-pager { display:flex; align-items:center; justify-content:center; gap:var(--sp-4);
  padding:var(--sp-4) 0 var(--sp-2); flex-wrap:wrap; }
.hist-pager button[disabled] { opacity:.45; cursor:default; }

/* Forecast allocation — the summary line reads at a glance, the per-team detail folds. */
.alloc-more { display:inline-flex; align-items:center; gap:5px; margin-top:var(--sp-2);
  font-size:var(--fs-caption); color:var(--ink-3); border-radius:var(--radius-chip); padding:3px 7px; }

/* A drawer head that is also a DEMOTED HEADING. .alloc-more and .hist-more set a caption
   size, which from .vs-h3 (16px/--ink) would be two steps down, not one. This puts it back
   to exactly one: 13px/--ink-2, still bold enough to read as a heading. It must stay AFTER
   .alloc-more — equal specificity, so source order is what decides. */
.dh-sub { font-size:var(--fs-body); color:var(--ink-2); font-weight:600; }
.dh-sub:hover { color:var(--ink); }

/* TIER 1 — the raised card. .hero-kpi is already the class the app puts on the ONE card
   answering a screen's question, so elevation attaches to the meaning that already exists
   rather than inventing a second vocabulary for it. Everything else keeps --shadow (tier 2)
   or sits on --surface-alt with no shadow (tier 3). At most one of these per screen —
   test/elevation-tiers.test.js counts them. */
.vs-card.hero-kpi { box-shadow:var(--shadow-raised); }

/* ⓘ — an explainer that is AVAILABLE rather than always present. A button, not a hover
   tooltip: hover-only content is unreachable by keyboard and on touch, and this text is
   sometimes the reason a number is defensible. */
.infow { display:inline; position:relative; }
.info-dot { display:inline-flex; align-items:center; justify-content:center;
  width:16px; height:16px; margin-left:5px; padding:0; border-radius:50%;
  border:1px solid var(--line); background:var(--line-2); color:var(--ink-3);
  font-size:var(--fs-micro); font-weight:700; font-family:inherit; line-height:1; cursor:pointer;
  vertical-align:baseline; position:relative; top:2px; }
.info-dot:hover { color:var(--ink-2); border-color:var(--ink-3); }
.info-dot:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
.info-dot[aria-expanded="true"] { background:var(--accent); border-color:var(--accent); color:var(--on-accent); }
/* The note sits in flow rather than floating: a positioned popover inside a .vs-card is
   trapped by the card's stacking context — the same trap the period picker hit. */
/* The note must survive being attached to ANY host, so it neutralises the text treatment it
   would otherwise inherit. Attached to a .vs-h3 it came out semibold, which reads as another
   heading rather than as prose; attached to a table header it would come out in spaced
   capitals, since that row sets text-transform and letter-spacing. A note is always a note. */
.info-body { display:block; margin-top:var(--sp-2); padding:var(--sp-2) var(--sp-3);
  background:var(--line-2); border-left:2px solid var(--ink-3); border-radius:var(--r);
  font-size:var(--fs-caption); font-weight:400; line-height:1.5; text-transform:none;
  letter-spacing:normal; color:var(--ink-2); max-width:62ch; white-space:normal; }
.info-body[hidden] { display:none; }
/* Wide variant for the methodology blocks relocated off the cards in the premium pass — the
   text is longer than a one-line note because it IS the full definition, kept verbatim. */
.info-body-wide { max-width:70ch; }
.info-body-wide > div { max-width:none; }

/* ── PREMIUM PASS, 24 Aug 2026 — rows and figures ─────────────────────────────
   Methodology-coverage delta rows (defect 9a). The label used to render empty because the
   client read a field the API does not send; now it renders, and it gets a real column so a
   list of dimensions lines up instead of ragging. */
.dim-delta-row{display:flex;gap:10px;align-items:baseline;font-size:var(--fs-caption);padding:4px 0;}
.dim-delta-name{min-width:150px;text-transform:capitalize;color:var(--ink-2);}
.dim-delta-val{font-weight:600;font-variant-numeric:tabular-nums;min-width:46px;text-align:right;}
.dim-delta-mv{font-variant-numeric:tabular-nums;font-weight:600;}
/* A percentage that has not earned severity yet (defect 9b) reads quiet rather than alarming. */
.bchart-thin{color:var(--ink-3);font-weight:500;}
/* TABULAR FIGURES EVERYWHERE. body already sets font-variant-numeric, but tables, KPI numbers
   and any element with its own font-family declaration re-inherit the proportional default —
   which is why a column of figures did not line up on its decimal. Stated explicitly for every
   surface that shows numbers, so a digit is always one digit wide. */
table, th, td, .ftree-num, .bchart-pct, .hero-num, .hero-num-sm, .hk-num, .vs-metric, .metric-val,
.dbar-value, .proof-val, .stat-num, .chip-metric, .dim-score, .tbar-head, input[type=number] {
  font-variant-numeric:tabular-nums;
}

/* ── COMPOSITION — a toolbar and a grid ──────────────────────────────────────────────
   Diagnosis behind these two rules, measured at 1440px: the dashboard was seven full-width
   bands in one column, and the FOCAL POINT was the narrowest element on the page — the
   trust hero capped at 720px inside a 1149px column, with a 429px void beside it. The dead
   space was not excess whitespace; it was the absence of anything placed next to the thing
   that mattered. Two chrome cards (period, company) were also carrying the same visual
   weight as the figures they qualify.

   "Premium" for this product is COMPOSURE, not ornament: a real grid, a focal point that
   owns its column, and chrome that reads as chrome. Nothing here adds or removes
   information — every card, control and sentence is the same one, placed differently. */

.vs-toolbar { display:flex; align-items:center; gap:var(--sp-3) var(--sp-6); flex-wrap:wrap;
  padding:0 var(--sp-1) var(--sp-4); margin-bottom:var(--sp-6);
  border-bottom:1px solid var(--line); }
.vs-tool, .vs-tool-group { display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap; }
.vs-tool-lbl { font-size:var(--fs-micro); letter-spacing:.1em; text-transform:uppercase;
  font-weight:700; color:var(--ink-3); }
/* The qualifying sentences keep their words and stop competing for the row: they wrap to
   their own line and read as the footnotes they are. */
.vs-tool-note { flex-basis:100%; margin-top:var(--sp-1); }

/* The grid. 1.62 is the golden section, so the hero genuinely dominates its row instead of
   splitting it — a 50/50 split would make the focal point look like a peer. */
/* STRETCH, not start. With align-items:start the companion card ended far above the hero and
   recreated the void immediately below itself — the same dead space, one row down. Stretching
   puts that whitespace INSIDE the card, where it reads as deliberate margin rather than as a
   gap the layout forgot to fill. The card is the container; the empty space belongs to it. */
/* align-items:start, not stretch (premium pass, 24 Aug 2026 — item 7).
   Stretch made the shorter card in a two-card row grow to match its neighbour, so the dashboard's
   "The gap" card ran roughly 300px past its own content as empty white. Two blind reviewers
   measured that void by eye and one called the row "unbalanced — looks like nobody checked the
   page at this height". A card that ends where its content ends reads as composed; a card padded
   out to match a taller sibling reads as unfinished. */
.vs-row { display:grid; gap:var(--sp-6); align-items:start; }
.vs-row.split-wide { grid-template-columns:minmax(0,1.62fr) minmax(0,1fr); }
.vs-row.split-even { grid-template-columns:repeat(2,minmax(0,1fr)); }
/* Below this the columns are narrower than the content they hold, so the grid collapses
   rather than squeezing. No information is hidden at any width. */
/* The modifier classes are specificity 0-2-0, so a bare .vs-row here would LOSE to them
   and the collapse would silently never fire — caught in a screenshot at 760px, where the
   grid was still two columns. Match the modifiers directly. */
@media (max-width:1080px) {
  .vs-row.split-wide, .vs-row.split-even { grid-template-columns:minmax(0,1fr); }
}

/* ── THE ERROR STATE ─────────────────────────────────────────────────────────────────
   Deliberately NOT red. Red means risk in this product (a deal at risk, a gap past the
   threshold) and a failed request is not a risk — it is an absence with a retry. Colouring
   it red would put a semantic weight on a network blip that the semantics reserve for money.
   So it reads as the recessed tier-3 surface it is: present, calm, and clearly not data. */
.vs-error { background:var(--surface-alt); border-left:2px solid var(--ink-3);
  border-radius:var(--r); padding:var(--sp-3) var(--sp-4); max-width:62ch; }
.vs-error-msg { font-size:var(--fs-body); color:var(--ink-2); line-height:1.55; }
.vs-error button[disabled] { opacity:.6; cursor:default; }

/* The manager picker sits in the roster row, beside the person it is about. Narrow enough
   that it reads as an attribute of the row rather than as the row's main event. */
.user-mgr { width:auto; min-width:150px; max-width:190px; font-size:var(--fs-micro);
  padding:3px 8px; margin-right:var(--sp-2); }
.user-mgr-saved { font-size:var(--fs-micro); color:var(--green); margin-right:var(--sp-2); }


/* ══════════════════════════════════════════════════════════════════════════════════════
   PART 2 — THE EXPRESSIVE LAYER
   Motion, one card system, and the chart primitives. Every rule here obeys two constraints
   that outrank how it looks: an animation may only move transform/opacity (so it stays on
   the compositor thread and can never shift layout), and no visual may imply a precision or
   a trend the underlying data does not have.
   ══════════════════════════════════════════════════════════════════════════════════════ */

/* ── MOTION TOKENS ────────────────────────────────────────────────────────────────────
   Two durations and two curves, because a system with five of each is not a system. The
   curves are the CSS equivalents of the house easing: decelerate for things arriving,
   in-out for things moving between two known places. Nothing bounces — an overshoot on a
   dense data table reads as sloppy, and this product is almost entirely dense data. */
:root{
  --dur-micro:150ms;          /* hover feedback, focus rings, chip states */
  --dur-move:220ms;           /* something changing place or size */
  --dur-enter:320ms;          /* something arriving on screen */
  --ease-out:cubic-bezier(.2,0,0,1);
  --ease-inout:cubic-bezier(.4,0,.2,1);
}

/* The single most important rule in this block. Motion is an enhancement; for a reader who
   has asked their OS for less of it, every duration collapses to almost nothing rather than
   being disabled outright — a 0.01ms transition still fires transitionend, so nothing that
   waits on a transition can hang. */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important;
  }
}

/* ── ONE CARD SYSTEM ──────────────────────────────────────────────────────────────────
   There were four: .vs-card (the Signal system) and three legacy aliases — .card, .rcard,
   .ss — which differed from each other ONLY by margin-bottom (12/8/14px). That is an
   accident, not an intent, so they collapse into one treatment and keep their own spacing
   as a documented modifier. Aliasing in CSS rather than rewriting 355 markup sites means
   the migration cannot break a single template.
   THE TIER RULE IS UNCHANGED (DESIGN_PRIMITIVES "Elevation"): tier 1 = --shadow-raised on
   the one card answering the screen's question, tier 2 = --shadow, tier 3 = --surface-alt
   and no shadow. These aliases are all tier 2 by default. */
.card, .rcard, .ss{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius-card);
  box-shadow:var(--shadow);
  overflow:hidden;
  transition:box-shadow var(--dur-micro) var(--ease-out),
             border-color var(--dur-micro) var(--ease-out);
}
.card{margin-bottom:var(--sp-3);}
.rcard{margin-bottom:var(--sp-2);}
.ss{margin-bottom:var(--sp-4);}

/* Hover belongs to cards that DO something. A card you cannot click must not lift, or the
   product promises an interaction it does not have. Displacement is deliberately absent:
   these sit in dense lists where a 4px lift makes neighbours look misaligned. */
.card[onclick]:hover, .rcard[onclick]:hover, .vs-card[onclick]:hover,
.card.is-clickable:hover, .vs-card.is-clickable:hover{
  box-shadow:var(--shadow-hover);
  border-color:var(--line);
  cursor:pointer;
}

/* ── ARRIVAL ──────────────────────────────────────────────────────────────────────────
   A short fade+rise on first paint, 8px so it reads as a fade rather than a slide. Applied
   only to top-level cards; staggering every row in a 23-item call list would make the last
   row arrive noticeably late, which is the "sluggish" failure mode. */
@keyframes vsRise{from{opacity:0;transform:translateY(8px);}to{opacity:1;transform:none;}}
.vs-card{animation:vsRise var(--dur-enter) var(--ease-out) both;}

/* ── CHART PRIMITIVES ─────────────────────────────────────────────────────────────────
   Everything below is inline SVG drawn from real numbers. No chart library, no canvas, no
   external request. */

/* BULLET BAR — performance against a target, in one line.
   Chosen because it is the one KPI form that grades AAA for accessibility, and the reason
   it does is the same reason it suits this product: the number is ALWAYS present as text,
   so the bar is a second reading of a fact the user can already see rather than the only
   way to read it. Colour never carries the meaning alone. */
.bullet{display:flex;align-items:center;gap:var(--sp-3);}
.bullet-track{
  position:relative;flex:1;min-width:80px;height:8px;
  background:var(--line-2);border-radius:999px;overflow:hidden;
}
.bullet-fill{
  position:absolute;inset-block:0;left:0;border-radius:999px;
  background:var(--accent);
  transform-origin:left center;
  animation:bulletGrow var(--dur-enter) var(--ease-out) both;
}
@keyframes bulletGrow{from{transform:scaleX(0);}to{transform:scaleX(1);}}
.bullet-fill.is-good{background:var(--green-fill);}
.bullet-fill.is-warn{background:var(--amber-fill);}
.bullet-fill.is-risk{background:var(--red);}
/* The target marker. A hard line, not a gradient: a target is a specific number, and a soft
   edge would imply a range it does not have. */
.bullet-target{
  position:absolute;top:-2px;bottom:-2px;width:2px;background:var(--ink);
  border-radius:1px;
}
.bullet-val{font-size:var(--fs-body);font-weight:700;color:var(--ink);white-space:nowrap;}
.bullet-meta{font-size:var(--fs-micro);color:var(--ink-3);white-space:nowrap;}

/* SPARKLINE — a shape, never a claim.
   Deliberately unlabelled and unaxised: it shows direction over a stated window and nothing
   else, and it is only ever rendered beside the number it summarises. The guard that it must
   have at least four real points lives in the JS, because "fewer than four points is not a
   trend" is a fact about data, not about styling. */
.spark{display:block;overflow:visible;}
.spark-line{fill:none;stroke:var(--ink-3);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;}
.spark-line.is-good{stroke:var(--green-fill);}
.spark-line.is-risk{stroke:var(--red);}
.spark-area{fill:currentColor;opacity:.10;}
.spark-dot{fill:var(--surface);stroke-width:1.5;}
/* The flat case is drawn flat and says so. A renderer that jitters a flat series to make it
   look "alive" is fabricating movement, which is the one thing this product may never do.
   PREMIUM PASS, 24 Aug 2026: the brief asked to "kill the dotted-line placeholder trend" here.
   It is not a placeholder — it is a real series that genuinely has not moved, and replacing it
   with a drawn curve would be inventing the movement this comment forbids. So the honesty is
   untouched and only the LEGIBILITY changed: at 1.5px, 80% opacity and a 2-3 dash it read as a
   stray hairline rule rather than a data mark, which is why it was mistaken for a placeholder.
   A longer dash at full opacity reads as a deliberately plotted flat line. */
.spark-flat{stroke:var(--ink-3);stroke-width:2;stroke-dasharray:5 4;stroke-linecap:round;opacity:1;}

/* FORECAST BAND — confirmed vs likely vs unconfirmed.
   Distinguished by STYLE first and colour second: solid for what a buyer actually accepted,
   dashed for what is only likely. A colourblind reader gets the same three-way split. */
.fbar{display:flex;height:10px;border-radius:999px;overflow:hidden;background:var(--line-2);}
.fbar-seg{height:100%;transition:flex-basis var(--dur-move) var(--ease-out);}
/* The DARKER semantic greens/ambers, not the -fill variants. The -fill tokens were solved
   against --surface (3.11 and 3.14) and clear it; against the bar TRACK they land at 2.73
   and 2.76, under the 3.0 floor for a non-text boundary. Measured, not assumed. */
.fbar-confirmed{background:var(--green);}
.fbar-likely{
  background:repeating-linear-gradient(135deg,
    var(--amber) 0 4px, color-mix(in srgb, var(--amber) 55%, var(--surface)) 4px 8px);
}
.fbar-unconfirmed{background:var(--line);}
.fbar-key{display:flex;gap:var(--sp-3);flex-wrap:wrap;margin-top:var(--sp-2);}
.fbar-key span{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-micro);color:var(--ink-3);}
/* The key swatches repeat the bar exactly, hatch included, so the legend and the bar are
   the same object read twice rather than two things that might drift apart. */
.fbar-key i{width:10px;height:10px;border-radius:3px;display:inline-block;flex:none;border:1px solid color-mix(in srgb, var(--ink) 12%, transparent);}

/* NUMBER TRANSITIONS — the count-up is opt-in and capped.
   It exists to draw the eye to a figure that CHANGED, not to make every number perform on
   arrival. Anything that animates is still the true final value in the DOM from the first
   frame, so a screen reader and a paused animation both read the fact, never a way-point. */
.num-anim{font-variant-numeric:tabular-nums;}

/* ── FOCUS ────────────────────────────────────────────────────────────────────────────
   Visible, token-driven, and never removed. :focus-visible so a mouse user does not see it
   on click but a keyboard user always does. */
:where(button,a,select,input,textarea,[tabindex]):focus-visible{
  outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--radius-input);
}

/* COACH HOME — the companion column reaches its row.
   Scoped to .coach-split on purpose: forcing height:100% on every .vs-card would stretch
   cards in grids that were composed to sit at their natural height, which trades one
   misalignment for a dozen. */
.coach-split > *{min-width:0;}
.coach-split > .vs-card,
.coach-split > * > .vs-card{height:100%;}

/* ── THE HOUSE NOTICE (RB4) ────────────────────────────────────────────────────────────
   Replaces alert(). Bottom-left, out of the way of the primary action, which on every screen
   in this product sits top or centre. Never red: red means RISK here — a deal at risk, a gap
   past threshold — and a write that did not land is an absence, not a risk. */
.notice-host{
  position:fixed; left:var(--sp-6); bottom:var(--sp-6); z-index:1200;
  display:flex; flex-direction:column; gap:var(--sp-2);
  max-width:min(420px, calc(100vw - var(--sp-8))); pointer-events:none;
}
.notice{
  pointer-events:auto;
  display:flex; align-items:flex-start; gap:var(--sp-3);
  background:var(--surface); border:1px solid var(--line);
  border-left:2px solid var(--ink-3);
  border-radius:var(--radius-card); box-shadow:var(--shadow-raised);
  padding:var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4);
  animation:noticeIn var(--dur-enter) var(--ease-out) both;
}
@keyframes noticeIn{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}
/* A receipt confirms something that WORKED, so it carries the confirmed semantic. A blocked
   notice deliberately does not carry red — it takes the same recessed treatment as .vs-error. */
.notice-done{border-left-color:var(--green);}
.notice-msg{font-size:var(--fs-body);color:var(--ink);line-height:1.5;flex:1;min-width:0;}
.notice-x{
  flex:none; background:none; border:0; cursor:pointer;
  font-size:var(--fs-subtitle); line-height:1; color:var(--ink-3);
  padding:2px 4px; border-radius:var(--radius-input);
}
.notice-x:hover{color:var(--ink);}

/* A validation nudge sits on the control. Recessed, not red — see above. */
.field-note{
  margin-top:var(--sp-1);
  font-size:var(--fs-caption); color:var(--ink-2); line-height:1.45;
  border-left:2px solid var(--ink-3); padding-left:var(--sp-2);
}

/* B1 — the MONTH header, for sparse older spans. Lighter than a day header on purpose: it is a
   coarser boundary, and giving it the same weight would trade one kind of chrome for another.
   The row still prints its own date, so collapsing a sparse span loses nothing but repetition. */
.hist-day.hist-month{
  font-size:var(--fs-caption); color:var(--ink-2); text-transform:none; letter-spacing:0;
  border-bottom:1px solid var(--line); margin:var(--sp-8) 0 var(--sp-2);
}
