/* ==========================================================================
   Dutch IX Labs — tokens.css
   The single source of truth for colour, type, space, motion and elevation.

   Rule: this is the ONLY file in the project allowed to contain a raw colour
   value. Everything downstream references var(--...).
   See brief/art-direction.md.

   The world: a large-format plan sheet in daylight. Ink on cool grey-green
   stock. Colour means a place — the five labs own the entire chromatic
   budget and nothing else on this site is allowed a hue.
   The one dark act is the Cyanotype: the same drawing, printed the other way.

   Order:
     1. Fonts
     2. Raw palette (never referenced by components)
     3. Semantic tokens (:root)
     4. Lab theming            [data-lab]
     5. The Cyanotype          .plate--cyanotype / [data-print="cyanotype"]
     6. Reset
     7. Base element styles
     8. Focus, selection, scrollbar
     9. Layout primitives
    10. Type utilities
    11. User-preference overrides
   ========================================================================== */


/* ==========================================================================
   1. FONTS — self-hosted, already vendored in /ixlabs-nl/fonts/.
   No CDN, no <link> to fonts.googleapis.com.

   Preload exactly two: the Archivo variable and IBM Plex Mono 400. Mono goes
   ahead of Plex Sans because the map's coordinate marks are above the fold
   and the body copy is not.
   ========================================================================== */

