/* Dominia — design tokens.
   Source of truth: dominia-claude-brand-brief.md (Brand Guidelines v1.1).
   Grid metrics mirror the measured teenage.engineering lattice: 12 cols, gutter ~1.04vw,
   page margin ~4.68vw. See REFERENCE-FACTS.md. */

/* ============================================================================
   THE FACES — self-hosted, not fetched from Google.

   These three families used to arrive through one <link> to fonts.googleapis.com in every
   page head. That stylesheet is 1KB and, measured cold, took roughly 2,000ms to resolve:
   DNS + TLS to fonts.googleapis.com, a third-party round trip, then a SECOND connection to
   fonts.gstatic.com for the woff2 itself. Being a stylesheet it is render-blocking, so
   nothing painted until it landed — and the 926KB hero plate could not even be DISCOVERED
   until the CSSOM finished, which is why its request started at 2,380ms rather than 340ms.
   <link rel="preconnect"> to both hosts was already there and did not save it. Cold desktop
   FCP median was 2,036ms on localhost, against a bar of ~750ms over the public internet.

   Self-hosting deletes the third party from the critical path. Font FILES never block first
   paint — only the stylesheet that declares them does, and that is now this file, which was
   already the first stylesheet on every page.

   FIDELITY. These blocks are Google's own CSS output, verbatim, with only the src URL
   rewritten. That is deliberate, in two places that matter:

     - font-weight: 400 500 is the axis clamp documented in index.html's head. It is
       load-bearing: it stops a stray font-weight:700 shipping a bold, with
       font-synthesis:none in base.css stopping the browser faking one. Archivo stays pinned
       at 900 for the footer wordmark, so nothing can reach a mid weight there either.
     - the unicode-range on each block is Google's, so exactly the same glyphs resolve to
       these faces as before and everything else still falls through to the stacks in
       --font-body / --font-mono / --font-brand. Worth knowing: U+2192, the arrow in the nav
       CTA, is in none of these ranges. It was never set in Instrument Sans and still is not.

   The subsets are the full set Google serves. unicode-range means a browser downloads only
   what the page actually uses, so cyrillic / vietnamese / symbols cost nothing at runtime;
   an English page fetches three files, 67KB, the same bytes gstatic was sending.

   Licences: all three are SIL Open Font Licence 1.1 — assets/fonts/OFL-archivo.txt,
   OFL-geist-mono.txt, OFL-instrument-sans.txt. Redistribution is permitted and the licence
   text must travel with the fonts, which is why it sits in the same folder.

   Paths are ../assets/ because that resolves both when this file is served from css/ and
   when tools/build-standalone.mjs inlines it into dist/ one level down — the same
   arrangement the satellite plate already relies on.
   ============================================================================ */

