/* ===========================================================================
   PORTFOLEO DESIGN SYSTEM v1 — "THE INSTRUMENT"  ·  TOKEN LAYER (canonical)
   ---------------------------------------------------------------------------
   Spec:      docs/launch/SPEC-DESIGN-SYSTEM.md
   Canonical: site/public/css/tokens.css   <- EDIT THIS FILE
   Copy:      server/static/css/tokens.css <- GENERATED, byte-identical
   Sync:      python3 scripts/sync_design_tokens.py        (writes the copy)
              python3 scripts/sync_design_tokens.py --check (fails on drift)
   Gate:      tests/test_design_tokens_sync.py

   This file declares CUSTOM PROPERTIES ONLY. It contains no component rules,
   no element selectors and no regular CSS declarations. Both surfaces — the
   marketing site (site/public, Cloudflare Pages) and the terminal chrome
   (server/static, FastAPI) — load it first and inherit one palette.

   THE TWO-PLANE LAW (spec s1). The DATA PLANE (table interiors, chart canvas,
   number cells) is austere: --s-void ground, neutral figures, --r-0, no
   shadow, no blur, no grain, no animation, ever. The INSTRUMENT PLANE (window
   chrome, palette, rails, tabs, toasts, marketing) carries all the craft.

   THE RATIONING RULE (spec s2.1).
     1. Neutral by default: all data and prose render in the --t-* grey ramp.
     2. Amber is interaction + identity ONLY. Amber never carries a value.
     3. Green/red are direction ONLY, and direction always also carries a
        glyph (spec s2.7 — no green/red pair survives deuteranopia).
     4. Anything coloured that is not (2) or (3) is a bug.

   CONTRAST. Every ratio in this file was recomputed with WCAG 2.x relative
   luminance in this session, not copied from the spec prose. Where the spec
   text and the measurement disagree, the MEASURED value is written here and
   the discrepancy is called out inline.

   No CDN. No third-party asset. No emoji. Dark-only (spec s2.10) with three
   scoped exceptions: the in-product user preset, @media print, and email.

   INTEGRATION NOTE FOR THE TERMINAL (server/static). This file shares exactly
   three custom-property names with the legacy `:root` block in app.css:
   `--up` (#00d100), `--down` (#ff4d4d) and `--info` (#56b4e9, app.css:2713).
   Because a later declaration at equal specificity wins, the load order is
   load-bearing:
       tokens.css BEFORE app.css -> app.css keeps its legacy up/down. Loading
         this file is then a pure no-op on the terminal's appearance, which is
         what makes it safe to land ahead of the migration.
       tokens.css AFTER app.css  -> the terminal's green and red change on the
         same commit. Do not do this by accident.
   The migration (spec s9.2) replaces app.css lines 6-52 with an alias map
   pointing every legacy name at a token here; that is the commit where the
   product's colour actually changes, and it is not this one.
   =========================================================================== */