@font-face {
  font-family: "Archivo";
  src: url("../fonts/archivo-latin-wdth-wght-normal.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
                 U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-latin-400-normal.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-latin-400-italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-latin-500-normal.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-latin-600-normal.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}

/* Metric-matched fallbacks so the swap does not reflow the map's label column. */
@font-face {
  font-family: "Archivo Fallback";
  src: local("Arial Narrow"), local("Helvetica Neue Condensed"), local("Liberation Sans Narrow"), local("Arial");
  size-adjust: 88%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Plex Sans Fallback";
  src: local("Segoe UI"), local("Helvetica Neue"), local("Arial");
  size-adjust: 102%;
  ascent-override: 96%;
  descent-override: 26%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Plex Mono Fallback";
  src: local("Consolas"), local("Menlo"), local("DejaVu Sans Mono"), local("Courier New");
  size-adjust: 97%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}


/* ==========================================================================
   2. RAW PALETTE
   These are the only literal colours in the project. Components must never
   reference a --p-* or --cy-* token directly; they use the semantic tokens
   in section 3, which is what makes the Cyanotype a one-line theme swap.
   ========================================================================== */

:root {
  /* ---- 2.1 The sheet — green drafting stock. Never #FFF. ---------------
     Explicitly NOT a warm cream: that ground with a contrast serif is the
     most reproduced machine-made design of the decade. The stock carries a
     real green cast now — the country the map draws is green, and the whole
     sheet is printed on it.                                               */
  --p-sheet:      #E1EBDD;
  --p-sheet-down: #D4E0CF;
  --p-sheet-deep: #C6D4C1;
  --p-sheet-up:   #EDF4EA;
  --p-paper:      #F6FBF3;

  /* Water is the one surface that is NOT the stock. On a Dutch map the sea is
     read before anything else, so it keeps a cool slate cast while the land is
     the green sheet — which is what makes the country legible as a shape. */
  --p-water:      #C6D3CD;
  --p-water-deep: #B7C7C1;

  /* ---- 2.2 Ink — chlorophyll-tinted graphite. Never #000. ------------- */
  --p-ink:    #0D1811;  /* 14.94:1 on sheet */
  --p-ink-2:  #26332B;  /* 10.86:1 */
  --p-ink-3:  #4A5C51;  /*  5.87:1 */
  --p-ink-4:  #4E6055;  /*  5.52:1 */
  --p-rule:   #B9C4BA;  /*  1.48:1 — decorative reinforcement ONLY */
  --p-rule-2: #6C7A70;  /*  3.71:1 — visible borders, graticule */

  /* ---- 2.3 The five labs — DRAWN value. Text-capable. -----------------
     ratios on sheet / sheet-down / sheet-deep. Every hue is pulled toward
     the green axis; two of the five now sit on it outright. The spread is
     kept wide enough that the five marks stay tellable apart by hue alone
     — which is a courtesy, never the only channel (9.3).                 */
  --p-ix3:   #B00060;  /* 5.73 / 5.19 / 4.61 — stage magenta    */
  --p-mix:   #00694A;  /* 5.54 / 5.02 / 4.45 — chroma green     */
  --p-prime: #4C3FBE;  /* 6.25 / 5.67 / 5.03 — violet, inherited from the child site */
  --p-brix:  #57630D;  /* 5.40 / 4.90 / 4.35 — workshop moss    */
  --p-shine: #00636F;  /* 5.73 / 5.19 / 4.61 — harbour teal     */

  /* ---- 2.4 The five labs — LIVE value. Traffic and active fills.
     >=3:1 on sheet and sheet-down. NEVER text. NEVER on sheet-deep
     (mix 2.79, shine 2.99) — section 4 rebinds them there.               */
  --p-ix3-signal:   #D30070;  /* 4.31 / 3.91 */
  --p-mix-signal:   #008D62;  /* 3.46 / 3.14 */
  --p-prime-signal: #6A57E8;  /* 4.17 / 3.78 */
  --p-brix-signal:  #6E7C14;  /* 3.79 / 3.44 */
  --p-shine-signal: #00838F;  /* 3.72 / 3.37 */

  /* ---- 2.5 Focus. A single ring fails on a two-ground system.
     Pale core carries saturated fills (>=5.58:1 on every lab value);
     ink halo carries the stock (>=12.02:1 on every sheet surface).       */
  --p-focus-core: #F7FBF5;
  --p-focus-ring: #0D1811;

  /* ---- 2.6 The ONLY non-lab colour on the site.
     Form validation errors only. Never content, never the map, never a
     status. Always ships with an icon and a message.                     */
  --p-alert: #A82A18;  /* 5.74:1 on sheet */

  /* ---- 2.7 The Chlorotype — the same drawing, printed the other way.
     It was a cyanotype; it is printed in green now, and the --cy-* names
     are kept so nothing downstream has to be renamed.                    */
  --cy-deep:   #05130D;
  --cy-down:   #081A12;
  --cy-ground: #0A2017;
  --cy-up:     #0D2A1D;
  --cy-paper:  #123626;

  --cy-line:   #E2F0E7;  /* 14.49:1 on ground */
  --cy-line-2: #AFD0BE;  /* 10.22:1 */
  --cy-line-3: #82A691;  /*  6.34:1 (4.94 on cy-paper — still AA) */
  --cy-rule:   #1F4433;  /*  1.57:1 — decorative reinforcement ONLY */
  --cy-rule-2: #5A8570;  /*  4.08:1 — visible borders */

  --cy-ix3:   #FF7DB4;  /*  7.18:1 on ground */
  --cy-mix:   #4BE0A8;  /* 10.17:1 */
  --cy-prime: #A79BFF;  /*  7.12:1 */
  --cy-brix:  #C6D65A;  /* 10.69:1 */
  --cy-shine: #4CD3E0;  /*  9.50:1 */

  --cy-ix3-signal:   #FFA6CD;  /*  9.41:1 */
  --cy-mix-signal:   #77EDC1;  /* 11.91:1 */
  --cy-prime-signal: #C4BAFF;  /*  9.59:1 */
  --cy-brix-signal:  #DCE88C;  /* 12.96:1 */
  --cy-shine-signal: #8AE4EC;  /* 11.68:1 */

  --cy-alert: #FF8E76;  /* 7.62:1 */
}


/* ==========================================================================
   3. SEMANTIC TOKENS
   Everything downstream uses these names and nothing else.
   ========================================================================== */

:root {

  /* ---------- 3.1 Surfaces ---------- */
  --c-sheet:      var(--p-sheet);
  --c-sheet-down: var(--p-sheet-down);
  --c-sheet-deep: var(--p-sheet-deep);
  --c-sheet-up:   var(--p-sheet-up);
  --c-paper:      var(--p-paper);
  --c-water:      var(--p-water);
  --c-water-deep: var(--p-water-deep);

  /* ---------- 3.2 Ink ---------- */
  --c-ink:    var(--p-ink);
  --c-ink-2:  var(--p-ink-2);
  --c-ink-3:  var(--p-ink-3);
  --c-ink-4:  var(--p-ink-4);
  --c-rule:   var(--p-rule);
  --c-rule-2: var(--p-rule-2);

  /* Foreground for text sitting ON a lab-coloured fill.
     6.09–6.58:1 on every lab value. Never use --c-ink on a lab fill
     (1.91–3.48 — fails).                                                 */
  --c-on-lab: var(--p-paper);

  /* ---------- 3.3 Labs ---------- */
  --c-ix3:   var(--p-ix3);
  --c-mix:   var(--p-mix);
  --c-prime: var(--p-prime);
  --c-brix:  var(--p-brix);
  --c-shine: var(--p-shine);

  --c-ix3-signal:   var(--p-ix3-signal);
  --c-mix-signal:   var(--p-mix-signal);
  --c-prime-signal: var(--p-prime-signal);
  --c-brix-signal:  var(--p-brix-signal);
  --c-shine-signal: var(--p-shine-signal);

  /* Lab aliases. Components reference --c-lab / --c-lab-signal; [data-lab]
     rebinds them. The default is lab-neutral so no colour leaks into an
     un-themed plate — colour means a place, and "no place" means ink.    */
  --c-lab:        var(--c-ink);
  --c-lab-signal: var(--c-ink-2);

  /* ---------- 3.4 Focus and the single alert ---------- */
  --c-focus-core: var(--p-focus-core);
  --c-focus-ring: var(--p-focus-ring);
  --c-alert:      var(--p-alert);

  /* ---------- 3.5 Typography ---------- */
  --font-display: "Archivo", "Archivo Fallback", "Arial Narrow",
                  "Helvetica Neue", system-ui, sans-serif;
  --font-body:    "IBM Plex Sans", "Plex Sans Fallback", "Segoe UI", system-ui,
                  -apple-system, "Helvetica Neue", Arial, sans-serif;
  --font-mono:    "IBM Plex Mono", "Plex Mono Fallback", ui-monospace,
                  "Cascadia Mono", "SFMono-Regular", Consolas, monospace;

  /* The width axis carries the NL/EN length difference, never the scale.
     Condensed, because that is how every plan sheet on earth is lettered. */
  --wdth-display: 78;
  --wdth-annot:   88;

  /* Display — Archivo condensed. Fluid, solved 360px -> 1600px. */
  --fs-d1: clamp(3.75rem, 1.209rem + 11.29vw, 12.5rem);  /*  60 -> 200px */
  --fs-d2: clamp(2.75rem, 1.371rem +  6.13vw,  7.5rem);  /*  44 -> 120px */
  --fs-d3: clamp(2rem,    1.274rem +  3.23vw,  4.5rem);  /*  32 ->  72px */
  --fs-d4: clamp(1.5rem,  1.210rem +  1.29vw,  2.5rem);  /*  24 ->  40px */

  /* Numerals — IBM Plex Mono, promoted to a display role. If it is
     measured, surveyed, dated or counted, it is mono, and it may be big. */
  --fs-n1: clamp(2rem,   0.984rem + 4.52vw, 5.5rem);     /*  32 ->  88px */
  --fs-n2: clamp(1.5rem, 1.064rem + 1.94vw,   3rem);     /*  24 ->  48px */

  /* Body — nearly static. A drawing's annotation does not move underfoot. */
  --fs-xl:  clamp(1.1875rem, 1.115rem + 0.32vw, 1.4375rem); /* 19 -> 23px */
  --fs-lg:  clamp(1.0625rem, 1.017rem + 0.20vw, 1.219rem);  /* 17 -> 19.5px */
  --fs-md:  clamp(1rem,      0.982rem + 0.08vw, 1.0625rem); /* 16 -> 17px */
  --fs-sm:  clamp(0.875rem,  0.857rem + 0.08vw, 0.9375rem); /* 14 -> 15px */
  --fs-xs:  0.8125rem;  /* 13px — mono meta, map labels */
  --fs-2xs: 0.75rem;    /* 12px — mono eyebrows ONLY, uppercase, +tracking */

  --fw-reg: 400; --fw-med: 500; --fw-semi: 600; --fw-bold: 700; --fw-black: 900;

  --track-d1:      -0.005em;
  --track-display:  0em;
  --track-annot:    0.06em;   /* uppercase condensed — the drawn-label register */
  --track-eyebrow:  0.18em;   /* mono, uppercase */
  --track-mono:     0.02em;
  --track-num:     -0.01em;
  --track-body:     0.002em;

  --lh-d1:      0.88;
  --lh-display: 0.98;
  --lh-d4:      1.12;
  --lh-num:     0.92;
  --lh-lead:    1.35;
  --lh-body:    1.58;
  --lh-dense:   1.45;
  --lh-mono:    1.40;

  /* ---------- 3.6 Space. 1-6 inside components, 7-11 between them. ----- */
  --s-1: 0.25rem; --s-2: 0.5rem;  --s-3: 0.75rem; --s-4: 1rem;
  --s-5: 1.5rem;  --s-6: 2rem;    --s-7: 3rem;    --s-8: 4rem;
  --s-9: 6rem;    --s-10: 8rem;   --s-11: 12rem;

  --plate-y:    clamp(4.5rem, 2.5rem  + 8vw,  10rem);
  --plate-y-lg: clamp(6.5rem, 3.5rem  + 12vw, 15rem);
  --plate-y-sm: clamp(2.5rem, 1.75rem + 3vw,   4rem);

  /* ---------- 3.7 Layout ----------
     16 columns, not 12: the map plate needs an exact 10/16 : 6/16 split,
     and the sheet's 0.25-degree longitude ticks fall on 16ths.           */
  --grid-cols: 16;
  --gutter:      clamp(0.875rem, 0.5rem + 1.1vw, 1.5rem);
  --pad-x:       clamp(1rem, 0.4rem + 2.2vw, 2.75rem);
  --margin-w:    clamp(0px, -22rem + 32vw, 5rem);   /* 0 below ~688px, 80px @1440+ */
  --sheet-inset: clamp(0px, -8rem + 12vw, 1.5rem);  /* the hairline sheet border */

  --w-prose:  62ch;
  --w-narrow: 44rem;   /*  704px */
  --w-field:  90rem;   /* 1440px */
  --w-sheet: 108rem;   /* 1728px */

  --nav-h: 3.75rem;    /* 60px */

  --bp-xs: 420px;  --bp-sm: 560px;  --bp-md: 760px;
  --bp-lg: 960px;  --bp-xl: 1200px; --bp-2xl: 1440px; --bp-3xl: 1728px;

  /* The accessibility floor for anything you can point at. */
  --target-min: 2.75rem;  /* 44px */

  /* ---------- 3.8 The map ----------
     Equidistant cylindrical, standard parallel 52.15 N.
     k = cos(52.15) = 0.61360
     x = (lon - 3.20) * k * 400 ; y = (53.70 - lat) * 400                  */
  --map-vb-w: 1007;
  --map-vb-h: 1200;
  --map-stroke-coast:  1.5;
  --map-stroke-prov:   1;
  --map-stroke-grat:   1;
  --map-stroke-conduit: 1.5;
  --map-mark: 18;        /* hub square, units */
  --map-mark-open: 16;   /* network-lab open square, units */
  --map-traffic-dash: 8 46;

  /* ---------- 3.9 Radii. A drawing has corners. ---------- */
  --r-0: 0px;
  --r-1: 2px;   /* interface chrome: chips, inputs */
  --r-2: 3px;   /* buttons, popovers — the maximum on this site */
  --r-pill: 999px;  /* the NL/EN toggle only */

  /* ---------- 3.10 Elevation. Paper lifted off paper.
     There is NO glow token in this project, deliberately. On a light
     ground depth is a hairline edge and a tight shadow.                  */
  --lift-1: 0 1px 2px  -1px rgb(13 24 17 / 0.10), 0 0 0 1px rgb(13 24 17 / 0.05);
  --lift-2: 0 2px 6px  -1px rgb(13 24 17 / 0.12), 0 0 0 1px rgb(13 24 17 / 0.07);
  --lift-3: 0 12px 28px -8px rgb(13 24 17 / 0.20), 0 0 0 1px rgb(13 24 17 / 0.09);

  /* ---------- 3.10b Cinematic media ----------
     Footage and photography are graded, never dropped in raw. Every frame
     on this site passes through the same three layers: a green duotone
     wash that prints it in the sheet's own ink, a scrim that guarantees
     type contrast whatever the frame does, and grain so it reads as a
     print and not as a stock photo. A frame is always BEHIND the drawing,
     never beside it.                                                     */
  --media-wash: linear-gradient(
                  180deg,
                  color-mix(in srgb, var(--cy-deep) 62%, transparent) 0%,
                  color-mix(in srgb, var(--cy-ground) 52%, transparent) 45%,
                  color-mix(in srgb, var(--cy-deep) 78%, transparent) 100%);
  --media-scrim: linear-gradient(
                  180deg,
                  transparent 0%,
                  color-mix(in srgb, var(--cy-deep) 30%, transparent) 55%,
                  color-mix(in srgb, var(--cy-deep) 88%, transparent) 100%);
  --media-veil:  color-mix(in srgb, var(--c-sheet) 82%, transparent);
  --media-filter: saturate(0.55) contrast(1.08) brightness(0.86)
                  sepia(0.22) hue-rotate(72deg);
  --media-grain: 0.055;
  /* Grain, generated rather than downloaded — the CSP-safe kind: one inline
     SVG turbulence, no request, no external host. It is what stops a graded
     frame from reading as a stock photograph.                             */
  --media-grain-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  --media-vignette: radial-gradient(
                  120% 90% at 50% 42%,
                  transparent 40%,
                  color-mix(in srgb, var(--cy-deep) 62%, transparent) 100%);
  --t-kenburns: 34s;

  /* ---------- 3.11 Motion. A plotter does not overshoot. ---------- */
  --ease-plot:     cubic-bezier(0.35, 0.02, 0.30, 1.00);
  --ease-register: cubic-bezier(0.85, 0.00, 0.15, 1.00);
  --ease-carriage: cubic-bezier(0.30, 0.00, 0.10, 1.00);
  --ease-land:     cubic-bezier(0.16, 0.84, 0.24, 1.00);
  --ease-shutter:  cubic-bezier(0.62, 0.00, 0.12, 1.00);
  --ease-flow:     linear;   /* traffic. Flow is flow. */

  --t-1: 80ms;   --t-2: 150ms; --t-3: 240ms; --t-4: 360ms;
  --t-5: 560ms;  --t-6: 900ms; --t-7: 1600ms;
  --t-traffic: 9s;
  --stagger: 70ms;

  /* Set to 0 under prefers-reduced-motion. JS reads this to gate every
     timeline BEFORE building it — never build then disable.             */
  --motion: 1;

  /* ---------- 3.12 z-index ladder. Nothing invents its own value. ----- */
  --z-below:     -1;
  --z-graticule:  1;
  --z-map:        2;
  --z-content:   10;
  --z-sticky:    20;
  --z-margin:    30;
  --z-nav:       60;
  --z-overlay:   80;
  --z-modal:     90;
  --z-toast:     95;
  --z-skip:     100;

  color-scheme: light;
}


/* ==========================================================================
   4. LAB THEMING
   Set data-lab on <body> for a lab sheet, or on any plate / component root.
   Only rules, marks and active states take the colour. Body copy stays ink
   on every sheet — a lab sheet is never flooded with its own colour.
   ========================================================================== */

[data-lab="ix3"]   { --c-lab: var(--c-ix3);   --c-lab-signal: var(--c-ix3-signal); }
[data-lab="mix"]   { --c-lab: var(--c-mix);   --c-lab-signal: var(--c-mix-signal); }
[data-lab="prime"] { --c-lab: var(--c-prime); --c-lab-signal: var(--c-prime-signal); }
[data-lab="brix"]  { --c-lab: var(--c-brix);  --c-lab-signal: var(--c-brix-signal); }
[data-lab="shine"] { --c-lab: var(--c-shine); --c-lab-signal: var(--c-shine-signal); }

/* Signal values measure 2.80 (mix) and 2.89 (brix) on --c-sheet-deep, below
   the 3:1 non-text floor. Inside a deep surface the signal collapses to the
   drawn value, which passes everywhere. Enforced, not documented. */
.surface-deep,
[data-surface="deep"] {
  background-color: var(--c-sheet-deep);
  --c-lab-signal: var(--c-lab);
  --c-ink-4: var(--c-ink-3);   /* ink-4 is 4.39 on deep — step it up */
}


/* ==========================================================================
   5. THE CYANOTYPE
   The same drawing, printed the other way. Used as content on exactly one
   plate, and offered as an opt-in reading theme from the footer.

   prefers-color-scheme: dark does NOT trigger this. The plate order is
   authored; flipping the "alternative" plate to be the whole site would
   invert the argument the site is making.
   ========================================================================== */

.plate--cyanotype,
[data-print="cyanotype"] {
  --c-sheet:      var(--cy-ground);
  --c-sheet-down: var(--cy-down);
  --c-sheet-deep: var(--cy-deep);
  --c-sheet-up:   var(--cy-up);
  --c-paper:      var(--cy-paper);
  --c-water:      var(--cy-deep);
  --c-water-deep: var(--cy-deep);

  --c-ink:    var(--cy-line);
  --c-ink-2:  var(--cy-line-2);
  --c-ink-3:  var(--cy-line-2);
  --c-ink-4:  var(--cy-line-3);
  --c-rule:   var(--cy-rule);
  --c-rule-2: var(--cy-rule-2);

  --c-on-lab: var(--cy-ground);

  --c-ix3:   var(--cy-ix3);
  --c-mix:   var(--cy-mix);
  --c-prime: var(--cy-prime);
  --c-brix:  var(--cy-brix);
  --c-shine: var(--cy-shine);

  --c-ix3-signal:   var(--cy-ix3-signal);
  --c-mix-signal:   var(--cy-mix-signal);
  --c-prime-signal: var(--cy-prime-signal);
  --c-brix-signal:  var(--cy-brix-signal);
  --c-shine-signal: var(--cy-shine-signal);

  --c-lab:        var(--c-ink);
  --c-lab-signal: var(--c-ink-2);

  --c-alert: var(--cy-alert);

  /* The ring inverts with the ground: pale core -> dark core, ink halo ->
     line halo. Core 6.14–8.97 on every lifted lab; halo 12.99 on ground. */
  --c-focus-core: var(--cy-ground);
  --c-focus-ring: var(--cy-line);

  color-scheme: dark;
  background-color: var(--c-sheet);
  color: var(--c-ink-2);
}

/* data-lab must re-resolve inside the cyanotype. Attribute order in the
   cascade is not guaranteed, so restate the pairs at higher specificity. */
[data-print="cyanotype"] [data-lab="ix3"],
.plate--cyanotype [data-lab="ix3"]   { --c-lab: var(--cy-ix3);   --c-lab-signal: var(--cy-ix3-signal); }
[data-print="cyanotype"] [data-lab="mix"],
.plate--cyanotype [data-lab="mix"]   { --c-lab: var(--cy-mix);   --c-lab-signal: var(--cy-mix-signal); }
[data-print="cyanotype"] [data-lab="prime"],
.plate--cyanotype [data-lab="prime"] { --c-lab: var(--cy-prime); --c-lab-signal: var(--cy-prime-signal); }
[data-print="cyanotype"] [data-lab="brix"],
.plate--cyanotype [data-lab="brix"]  { --c-lab: var(--cy-brix);  --c-lab-signal: var(--cy-brix-signal); }
[data-print="cyanotype"] [data-lab="shine"],
.plate--cyanotype [data-lab="shine"] { --c-lab: var(--cy-shine); --c-lab-signal: var(--cy-shine-signal); }


/* ==========================================================================
   6. RESET
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-block-start: calc(var(--nav-h) + var(--s-4));
  tab-size: 2;
}

body {
  min-block-size: 100svh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img, picture, video, svg { display: block; max-inline-size: 100%; }
img, video { block-size: auto; }

input, button, textarea, select { font: inherit; color: inherit; letter-spacing: inherit; }
button { background: none; border: none; padding: 0; cursor: pointer; }

p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }

ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }

table { border-collapse: collapse; border-spacing: 0; }

hr { border: none; border-block-start: 1px solid var(--c-rule-2); }

[hidden] { display: none !important; }

:target { scroll-margin-block-start: calc(var(--nav-h) + var(--s-6)); }


/* ==========================================================================
   7. BASE ELEMENT STYLES
   ========================================================================== */

body {
  background-color: var(--c-sheet);
  color: var(--c-ink-2);
  font-family: var(--font-body);
  font-size: var(--fs-md);
  font-weight: var(--fw-reg);
  line-height: var(--lh-body);
  letter-spacing: var(--track-body);
  font-synthesis-weight: none;
}

/* Dutch runs ~15% longer. The width axis carries it, never the type scale. */
:lang(nl) { hyphens: auto; --wdth-display: 66; --wdth-annot: 76; }
:lang(en) { hyphens: manual; --wdth-display: 78; --wdth-annot: 88; }

/* Display names and annotates. Mono measures. Body explains. */
h1, h2, h3 {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--wdth-display), "wght" var(--fw-black);
  font-weight: var(--fw-black);
  color: var(--c-ink);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  text-wrap: balance;
}

