/* ==========================================================================
   ZONETTA TRADING — TOKEN SYSTEM
   Direction: HELD LINE — modern white skin (2026-09-05)
   File: /assets/tokens.css

   THIS IS THE ONLY FILE IN THE PROJECT THAT CONTAINS A COLOUR VALUE.
   No stylesheet, no page and no inline style may ever name a colour.
   Components consume the semantic tokens below and nothing else.
   No component styles belong in this file.

   WHAT THIS SKIN IS
   A white, clean page in the tone of a 2026 broker site: one compact sticky
   header, a big light Poppins headline, short blocks with one pill button,
   hairline cards, an ad slot. ONE typeface: Poppins 300/400/500, for the
   headings as well as the copy. The display serif that the first white
   candidate kept on h1/h2 is gone — the owner's words were "big and full",
   and the serif was neither. --f-display survives only as an alias of
   --f-head so that no old selector breaks.

   COLOUR NOTATION
   Every raw value is written in oklch(). The sRGB hex it was converted from
   stays in a trailing comment so a human (and the contrast script) can read
   it; the comment is documentation, not a fallback. Interaction steps are
   RELATIVE colours (oklch(from …)), alpha inks are color-mix(): there is no
   second literal for a hover state anywhere. Baseline 2023/2024 — a browser
   too old for oklch() would see the UA default colours, which is why the
   rollout notes name the @supports fallback should such traffic ever appear.

   LINES VERSUS BARS — the one rule that keeps amber looking amber
   A 1px or 2px LINE on the white page is amber-ink (--level, 7.05:1): the
   section rule, the level line in a chart, the scroll hairline, a focus ring.
   A BAR — 4px or thicker, no text on it — is raw amber (--level-solid): the
   4px leading edge of the inset panel, the 4px top edge of the ad slot, the
   filled meter slots, the pill buttons. Bars are fills, so the text-contrast
   rule does not bind them, and they are what keep the brand hue visible on
   white without ever putting raw amber under a glyph.

   --------------------------------------------------------------------------
   CONTRAST WARNING — READ THIS BEFORE "FIXING" ANY COLOUR
   --------------------------------------------------------------------------
   Raw amber #D89B3C on white #FFFFFF measures 2.42:1.
   Raw teal  #2E9E93 on white #FFFFFF measures 3.27:1.
   Both FAIL for text, thin rules, SVG strokes and focus rings.

   On the white page, raw amber and raw teal survive ONLY as FILLED BLOCKS
   carrying navy text (navy on amber = 6.56:1, navy on teal = 4.87:1) or as
   low-alpha tints under ink text. Every amber or teal TEXT, RULE, STROKE,
   BORDER and FOCUS RING resolves to --zt-amber-ink (7.05:1 on white) or
   --zt-teal-ink (8.06:1 on white).

   That is what the semantic split below encodes:
     --level        amber-ink: lines, strokes and text
     --level-solid  raw amber, fill only, always carrying --on-level text
     --state        teal-ink: lines, strokes and text
     --state-solid  raw teal, fill only, always carrying --on-state text
   Do not collapse those pairs.

   --------------------------------------------------------------------------
   COLOUR LAW (enforced, not stylistic)
   --------------------------------------------------------------------------
   Amber  = the thing to look at. The level line, filled meter segments, the
            primary button fill, supply bands, the active index, the focus ring.
   Teal   = state that is true right now. Fresh-data marks, demand bands,
            the confirmation rule, in-daily-use chips.
   RED AND GREEN DO NOT EXIST IN THIS FILE, and must never be added.
   A failed zone is a DASHED amber band plus a typographic outcome line.
   Failure is typographic in this brand, never chromatic.

   --------------------------------------------------------------------------
   THEME MECHANICS
   --------------------------------------------------------------------------
   `:root, :root[data-theme="light"]` carries the canonical LIGHT values once
   (the earlier duplicate light block is gone) and `color-scheme: light`.
   There is deliberately NO light-dark() and no prefers-color-scheme: the
   page renders white whatever the visitor's OS setting says — the owner's
   decision, and one no marketing page in the 2026 broker set contradicts.
   The :root[data-theme="dark"] block is the archive of the dark skin that
   shipped first and the other half of a future manual toggle; it is complete
   (every token the white skin introduced has a dark value there).
   No toggle ships: there is no JavaScript on this site.

   ROLLOUT WARNING: 16 of the 26 pages still carry <html data-theme="dark">
   today. With THIS file that attribute selects the archived dark skin, so
   the two CSS files and the 26 <html>/<meta> edits must go out in ONE
   deploy — never CSS first.

   --------------------------------------------------------------------------
   FONT LOADING — preview vs production
   --------------------------------------------------------------------------
   The preview links Poppins 300/400/500 from Google Fonts. PRODUCTION MUST
   NOT: the CSP is `style-src 'self'`, and `font-src 'none'` becomes
   `font-src 'self'`. Self-host /assets/fonts/poppins-300.woff2, -400, -500
   (latin subset) with three @font-face rules and font-display: swap at the
   top of base.css (section 0) — tokens.css never contains a url(). Poppins
   has no variable axis: these are static faces. A metric-matched local()
   fallback face ("Poppins Fallback", also in section 0) holds the layout
   while the real files arrive.
   /legal/privacy/, /start/ and /about/ were reworded on 2026-09-05, when the
   three woff2 files shipped (see ROLLOUT.md §3).
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. RAW RAMPS — the only literals in the project
   oklch(L C H); the hex in the trailing comment is the sRGB value each was
   converted from (D65, computed). Contrast ratios below are on the hex.
   -------------------------------------------------------------------------- */