:root {

  /* -------------------------------------------------------------------------
     1. FONT FAMILIES  (spec s3.1)
     Both families are self-hosted under /fonts (site) and
     /static/vendor/fonts (terminal). No Google Fonts, no CDN.

     JetBrains Mono  — vendored variable woff2. VERIFIED axis: wght 400-800
       (the older vendor comment claiming 100-800 is wrong; fontTools fvar
       reports [('wght', 400.0, 400.0, 800.0)]). GSUB: calt ccmp frac locl.
     Archivo         — vendored variable woff2, SIL OFL 1.1, v2.001.
       VERIFIED axes this session: wght 100-900, wdth 62-125. Two axes in one
       file is the entire reason it was chosen: one family yields both a
       semi-condensed editorial display voice and a normal-width UI voice.
       Inter/Sofia Sans/Geist/Space Grotesk are rejected by spec s3.1.
     ---------------------------------------------------------------------- */
  --f-mono:    'JetBrains Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
  --f-sans:    'Archivo', system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;
  --f-display: 'Archivo', system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;


  /* -------------------------------------------------------------------------
     2. SURFACES — 7 steps, warm-neutral  (spec s2.2)
     Neutral spine is hue 40deg / chroma ~0.004 OKLCH: a sub-perceptual warmth
     so the greys do not read blue against amber, without Bloomberg sepia.
     Hex is canonical; the generator is documentation only:
         --n-hue: 40; --n-chroma: 0.004;
         s-XXX = oklch(L% var(--n-chroma) var(--n-hue))

     THE INVERSION (the depth story, at zero cost): the table body is
     --s-void (#000) while the panel body is --s-100. Data is a WELL RECESSED
     INTO the housing, not paint on top of it. Godel does not do this.

     Measured step separation (WCAG CR of each step against the one below):
       void->000 1.036 · 000->100 1.051 · 100->150 1.039 · 150->200 1.057
       200->300 1.081 · 300->400 1.119
     Steps widen with elevation on purpose so the top two stay separable on
     cheap panels (Linear's ladder is a flat ~2.2 L*; ours accelerates).
     ---------------------------------------------------------------------- */
  --s-void: #000000;   /* L* 0.00  — DATA WELL: table interiors, chart canvas   */
  --s-000:  #060605;   /* L* 1.60  — desktop / app canvas behind windows        */
  --s-100:  #0F0E0C;   /* L* 3.99  — panel body (the housing)                   */
  --s-150:  #141311;   /* L* 5.92  — titlebar, column header, rails, hint bar   */
  --s-200:  #1A1917;   /* L* 8.80  — raised: palette, menus, toasts, modals     */
  --s-300:  #22201D;   /* L* 12.36 — hover / pressed fills, input wells         */
  --s-400:  #2B2925;   /* L* 16.67 — highest: selected row, drag, kbd caps      */


  /* -------------------------------------------------------------------------
     3. HAIRLINES  (spec s2.3)
     Measured against --s-void / --s-100 / --s-150:
       --ln-100  1.16 / 1.07 / 1.03    --ln-200  1.32 / 1.22 / 1.17
       --ln-300  1.58 / 1.45 / 1.40    --ln-400  2.01 / 1.85 / 1.78
     (Godel's measured column rules are #1f2120 / #272928 at 1.30 / 1.43 vs
     black; ours are the warm-neutral equivalents at 1.16 / 1.32.)
     Hairlines are NON-TEXT. They are exempt from the 4.5:1 floor; the 3:1
     non-text floor does not apply to purely decorative rules either, but the
     borders that carry meaning (input border, focus ring) use --ln-400/--ac-*.
     ---------------------------------------------------------------------- */
  --ln-100: #171613;   /* column rules INSIDE the data well                     */
  --ln-200: #24221E;   /* DEFAULT panel / table border                          */
  --ln-300: #332F29;   /* strong: section breaks, active dividers, prov chip     */
  --ln-400: #443F37;   /* input borders, kbd chips, hover borders                */


  /* -------------------------------------------------------------------------
     4. TEXT RAMP — exactly three tokens may carry meaning  (spec s2.4)

     MEASURED CONTRAST MATRIX (WCAG 2.x, recomputed this session).
     Every text-on-surface pair in the system:

       token      hex       void   s-000  s-100  s-150  s-200  s-300  s-400
       --t-100  #E9E7E3    17.00  16.41  15.62  15.03  14.23  13.16  11.75
       --t-200  #B5B1AA     9.83   9.49   9.04   8.70   8.23   7.61   6.80
       --t-300  #8B867E     5.81   5.61   5.34   5.14   4.86   4.49*  4.02*
       --t-400  #6E6963     3.86   3.73   3.55   3.42   3.23   2.99   2.67
       --t-ghost#46433E     2.13   2.06   1.96   1.89   1.78   1.65   1.47

     HARD RULE: --t-400 and --t-ghost may NEVER render information. Any label,
     number, unit or timestamp uses --t-300 or better.

     * CORRECTION TO THE SPEC. Spec s2.4 states --t-300 "clears WCAG AA on
       every surface in s2.2". Measured, it does NOT: 4.49:1 on --s-300 and
       4.02:1 on --s-400. --s-300 is the hover/pressed fill and --s-400 the
       selected-row fill, so meta text CAN land on them. Therefore:
         --t-300 is approved for text on void / s-000 / s-100 / s-150 / s-200.
         On --s-300 and --s-400, meta text must step up to --t-200 (7.61 /
         6.80). components.css encodes this in the row-hover / row-selected
         rules; tests/test_design_contrast.py should assert it.
     ---------------------------------------------------------------------- */
  --t-100:   #E9E7E3;   /* primary figures, symbols, headings, prose            */
  --t-200:   #B5B1AA;   /* secondary values, column headers, body copy          */
  --t-300:   #8B867E;   /* meta, units, as-of, provenance chips, hints          */
  --t-400:   #6E6963;   /* NON-TEXT ONLY — disabled glyphs, decorative rules    */
  --t-ghost: #46433E;   /* NON-TEXT ONLY — placeholder dashes, empty-cell fill  */


  /* -------------------------------------------------------------------------
     5. ACCENT — amber, refined and rationed  (spec s2.5)

     MEASURED:  --ac      #FFA51F  void 10.65 · s-000 10.28 · s-100  9.79
                                   s-150  9.42 · s-200  8.91 · s-300  8.24
                                   s-400  7.36
                --ac-bri  #FFC46B  void 13.36 · s-100 12.27 · s-400  9.24
                --ac-deep #C97F0C  void  6.52 · s-100  5.99 · s-400  4.51
                --on-ac   #140F06 on --ac = 9.68  (spec prose says "10.0:1";
                          MEASURED 9.68 — still far above AA. Value kept.)

     WHY #FFA51F AND NOT #ff8c00 (product) OR #e8a13a (site): #ff8c00 sits at
     hue 32.9deg, three degrees from the danger hue. #FFA51F is hue 35.9deg;
     that +3deg lifts contrast on black 9.00 -> 10.65 and improves deuteranopic
     separation from the down-red 1.24 -> 1.42 under a Vienot simulation.
     #e8a13a is 79% saturation and reads dusty next to real data.

     THE RATIONING SCHEDULE — the complete list of places amber may appear.
     Anything else amber is a bug:
       1  focus ring                     7  primary CTA fill
       2  command-code badge             8  brand wordmark
       3  active tab underline           9  active workspace tab
       4  command-bar caret + prompt    10  sort-active column arrow
       5  ::selection                   11  palette kbd-chip border
       6  hint-bar prompt glyph
     ---------------------------------------------------------------------- */
  --ac:      #FFA51F;
  --ac-bri:  #FFC46B;                  /* hover / focus-visible                 */
  --ac-deep: #C97F0C;                  /* pressed, dim rules, disabled-accent   */
  --ac-a08:  rgba(255, 165, 31, .08);  /* faintest fill: active tab bg          */
  --ac-a14:  rgba(255, 165, 31, .14);  /* command-badge fill, selected chip     */
  --ac-a24:  rgba(255, 165, 31, .24);  /* badge border, palette border          */
  --ac-a40:  rgba(255, 165, 31, .40);  /* FOCUS RING                            */
  --on-ac:   #140F06;                  /* text on a solid amber fill — 9.68:1   */


  /* -------------------------------------------------------------------------
     6. SEMANTIC COLOURS  (spec s2.6)

     MEASURED:  --up       #31C97E  void 9.79 · s-100 8.99 · s-200 8.19
                --up-bri   #5CDDA0  void 12.31 · s-100 11.31
                --down     #FF5A6E  void 6.94 · s-100 6.38 · s-200 5.81
                --down-bri #FF8C9B  void 9.48 · s-100 8.71
                --warn     #F0B429  void 11.27 · s-100 10.35
                --info     #56B4E9  void 9.10 · s-100 8.36

     WHY THE RED MOVED #ff4d4d -> #FF5A6E: #ff4d4d is hue 0deg at 100% sat, a
     tomato in the same visual family as the amber, so a red delta and the
     accent read as "two warm things" peripherally. #FF5A6E is hue 353deg with
     a rose bias (Godel independently landed on #da3253). Contrast also rises
     6.42 -> 6.94 on black.

     WHY UP AND DOWN ARE NOT LUMINANCE-MATCHED (9.79 vs 6.94): deliberate. The
     Helmholtz-Kohlrausch effect makes high-chroma reds appear brighter than
     their measured luminance; matching Y would let the red dominate.

     --err ALIASES --down on purpose: a failure IS a negative. One red.
     ---------------------------------------------------------------------- */
  --up:       #31C97E;
  --up-bri:   #5CDDA0;
  --up-a10:   rgba(49, 201, 126, .10);
  --up-a45:   rgba(49, 201, 126, .45);
  --down:     #FF5A6E;
  --down-bri: #FF8C9B;
  --down-a10: rgba(255, 90, 110, .10);
  --down-a45: rgba(255, 90, 110, .45);
  --warn:     #F0B429;
  --warn-a10: rgba(240, 180, 41, .10);
  --warn-a45: rgba(240, 180, 41, .45);
  --err:      #FF5A6E;                 /* = --down                              */
  --err-a10:  rgba(255, 90, 110, .10);
  --info:     #56B4E9;
  --info-a10: rgba(86, 180, 233, .10);
  --info-a45: rgba(86, 180, 233, .45);


  /* -------------------------------------------------------------------------
     7. STALENESS / FRESHNESS CHANNEL — luminance, not hue  (spec s2.9)
     Provenance is the brand, so it gets a channel that costs zero colours.

     MEASURED COMPOSITES of --t-100 (#E9E7E3) over --s-void:
       alpha .42 -> #62615F = 3.39:1   BELOW AA — rejected
       alpha .50 -> #747472 = 4.46:1   BELOW AA — rejected (see below)
       alpha .54 -> #7D7C7B = 5.09:1   the floor we ship
       alpha .55 -> #807F7D = 5.25:1
     (Spec prose says .42 composites to 3.77:1; MEASURED 3.39:1. The spec's
     conclusion that .42 is below AA is right; its claim that .50 clears AA at
     4.48:1 is not — .50 measures 4.46:1, which is UNDER the 4.5 floor. A
     browser-rendered contrast audit of the live page caught it. .54 is the
     lowest step on the ladder that actually clears AA, and the fresh/stale
     delta is still 17.00:1 vs 5.09:1, i.e. unmistakable.)

     Do not dim any text that starts below --t-100: --t-300 at .54 composites
     to 2.33:1. That is why components.css exempts the AGE cell — it carries
     --down, and multiplying a 6.94:1 colour by the row dim would leave the one
     glyph that explains the dimming at 2.34:1.
     ---------------------------------------------------------------------- */
  --fresh-opacity: 1;
  --stale-opacity: .54;
  --dead-opacity:  .54;   /* same dim + a strike glyph in the AGE column        */


  /* -------------------------------------------------------------------------
     8. DATA-VISUALISATION PALETTES  (spec s2.8)

     CATEGORICAL (8, amber-led, Wong-derived). Series 1 is ALWAYS the subject
     or benchmark and is ALWAYS the accent, so a chart legend and the window's
     command badge are the same colour — chrome linked to content.
     MEASURED on --s-100 / --s-void:
       viz-1  9.79 / 10.65     viz-2  8.36 / 9.10     viz-3  8.07 / 8.78
       viz-4  6.30 / 6.86      viz-5  7.85 / 8.54     viz-6  7.87 / 8.56
       viz-7 14.59 / 15.88     viz-8  7.96 / 8.66
     All >= 6.30 on the panel body. #0072B2 from raw Wong is REJECTED at
     3.72:1. #FF6E3D (collides with the accent hue) and #3B9AE1 (redundant
     with viz-2) are dropped from the legacy theme.js set.
     viz-7 is 7th ON PURPOSE: at 14.59 it is brighter than primary text.
     ---------------------------------------------------------------------- */
  --viz-1: #FFA51F;   /* amber  — subject / benchmark (= --ac)                  */
  --viz-2: #56B4E9;   /* sky                                                    */
  --viz-3: #1BBF7A;   /* teal                                                   */
  --viz-4: #CC79A7;   /* mauve                                                  */
  --viz-5: #B692FF;   /* violet                                                 */
  --viz-6: #9AA7B4;   /* slate                                                  */
  --viz-7: #F0E442;   /* yellow — the loudest, deliberately last                */
  --viz-8: #FF7D9B;   /* rose                                                   */

  /* SEQUENTIAL — amber (magnitude, treemaps, heat). 7 steps, gamma 0.85 in
     linear light. MEASURED vs --s-100:
       1.00 · 4.00 · 6.40 · 8.61 · 10.74 · 12.73 · 14.75                       */
  --seq-0: #0F0E0C;
  --seq-1: #826F54;
  --seq-2: #A9916E;
  --seq-3: #C5A981;
  --seq-4: #DBBD90;
  --seq-5: #EECD9D;
  --seq-6: #FFDCA8;

  /* SEQUENTIAL — cool. Use where amber would collide with the accent (e.g. an
     amber-highlighted row). MEASURED vs --s-100:
       1.00 · 3.96 · 6.39 · 8.58 · 10.73 · 12.72 · 14.68                       */
  --seqc-0: #0F0E0C;
  --seqc-1: #607481;
  --seqc-2: #7E98A9;
  --seqc-3: #93B1C5;
  --seqc-4: #A4C6DB;
  --seqc-5: #B2D7EE;
  --seqc-6: #BFE6FF;

  /* DIVERGING (correlation, z-scores, P&L heat) — 9 steps through the PANEL
     SURFACE, never through white. A white midpoint on a black canvas creates
     a bright band that dominates the figure; an --s-150 midpoint makes "zero"
     disappear into the panel, which is correct.
     MEASURED vs --s-100 (these are FILLS, not text — the >=4.5 floor does not
     apply; any label printed on top of them must be --t-100 or --on-ac):
       n4 6.38 · n3 5.05 · n2 3.71 · n1 2.37 · 0 1.04
       p1 3.01 · p2 5.02 · p3 7.01 · p4 8.99
     KNOWN ASYMMETRY: n1 is lighter than p1 (red's lower luminance at equal
     chroma). Acceptable under Helmholtz-Kohlrausch for P&L heat. For a
     correlation matrix where |value| must read symmetrically, use --seqc-* on
     |value| plus a sign glyph instead.                                        */
  --div-n4: #FF5A6E;
  --div-n3: #E14F61;
  --div-n2: #BC4250;
  --div-n1: #8A313B;
  --div-0:  #141311;
  --div-p1: #1E6C43;
  --div-p2: #25945C;
  --div-p3: #2CB16F;
  --div-p4: #31C97E;


  /* -------------------------------------------------------------------------
     9. PRINT PALETTE  (spec s2.10 exception 2)
     Statements and risk reports get printed and emailed. @media print inverts
     to a light ground; these are the only light-mode tokens in the system.
     MEASURED on #ffffff: --print-ink 18.88 · --print-ink-2 7.46
                          --print-ink-3 4.54 (AA floor for small text)
     ---------------------------------------------------------------------- */
  --print-bg:    #ffffff;
  --print-ink:   #111111;
  --print-ink-2: #555555;
  --print-ink-3: #767676;
  --print-rule:  #cccccc;


  /* -------------------------------------------------------------------------
     10. TYPE — INSTRUMENT SCALE  (spec s3.3)
     Product chrome + data. rem base is 16px; px is given for certainty
     because a table is a metronome and rem drifts with user zoom settings on
     some platforms. Weight for the mono is set with font-weight (axis is
     400-800). Weight for the sans is set with font-variation-settings.

     DEFAULT DATA SIZE IS 12px, RAISED FROM 10.5px. Godel's measured
     professional benchmark is ~13px mono at a 24px pitch; 10.5px reads
     "hobby project" at 1x on a 27in panel. Today's density survives as the
     COMPACT mode (see 12. DENSITY).
     ---------------------------------------------------------------------- */
  --ti-micro-size: 9px;    --ti-micro-lh: 12px;  --ti-micro-wt: 700;  --ti-micro-track: .14em;
  --ti-nano-size:  10px;   --ti-nano-lh:  14px;  --ti-nano-wt:  700;  --ti-nano-track:  .12em;
  --ti-xs-size:    11px;   --ti-xs-lh:    16px;  --ti-xs-wt:    500;  --ti-xs-track:    .01em;
  --ti-sm-size:    12px;   --ti-sm-lh:    18px;  --ti-sm-wt:    400;  --ti-sm-track:    0;
  --ti-md-size:    13px;   --ti-md-lh:    20px;  --ti-md-wt:    400;  --ti-md-track:    0;
  --ti-lg-size:    15px;   --ti-lg-lh:    22px;  --ti-lg-wt:    500;  --ti-lg-track:    -.005em;
  --ti-xl-size:    20px;   --ti-xl-lh:    24px;  --ti-xl-wt:    600;  --ti-xl-track:    -.01em;
  --ti-2xl-size:   26px;   --ti-2xl-lh:   28px;  --ti-2xl-wt:   600;  --ti-2xl-track:   -.015em;
  --ti-3xl-size:   32px;   --ti-3xl-lh:   34px;  --ti-3xl-wt:   500;  --ti-3xl-track:   -.02em;

  /* `font:` shorthands — the same nine steps, ready to drop into a rule.      */
  --ti-micro: var(--ti-micro-wt) var(--ti-micro-size) / var(--ti-micro-lh) var(--f-mono);
  --ti-nano:  var(--ti-nano-wt)  var(--ti-nano-size)  / var(--ti-nano-lh)  var(--f-mono);
  --ti-xs:    var(--ti-xs-wt)    var(--ti-xs-size)    / var(--ti-xs-lh)    var(--f-mono);
  --ti-sm:    var(--ti-sm-wt)    var(--ti-sm-size)    / var(--ti-sm-lh)    var(--f-mono);
  --ti-md:    var(--ti-md-wt)    var(--ti-md-size)    / var(--ti-md-lh)    var(--f-mono);
  --ti-lg:    var(--ti-lg-wt)    var(--ti-lg-size)    / var(--ti-lg-lh)    var(--f-mono);
  --ti-xl:    var(--ti-xl-wt)    var(--ti-xl-size)    / var(--ti-xl-lh)    var(--f-mono);
  --ti-2xl:   var(--ti-2xl-wt)   var(--ti-2xl-size)   / var(--ti-2xl-lh)   var(--f-mono);
  --ti-3xl:   var(--ti-3xl-wt)   var(--ti-3xl-size)   / var(--ti-3xl-lh)   var(--f-mono);

  /* -------------------------------------------------------------------------
     11. TYPE — DISPLAY SCALE  (spec s3.3, marketing)
     All --f-display with explicit font-variation-settings. The H1 spec is the
     deliberate move: a LIGHT weight (380) at SEMI-CONDENSED width (88) with
     -.025em tracking and line-height 1.02. The elite set's tightness ladder:
     Rogo -.05em/0.95 · AlphaSense -.03em/1.05 · Linear -.022em/1.10 ·
     Godel -.01em/1.10. The current site H1 is clamp(27px,4.6vw,46px) at
     WEIGHT 800 in MONO — heavy, loose, and the wrong family.

     Dark-mode optical compensation: light text on dark bleeds heavier, so
     every weight below is already the dark-mode value. A light print/email
     variant adds +50 to each wght.
     ---------------------------------------------------------------------- */
  --td-1-size:  clamp(38px, 5.4vw, 68px);  --td-1-wght: 380;  --td-1-wdth: 88;   --td-1-track: -.025em;  --td-1-lh: 1.02;
  --td-2-size:  clamp(26px, 3.2vw, 40px);  --td-2-wght: 420;  --td-2-wdth: 92;   --td-2-track: -.022em;  --td-2-lh: 1.08;
  --td-3-size:  clamp(19px, 1.9vw, 24px);  --td-3-wght: 560;  --td-3-wdth: 100;  --td-3-track: -.012em;  --td-3-lh: 1.25;
  --td-4-size:  17px;                      --td-4-wght: 600;  --td-4-wdth: 100;  --td-4-track: -.008em;  --td-4-lh: 1.35;
  --td-body-size:  16px;                   --td-body-wght: 400;  --td-body-wdth: 100;  --td-body-track: -.006em;  --td-body-lh: 1.6;
  --td-small-size: 13px;                   --td-small-wght: 400; --td-small-wdth: 100; --td-small-track: 0;       --td-small-lh: 1.55;
  /* the eyebrow is MONO on purpose — it is the machine's voice (spec s3.2)    */
  --td-eyebrow-size: 11px;  --td-eyebrow-wt: 700;  --td-eyebrow-track: .22em;  --td-eyebrow-lh: 1.4;


  /* -------------------------------------------------------------------------
     12. SPACE  (spec s4.1) — base 4px, strict ladder, no in-between values
     ---------------------------------------------------------------------- */
  --sp-0:  0;      --sp-1:  4px;    --sp-2:  8px;    --sp-3:  12px;
  --sp-4:  16px;   --sp-5:  20px;   --sp-6:  24px;   --sp-8:  32px;
  --sp-10: 40px;   --sp-12: 48px;   --sp-14: 56px;   --sp-16: 64px;
  --sp-20: 80px;   --sp-24: 96px;   --sp-28: 112px;  --sp-40: 160px;
  --sp-56: 224px;

  /* Marketing rhythm: --sp-24 (96px) between sections on desktop, --sp-14
     (56px) on mobile. base.css sets --section-y from a media query.           */
  --section-y:    var(--sp-24);
  --site-maxw:    1180px;
  --site-gutter:  var(--sp-6);


  /* -------------------------------------------------------------------------
     13. DENSITY + THE ROW METRONOME  (spec s4.2, s4.3)
     One variable retunes the terminal (Warp's mechanism). STANDARD is the
     default; COMPACT preserves today's density; COMFORT is the 27in mode.

       mode      --row-h   data font   header font   cell padding
       COMPACT     20px      11px         9px          0 6px
       STANDARD    24px      12px        10px          0 8px      <- default
       COMFORT     28px      13px        11px          0 10px

     Row height is set by height + line-height, NEVER by padding, so the pitch
     is a metronome regardless of content. Godel's FA measured
     24,24,24,24,24,24,25,24,24,24 — a machine. The only permitted rhythm
     break is .group-break (+12px and one --ln-300 rule).
     ---------------------------------------------------------------------- */
  --density:      1;
  --row-h:        24px;
  --row-font:     var(--ti-sm-size);
  --row-head-font: var(--ti-nano-size);
  --cell-px:      var(--sp-2);


  /* -------------------------------------------------------------------------
     14. PANEL GEOMETRY  (spec s4.4)
     Windows tile FLUSH at 1px; they do not float on margins. A tiled
     workspace with 8px gutters reads "dashboard"; 1px reads "terminal".
     (Godel's panel-to-panel gutter measured ~2 device px = ~1 CSS px.)
     ---------------------------------------------------------------------- */
  --panel-title-h:  24px;          /* 12px title + a chip need 24, not 22      */
  --panel-tabs-h:   22px;
  --panel-pad:      var(--sp-3);   /* prose / chart panels                     */
  --panel-pad-data: 0;             /* table panels bleed to the ring           */
  --panel-gutter:   1px;
  --rail-w:         208px;
  --rail-tile-h:    68px;
  --hintbar-h:      22px;
  --cmdbar-h:       32px;
  --topbar-h:       56px;          /* marketing site chrome                    */


  /* -------------------------------------------------------------------------
     15. RADII + BORDER WIDTHS  (spec s4.5)
     SQUARE PRODUCT, <=10px MARKETING. Both Godel and Linear run this exact
     split. Fiscal.ai's 24/32px radii are what make "institutional-grade" copy
     read consumer.
     ---------------------------------------------------------------------- */
  --r-0:    0;        /* THE ENTIRE DATA PLANE + all window chrome + panels    */
  --r-1:    3px;      /* command-code badge, kbd chip, prov chip               */
  --r-2:    4px;      /* toasts, context menus, command palette, tooltips      */
  --r-3:    6px;      /* marketing cards, inputs on the marketing site         */
  --r-4:    10px;     /* marketing hero cards MAX — nothing rounder exists     */
  --r-pill: 9999px;   /* marketing CTA pills and status chips ONLY             */

  --bw-hair:       1px;
  --bw-hair-hidpi: .5px;   /* applied under @media (min-resolution: 2dppx)     */
  --bw-focus:      1.5px;


  /* -------------------------------------------------------------------------
     16. DEPTH  (spec s5)
     Vercel's dark-mode insight, verified in their token file: IN DARK UI,
     REPLACE DROP SHADOWS WITH HAIRLINE RINGS. Shadows on near-black surfaces
     are invisible; a 1px light ring is what reads as "raised". Shadows are
     then used only for SEPARATION FROM CONTENT, not for elevation.
     ---------------------------------------------------------------------- */

  /* 16a. Panel (the housing) — ring, not border. border:0 means a focus
     change costs zero layout shift.                                          */
  --sh-panel: inset 0 0 0 1px var(--ln-200);
  --sh-panel-hidpi: inset 0 0 0 var(--bw-hair-hidpi) var(--ln-200);

  /* 16b. Raised objects (palette, menus, toasts, modals) — the three-layer
     stack. Ring + top-edge inner highlight + outer black ring + ambient +
     contact. (Raycast's floating window is the same three ideas at a brighter
     highlight because their backdrop is a photo; ours is .075 because our
     backdrop is --s-000 and .3 would look like a bezel.)                     */
  --sh-raised:
    inset 0 0 0 1px rgba(255, 255, 255, .055),
    inset 0 1px 0 0 rgba(255, 255, 255, .075),
    0 0 0 1px rgba(0, 0, 0, .85),
    0 12px 32px -8px rgba(0, 0, 0, .72),
    0 2px 6px -2px rgba(0, 0, 0, .5);

  /* 16c. Focus — inset accent ring, zero layout shift. This replaces the
     solid-amber titlebar flip, which paints a ~24px x panel-width block of
     full-chroma orange on EVERY focus change. Godel signals focus with a ~2px
     inset green stroke sampled at #3c594b/#425f51 and leaves the titlebar
     dark; --ac-a40 over --s-100 is the amber equivalent.                     */
  --sh-focus-panel:
    inset 0 0 0 var(--bw-focus) var(--ac-a40),
    inset 0 0 0 1px var(--ln-300);
  /* Keyboard focus on CONTROLS (distinct from panel focus) — Vercel's
     two-ring pattern. The inner ring is the surface colour so the accent ring
     never touches the control's own edge.

     NOT --ac-a40. A focus indicator is governed by WCAG 2.2 SC 1.4.11 /
     2.4.11, which demand 3:1 against the ADJACENT colour — and the adjacent
     colour here is the 2px --s-100 inner ring. rgba(255,165,31,.40) over
     --s-000 composites to #6A4610 and measures 2.28:1 against #0F0E0C: it
     FAILS. .62 alpha composites to ~#A0691 5 and measures 4.15:1.
     --ac-a40 is unchanged and still correct for the PANEL ring in
     --sh-focus-panel: a window-manager affordance is not a WCAG focus
     indicator, and Godel's measured equivalent sits at the same ~30-35%.    */
  --ac-a62: rgba(255, 165, 31, .62);
  --sh-focus-ring: 0 0 0 2px var(--s-100), 0 0 0 4px var(--ac-a62);

  /* 16d. Recessed well (inputs, the table body inside a panel)               */
  --sh-well:
    inset 0 1px 0 0 rgba(0, 0, 0, .6),
    inset 0 0 0 1px rgba(255, 255, 255, .028);

  /* 16e. Secondary-button lighting                                           */
  --sh-btn-secondary:
    inset 0 0 0 1px var(--ln-400),
    inset 0 1px 0 0 rgba(255, 255, 255, .05);

  /* 16f. Backdrop blur — OVERLAYS ONLY. Never blur a panel and never blur
     anything behind a table: blur is CPU per frame and it makes small mono
     illegible.                                                               */
  --blur-overlay: blur(20px);
  --blur-scrim:   blur(3px);
  --blur-chrome:  blur(6px);     /* marketing topbar only                     */
  --scrim:        rgba(0, 0, 0, .62);

  /* 16g. Grain — INSTRUMENT PLANE ONLY. One 256px inline-SVG turbulence tile
     as a data URI: no request, no CDN. It must never composite over a table,
     a chart canvas or a number.                                              */
  --grain-size: 256px;
  --grain-opacity: .32;
  --grain-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='256' height='256'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='256' height='256' filter='url(%23n)' opacity='.55'/%3E%3C/svg%3E");

  /* 16h. Gradients — three, total (spec s5.7).                               */
  --g-chrome: linear-gradient(180deg, var(--s-150) 0%, var(--s-100) 100%);
  --g-card:
    radial-gradient(120% 80% at 0% 0%, rgba(255, 165, 31, .055) 0%, transparent 55%),
    linear-gradient(180deg, #121110 0%, #0B0A09 100%);
  --g-card-pos:
    radial-gradient(120% 80% at 100% 0%, rgba(49, 201, 126, .07) 0%, transparent 55%),
    linear-gradient(180deg, #0E1310 0%, #090B0A 100%);
  --g-desktop:
    radial-gradient(140% 100% at 50% 0%, rgba(255, 165, 31, .022) 0%, transparent 62%);

  /* 16i. Edge fades for scrollers — masks, not shadows.                      */
  --m-fade-x: linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent);
  --m-fade-y: linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent);


  /* -------------------------------------------------------------------------
     17. MOTION  (spec s6)
     EXACTLY TWO CURVES. Linear ships cubic-bezier(.25,.46,.45,.94) for
     everything; two is the ceiling and a third is a template smell.
     NO SPRINGS, NO OVERSHOOT, ANYWHERE. Vercel's swift timing overshoots
     (cubic-bezier(.175,.885,.32,1.1)) — correct for a dev tool, wrong for an
     instrument. A number that bounces past its value and settles back is a
     lie about the value.

     WHAT MUST NEVER ANIMATE (the instrument precision rules, spec s6.3):
       1. numeric cell content — no tween, no count-up, no transition on a
          <td> value. A tween on a price asserts values never quoted.
       2. row order — a live sort repaints, it does not FLIP-animate.
       3. chart series on data update — redraw; only user pan/zoom may ease.
       4. layout of the data plane — no width/height transitions.
       5. the provenance chip — present from first paint or the data is not
          shown at all.
       6. anything at all in a [data-stale] state.
     ---------------------------------------------------------------------- */
  --ease:     cubic-bezier(.25, .46, .45, .94);  /* easeOutQuad — ALL instrument motion */
  --ease-in:  cubic-bezier(.23, 1, .32, 1);      /* easeOutQuint — entrances only       */
  --ease-lin: linear;                            /* progress + latency bars ONLY        */

  --dur-1:     90ms;    /* colour, background-color, border-color, opacity     */
  --dur-2:     150ms;   /* transform, opacity, box-shadow, panel focus ring    */
  --dur-3:     220ms;   /* window open/close, palette, menu, toast in          */
  --dur-4:     420ms;   /* MARKETING reveals only — never in the product       */
  --dur-flash: 320ms;   /* value-change tint decay                             */


  /* -------------------------------------------------------------------------
     18. Z-INDEX LADDER — declared once so nothing invents a 99999.
     ---------------------------------------------------------------------- */
  --z-desktop:  0;
  --z-grain:    1;      /* below .win, scoped to .desktop > .grain             */
  --z-win:      10;
  --z-win-drag: 20;
  --z-rail:     30;
  --z-chrome:   40;     /* cmdbar, hintbar, topbar                            */
  --z-menu:     50;
  --z-palette:  60;
  --z-toast:    70;
  --z-scrim:    80;
  --z-modal:    90;
  --z-tooltip:  100;
}