/* Hyphenation is a reading aid for prose. A map label or a plate title
   broken with a hyphen stops being a label. The width axis carries Dutch. */
h1, h2, h3, .statement, .annot { hyphens: manual; }

h1 { font-size: var(--fs-d2); }
h2 { font-size: var(--fs-d3); }
h3 {
  font-size: var(--fs-d4);
  font-variation-settings: "wdth" var(--wdth-display), "wght" var(--fw-bold);
  font-weight: var(--fw-bold);
  line-height: var(--lh-d4);
}

/* Below --fs-d4 nothing uses the display face. */
h4, h5, h6 {
  font-family: var(--font-body);
  font-weight: var(--fw-semi);
  color: var(--c-ink);
  line-height: 1.28;
  letter-spacing: var(--track-body);
  text-wrap: balance;
}
h4 { font-size: var(--fs-xl); }
h5 { font-size: var(--fs-lg); }
h6 { font-size: var(--fs-md); }

p { text-wrap: pretty; max-inline-size: var(--w-prose); }

a {
  color: var(--c-lab);
  text-decoration: none;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  transition: color var(--t-2) var(--ease-register),
              text-decoration-color var(--t-2) var(--ease-register);
}

/* Inline links are always underlined — colour is never the only signal. */
p a, li a, dd a, td a {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
}
p a:hover, li a:hover, dd a:hover, td a:hover { text-decoration-color: currentColor; }

