/* ════════════════════════════════════════════════════════════════════════════
   cfin-tokens.css — THE PUBLIC-SITE LAYOUT TOKEN LAYER
   Spec: docs/PUBLIC-SITE-DESIGN-SPEC.md §4 (Spacing and rhythm)
   ════════════════════════════════════════════════════════════════════════════

   WHY THIS FILE EXISTS
   ────────────────────
   The spec declares `--container`, `--gutter` and `--measure` as the tokens that
   make every public page share one content edge. They were only ever written in
   the spec DOCUMENT — measured 2026-08-14, all three resolved to UNSET on all 11
   public pages (landing, /performance, the four legal pages, register, the auth
   shell, /registration/thankyou, /payment/success and the error shell). Every
   consumer therefore fell through to its own hard-coded fallback, which is the
   direct mechanism behind three pages showing three different content edges.

   There is NO single stylesheet common to all five public shells:

     landing      landing.css · site-footer.css · cfin-disclose.css · generated tokens
     performance  perf-page.css · site-footer.css · theme.css
     legal (×4)   auth.css · legal.css · site-footer.css · theme.css · cfin-disclose.css
     auth/paysuccess/thankyou   auth.css · theme.css · cfin-disclose.css
     error        auth.css · theme.css · cfin-disclose.css

   The intersection is empty — `theme.css` covers everything EXCEPT landing, and
   `site-footer.css` covers everything EXCEPT the auth/error shells. Defining the
   tokens twice (once in each) is what lets them drift, which is the failure mode
   this file exists to end. So: ONE definition, in a file with no other job,
   linked FIRST from all five shells.

   ⚠ COLOUR TOKENS DO NOT LIVE HERE. `--color-accent` and the `--cfin-*` brand
   tokens are GENERATED from frontend/src/shared/theme/themeVariants.js into
   static/css/generated/cfin-theme.tokens.css by `npm run variants:build`. That
   artifact is the source of truth and is now linked from every public shell
   alongside this file. Never hand-copy a colour token into this file — a second
   definition would out-cascade the generated one on whichever pages load it
   later, and the host-variant system (www = CFIN, trade = Iris) would silently
   stop working on those pages.
   ──────────────────────────────────────────────────────────────────────────── */