/* ===========================================================================
   DENSITY OVERRIDES  (spec s4.2/s4.3) — custom properties only.
   Set data-density on <html> or on any subtree. STANDARD is the :root default.
   =========================================================================== */
[data-density="compact"] {
  --density: .84;
  --row-h: 20px;
  --row-font: var(--ti-xs-size);
  --row-head-font: var(--ti-micro-size);
  --cell-px: 6px;
}
[data-density="standard"] {
  --density: 1;
  --row-h: 24px;
  --row-font: var(--ti-sm-size);
  --row-head-font: var(--ti-nano-size);
  --cell-px: var(--sp-2);
}
[data-density="comfort"] {
  --density: 1.16;
  --row-h: 28px;
  --row-font: var(--ti-md-size);
  --row-head-font: var(--ti-xs-size);
  --cell-px: 10px;
}


/* ===========================================================================
   CVD DIRECTION MODE  (spec s2.7) — custom properties only.
   Two token overrides, no other change, and a feature no competitor in the
   research set offers. Under a Vienot deuteranope simulation NO green/red
   pair survives (#31C97E vs #FF5A6E separate by only 1.15:1), so the shipped
   escape hatch moves direction onto the blue/yellow axis, which is the
   residual channel.

   MEASURED: --up #56B4E9 = 9.10:1 on void, 8.36:1 on --s-100
             --down #E0742A = 6.70:1 on void, 6.16:1 on --s-100
   (Spec prose estimates the down at "5.9:1"; MEASURED 6.70:1.)

   HONEST NOTE: WCAG contrast ratio is a LUMINANCE metric and understates
   blue/yellow separation under deuteranopia. The pair is chosen for hue-axis
   survival, not for its luminance ratio. The direction glyph (s2.7) is
   mandatory in this mode too — colour is never the only channel.
   =========================================================================== */
[data-dir-mode="cvd"] {
  --up:       #56B4E9;
  --up-bri:   #8ACCF1;
  --up-a10:   rgba(86, 180, 233, .10);
  --up-a45:   rgba(86, 180, 233, .45);
  --down:     #E0742A;
  --down-bri: #EE9A5E;
  --down-a10: rgba(224, 116, 42, .10);
  --down-a45: rgba(224, 116, 42, .45);
  --err:      #E0742A;
  --err-a10:  rgba(224, 116, 42, .10);
}