strong, b { font-weight: var(--fw-semi); color: var(--c-ink); }
em, i { font-style: italic; }
small { font-size: var(--fs-sm); }

code, kbd, samp, pre {
  font-family: var(--font-mono);
  font-size: 0.92em;
  letter-spacing: var(--track-mono);
}

/* Every measured thing is mono, and every number is tabular. */
:is(code, kbd, samp, pre, .mono, .num, [data-measure], td, th) {
  font-variant-numeric: tabular-nums;
}

table { inline-size: 100%; font-size: var(--fs-sm); line-height: var(--lh-dense); }

th {
  text-align: start;
  font-family: var(--font-mono);
  font-weight: var(--fw-med);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--track-eyebrow);
  color: var(--c-ink-3);
  background-color: var(--c-sheet-deep);
  padding: var(--s-3) var(--s-4);
  border-block-end: 1px solid var(--c-rule-2);
}

td {
  padding: var(--s-3) var(--s-4);
  border-block-end: 1px solid var(--c-rule);
  vertical-align: top;
}

caption {
  text-align: start;
  padding-block-end: var(--s-3);
  color: var(--c-ink-3);
  font-size: var(--fs-sm);
}

figcaption {
  font-size: var(--fs-sm);
  color: var(--c-ink-3);
  margin-block-start: var(--s-3);
  max-inline-size: var(--w-prose);
}