:root {
    /* Max content column width. Consumers pair it with `margin-inline: auto`. */
    --container: 1200px;

    /* Page side gutter: 16px at 320-400, growing to 32px from 800 up.
       `clamp(1rem, 4vw, 2rem)` — spec §4 verbatim. */
    --gutter: clamp(1rem, 4vw, 2rem);

    /* Max prose line length. The disclosure primitive already reads this via
       `var(--measure, 68ch)`; until now it only ever got the fallback. */
    --measure: 68ch;

    /* Vertical section rhythm — spec §4. Declared for consumers that want it;
       landing.css keeps its own tuned `clamp(44px, 6.5vw, 84px)` density pass
       and is deliberately NOT retrofitted here (see the report). */
    --section-y: clamp(3rem, 8vw, 7rem);

    /* ════════════════════════════════════════════════════════════════════════
       THE TYPE SCALE — spec §3, verbatim.
       ════════════════════════════════════════════════════════════════════════
       ⚠ ADDING THESE IS PROVABLY A NO-OP FOR EVERY EXISTING CONSUMER, which is
       why it is safe to land them in the shared layer rather than per-shell.
       Grepped 2026-08-15, only two files read a `--type-*` name today and BOTH
       pass the spec value as their own fallback:
           cfin-disclose.css:382,450  var(--type-body,    clamp(0.9375rem, 0.9rem + 0.2vw, 1.0625rem))
           landing.css:9697,11705     var(--type-body-sm, 0.875rem)
       The declarations below are byte-identical to those fallbacks, so the
       computed value on the landing page and inside the disclosure primitive
       does not move by a pixel. Everything else picks them up only when a
       stylesheet is edited to consume one — which is the point: a shell can
       adopt the scale a rule at a time instead of in one unreviewable sweep.

       Fluid by construction (spec §2: "a layout with no media query cannot
       break at an untested width"). Body copy never below 15px, captions never
       below 12px — 11px is banned on the public site. */
    --type-caption:  0.75rem;                                     /* 12px floor */
    /* ── --type-meta: THE STEP THE PAGE ALREADY HAD, UNNAMED ──────────────
       MEASURED on the rendered landing DOM, 1440 dark, 2026-08-21: the scale
       above jumps 12px -> 14px with nothing between, and ~200 body nodes fell
       into that gap and invented NINE values to fill it — 12.5, 12.6, 12.75,
       13, 13.25, 13.33, 13.5, 13.6 and 13.8 were all in simultaneous use for
       ordinary label/annotation copy. The fractional ones are the proof that
       none of them was a decision: 13.3333px is Chrome's UNSTYLED <button>
       default (.faq-more sets no size at all), 13.25/12.75 are two halves of
       one .fee-rule component, and 13.6/13.8 are `em`/`vw` compounding.
       A gap in a scale does not stay empty — it gets filled at random. So the
       gap gets a name, ONE step, and the strays collapse onto it.
       0.8125rem = 13px: the modal value of the band (83 nodes were already
       exactly 13px), so naming it moves the FEWEST pixels of any choice. */
    --type-meta:     0.8125rem;                                   /* 13px */
    --type-body-sm:  0.875rem;                                    /* 14px */
    --type-body:     clamp(0.9375rem, 0.9rem + 0.2vw, 1.0625rem); /* 15 → 17 */
    --type-lead:     clamp(1.0625rem, 1rem + 0.4vw, 1.25rem);     /* 17 → 20 */
    --type-h3:       clamp(1.1875rem, 1.05rem + 0.6vw, 1.5rem);   /* 19 → 24 */
    --type-h2:       clamp(1.5rem, 1.2rem + 1.4vw, 2.125rem);     /* 24 → 34 */
    --type-h1:       clamp(1.875rem, 1.4rem + 2.6vw, 3rem);       /* 30 → 48 */
    --type-display:  clamp(2.25rem, 1.6rem + 4vw, 4rem);          /* 36 → 64 */

    /* Line heights, so "1.6 for body / 1.25 for headings" (spec §3) is a token
       and not a number retyped in forty places. */
    --leading-body: 1.6;
    --leading-head: 1.25;

    /* ════════════════════════════════════════════════════════════════════════
       CARD AND CONTROL ANATOMY — MEASURED off the landing page, 2026-08-15.
       ════════════════════════════════════════════════════════════════════════
       These exist because the two public shells ship two different radius
       scales and neither is wrong on its own — they simply are not the same
       site. Measured on the rig at 1440, dark:

           landing (landing.css)   --radius 12px  --radius-lg 20px
           theme.css shells        --radius  8px  --radius-lg 12px
             (performance, the four legal pages, register, the auth/error shell)

       So `border-radius: var(--radius-lg)` — which reads like one decision —
       draws a 20px corner on the landing page and a 12px corner on /register.
       That is the mechanism behind "card radius 16 / 12 / 20 depending on the
       page" and it cannot be fixed by editing either scale: remapping
       --radius-lg in theme.css would move every corner in the dashboard and
       the admin too, and remapping it in landing.css is another owner's file.

       The fix is a NAME OF ITS OWN for the public-site card, set from the
       landing page's measured values, consumed only by the public shells.
       Measured source: `.floating-card` at 1440 dark —
           border-radius 20px
           background    rgba(44,44,46,0.72)   (= --glass-bg, dark half)
           border        1px rgba(255,255,255,0.12)  (= --glass-border)
           box-shadow    0 8px 32px rgba(0,0,0,0.3)

       ⚠ NO --card-bg / --card-border HERE, ON PURPOSE. Both already exist as
       --glass-bg / --glass-border and are ALREADY correct per shell; adding a
       second name for them is how tokens drift (this file's own header note).
       Only the two values that genuinely disagree get a name. */
    --card-radius: 20px;

    /* Every primary and secondary button on the public site is a pill. Measured
       landing: `.btn-primary`, `.btn-secondary` and `.nav-cta` are all 999px,
       while the auth shell ships 8px / 12px / 16px on the same control. 999px
       and not 50%: a percentage radius on a wide button draws an ellipse. */
    --btn-radius: 999px;

    /* The landing primary CTA, so a "join" button looks the same wherever the
       funnel puts it. Stops are landing's measured literals rather than
       var(--profit)/var(--teal): those two resolve LIGHTER in dark mode, and
       the button's ink (--btn-primary-ink) is a near-black that needs the
       saturated ends to clear AA. Measured white-on-… is not the test here —
       the ink is dark ON the gradient: rgb(6,40,20) on rgb(82,202,116) = 8.6:1,
       on rgb(90,200,250) = 9.9:1, so both ends pass at both stops. */
    --btn-primary-grad: linear-gradient(135deg, rgb(82, 202, 116) 0%, rgb(90, 200, 250) 100%);
    --btn-primary-ink: rgb(6, 40, 20);

    /* ════════════════════════════════════════════════════════════════════════
       THE FLOATING POPOUT SURFACE — ONE RECIPE, TWO IMPLEMENTATIONS
       CEO 2026-08-15: "There is a difference between the popout windows on the
       performance page ... and the ones on the landing page. Make sure they are
       not see-through and look like the ones on the performance page."
       ════════════════════════════════════════════════════════════════════════
       The site ships TWO floating explain-panels and they were never the same
       control. MEASURED on the rig, dark, 390px, before this block existed:

         .perf-explpop          .cfin-disclose popout (landing/legal/auth)
         ───────────────────    ──────────────────────────────────────────
         rgba(44,44,46,0.72)    rgb(50,50,52)          fill
         1px + 3px green LEFT   2px block / 1px inline + green block edge
         12px                   20px                   radius
         --glass-shadow +24px   --shadow-xl +40px      shadow
         11px 13px              12px 14px              padding
         300px                  360px                  width cap
         12.48px body           17px body              prose

       ⚠ THE TRANSLUCENT ONE IS /performance, NOT THE LANDING PAGE. Screenshot
       evidence, 390 dark: the page's own prose reads straight THROUGH the
       .perf-explpop and collides with the panel's text. Its rule asks for
       `var(--glass-bg, var(--surface-1, ...))` and the comment above it calls
       --surface-1 (= --bg-elevated, opaque) the intent — but --glass-bg is
       defined by theme.css on every shell that loads .perf-explpop, so the
       opaque fallback is DEAD CODE and the 0.72-alpha value always wins. A
       fallback cannot be a decision; only the FIRST link in the chain is.
       So the fill is named here, first link, opaque, and both panels read it.

       The landing popout's own contribution to "see-through" was the 135deg
       green wash it composited on top of its opaque base: a tinted sheen over
       one corner reads as something showing through, whatever the alpha says.
       It is gone; the fill is flat, like /performance's.

       ⚠ NO backdrop-filter, EVER, on anything built from these. --glass-blur is
       0px sitewide by decision (theme.css:276-284 records blur on scrolling
       surfaces as the cause of the "laggy" reports). The glass idiom here is
       the hairline border plus the layered shadow, not blur and not alpha.

       WHY THESE LIVE IN THIS FILE. /performance does NOT link
       cfin-disclose.css and landing does NOT link theme.css — this file is the
       only stylesheet both shells load (header note above), so it is the only
       place a shared value cannot drift back apart. */

    /* ⚠ SURFACE, HAIRLINE AND SHADOW ARE LITERALS, NOT var(--bg-elevated) /
       var(--glass-border) / var(--glass-shadow). Those three names look shared
       and are not: landing reads them from the generated token artifact and
       /performance from theme.css, and MEASURED 2026-08-15 at 390 light they
       disagree — surface rgb(229,229,234) vs rgb(238,238,243), hairline
       rgba(0,0,0,.14) vs rgba(0,0,0,.10), and two completely different
       --glass-shadow recipes (`0 4px 16px/.08 + 0 1px 3px/.06` vs
       `0 8px 32px/.06`). Routing both panels through the same NAME would still
       have drawn two different panels. This file's own rule (see --card-radius
       above) is that a value which genuinely disagrees gets a literal here.

       The light surface is rgb(229,229,234) — theme.css's own --bg-tertiary,
       not an invented colour — and NOT the 238 that /performance resolved to.
       Both page grounds are pure rgb(255,255,255) in light (measured, both
       shells), so 238 is a 7% step off the page and 229 is a 10% one: on the
       one control the CEO called see-through, take the separation. The light
       shadow is likewise lifted from .06 to .10 alpha so a floating panel on a
       white page is actually delineated. Dark needs no such argument: both
       shells already agreed on rgb(50,50,52) over an rgb(28,28,30) ground. */
    --cfin-pop-bg: rgb(50, 50, 52);
    --cfin-pop-border: rgba(255, 255, 255, 0.12);
    /* The 3px brand-green rule down the leading edge — .perf-explpop's signature
       and the thing that ties the panel to the green (i) that opened it. This
       one STAYS a var: --profit is AA-tuned per scheme (it darkens to
       rgb(21,118,53) on white) and the two shells agree on it to within a
       single RGB unit. */
    --cfin-pop-rule: var(--profit, rgb(82, 202, 116));
    --cfin-pop-rule-w: 3px;
    /* 12px, NOT var(--radius-lg): --radius-lg is 12px in theme.css and 20px in
       landing.css (see --card-radius above), which is precisely how one control
       came to draw two different corners. A literal cannot drift. */
    --cfin-pop-radius: 12px;
    --cfin-pop-pad-y: 11px;
    --cfin-pop-pad-x: 13px;
    /* theme.css's dark --glass-shadow verbatim (the /performance one the CEO
       pointed at), plus the brand glow. The insets ARE the glass idiom now that
       there is no alpha and no blur left to carry it. */
    --cfin-pop-shadow: 0 8px 32px rgba(0, 0, 0, 0.3),
                       inset 0 1px 0 rgba(255, 255, 255, 0.12),
                       inset 0 -1px 0 rgba(255, 255, 255, 0.04),
                       0 0 24px rgba(var(--profit-rgb, 82, 202, 116), 0.12);
    /* 360px, clamped to the viewport with the 12px margin both scripts already
       enforce. At 320 that is 296px of readable panel. Two names because the
       disclose popout clamps against the SCRIPT's clientWidth measurement (it
       excludes a classic scrollbar, 100vw does not), so it needs the raw cap. */
    --cfin-pop-max-w: 360px;
    --cfin-pop-max: min(var(--cfin-pop-max-w, 360px), calc(100vw - 24px));
}