:root {

  /* DARK (archived). Nine-step navy ramp. Ratios computed against --zt-ink-900.
     Still referenced by :root[data-theme="dark"] and by the .band re-map. */
  --zt-ink-900: oklch(18.68% 0.0403 267.20);  /* #0B1225  page field of the dark skin */
  --zt-ink-800: oklch(22.33% 0.0510 265.97);  /* #101A33  raised: masthead, table headers, levels panel */
  --zt-ink-700: oklch(25.43% 0.0571 266.71);  /* #16213E  BRAND NAVY. inset panels, founder block, OG field */
  --zt-ink-600: oklch(29.22% 0.0647 266.65);  /* #1D2A4C  third surface: hovered rows, open panels */
  --zt-ink-500: oklch(33.65% 0.0717 266.69);  /* #26355C  hairlines on raised surfaces */
  --zt-ink-400: oklch(40.43% 0.0704 263.75);  /* #35486F  hairlines on the ground; table rows, empty meter slots */
  --zt-ink-300: oklch(49.87% 0.0663 261.92);  /* #4E6389   3.08:1 — GRAPHIC ONLY, never text */
  --zt-ink-200: oklch(64.33% 0.0497 260.59);  /* #7C8EAC   5.61:1 — muted text on the dark skin */
  --zt-ink-100: oklch(80.45% 0.0300 259.59);  /* #B4C0D3  10.13:1 — secondary text on the dark skin */
  --zt-cream:   oklch(92.06% 0.0376  88.47);  /* #EFE4C9  14.73:1 on ink-900 — the dark skin's copy colour */
  --zt-amber:   oklch(73.18% 0.1305  74.74);  /* #D89B3C   7.69:1 on ink-900 — the level, the fill, the thing to look at */
  --zt-teal:    oklch(63.52% 0.0991 185.45);  /* #2E9E93   5.70:1 on ink-900 — state and confirmation only */

  /* Amber interaction steps (primary control fill only). Relative colours:
     the same hue and chroma, lightness moved by one step. */
  --zt-amber-hi: oklch(from var(--zt-amber) calc(l + 0.048) c h);   /* computes to #E8AA4D  hover lightening; navy on it 7.79:1 */
  --zt-amber-lo: oklch(from var(--zt-amber) calc(l - 0.054) c h);   /* computes to #C68A27  active darkening; navy on it 5.35:1 */

  /* LIGHT (canonical). Fully authored — nothing here is an inversion.
     Ratios computed against --zt-paper unless stated. */
  --zt-paper:      oklch(97.00% 0.0029 264.54);  /* #F4F5F7  the FIELD, not the page. cool near-white. NOT cream */
  --zt-paper-2:    oklch(93.97% 0.0058 264.53);  /* #E9EBEF  second field step: hovered rows, open panels */
  --zt-cream-inset: var(--zt-cream);             /* #EFE4C9  BRAND CREAM, kept for the archived dark skin's inset text.
                                                    On the white page the inset is the amber tint below.
                                                    IT IS NEVER A PAGE FIELD. */
  --zt-ink:        var(--zt-ink-700);            /* #16213E  14.57:1 on paper, 15.89:1 on white — body and headings.
                                                    The same colour as brand navy, one source. */
  --zt-ink-2:      oklch(35.04% 0.0501 265.52);  /* #2E3A55  10.39:1 — SECONDARY text. One step from --zt-ink so a
                                                    footer link and a paragraph are never the same colour (WCAG 1.4.1). */
  --zt-muted-ink:  oklch(46.15% 0.0538 265.55);  /* #4A5877   6.52:1 — captions, metadata, disclosure */
  --zt-amber-ink:  oklch(46.74% 0.0982  70.90);  /* #7C4F06   6.46:1 on paper, 7.05:1 on white — level line, rules, focus ring */
  --zt-teal-ink:   oklch(42.26% 0.0699 183.98);  /* #0F5A52   7.39:1 on paper, 8.06:1 on white — state indicator */

  /* WHITE-PAGE SURFACES. */
  --zt-white:      oklch(100% 0 0);              /* #FFFFFF  the page. Paper is the field */
  --zt-field:      var(--zt-paper);              /* cool-grey section field, table headers, disclosure strip, footer */
  --zt-field-2:    var(--zt-paper-2);            /* hovered rows, an open panel on a field */
  --zt-card-edge:  oklch(90.63% 0.0058 264.53);  /* #DEE0E4  card border: navy at 14% on white, flattened so it is the
                                                    same colour on every surface. 1.32:1 — decorative only */
  --zt-card-shadow:      color-mix(in srgb, var(--zt-ink) 8%, transparent);       /* the ONE shadow colour, navy-tinted */
  --zt-card-shadow-dark: color-mix(in srgb, var(--zt-ink-900) 55%, transparent);  /* the same shadow for the archived dark skin */
  --zt-band:       var(--zt-ink-700);            /* navy band (brand navy, not ink-900). white on it 15.89:1 */
  --zt-amber-tint: oklch(96.17% 0.0198  77.32);  /* #FAF1E4  amber at 14% on white, flattened. inset panels, pending chips.
                                                    ink 14.20 / amber-ink 6.30 on it */
  --zt-teal-tint:  oklch(94.67% 0.0159 192.08);  /* #E2F1F0  teal at 14% on white, flattened. live chips.
                                                    ink 13.68 / teal-ink 6.94 on it */

  /* STRUCTURAL INKS. color-mix() in sRGB reproduces the old rgba() flattening
     exactly. Authored to the ratios the dark ramp was tuned to, not eyeballed:
     a light theme whose lines are 36% weaker than the dark theme's is an
     inversion by omission, which this file exists to prevent. */
  --zt-rule:         color-mix(in srgb, var(--zt-ink) 34%, transparent);  /* #B0B4BD on white — 2.08:1, matching --zt-ink-400 (2.05:1) */
  --zt-rule-soft:    color-mix(in srgb, var(--zt-ink) 19%, transparent);  /* #D3D5DA on white — 1.47:1, matching --zt-ink-500 on raised */
  --zt-graphic-lt:   color-mix(in srgb, var(--zt-ink) 49%, transparent);  /* #8D92A0 on white — 3.11:1, matching --zt-ink-300,
                                                                             and clearing the 3:1 non-text threshold */
  --zt-mark-edge-lt: var(--zt-graphic-lt);                                /* the empty Zone Score slots and the portrait frame.
                                                                             Never --zt-rule: at 4x16px a 2:1 edge is not there. */

  /* The scrim behind the open mobile menu: navy at 24% over whatever is
     under it. A sheet, not a modal — the header stays readable above it. */
  --zt-scrim: color-mix(in srgb, var(--zt-ink) 24%, transparent);

  /* The sticky header's face: the page colour at 86%, blurred by base.css.
     Two values so the archived dark skin has its own. */
  --zt-glass-lt:   color-mix(in srgb, var(--zt-white) 86%, transparent);
  --zt-glass-dark: color-mix(in srgb, var(--zt-ink-900) 86%, transparent);

  /* Grid substrate inks. Off on the white page (see --grid-h below); kept for
     the archived dark skin. */
  --zt-grid-h-dark: color-mix(in srgb, var(--zt-cream) 4.5%, transparent);
  --zt-grid-v-dark: color-mix(in srgb, var(--zt-cream) 6%, transparent);
  --zt-grid-h-lt:   color-mix(in srgb, var(--zt-ink) 4.5%, transparent);
  --zt-grid-v-lt:   color-mix(in srgb, var(--zt-ink) 6%, transparent);
  --zt-grid-off:    color-mix(in srgb, var(--zt-ink) 0%, transparent);   /* transparent, written as navy so the hue never drifts */
}