blockquote {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--wdth-display), "wght" var(--fw-med);
  font-size: var(--fs-d4);
  line-height: var(--lh-d4);
  color: var(--c-ink);
  border-inline-start: 2px solid var(--c-lab);
  padding-inline-start: var(--s-5);
  max-inline-size: 26ch;
}

/* Definition lists are the title block and the ambition list. */
dt { font-family: var(--font-mono); font-size: var(--fs-2xs); text-transform: uppercase;
     letter-spacing: var(--track-eyebrow); color: var(--c-ink-3); }
dd { margin-inline-start: 0; }

/* No canvas ships on this site. If one appears, it is a defect — but if it
   somehow does, it carries no meaning. */
canvas { display: none !important; }


/* ==========================================================================
   8. FOCUS, SELECTION, SCROLLBAR
   ========================================================================== */

/* Two-tone ring. Never remove it. See art-direction.md 2.7. */
:focus-visible {
  outline: 2px solid var(--c-focus-core);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--c-focus-ring);
  border-radius: var(--r-1);
}

/* Nothing on this site puts focus inside an SVG (the map is aria-hidden and
   its list carries every target). This is the safety net if one ever does. */
svg :focus-visible {
  outline: none;
  box-shadow: none;
  stroke: var(--c-focus-ring);
  stroke-width: 3;
  paint-order: stroke fill;
}