/* --- Archivo --- */
@font-face {
  /* vietnamese */
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 900;
  font-stretch: 100%;
  font-display: swap;
  src: url("../assets/fonts/archivo-vietnamese.woff2") format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  /* latin-ext */
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 900;
  font-stretch: 100%;
  font-display: swap;
  src: url("../assets/fonts/archivo-latin-ext.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  /* latin */
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 900;
  font-stretch: 100%;
  font-display: swap;
  src: url("../assets/fonts/archivo-latin.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --- Geist Mono --- */
/* Sofia Sans Extra Condensed 800 — the corporates page's display face, and nowhere else. It is the
   face the HR deck and the Season One trailer set their stacked capitals in, and the page was
   rebuilt off the deck's title slide, so the two have to read as one campaign. Latin and
   latin-ext only: it sets English capitals and nothing else. Same OFL 1.1 as the other three;
   the licence is assets/fonts/OFL-sofia-sans-extra-condensed.txt. */
@font-face {
  font-family: 'Sofia Sans Extra Condensed';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("../assets/fonts/sofia-sans-extra-condensed-latin-ext.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Sofia Sans Extra Condensed';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("../assets/fonts/sofia-sans-extra-condensed-latin.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  /* cyrillic-ext */
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("../assets/fonts/geist-mono-cyrillic-ext.woff2") format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  /* cyrillic */
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("../assets/fonts/geist-mono-cyrillic.woff2") format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  /* symbols2 */
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("../assets/fonts/geist-mono-symbols2.woff2") format('woff2');
  unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;
}
@font-face {
  /* vietnamese */
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("../assets/fonts/geist-mono-vietnamese.woff2") format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  /* latin-ext */
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("../assets/fonts/geist-mono-latin-ext.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  /* latin */
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("../assets/fonts/geist-mono-latin.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --- Instrument Sans --- */
@font-face {
  /* latin-ext */
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 500;
  font-stretch: 100%;
  font-display: swap;
  src: url("../assets/fonts/instrument-sans-latin-ext.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  /* latin */
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 500;
  font-stretch: 100%;
  font-display: swap;
  src: url("../assets/fonts/instrument-sans-latin.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* ---- Colour: Brand Guidelines v3 (dominia-brand-guidelines-v3.md), adopted 2026-09-26 ----
     "Cold instrument, warm city." The interface ladder is v3's cool Ink ladder; the warmth on the
     page now comes from the dusk recording and the footage, not from the ground. This replaced a
     hand-tuned warm ladder (#0F0C09 / #2C271F / #EFEBE1 …) — see git history for its L* notes.

     Adopted from v3: the palette, grey section labels, the Claim 600 primary button, the light
     tints for signal-coloured text, one lit numeral per section, and tracked mono labels.
     Kept from the site as it was, deliberately: the paper sections (css/surface.css), the
     chamfers, the frosted hero glass, the instrument chrome, and the headline reveal. */

  /* The ground ladder. v3 names Ink, Ink 2 and Ink 3; --ink-0 (the well under the nav and
     footer) and --ink-4 (raised strips) are the same cool hue a step either side. */
  --ink-0:          #08090C;
  --ink:            #0E1014;   /* Ink   — screen ground */
  --ink-2:          #1A1D24;   /* Ink 2 — cards, sheets, the board's plane */
  --ink-3:          #252932;   /* Ink 3 — nested panels, empty tracks */
  --ink-4:          #323744;   /* raised: window bars, chip grounds */

  --bone:           #F2EEE6;   /* 16.45:1 on Ink */
  --bone-2:         #E8E3D8;   /* 14.88:1 */

  /* v3's Slate #5C6068 is for lines and disabled states only and fails as text (3.02:1). Both
     text greys therefore resolve to Slate 2 (5.88:1 on Ink, 5.20:1 on Ink 2). It drops to
     4.49:1 on Ink 3, so small grey text never sits on --ink-3. */
  --slate:          #8B8F98;
  --slate-2:        #8B8F98;

  /* Territory colours. The game's, and unchanged. */
  --claim:          #D64525;
  --alliance:       #3F8F4E;
  --enemy:          #4A6B8A;

  --claim-soft:     rgba(214, 69, 37, 0.14);
  --alliance-soft:  rgba(63, 143, 78, 0.14);
  --enemy-soft:     rgba(74, 107, 138, 0.14);

  /* An instrument-weight edge in Claim red, for panels that read as equipment: the dossier and
     the four beat plates. */
  --claim-edge:     rgba(214, 69, 37, 0.46);

  /* v3 Claim 600: the primary button's fill, so Bone type on it passes (4.82:1). */
  --claim-600:      #C3310D;
  /* Type on Claim 600. Its own token, not --bone, because the paper sections rebind --bone to
     their near-black ink and the button must read the same on both grounds. */
  --on-claim:       #F2EEE6;

  /* v3 light tints. Signal colours fail as small text on Ink (Claim 4.30:1, Enemy 3.41:1), so
     coloured TEXT uses the text tint and a live mark uses the graphics tint. The map keeps its
     own colours. */
  --claim-lit:      #FF5A36;   /* graphics */
  --claim-text:     #FF6A48;   /* 6.72:1 on Ink */
  --alliance-lit:   #5CC977;
  --alliance-text:  #9BF0B0;
  --enemy-lit:      #86A9D1;   /* graphics and text, 7.81:1 */

  /* The signal is Claim, set as its text tint because it is spent on text: status words, the
     contest strip, the travel progress. The paper side restates it as a printing-ink red. */
  --signal:         #FF6A48;
  --signal-dim:     #C3310D;
  --signal-soft:    rgba(214, 69, 37, 0.14);

  /* v3 glow, always in the tint of the mark itself. G2 lights the one readout per section that
     carries a live quantity; G3 is the halo on the primary button. Never on sentences, labels
     or panels at rest. */
  --g2-bone:        0 0 12px rgba(242, 238, 230, 0.5);
  --g3-claim:       rgba(255, 90, 54, 0.45);

  --hairline:       rgba(242, 238, 230, 0.08);
  --hairline-strong:rgba(242, 238, 230, 0.16);
  --hairline-hi:    rgba(242, 238, 230, 0.30);

  /* ---- Type. TWO faces. One grotesk sets everything; one mono is the instrument. ----
     Counted on inversa.com: 1,715 elements in a single grotesk and 330 in JetBrains Mono.
     Nothing else. We were running three — Archivo 12, Inter 289, Geist Mono 75 — and the
     Inter share is the problem: it is the most widely deployed interface font on the web, so
     a page carrying it reads as a template before anyone reads a word.

     Instrument Sans replaces both Archivo and Inter. Chosen by rendering the reference's own
     four-line headline string at 72px/-0.03em in nine Google grotesks against a 4x crop of the
     original (shots/work/spec2.png): Instrument Sans broke at the identical four words and set
     "Environmental" 474px against the reference's 460px — inside 3% — with the same flat
     terminals and tall x-height. Archivo broke the same way but sets on a Roboto skeleton with
     tighter apertures, which at 18px running text reads institutional rather than instrument.
     Chivo and Schibsted ran 5-8% wide; Host and Hanken are rounder than the reference; Familjen
     is more idiosyncratic than a face carrying every sentence on the site can afford.

     Geist Mono stays. At 12.6px untracked it sets EXPLORE ORIGIN in 272px against the
     reference chip's 275px (shots/work/spec3.png, both crops at 4x) — the swap to JetBrains
     buys nothing measurable. The tell was never the mono face, it was the tracking. */
  --font-display: "Instrument Sans", "Helvetica Neue", Arial, sans-serif; /* ceremony */
  --font-mono:    "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace; /* chrome, data, labels */
  --font-body:    "Instrument Sans", "Helvetica Neue", Arial, sans-serif; /* sentences */
  /* The brand face. Scoped to the wordmark by the brand brief ("Archivo Black uppercase"),
     which on this site means the footer sign-off and nothing else. Not a third body face —
     if this token starts appearing on headlines, the two-face rule above has been broken. */
  --font-brand:   "Archivo", "Arial Black", "Helvetica Neue", Arial, sans-serif; /* wordmark */
  /* The corporates page's stacked capitals (css/corporates.css). Scoped to that page by use, the
     way --font-brand is scoped to the wordmark: it is not a third face for the home page. */
  --font-condensed: "Sofia Sans Extra Condensed", "Arial Narrow", "Helvetica Neue", sans-serif;

  /* Type scale. EIGHT sizes, each with exactly one job. teenage.engineering ships an entire
     homepage on seven, and we were running thirteen — 9/10/11 and 13/14/15/16 were four sizes
     doing two jobs. If a component needs a size it reuses one of these; a ninth is a bug.
     No hardcoded px font-size is permitted outside this block. */
  /* The earlier note here said this scale was "measured off inversa.com at 1280 and adopted
     wholesale". It measured the wrong viewport. Re-measured at 1440 the bar runs 9, 9.9, 12.6,
     14.4, 18, 28.8, 57.6, 64.8, 72 — which is the same rem grid we had, but rooted at 18px
     rather than 16. Every size we shipped was the bar's size times 0.889, all the way up: body
     16 against 18, display 64 against 72. Nothing was wrong with the intervals; the whole scale
     was simply one root short, which is why the page reads small and careful next to it.
     Multiplying through by 1.125 lands six of the eight exactly on a measured bar size —
     12.6, 14.4, 18, 28.8, 57.6, 72 — and keeps the names, so the section files still resolve. */
  --t-label-sm: 0.675rem;                   /* 10.8 — mono, smallest chrome */
  --t-label:    0.7875rem;                  /* 12.6 — mono, uppercase. Bar size. */
  --t-data:     0.9rem;                     /* 14.4 — mono readouts. Bar size. */
  --t-body:     1.125rem;                   /* 18 — sentences. The bar sets 1,623 of its 2,046
                                                  elements here; it is the site's real voice. */
  --t-body-lg:  1.35rem;                    /* 21.6 — lead, primary action */
  --t-title:    1.8rem;                     /* 28.8 — large readouts. Bar size. */
  --t-headline: clamp(32px, 4vw, 3.6rem);   /* 57.6 at 1440. Bar size. */
  /* 5vw and 4.5rem cross at exactly 1440, so the desktop hero resolves to the bar's 72px with
     no breakpoint. The floor went 36 -> 42 because the phone shot (shots/ref/home-m-00.png,
     390pt) sets its two-line headline at roughly 42-48px: the bar does not shrink its display
     type for the phone, it just re-breaks the line. */
  --t-display:  clamp(42px, 5vw, 4.5rem);   /* 72 at 1440. Bar size. */

  /* Mono labels are tracked, per Brand Guidelines v3. This reverses an earlier decision to
     match the reference's untracked mono; v3 tracks every label at 0.14 to 0.2em on app screens,
     and the site takes the low end because its labels are larger. Some long mono strings pull
     it back locally where a character budget or a line would break (the comms log, the footer
     attribution, phone window bars). */
  --track-label: 0.14em;
  --track-label-wide: 0.12em;
  /* The only tracking anywhere on the bar is negative, and only on 72px and 57.6px. */
  --track-display: -0.03em;

  /* ---- polarity compensation ----
     Mono set DARK ON LIGHT runs heavier than the same mono set light on dark, because it has to.
     Measured, same face and size, only the ground swapped: light-on-dark blooms under
     antialiasing by 1.19-1.38x. The ink literally spreads.

     This is the real content of a critic finding that nearly sent the type piece the wrong way.
     It measured our CTA label at 0.115 stem-to-cap and the reference's at 0.170 and concluded our
     mono was 32% too light, prescribing weight 500-600. Measured off the OUTLINES instead — at
     400px, where a stem is 50 device pixels rather than 2 and cannot quantise — Geist Mono 400
     sits at 0.1197 against JetBrains Mono 300 at 0.1096. Ours is already 9% HEAVIER than the
     bar's. The gap the critic saw was bloom: its label is bone on near-black, ours is ink on a
     bone chip.
     So the effect was real and the cause was not. Matching apparent ink (98.0 light-on-dark
     against 86.2 dark-on-light) interpolates to 465 — not 500, and certainly not 600, which would
     have overshot the thing it was trying to match by half again. */
  --mono-on-light: 465;

  /* ---- Space. 4px base unit. No value outside this scale. ---- */
  --s-1:  4px;
  --s-2:  8px;
  --s-3:  12px;
  --s-4:  16px;
  --s-6:  24px;
  --s-8:  32px;
  --s-12: 48px;
  --s-16: 64px;
  --s-24: 96px;
  --s-32: 128px;

  /* ---- Grid. 12 columns, measured from the reference. ---- */
  --cols: 12;
  --gutter: clamp(8px, 1.04vw, 16px);
  --page-margin: clamp(16px, 4.68vw, 72px);
  --measure: 660px;   /* Reuters prose measure, measured */

  /* ---- Motion. Three durations only. One curve. ---- */
  --dur-micro: 120ms;
  --dur-ui:    280ms;
  --dur-ambient: 2000ms;
  --ease: cubic-bezier(0.2, 0, 0, 1);

  /* ---- Cartography. Road hierarchy measured off a real Toner tile. ---- */
  --road-motorway: 5px;
  --road-arterial: 3.5px;
  --road-secondary: 2px;
  --road-minor: 1px;
}

/* Reduced motion is honoured — the brand's fixed vocabulary still respects the OS. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-micro: 0ms;
    --dur-ui: 0ms;
    --dur-ambient: 0ms;
  }
}