/* --------------------------------------------------------------------------
   2. SEMANTIC TOKENS — light canonical, declared ONCE for both the bare
   root and the explicit data-theme="light" pin (the two selectors used to
   be two copies of the same 41 lines).
   -------------------------------------------------------------------------- */

:root,
:root[data-theme="light"] {
  color-scheme: light;

  --surface-page:   var(--zt-white);        /* html, body, masthead, card faces */
  --surface-raised: var(--zt-field);        /* table th, disclosure strip, field bands, footer, sponsored ad face */
  --surface-inset:  var(--zt-amber-tint);   /* the callout panel. cream is a stain on a white page */
  --surface-hover:  var(--zt-field-2);      /* one step below the field, so a hovered row shows on a field */
  --surface-band:   var(--zt-band);         /* navy promo band; nothing on the home uses it */
  --surface-glass:  var(--zt-glass-lt);     /* the sticky header, over a backdrop blur */

  --text-primary:   var(--zt-ink);          /* 15.89:1 on white */
  --text-secondary: var(--zt-ink-2);        /* 11.34:1 */
  --text-muted:     var(--zt-muted-ink);    /*  7.11:1 */
  --text-on-inset:  var(--zt-ink);          /* 14.20:1 on the amber tint */
  --text-on-band:   var(--zt-white);        /* 15.89:1 on the band */
  --text-on-band-muted: var(--zt-ink-100);  /*  8.64:1 on the band. NOT ink-200 (4.78, too thin at 12px) */

  --hairline:        var(--zt-rule);        /* dividers, row rules, the ad-slot border, the header's bottom edge */
  --hairline-raised: var(--zt-rule-soft);   /* rules inside field surfaces */
  --graphic:         var(--zt-graphic-lt);  /* chart axis, neutral state mark. 3.11:1 — do not lighten */
  --mark-edge:       var(--zt-mark-edge-lt);/* empty Zone Score slots, the portrait frame */
  --card-edge:       var(--zt-card-edge);   /* card borders that need not identify the component */
  --card-shadow:     var(--zt-card-shadow); /* consumed by exactly one element: the ad slot */
  --scrim:           var(--zt-scrim);       /* the popover backdrop */

  --level:          var(--zt-amber-ink);    /* amber TEXT, RULES, STROKES, FOCUS. NEVER raw amber: 2.42:1 */
  --level-solid:    var(--zt-amber);        /* fill only, always carrying --on-level */
  --level-solid-hi: var(--zt-amber-hi);     /* navy on it 7.79:1 */
  --level-solid-lo: var(--zt-amber-lo);     /* navy on it 5.35:1 */
  --level-tint:     var(--zt-amber-tint);   /* pending chips */
  --on-level:       var(--zt-ink);          /* 6.56:1 on raw amber */

  --state:       var(--zt-teal-ink);        /* teal TEXT, RULES, STROKES. NEVER raw teal: 3.27:1 */
  --state-solid: var(--zt-teal);            /* demand-band fill, fresh mark on a field */
  --state-tint:  var(--zt-teal-tint);       /* live chips */
  --on-state:    var(--zt-ink);             /* 4.87:1 on raw teal — never below 14px on a teal fill */

  --focus:       var(--zt-amber-ink);
  --focus-inner: var(--zt-white);

  /* The chart-grid substrate is OFF on the white page. .page's background
     rules stay in base.css and simply paint nothing. */
  --grid-h: var(--zt-grid-off);
  --grid-v: var(--zt-grid-off);

  --select-bg: var(--zt-amber);
  --select-fg: var(--zt-ink);

  /* Inside a navy band only: raw amber / raw teal are the dark skin's own
     values there (6.56 / 4.87 on the band). */
  --band-level:    var(--zt-amber);
  --band-state:    var(--zt-teal);
  --band-graphic:  var(--zt-ink-200);       /* 5.61 on band — clears 3:1 for a chart axis inside a band */
  --band-hairline: var(--zt-ink-400);
}