.skip-link {
  position: absolute;
  inset-block-start: var(--s-2);
  inset-inline-start: var(--s-2);
  z-index: var(--z-skip);
  padding: var(--s-3) var(--s-5);
  background-color: var(--c-paper);
  color: var(--c-ink);
  border: 1px solid var(--c-ink);
  border-radius: var(--r-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-med);
  transform: translateY(-200%);
  transition: transform var(--t-2) var(--ease-register);
}
.skip-link:focus-visible { transform: translateY(0); }

::selection { background-color: var(--c-ink); color: var(--c-paper); }

::-webkit-scrollbar { inline-size: 12px; block-size: 12px; }
::-webkit-scrollbar-track { background-color: var(--c-sheet-down); }
::-webkit-scrollbar-thumb {
  background-color: var(--c-rule-2);
  border: 3px solid var(--c-sheet-down);
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-thumb:hover { background-color: var(--c-ink-3); }
html { scrollbar-color: var(--c-rule-2) var(--c-sheet-down); scrollbar-width: thin; }


/* ==========================================================================
   9. LAYOUT PRIMITIVES
   ========================================================================== */

.container {
  inline-size: 100%;
  max-inline-size: var(--w-field);
  margin-inline: auto;
  padding-inline: var(--pad-x);
}
.container--sheet  { max-inline-size: var(--w-sheet); }
.container--narrow { max-inline-size: var(--w-narrow); }
.container--prose  { max-inline-size: var(--w-prose); }

.grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: var(--s-6);
}

@media (max-width: 1199px) { :root { --grid-cols: 12; } }
@media (max-width: 959px)  { :root { --grid-cols: 8; } }
@media (max-width: 759px)  { :root { --grid-cols: 6; } }
@media (max-width: 559px)  { :root { --grid-cols: 4; --gutter: 0.875rem; } }