/* The three values above are the DARK halves; these are the light ones. Both
   scopes are written out — the `.light` class for an explicit choice and the
   media query for the OS default — which is the same three-scope shape
   theme.css:305/445 and cfin-disclose.css:200/208 already use. A popout is the
   one surface on this site where getting the scheme wrong is not cosmetic: it
   floats over prose, so a dark box on a white page is unreadable, not just
   ugly. */
:root.light {
    --cfin-pop-bg: rgb(229, 229, 234);
    --cfin-pop-border: rgba(0, 0, 0, 0.12);
    --cfin-pop-shadow: 0 8px 32px rgba(0, 0, 0, 0.10),
                       inset 0 1px 0 rgba(255, 255, 255, 0.9),
                       inset 0 -1px 0 rgba(255, 255, 255, 0.3),
                       0 0 24px rgba(var(--profit-rgb, 82, 202, 116), 0.12);
}
@media (prefers-color-scheme: light) {
    /* Bare `:root`, exactly as theme.css:445 scopes its own light half — the
       site has a `.light` class and no `.dark` one, so this is the same shape. */
    :root {
        --cfin-pop-bg: rgb(229, 229, 234);
        --cfin-pop-border: rgba(0, 0, 0, 0.12);
        --cfin-pop-shadow: 0 8px 32px rgba(0, 0, 0, 0.10),
                           inset 0 1px 0 rgba(255, 255, 255, 0.9),
                           inset 0 -1px 0 rgba(255, 255, 255, 0.3),
                           0 0 24px rgba(var(--profit-rgb, 82, 202, 116), 0.12);
    }
}
/* ⚠ `.light` must still win when the OS says dark — the media query above and
   the class rule have the same specificity, so an explicit dark choice on a
   light-OS machine needs its own scope or the media query keeps overriding it
   by source order. There is no `.dark` class on this site (grepped), so the
   only pair that can conflict is (OS light + `.light`), which agree. Recorded
   here so the next person adding a `.dark` class knows to add its scope too. */