/* Manual override — the archive of the skin that shipped first, and the
   other half of a future toggle. Nothing sets it at launch. */

:root[data-theme="dark"] {
  color-scheme: dark;

  --surface-page:   var(--zt-ink-900);
  --surface-raised: var(--zt-ink-800);
  --surface-inset:  var(--zt-ink-700);
  --surface-hover:  var(--zt-ink-600);

  --text-primary:   var(--zt-cream);
  --text-secondary: var(--zt-ink-100);
  --text-muted:     var(--zt-ink-200);
  --text-on-inset:  var(--zt-cream);

  --hairline:        var(--zt-ink-400);
  --hairline-raised: var(--zt-ink-500);
  --graphic:         var(--zt-ink-300);
  --mark-edge:       var(--zt-ink-300);

  --level:          var(--zt-amber);
  --level-solid:    var(--zt-amber);
  --level-solid-hi: var(--zt-amber-hi);
  --level-solid-lo: var(--zt-amber-lo);
  --on-level:       var(--zt-ink-900);

  --state:       var(--zt-teal);
  --state-solid: var(--zt-teal);
  --on-state:    var(--zt-ink-900);

  --focus:       var(--zt-amber);
  --focus-inner: var(--zt-ink-900);

  --grid-h: var(--zt-grid-h-dark);
  --grid-v: var(--zt-grid-v-dark);

  --select-bg: var(--zt-amber);
  --select-fg: var(--zt-ink-900);

  --surface-band:       var(--zt-ink-700);
  --surface-glass:      var(--zt-glass-dark);
  --text-on-band:       var(--zt-cream);
  --text-on-band-muted: var(--zt-ink-100);
  --card-edge:          var(--zt-ink-500);
  --card-shadow:        var(--zt-card-shadow-dark);
  --scrim:              var(--zt-card-shadow-dark);
  --level-tint:         var(--zt-ink-800);   /* raw amber on it 7.12:1 */
  --state-tint:         var(--zt-ink-800);   /* raw teal on it 5.28:1 (ink-600 would be 4.32 — fails) */
  --band-level:         var(--zt-amber);
  --band-state:         var(--zt-teal);
  --band-graphic:       var(--zt-ink-200);
  --band-hairline:      var(--zt-ink-400);
}