/* The sheet: a hairline border inset from the viewport with tick marks.
   Painted by CSS before any JS runs. */
.sheet {
  position: relative;
  min-block-size: 100svh;
}
.sheet::before {
  content: "";
  position: fixed;
  inset: var(--sheet-inset);
  border: 1px solid var(--c-rule-2);
  pointer-events: none;
  z-index: var(--z-margin);
}

/* The margin rail is part of the grid, never an overlay. */
.page {
  display: grid;
  grid-template-columns: var(--margin-w) minmax(0, 1fr);
}
.margin { grid-column: 1; z-index: var(--z-margin); }
.page > :not(.margin) { grid-column: 2; }

@media (max-width: 687px) {
  .page { grid-template-columns: 1fr; }
  .margin { display: none; }
}

/* Plates cut. They never cross-fade into each other. */
.plate {
  position: relative;
  padding-block: var(--plate-y);
}
.plate--lg   { padding-block: var(--plate-y-lg); }
.plate--sm   { padding-block: var(--plate-y-sm); }
.plate--band { background-color: var(--c-sheet-down); }

/* The cut: a 1px rule at the plate's base, full field width. */
.plate__cut {
  block-size: 1px;
  inline-size: 100%;
  background-color: var(--c-rule-2);
  border: none;
}

/* The graticule. The only soft edge on the site is this mask, and it is a
   mask, not a fill — there are no gradients in this project. */
.graticule {
  position: absolute;
  inset: 0;
  z-index: var(--z-graticule);
  pointer-events: none;
  -webkit-mask-image: radial-gradient(closest-side, #000 72%, transparent 100%);
          mask-image: radial-gradient(closest-side, #000 72%, transparent 100%);
}

/* Rule-separated columns. This is the anti-card primitive: no border on the
   outer box, no radius, no fill, no shadow. A departures board. */
.columns {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 0;
}
.columns > * + * {
  border-inline-start: 1px solid var(--c-rule-2);
  padding-inline-start: var(--s-5);
  margin-inline-start: var(--s-5);
}
@media (max-width: 759px) {
  .columns { grid-auto-flow: row; grid-auto-columns: auto; }
  .columns > * + * {
    border-inline-start: none;
    border-block-start: 1px solid var(--c-rule-2);
    padding-inline-start: 0;
    margin-inline-start: 0;
    padding-block-start: var(--s-5);
    margin-block-start: var(--s-5);
  }
}

/* A lab band's 6px top rule — the only place a lab colour appears at scale. */
.band { border-block-start: 6px solid var(--c-lab); padding-block-start: var(--s-6); }


/* ==========================================================================
   10. TYPE UTILITIES
   ========================================================================== */

/* --- The statement register: editorial. Sentence case. Never uppercase. --- */
.statement,
.display-1, .display-2, .display-3, .display-4 {
  font-family: var(--font-display);
  color: var(--c-ink);
  text-wrap: balance;
  text-transform: none;
}
.display-1 {
  font-size: var(--fs-d1);
  font-variation-settings: "wdth" var(--wdth-display), "wght" 900;
  line-height: var(--lh-d1);
  letter-spacing: var(--track-d1);
}
.display-2 {
  font-size: var(--fs-d2);
  font-variation-settings: "wdth" var(--wdth-display), "wght" 900;
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
}
.display-3 {
  font-size: var(--fs-d3);
  font-variation-settings: "wdth" var(--wdth-display), "wght" 800;
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
}
.display-4 {
  font-size: var(--fs-d4);
  font-variation-settings: "wdth" var(--wdth-display), "wght" 700;
  line-height: var(--lh-d4);
  letter-spacing: var(--track-display);
}

/* --- The annotation register: marks drawn ONTO the sheet. UPPERCASE.
   Map labels, legend heads, title-block keys, the six verbs. Positive
   tracking is correct HERE and nowhere else. --- */
.annot {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--wdth-annot), "wght" var(--fw-semi);
  font-weight: var(--fw-semi);
  text-transform: uppercase;
  letter-spacing: var(--track-annot);
  line-height: var(--lh-d4);
  color: var(--c-ink);
}
.annot--lab { color: var(--c-lab); }

/* --- Mono, promoted. If it is measured, surveyed, dated or counted. --- */
.num-1 {
  font-family: var(--font-mono);
  font-size: var(--fs-n1);
  font-weight: var(--fw-med);
  line-height: var(--lh-num);
  letter-spacing: var(--track-num);
  font-variant-numeric: tabular-nums;
  color: var(--c-ink);
}
.num-2 {
  font-family: var(--font-mono);
  font-size: var(--fs-n2);
  font-weight: var(--fw-reg);
  line-height: var(--lh-num);
  letter-spacing: var(--track-num);
  font-variant-numeric: tabular-nums;
  color: var(--c-ink);
}
.mono {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--track-mono);
  line-height: var(--lh-mono);
  font-variant-numeric: tabular-nums;
  color: var(--c-ink-3);
}

/* Eyebrow: mono, uppercase, 12px. Only ever labels something stated in full
   nearby — it never carries unique information. */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-med);
  text-transform: uppercase;
  letter-spacing: var(--track-eyebrow);
  line-height: var(--lh-mono);
  color: var(--c-ink-3);
}

.lead {
  font-size: var(--fs-xl);
  line-height: var(--lh-lead);
  color: var(--c-ink-2);
  max-inline-size: 54ch;
}