/* --------------------------------------------------------------------------
   2b. SCOPED RE-MAP — anything inside a .band is set on brand navy.
   Tokens, not component style, so it lives here. Nothing on the home page
   uses it; it ships so that a navy panel can be added without touching
   base.css. It re-maps EVERY surface and tint token, not only the text
   ones: a chip, an inset panel or a card that lands inside a band must not
   keep its light tint under raw-amber text (2.16:1 — measured on
   /tools/zone-score/, whose .pagehead carries a .chip--pending).
   -------------------------------------------------------------------------- */

.band {
  --surface-page:    var(--surface-band);
  --surface-glass:   var(--zt-glass-dark);
  --surface-raised:  var(--zt-ink-800);
  --surface-hover:   var(--zt-ink-600);
  --surface-inset:   var(--zt-ink-700);
  --text-primary:    var(--text-on-band);
  --text-secondary:  var(--text-on-band-muted);
  --text-muted:      var(--text-on-band-muted);
  --text-on-inset:   var(--text-on-band);
  --hairline:        var(--band-hairline);
  --hairline-raised: var(--zt-ink-500);
  --graphic:         var(--band-graphic);
  --mark-edge:       var(--band-graphic);
  --card-edge:       var(--band-hairline);
  --card-shadow:     var(--zt-card-shadow-dark);
  --level:           var(--band-level);          /* raw amber: 6.56:1 on the band */
  --level-tint:      var(--zt-ink-800);          /* raw amber on it 7.12:1 */
  --state:           var(--band-state);          /* raw teal: 4.87:1 — 14px and up only */
  --state-tint:      var(--zt-ink-800);          /* raw teal on it 5.28:1 */
  --focus:           var(--band-level);
  --focus-inner:     var(--surface-band);
}


/* --------------------------------------------------------------------------
   3. TYPE — ONE VOICE plus one mono. Poppins for everything: h1 and h2 at
   300 (Light — big and full, the way the owner described forex.com's
   headlines), h3 at 400, copy at 400, UI at 500. Nothing heavier exists in
   the project and font-synthesis is off, so nothing can be faked.
   --f-display is an ALIAS of --f-head: the serif is gone, the name stays so
   that no selector in any page or stylesheet breaks.
   Every font-family declaration in the project must use one of these and
   must terminate in a generic family: fallback resolves per declaration and
   does NOT inherit. "Poppins Fallback" is the metric-matched local() face
   declared in base.css section 0; it holds the layout while the woff2
   files arrive. DO NOT build on Charter, Cascadia or Aptos — all absent
   from Windows 10.
   -------------------------------------------------------------------------- */

:root {
  --f-head:    "Poppins", "Poppins Fallback", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --f-text:    var(--f-head);
  --f-display: var(--f-head);   /* alias — the display serif was retired on 2026-09-05 */
  --f-mono:    ui-monospace, Consolas, "Roboto Mono", "DejaVu Sans Mono", monospace;

  /* THE SCALE. Fluid from 375px to 1280px (slope = delta / 9.05vw), then
     held. Body 17→18, lead 20→22, h3 22→26, h2 32→40, h1 40→56 — the
     "big and full" the owner asked for; the previous skin stopped growing
     at ~1000px and topped out at 52/36/22/20/17. Three sizes below body,
     four above. Do not add a step. */
  --fs-display: clamp(2.5rem,    2.09rem  + 1.77vw, 3.5rem);     /* 40 → 56  h1, weight 300 */
  --fs-h2:      clamp(2rem,      1.79rem  + 0.88vw, 2.5rem);     /* 32 → 40  weight 300 */
  --fs-h3:      clamp(1.375rem,  1.27rem  + 0.44vw, 1.625rem);   /* 22 → 26  weight 400 */
  --fs-lead:    clamp(1.25rem,   1.20rem  + 0.22vw, 1.375rem);   /* 20 → 22  weight 300 */
  --fs-body:    clamp(1.0625rem, 1.037rem + 0.11vw, 1.125rem);   /* 17 → 18  weight 400 */
  --fs-data:    clamp(0.9375rem, 0.914rem + 0.11vw, 1rem);       /* 15 → 16  mono, tabular — levels, scores, timestamps */
  --fs-label:   clamp(0.875rem,  0.85rem  + 0.11vw, 0.9375rem);  /* 14 → 15  buttons, nav, footer links; uppercase 500 */
  --fs-micro:   clamp(0.75rem,   0.724rem + 0.11vw, 0.8125rem);  /* 12 → 13  eyebrows, disclosure, captions, chips */

  --lh-display: 1.08;
  --lh-h2:      1.15;
  --lh-h3:      1.25;
  --lh-lead:    1.45;
  --lh-body:    1.6;
  --lh-mono:    1.4;

  /* Optical tracking by size band. Poppins Light at 56px wants a touch more
     negative tracking than the serif did; the small uppercase wants air. */
  --tr-display: -0.02em;    /* >= 1200px h1 */
  --tr-h1:      -0.015em;
  --tr-h2:      -0.01em;
  --tr-h3:       0em;
  --tr-body:     0em;
  --tr-label:    0.05em;    /* 14–15px uppercase: buttons, nav */
  --tr-micro:    0.08em;    /* 12–13px uppercase */

  /* Measure. Prose never exceeds --measure regardless of column width.
     Poppins is wide: 62ch of 18px Poppins ≈ 670px, inside the 1024px column. */
  --measure:        62ch;
  --measure-lead:   46ch;
  --measure-mobile: 34ch;
}