.prose { max-inline-size: var(--w-prose); line-height: var(--lh-body); }

.text-ink  { color: var(--c-ink); }
.text-mid  { color: var(--c-ink-3); }
.text-meta { color: var(--c-ink-4); }
.text-lab  { color: var(--c-lab); }

/* Text sitting on a lab-coloured fill. 6.09-6.58:1 on every lab value. */
.on-lab { background-color: var(--c-lab); color: var(--c-on-lab); }

.visually-hidden:not(:focus):not(:active) {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* ==========================================================================
   11. USER-PREFERENCE OVERRIDES
   ========================================================================== */

/* ---- 11.1 Reduced motion.
   Per-move fallbacks live in the components (art-direction.md 5.5); this is
   the safety net. JS must read --motion BEFORE building any GSAP timeline or
   Lenis instance, and must tear down TRAFFIC on a runtime change. -------- */
@media (prefers-reduced-motion: reduce) {
  :root {
    --motion: 0;
    --t-1: 1ms; --t-2: 1ms; --t-3: 1ms; --t-4: 1ms;
    --t-5: 120ms; --t-6: 120ms; --t-7: 120ms;
    --t-traffic: 0s;
    --stagger: 0ms;
  }
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
  /* Traffic is removed entirely; conduits render as static solid hairlines. */
  .traffic { display: none !important; }
}

/* ---- 11.2 Reduced transparency ---- */
@media (prefers-reduced-transparency: reduce) {
  .nav, .popover, .modal {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    background-color: var(--c-sheet) !important;
  }
}

/* ---- 11.3 More contrast — ink darkens, hairlines become real strokes,
   lab values step to their darkest usable form. ---- */
@media (prefers-contrast: more) {
  :root {
    --c-ink-2: #0D1811;
    --c-ink-3: #26332B;
    --c-ink-4: #26332B;
    --c-rule:   #6C7A70;
    --c-rule-2: #4A5C51;
    /* Signals collapse to drawn values: no reduced-contrast marks anywhere. */
    --c-ix3-signal:   var(--p-ix3);
    --c-mix-signal:   var(--p-mix);
    --c-prime-signal: var(--p-prime);
    --c-brix-signal:  var(--p-brix);
    --c-shine-signal: var(--p-shine);
  }
  .plate--cyanotype, [data-print="cyanotype"] {
    --c-ink-2: var(--cy-line);
    --c-ink-3: var(--cy-line);
    --c-ink-4: var(--cy-line-2);
    --c-rule:   var(--cy-rule-2);
    --c-rule-2: var(--cy-line-3);
  }
  :focus-visible { outline-width: 3px; box-shadow: 0 0 0 6px var(--c-focus-ring); }
}

/* ---- 11.4 Forced colors (Windows High Contrast).
   The system owns colour. Every fill drops to an outline; the drawing
   survives because it was always line work. ---- */
@media (forced-colors: active) {
  :root {
    --c-lab: CanvasText;
    --c-lab-signal: CanvasText;
    --c-on-lab: Canvas;
  }
  .sheet::before,
  .plate__cut,
  th, td { border-color: CanvasText; }
  .graticule { display: none; }
  .band { border-block-start-color: CanvasText; }
  a { color: LinkText; }
  .on-lab { background-color: Canvas; color: CanvasText; border: 1px solid CanvasText; }
  :focus-visible { outline: 3px solid Highlight; box-shadow: none; }
  svg [fill]:not([fill="none"]) { fill: CanvasText; }
  svg [stroke]:not([stroke="none"]) { stroke: CanvasText; }
}

/* ---- 11.5 Print.
   Nearly free: the site is already ink on light stock. A ministry will PDF
   this, so the map must fit beside its legend and the title block intact. -- */
@media print {
  :root {
    --c-sheet: #FFFFFF; --c-sheet-down: #FFFFFF; --c-sheet-deep: #F0F0F0;
    --c-sheet-up: #FFFFFF; --c-paper: #FFFFFF;
    --c-ink: #000000; --c-ink-2: #1A1A1A; --c-ink-3: #3A3A3A; --c-ink-4: #4A4A4A;
    --c-rule: #C4C4C4; --c-rule-2: #7A7A7A;
    --plate-y: 1.5rem; --plate-y-lg: 2rem; --plate-y-sm: 1rem;
    --margin-w: 0px; --sheet-inset: 0px;
  }
  /* The cyanotype plate prints as ink on paper like everything else —
     inverting a full page of prussian is hostile to a printer and a reader. */
  .plate--cyanotype, [data-print="cyanotype"] {
    --c-sheet: #FFFFFF; --c-sheet-down: #FFFFFF; --c-sheet-deep: #F0F0F0;
    --c-ink: #000000; --c-ink-2: #1A1A1A; --c-ink-3: #3A3A3A; --c-ink-4: #4A4A4A;
    --c-rule: #C4C4C4; --c-rule-2: #7A7A7A;
    background: #FFFFFF !important;
  }
  body { background: #FFFFFF; color: #1A1A1A; }
  .sheet::before, .nav, .skip-link, .margin, .traffic { display: none !important; }
  .page { grid-template-columns: 1fr; }
  .page > :not(.margin) { grid-column: 1; }
  a { color: #000000; text-decoration: underline; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 0.8em; }
  .plate, table, figure, .band { break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }
}