/* --------------------------------------------------------------------------
   4. SPACE, LAYOUT, RADII
   The tell is that every gap is the same. Section rhythm is deliberately
   3–5x the internal rhythm, and section heights vary on purpose. The
   --sp-* steps are FIXED by design (a 4/8/12/16 rhythm inside a component
   should not breathe with the viewport); the section rhythm, the gutter and
   the header height are the fluid ones.
   -------------------------------------------------------------------------- */

:root {
  --sp-1:  4px;
  --sp-2:  8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  24px;
  --sp-6:  32px;
  --sp-7:  48px;
  --sp-8:  64px;
  --sp-9:  96px;
  --sp-10: 128px;
  --sp-11: 160px;
  --sp-12: 224px;

  /* Section rhythm, alternating and FLUID: 56/72/88 on a phone, 80/104/136
     from ~1260px, no breakpoint in between. The 2026 broker set runs
     64–96 mobile and 96–128 desktop; forex.com runs 60–80. */
  --rhythm-a: clamp(56px, 46px + 2.7vw, 80px);
  --rhythm-b: clamp(72px, 59px + 3.5vw, 104px);
  --rhythm-c: clamp(88px, 68px + 5.3vw, 136px);

  /* The gutter respects a notched phone in landscape (viewport-fit=cover is
     declared on every page; this is the first rule that reads it). */
  --gutter:      max(clamp(20px, 5vw, 56px), env(safe-area-inset-left), env(safe-area-inset-right));
  --content-max: 64rem;      /* 1024px */

  /* The compact sticky header: one row, every width. The rail's sticky
     offset and every anchor's scroll-margin read this token, so a page
     whose header is a different height is a bug, not a variant. */
  --masthead-h: 60px;

  /* Radii. Cards 16px (the 2026 convention: TradingView, Public, Kraken all
     sit at 16–20; the owner asked for 12–16), the callout and the portrait
     12px, controls 6px, a pill on every button. Nothing else is rounded. */
  --r:      12px;
  --r-card: 16px;
  --r-ctl:  6px;
  --r-pill: 999px;

  /* Controls */
  --tap:     44px;   /* minimum tap target */
  --tap-cta: 52px;   /* primary button */
  --tap-hd:  40px;   /* the header pill — a 60px bar cannot carry a 52px button */
  --hair:    1px;    /* the level line is always exactly this */
}


/* --------------------------------------------------------------------------
   5. MOTION — a closed vocabulary. Nothing else on this site moves.
   There is no infinite animation anywhere. Numbers never count up.
   The cross-document view transition and the menu sheet both run on
   --t-nav; the hero draws on --t-draw.
   -------------------------------------------------------------------------- */

:root {
  --t-state:  120ms;   /* button press, chip hover, underline thickening */
  --t-nav:    220ms;   /* navigation-scale state change: the sheet, the page fade */
  --t-reveal: 480ms;   /* a figure entering */
  --t-draw:   700ms;   /* the level draw */
  --ease: cubic-bezier(.2, .7, .2, 1);   /* never the default `ease`, never bounce, never spring */
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --t-state:  1ms;
    --t-nav:    1ms;
    --t-reveal: 1ms;
    --t-draw:   1ms;
  }
}
