/* ---- tokens.css ---- */
/*
  ENKOP design tokens (round 4 design system; docs/DESIGN-SYSTEM.md).

  The only file in the build where a colour, length, duration, weight or
  ratio literal may appear. Stylelint enforces it everywhere else
  (stylelint.config.js and stylelint-plugins/no-magic-numbers.js).

  Colour: three ramps (petrol, amber, neutral) and the semantic names the
  components use. A trailing comment on a colour states its role; /specimen
  prints it beside the WCAG contrast measured from these values
  (src/_data/palette.js).

  Fluid values run from a 360px viewport to a 1440px one. Every spacing
  token lands on a multiple of 8px at both ends.
*/

:root {
  /* ---- Petrol: the brand anchor is 700, #0E4D68 ---- */
  --petrol-50: #eff6fb; /* Quietest petrol ground. */
  --petrol-100: #e0eef5; /* Petrol wash: an alternate light ground. */
  --petrol-200: #c5dbe8; /* Hairlines and fills on dark petrol. */
  --petrol-300: #9dc1d5; /* Links and marks on ink and dark petrol. */
  --petrol-400: #6da0bb; /* Map fills and charts only; not for text on light. */
  --petrol-500: #427c9a; /* Large text and marks only on light grounds. */
  --petrol-600: #14698e; /* Hover and active state for petrol on light grounds. */
  --petrol-700: #0e4d68; /* Brand anchor. Links, rules, active and prospective markets. */
  --petrol-800: #013b52; /* Dark petrol ground for a chapter. */
  --petrol-900: #012839; /* Deepest petrol ground. */
  --petrol-950: #001925; /* Reserved. */

  /*
    Amber: ENKOP's own warm colour, from the legacy site (--amber #C8922A,
    amber-light #F5E6C8, amber-pale #FDF6EC) and the gold of the emblem.
    500 is the one warm signal. It replaced a brighter "amber" (#E3A72C) on
    14 October 2026 so the palette matches the brand.
  */
  --amber-50: #fdf6ec; /* ENKOP amber-pale. Warm ground, used sparingly. */
  --amber-100: #f5e6c8; /* ENKOP amber-light. Highlight behind text on light grounds. */
  --amber-200: #f9d392; /* Reserved. */
  --amber-300: #edc070; /* Marks on dark grounds. */
  --amber-400: #dda94b; /* Charts only. */
  --amber-500: #c8922a; /* ENKOP amber, the one warm signal: inspected marks, Kenya, the focus ring. Never text on light. */
  --amber-600: #aa7404; /* Large text and marks only on light grounds. */
  --amber-700: #845702; /* Warm text on light grounds (AA on every light ground). */
  --amber-800: #6d4605; /* Reserved. */
  --amber-900: #4f3001; /* Reserved. */
  --amber-950: #392001; /* Reserved. */

  /* ---- Neutral: cool limewash paper to petrol-black ink (OKLab steps) ---- */
  --neutral-0: #f4f5f1; /* Raised surfaces: inputs, panels. */
  --neutral-50: #f1f3ee; /* Reserved. */
  --neutral-100: #eceee9; /* Cool limewash. The page. */
  --neutral-200: #dde0dd; /* Stone: an alternate light ground (every light-ground text colour passes AA on it). */
  --neutral-300: #c2c8c7; /* Hairlines on light grounds. */
  --neutral-400: #9da7a9; /* Disabled marks. */
  --neutral-500: #7b888d; /* Placeholder marks; not for text. */
  --neutral-600: #5e6e75; /* Secondary text on paper and raised grounds only. */
  --neutral-700: #44575f; /* Secondary text on every light ground. */
  --neutral-800: #2b404a; /* Reserved. */
  --neutral-900: #19303b; /* Raised surfaces on ink. */
  --neutral-950: #0b2430; /* Deep petrol-black. Body text on light, the primary button. */
  --neutral-975: #071a22; /* Deepest ink: the footer, the full-screen menu, statement heroes. */

  /* ---- Accent neutrals and states ---- */
  --silver: #9fb4bc; /* Paddy-water grey. Secondary text on dark only; map fills. */
  --char: #1a1917; /* Biochar contexts only. */
  --state-error: #a3352b; /* Error text and borders. */
  --state-ok: #2f6b3f; /* Success text and borders. */

  /* ---- Semantic colour: what components use ---- */
  --ink: var(--neutral-950);
  --ink-deep: var(--neutral-975);
  --paper: var(--neutral-100);
  --paper-raised: var(--neutral-0);
  --petrol: var(--petrol-700);
  --petrol-bright: var(--petrol-600);
  --amber: var(--amber-500);
  --text-muted: var(--neutral-700);
  --rule-light: color-mix(in oklab, var(--ink) 12%, transparent);
  --rule-heavy: color-mix(in oklab, var(--ink) 28%, transparent);
  --rule-on-dark: color-mix(in oklab, var(--silver) 24%, transparent);
  --scrim: color-mix(in oklab, var(--ink) 64%, transparent); /* Behind open dialogs. */

  /* Grounds: each section sits on one; a change of ground is a change of chapter. */
  --ground-paper: var(--paper);
  --ground-raised: var(--paper-raised);
  --ground-stone: var(--neutral-200);
  --ground-wash: var(--petrol-100);
  --ground-ink: var(--ink);
  --ground-petrol: var(--petrol-800);

  /* ---- Focus ring: amber inside ink on light grounds; .surface-dark swaps the outer ring to amber ---- */
  --focus-inner: var(--amber);
  --focus-outer: var(--ink);
  --focus-width: 2px;
  --focus-offset: 2px;

  /* ---- Type families ---- */
  --font-structural: "Archivo", "Archivo Fallback", system-ui, sans-serif; /* display, interface, records */
  --font-editorial: "Newsreader", "Newsreader Fallback", georgia, serif; /* body prose, captions */
  --font-body: var(--font-editorial);

  /* Archivo width stops: four values only, each with a meaning */
  --wdth-display: 118; /* hero and chapter headlines (immersive redesign) */
  --wdth-statement: 112; /* H1, display, pillar names */
  --wdth-heading: 105; /* H2 */
  --wdth-voice: 100; /* interface: nav, buttons, labels */
  --wdth-record: 78; /* ledger, title-block records, figures; never below --step-0 */

  /* Weights */
  --weight-regular: 400;
  --weight-medium: 500; /* records */
  --weight-statement: 560; /* pillar names and other statements */
  --weight-bold: 600; /* headings, emphasis */
  --weight-display: 720; /* hero and chapter headlines */
  --weight-heavy: 800; /* the footer wordmark only */

  /*
    Modular scale: base 17px at a 360px viewport rising to 19px at 1440px;
    ratio 1.25 on small screens widening to 1.333 on large ones.
  */
  --step--1: clamp(0.85rem, 0.8365rem + 0.0602vw, 0.8906rem); /* 13.6 to 14.2px: captions, small print */
  --step-0: clamp(1.0625rem, 1.0208rem + 0.1852vw, 1.1875rem); /* 17 to 19px: body */
  --step-1: clamp(1.3281rem, 1.2431rem + 0.3781vw, 1.5833rem); /* 21.2 to 25.3px: standfirst, H3 */
  --step-2: clamp(1.6602rem, 1.5098rem + 0.6681vw, 2.1111rem); /* 26.6 to 33.8px: statements */
  --step-3: clamp(2.0752rem, 1.8287rem + 1.0957vw, 2.8148rem); /* 33.2 to 45px: pull quotes, figures */
  --step-4: clamp(2.594rem, 2.2076rem + 1.7172vw, 3.7531rem); /* 41.5 to 60px: H2 */
  --step-5: clamp(3.2425rem, 2.6553rem + 2.6098vw, 5.0041rem); /* 51.9 to 80px: inner-page H1 */
  --step-6: clamp(4.0531rem, 3.1801rem + 3.8801vw, 6.6722rem); /* 64.8 to 106.8px: chapter display */
  --step-7: clamp(5.0664rem, 3.7898rem + 5.6738vw, 8.8962rem); /* 81.1 to 142.3px: the homepage headline */

  /* Leading */
  --leading-display: 0.94;
  --leading-heading: 1.04;
  --leading-snug: 1.2;
  --leading-ui: 1.35;
  --leading-caption: 1.45;
  --leading-body: 1.6;

  /* Tracking: tightens as size grows; records open slightly */
  --tracking-display: -0.025em;
  --tracking-heading: -0.012em;
  --tracking-record: 0.01em;
  --tracking-label: 0.08em; /* small uppercase labels */

  /* Underlines */
  --underline-rest: 1px;
  --underline-hover: 2px;
  --underline-offset: 0.2em;

  /*
    H1 cap (brief Part 4.2, copy-tolerant headlines). Derived from the font and the
    language, not the copy. Browsers do not hyphenate capitalised words, so a
    sentence-case headline's first word can never be hyphenated; the cap makes
    a 15-letter capitalised word fit one line (decarbonisation, transformative,
    sustainability, infrastructure). Archivo at wdth 112 / wght 600 averages
    0.5696 em per letter (English letter frequencies, measured by
    scripts/fonts/build_fonts.py): 100 / (15 x 0.5696) = 11.7cqi.
    Longer words hyphenate if lowercase; otherwise overflow-wrap breaks them.
  */
  --h1-cap: 10.67cqi; /* at the display setting, wdth 118 / wght 720 (build_fonts.py, October 2026) */

  /* The same rule for chapter display lines, measured against the viewport
     because they sit in ordinary grid cells rather than a .headline container. */
  --display-cap: calc((100vw - 2 * var(--page-margin)) * 0.1067);

  /*
    Reading measures. ch is the width of "0", wider than the average letter:
    real lines run about 1.3x the ch count. Each value is set so the longest
    rendered line holds 60 to 75 characters, measured on the proof page.
  */
  --measure-body: 52ch; /* serif body: 66 to 74 characters on a full line */
  --measure-ui: 52ch; /* sans text: pillar descriptions, panels, captions */
  --measure-standfirst: 36ch;
  --measure-display: 18ch; /* chapter display lines */
  --measure-quote: 28ch;
  --measure-title: 26ch; /* headline-sized titles in lists and cards */
  --measure-legal: 50ch; /* legal pages: a little narrower than the body */

  /* ---- Spacing: an 8px scale ---- */
  --space-3xs: 0.25rem; /* 4px: the only half step, for optical tightening (label to value, icon to text) */
  --space-2xs: 0.5rem; /* 8px */
  --space-xs: clamp(0.5rem, 0.3333rem + 0.7407vw, 1rem); /* 8 to 16px */
  --space-s: clamp(1rem, 0.8333rem + 0.7407vw, 1.5rem); /* 16 to 24px */
  --space-m: clamp(1.5rem, 1.3333rem + 0.7407vw, 2rem); /* 24 to 32px */
  --space-l: clamp(2rem, 1.6667rem + 1.4815vw, 3rem); /* 32 to 48px */
  --space-xl: clamp(3rem, 2.3333rem + 2.963vw, 5rem); /* 48 to 80px */
  --space-2xl: clamp(4rem, 2.6667rem + 5.9259vw, 8rem); /* 64 to 128px */
  --space-3xl: clamp(6rem, 4rem + 8.8889vw, 12rem); /* 96 to 192px: the space before a chapter display */
  --space-section: clamp(5rem, 3.6667rem + 5.9259vw, 9rem); /* 80 to 144px: block padding of every section */

  /* ---- Layout ---- */
  --content-max: 85rem; /* 1360px */
  --page-margin: clamp(1rem, 4.4444vw, 4rem); /* 16 to 64px */
  --gutter: clamp(1rem, 0.6667rem + 1.4815vw, 2rem); /* 16 to 32px */
  --plate-max: 100rem; /* 1600px: the width of the committed field photographs; never upscaled */
  --narrow-max: 44rem; /* 704px: centred single columns (articles, legal) */
  --form-max: 32rem; /* 512px */
  --target-min: 3rem; /* 48px: minimum height of every control */
  --icon-s: 1rem; /* 16px */
  --mark-m: 3rem; /* 48px: ledger and step marks */
  --mark-s: 0.75em; /* the ledger tick, sized to its text */
  --mark-strip: 1.5rem; /* 24px: one stage in the ledger strip on /field */
  --tile-min: 15rem; /* 240px: the narrowest tile in an auto-fill grid (partners, team) */
  --plate-pair-max: 52rem; /* 832px: two portrait plates side by side */
  --dialog-max-block: 52rem; /* 832px: the tallest a bio dialog grows */
  --list-indent: 1.1em; /* bullets hang just outside the text column */
  --photo-inset: 75%; /* photographs on small screens stop short of the margin */
  --margin-column: 25%; /* three of twelve columns: where report text starts */
  --visually-hidden-size: 1px;
  --offscreen: -200%; /* the skip link waits above the page */

  /* Breakpoints, for documentation: media queries cannot read custom
     properties, so these exact values are the only ones allowed in @media. */
  --bp-md: 48rem; /* 768px */
  --bp-lg: 64rem; /* 1024px */

  /* ---- Rules and borders ---- */
  --rule-hair: 1px;
  --rule-strong: 2px;
  --rule-bold: 4px;

  /* ---- Radii ---- */
  --radius-0: 0; /* structural surfaces, photography, map, tables */
  --radius-1: 3px; /* inputs, small controls */
  --radius-card: 1rem; /* cards and panels */
  --radius-pill: 999px; /* pills: navigation, buttons, chips */

  /* ---- Immersive layer (PLAN.md, section 3) ---- */
  --hero-min: 100svh; /* heroes fill the first screen, measured against the small viewport on phones */
  --nav-top: 1rem; /* 16px from the top of the window */
  --nav-pill-h: 3rem; /* 48px */
  --nav-blur: 14px;
  --nav-on-light: color-mix(in oklab, var(--ink) 75%, transparent); /* the pills over light sections */
  --nav-on-dark: color-mix(in oklab, var(--paper) 15%, transparent); /* the pills over dark sections */
  --nav-hide-after: 120; /* px scrolled before the pills may hide */
  --anchor-gap-px: 16; /* px between the nav and an anchor's target */
  --lenis-lerp: 0.09;
  --parallax-range-px: 240;
  --lift: -0.25rem; /* cards rise 4px on hover */
  --nudge: 0.25rem; /* arrow links move 4px on hover */
  --shadow-lift: 0 1.5rem 3rem -1.5rem color-mix(in oklab, var(--ink) 40%, transparent);
  --scrim-photo: linear-gradient(180deg, color-mix(in oklab, var(--ink-deep) 55%, transparent) 0%, color-mix(in oklab, var(--ink-deep) 20%, transparent) 40%, color-mix(in oklab, var(--ink-deep) 85%, transparent) 100%);
  --scrim-side: linear-gradient(90deg, color-mix(in oklab, var(--ink-deep) 72%, transparent) 0%, color-mix(in oklab, var(--ink-deep) 30%, transparent) 55%, transparent 100%); /* behind text set on the left of a photograph */
  --frost: color-mix(in oklab, var(--petrol-800) 72%, transparent); /* the full-bleed band's panel */
  --grain-opacity: 0.05;
  --grain-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='192' height='192'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.4 -0.2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  --dot-fade: 70%; /* the dot field is gone this far from its corner */
  --hero-statement-min: 72svh;
  --hero-canvas-opacity-small: 0.55; /* the particles sit behind the headline on phones */
  --hero-canvas-inset: 40%; /* from 64rem the particles take the right of the hero */
  --hero-rise: 0.4em; /* the headline rises this far on arrival */
  --dur-hero-rise: 1.1s;
  --grain-shift: 3%; /* each step of the grain moves the tile by this much */
  --dur-grain: 1s;
  --ease-grain: steps(2);
  --clamp-lines: 2; /* excerpts in cards and rows */
  --preloader-line-max: 50vw;
  --theme-muted-dark: var(--silver);
  --hero-pad-top: 7.5rem; /* clears the floating nav */
  --hero-title-max: 14ch;
  --hero-lede-max: 34ch;
  --link-grid-min: 12rem;
  --cue-travel: 100%;
  --dur-cue: 2.4s;
  --card-pad: clamp(1.25rem, 1rem + 1vw, 2rem);
  --chip-pad: 0.375rem 0.75rem;
  --lead-line-h: clamp(3rem, 2rem + 4vw, 6rem);
  --stat-min: 18rem;
  --thumb-row: clamp(4.5rem, 3.5rem + 3vw, 6.5rem);
  --round-button: 3.5rem;
  --collage-shift: 12%;
  --band-min: 32rem;
  --tabs-h: 3.5rem;
  --slide-min: 100svh;
  --slide-segment-h: 3px;
  --team-photo: 10rem;
  --ring-amber: 3px;
  --logo-cell: 11rem;
  --logo-cell-h: 7rem;
  --focus-people: 50% 30%; /* wide crops of photographs of people: keep the upper third, where faces are */
  --collage-peek: 70%; /* on phones each collage photo shows the next one's edge */
  --chain-dot: 1.25rem;
  --progress-h: 3px;
  --step-pop: 1.06; /* 'How we work': the step in view grows by 6% */
  --grain-tile: 12rem;
  --dot-size: 1.5px;
  --dot-gap: 1.25rem; /* 20px */
  --dot-colour: color-mix(in oklab, var(--petrol-700) 22%, transparent);
  --particle-base: color-mix(in oklab, var(--silver) 45%, transparent);
  --particle-market: var(--petrol-200);
  --particle-hq: var(--amber);
  --particle-gap: 7; /* px between dots on large screens */
  --particle-gap-small: 8;
  --particle-margin: 0.06;
  --particle-assemble-s: 1.8;
  --particle-drift: 1.6;
  --particle-reach: 90; /* px the cursor pushes dots away */
  --orbit-size: clamp(16rem, 6rem + 24vw, 28rem);
  --ring-gap: 2.5rem; /* the inner orbit ring sits 40px outside the image */
  --ring-gap-outer: 6rem; /* the outer ring 96px outside */
  --ken-burns-scale: 1.08;
  --dur-ken-burns: 24s;
  --dur-wipe: 280ms; /* each way, so a page change stays under 600ms */
  --dur-preloader: 1.1s;
  --scroll-cue-h: 4rem;
  --wordmark-opacity: 0.92;
  --wordmark-size: min(21rem, 21vw); /* the footer wordmark fills the content width (5 letters at wdth 118) */

  /* ---- Imagery: the crop set and the shared treatment ---- */
  --ratio-wide: 16 / 9; /* full-bleed plates from 48rem */
  --ratio-landscape: 3 / 2; /* row photographs, plates on small screens, illustrations */
  --ratio-portrait: 4 / 5; /* service photographs, portrait pairs, team portraits */
  --ratio-square: 1; /* the homepage hero plate */
  --photo-filter: none; /* the grade is baked into every photograph by scripts/build-media.mjs (immersive redesign); a CSS filter on top would grade twice */
  --portrait-filter: grayscale(1) contrast(1.06); /* the four portraits as one set */
  --hover-zoom: 1.03; /* a photograph in a link, on hover */
  --portrait-colour-range: cover 25% cover 75%; /* touch screens: where a portrait passes through colour */

  /* ---- Map (SVG strokes are drawn with vector-effect: non-scaling-stroke, in screen px) ---- */
  --map-stroke: 0.6;
  --map-stroke-hq: 1.4;
  --map-stroke-node: 1.5;
  --map-stroke-selected: 2;
  --map-focus-outer: 6;
  --map-focus-inner: 2.5;
  --map-dash: 3 3; /* the disputed boundary */
  --map-fill-quiet: 0.24; /* countries outside our markets */
  --map-fill-hover: 0.55;
  --map-name-grow: 1.3; /* a market's name in the register while its country is pointed at */

  /* ---- Logos ---- */
  --logo-ink-opacity: 0.7; /* partner logos in ink monochrome at rest */

  /* ---- Layers ---- */
  --layer-raised: 1;
  --layer-header: 10;
  --layer-dialog: 100;

  /* ---- Motion ---- */
  --dur-none: 0.01ms; /* reduced motion: transitions end at once, but still fire their events */
  --dur-instant: 100ms; /* press feedback */
  --dur-fast: 160ms; /* hover: colour, underline */
  --dur-base: 240ms; /* interface state: menus, panels */
  --dur-enter: 520ms; /* scroll entrances (400 to 600ms) */
  --dur-slow: 680ms; /* the map draw-in, per country */
  --dur-page: 320ms; /* the cross-page view transition */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* arrivals: fast start, long settle */
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* state changes that go and come back */
  --ease-exit: cubic-bezier(0.4, 0, 1, 1); /* departures */
  --stagger-step: 70ms; /* between siblings in a group */
  --stagger-cap: 5; /* items after the fifth enter with the fifth */
  --enter-distance: 1rem; /* 16px: entrances travel two grid units, never more */
  --dur-count: 1200ms; /* a counter running up to its figure */
  --plate-scale: 1.06; /* scroll-linked scale on the two hero images, at its furthest */

  /* Hero crossfade: read by the sequence script, so timing lives in one place */
  --hero-hold: 5s;
  --hero-dissolve: 1.2s;
}

/* ---- fonts.css ---- */
/*
  Self-hosted variable fonts (brief Part 4.1), built by scripts/fonts/build_fonts.py.
  Each face is split into latin and latin-ext; unicode-range means latin-ext
  downloads only when a page uses those characters. Only archivo-latin is
  preloaded site-wide (base layout); pages preload others via front matter.
*/

@font-face {
  font-family: Archivo;
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 78% 125%;
  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;
}

@font-face {
  font-family: Archivo;
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 78% 125%;
  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 {
  font-family: Newsreader;
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/assets/fonts/newsreader-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 {
  font-family: Newsreader;
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/assets/fonts/newsreader-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 {
  font-family: Newsreader;
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/newsreader-italic-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 {
  font-family: Newsreader;
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/newsreader-italic-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;
}

/*
  Metric-matched fallbacks (brief 4.2): while the web fonts load, local
  Arial and Georgia are resized to Archivo's and Newsreader's average
  advance and vertical metrics, so the swap moves text as little as
  possible. Values come from build_fonts.py. Where Arial is absent
  (Android), the stack falls through to system-ui.
*/
@font-face {
  font-family: "Archivo Fallback";
  src: local("Arial"), local("ArialMT");
  size-adjust: 98.59%;
  ascent-override: 89.05%;
  descent-override: 21.3%;
  line-gap-override: 0%;
}

@font-face {
  font-family: "Newsreader Fallback";
  src: local("Georgia");
  size-adjust: 96.07%;
  ascent-override: 76.51%;
  descent-override: 27.58%;
  line-gap-override: 0%;
}

/* ---- reset.css ---- */
/* Minimal reset: only what the design system relies on. */

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

* {
  margin: 0;
}

html {
  text-size-adjust: none;
  hanging-punctuation: first last;
}

body {
  min-block-size: 100svb;
  -webkit-font-smoothing: antialiased;
}

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

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
}

table {
  border-collapse: collapse;
}

[hidden] {
  display: none;
}

:focus-visible {
  outline: var(--focus-width) solid var(--focus-outer);
  outline-offset: var(--focus-width);
  box-shadow: 0 0 0 var(--focus-width) var(--focus-inner);
}

.skip-link {
  position: absolute;
  inset-block-start: var(--space-s);
  inset-inline-start: var(--space-s);
  padding: var(--space-2xs) var(--space-s);
  background: var(--ink);
  color: var(--paper);
  transform: translateY(var(--offscreen));
}

.skip-link:focus-visible {
  transform: none;
}

/* ---- type.css ---- */
/*
  Typography (round 4 design system; docs/DESIGN-SYSTEM.md).

  Two faces, each with one job:
  - Newsreader (--font-body) sets everything a person reads: paragraphs,
    standfirsts, lists, captions.
  - Archivo (--font-structural) sets everything a person scans or uses:
    headings, records, navigation, controls, tables.

  Archivo's width carries meaning: four stops only (statement 112, heading
  105, voice 100, record 78). Width never changes inside a line or a
  heading. No italic Archivo.

  Scale (tokens.css): body --step-0; H3 --step-1; statements --step-2;
  H2 --step-4; chapter display --step-5; inner-page H1 --step-6; the
  homepage headline --step-7. One very large thing per section.
*/

html {
  font-family: var(--font-body);
  font-size: 100%;
  font-stretch: calc(var(--wdth-voice) * 1%);
  font-weight: var(--weight-regular);
  font-optical-sizing: auto;
  color: var(--ink);
  background: var(--paper);
}

body {
  font-size: var(--step-0);
  line-height: var(--leading-body);
}

/* The structural face: everything scanned or operated rather than read.
   :is() so it outranks the reset's "font: inherit" on form controls. */
:is(
    h1,
    h2,
    h3,
    h4,
    h5,
    h6,
    .h1,
    .h2,
    .h3,
    .h4,
    .h5,
    .h6,
    .statement,
    .display,
    .record,
    .ui,
    nav,
    button,
    .button,
    input,
    select,
    textarea,
    label,
    legend,
    dt,
    th
  ) {
  font-family: var(--font-structural);
}

/* ---- Headings ---- */

h1,
h2,
h3,
h4,
h5,
h6,
.h1,
.h2,
.h3,
.h4,
.h5,
.h6 {
  font-weight: var(--weight-bold);
}

/*
  Copy-tolerant H1 (Part 4.2): one declaration for every headline.
  The cap is container-relative, so wrap each H1 in .headline.
  No line counts, no max-height, no per-headline overrides.
*/
.headline {
  container-type: inline-size;
}

h1,
.h1 {
  font-size: min(var(--step-6), var(--h1-cap));
  font-stretch: calc(var(--wdth-statement) * 1%);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
  hyphens: auto;
  overflow-wrap: break-word;
}

/* Chapter display: the one very large line that opens a chapter of a page. */
.display {
  max-inline-size: var(--measure-display);
  font-size: min(var(--step-5), var(--display-cap));
  font-stretch: calc(var(--wdth-statement) * 1%);
  font-weight: var(--weight-bold);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

h2,
.h2 {
  font-size: min(var(--step-4), var(--display-cap));
  font-stretch: calc(var(--wdth-heading) * 1%);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
  text-wrap: balance;
}

h3,
.h3 {
  font-size: var(--step-1);
  font-stretch: calc(var(--wdth-voice) * 1%);
  line-height: var(--leading-snug);
}

/* h4 to h6 share one minor style; the document outline, not size, separates them. */
h4,
h5,
h6,
.h4,
.h5,
.h6 {
  font-size: var(--step-0);
  font-stretch: calc(var(--wdth-voice) * 1%);
  line-height: var(--leading-ui);
}

/* Pillar names and other short public statements below H1 */
.statement {
  font-size: var(--step-2);
  font-stretch: calc(var(--wdth-statement) * 1%);
  font-weight: var(--weight-statement);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
}

/* ---- Running text ---- */

p,
li,
dd {
  max-inline-size: var(--measure-body);
}

.standfirst {
  font-size: var(--step-1);
  line-height: var(--leading-ui);
  max-inline-size: var(--measure-standfirst);
}

strong,
b {
  font-weight: var(--weight-bold);
}

/* Selected text in the brand's colours, not the browser's blue. */
::selection {
  background: var(--petrol-200);
  color: var(--ink);
}

.surface-dark ::selection {
  background: var(--amber-300);
  color: var(--ink);
}

a {
  color: var(--petrol);
  text-decoration-line: underline;
  text-decoration-thickness: var(--underline-rest);
  text-underline-offset: var(--underline-offset);
  transition:
    text-decoration-thickness var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}

a:hover,
a:focus-visible {
  color: var(--petrol-bright);
  text-decoration-thickness: var(--underline-hover);
}

/* Long-form reading: field reports and articles. */
.prose {
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--leading-body);
  overflow-wrap: break-word; /* long addresses in reference lists wrap on phones */
}

.prose p,
.prose li {
  max-inline-size: var(--measure-body);
}

/* Lines made only of links (citations such as "[5] link.springer.com", lists
   of names) need room between targets: 24px clear around each (WCAG 2.5.8).
   Neighbouring links keep their words together and stand a little apart;
   a long address can still wrap inside its own box. */
.prose :is(a + a, a:has(+ a)) {
  display: inline-block;
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}

.prose a + a {
  margin-inline-start: var(--space-3xs);
}

.pull-quote {
  font-family: var(--font-editorial);
  font-size: var(--step-3);
  font-weight: var(--weight-regular);
  line-height: var(--leading-snug);
  max-inline-size: var(--measure-quote);
  text-wrap: balance;
}

figcaption,
.caption {
  font-family: var(--font-editorial);
  font-size: var(--step--1);
  font-style: italic;
  line-height: var(--leading-caption);
  color: var(--text-muted);
  max-inline-size: var(--measure-ui);
}

/*
  Archivo is never italic: in structural text, emphasis is weight, not slope.
  In Newsreader text emphasis is italic; inside an italic caption it
  reverses to upright, the usual convention.
*/
em,
i {
  font-style: italic;
}

:where(h1, h2, h3, h4, h5, h6, .statement, .display, .record, .ui, nav, button, label, dt, th) :is(em, i) {
  font-style: normal;
  font-weight: var(--weight-bold);
}

:is(figcaption, .caption) :is(em, i) {
  font-style: normal;
}

/* ---- Records: what ENKOP can show on file ---- */

.record {
  font-size: var(--step-0);
  font-stretch: calc(var(--wdth-record) * 1%);
  font-weight: var(--weight-medium);
  line-height: var(--leading-ui);
  letter-spacing: var(--tracking-record);
  font-variant-numeric: tabular-nums lining-nums;
}

.numeric {
  font-variant-numeric: tabular-nums lining-nums;
}

.muted {
  color: var(--text-muted);
}

/* ---- layout.css ---- */
/*
  Layout primitives (brief 2.4):
  .page         content grid with named lines; children sit in the content
                column, .full-bleed children span edge to edge (no 100vw,
                so no horizontal scroll from scrollbars)
  .container    the same content width for contexts outside .page
  .grid         4 columns below 48rem, 12 above; spans by role:
                margin (1-3), main (4-12), statement (1-10), wide (1-12)
  .stack        vertical rhythm; set --stack-space to change the gap
  .cluster      a wrapping row of inline items, e.g. actions
  .surface-dark ink ground for the footer and other dark contexts
                (sections now take a data-theme instead: themes.css)
  .rule-heavy   the section divider between parts of a page
*/

/* overflow-x: clip, not hidden: decorative layers that reach past the edge
   (the outer orbit rings, parallax) are cut off without making the page a
   scroll container, so position: sticky still works inside it. */
.page {
  display: grid;
  overflow-x: clip;
  grid-template-columns:
    [full-start] minmax(var(--page-margin), 1fr)
    [content-start] min(100% - 2 * var(--page-margin), var(--content-max))
    [content-end] minmax(var(--page-margin), 1fr)
    [full-end];
}

.page > * {
  grid-column: content;
}

.page > .full-bleed {
  grid-column: full;
}

.container {
  inline-size: min(100% - 2 * var(--page-margin), var(--content-max));
  margin-inline: auto;
}

.grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--stack-space, var(--space-m)) var(--gutter);
}

.grid > * {
  grid-column: 1 / -1;
}

@media (width >= 48rem) {
  .grid {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }

  .grid > .span-margin {
    grid-column: 1 / 4;
  }

  .grid > .span-main {
    grid-column: 4 / -1;
  }

  .grid > .span-statement {
    grid-column: 1 / 11;
  }

  .grid > .span-wide {
    grid-column: 1 / -1;
  }
}

/* Lists used for layout (role="list") are not running text: no reading measure. */
[role="list"] > li {
  max-inline-size: none;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--stack-space, var(--space-m));
}

.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cluster-space, var(--space-s));
}

.section {
  padding-block: var(--space-section);
}

/* A section opened by a heavy rule: the document rhythm between parts of a page. */
.ruled-section {
  border-block-start: var(--rule-strong) solid var(--rule-heavy);
}

.ruled-section h2 {
  margin-block-end: var(--space-l);
}

.rule-heavy {
  border: 0;
  border-block-start: var(--rule-strong) solid var(--rule-heavy);
}

.rule-light {
  border: 0;
  border-block-start: var(--rule-hair) solid var(--rule-light);
}

.surface-dark {
  --focus-outer: var(--amber);

  background: var(--ink);
  color: var(--paper);
}

.surface-dark a {
  color: var(--paper);
}

.surface-dark .muted {
  color: var(--silver);
}

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

/* A missing fact awaiting the reviewer (src/_data/build.js): shown on local
   and preview builds only, never in production. Amber so it cannot be missed. */
.placeholder {
  padding-inline: var(--space-3xs);
  background: var(--amber);
  color: var(--ink);
}

/* ---- themes.css ---- */
/*
  Section themes (PLAN.md, section 3). Every full-width section carries one
  data-theme; the nav reads it to choose its pill colour (core/nav.js).

    light   limewash paper, ink text: the default reading ground
    raised  the lighter paper of inputs and panels
    wash    the petrol wash: the map
    grain   stone with a moving film grain (texture.css)
    dark    ink
    deep    the deepest ink: the particle hero, the menu, the footer
    petrol  dark petrol: statement heroes, the stat band
    photo   a photograph under a scrim; text in paper

  Each theme sets the same few roles, so components never ask which theme
  they are on: --theme-bg, --theme-fg, --theme-muted, --theme-rule,
  --theme-link and the focus ring's outer colour.
*/

[data-theme] {
  background: var(--theme-bg);
  color: var(--theme-fg);
}

[data-theme="light"] {
  --theme-bg: var(--paper);
  --theme-fg: var(--ink);
  --theme-muted: var(--text-muted);
  --theme-rule: var(--rule-heavy);
  --theme-link: var(--petrol);
}

[data-theme="raised"] {
  --theme-bg: var(--paper-raised);
  --theme-fg: var(--ink);
  --theme-muted: var(--text-muted);
  --theme-rule: var(--rule-heavy);
  --theme-link: var(--petrol);
}

[data-theme="wash"] {
  --theme-bg: var(--ground-wash);
  --theme-fg: var(--ink);
  --theme-muted: var(--text-muted);
  --theme-rule: var(--rule-heavy);
  --theme-link: var(--petrol);
}

[data-theme="grain"] {
  --theme-bg: var(--ground-stone);
  --theme-fg: var(--ink);
  --theme-muted: var(--text-muted);
  --theme-rule: var(--rule-heavy);
  --theme-link: var(--petrol);
}

[data-theme="dark"],
[data-theme="deep"],
[data-theme="petrol"],
[data-theme="photo"] {
  --theme-fg: var(--paper);
  --theme-muted: var(--theme-muted-dark);
  --theme-rule: var(--rule-on-dark);
  --theme-link: var(--petrol-300);
  --focus-outer: var(--amber);
}

[data-theme="dark"] {
  --theme-bg: var(--ink);
}

[data-theme="deep"] {
  --theme-bg: var(--ink-deep);
}

[data-theme="petrol"] {
  --theme-bg: var(--ground-petrol);
}

[data-theme="photo"] {
  --theme-bg: var(--ink-deep);
}

/* Plain links take the theme's link colour; :where() keeps this at the
   weight of a bare "a", so any component's own link colour wins. */
:where([data-theme]) a:where(:not([class])) {
  color: var(--theme-link);
}

[data-theme] .muted,
[data-theme] .caption,
[data-theme] figcaption {
  color: var(--theme-muted);
}

:is([data-theme="dark"], [data-theme="deep"], [data-theme="petrol"], [data-theme="photo"]) ::selection {
  background: var(--amber-300);
  color: var(--ink);
}

/* A themed section inside .page spans edge to edge; its content keeps to
   the content column through subgrid. */
.page > .band {
  display: grid;
  grid-column: full;
  grid-template-columns: subgrid;
  padding-block: var(--space-section);
}

.page > .band > * {
  grid-column: content;
}

.page > .band > .full-bleed {
  grid-column: full;
}

/* A band that continues the one before it on the same ground. */
.page > .band-flush {
  padding-block-start: 0;
}

/* ---- texture.css ---- */
/*
  Grain and DotField (PLAN.md, component 17).

  Grain: an SVG-noise tile at --grain-opacity over the grain theme, moved in
  steps so it reads as film rather than motion. Still under reduced motion.
  DotField: a fine dot grid that fades in from the top right corner, for the
  dot-grid hero and the particle hero's ground. Both are decorative layers
  behind content and never take pointer events.
*/

[data-theme="grain"] {
  position: relative;
  isolation: isolate;
}

[data-theme="grain"]::before {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--grain-image);
  background-size: var(--grain-tile);
  opacity: var(--grain-opacity);
  pointer-events: none;
  content: "";
}

@media (prefers-reduced-motion: no-preference) {
  [data-theme="grain"]::before {
    animation: grain var(--dur-grain) var(--ease-grain) infinite;
  }
}

@keyframes grain {
  50% {
    background-position: var(--grain-shift) calc(var(--grain-shift) * -2);
  }

  100% {
    background-position: calc(var(--grain-shift) * -1) var(--grain-shift);
  }
}

.dot-field {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle, var(--dot-colour) var(--dot-size), transparent calc(var(--dot-size) + var(--rule-hair)));
  background-size: var(--dot-gap) var(--dot-gap);
  mask-image: radial-gradient(ellipse at 100% 0%, var(--ink) 0%, transparent var(--dot-fade));
  pointer-events: none;
}

:is([data-theme="dark"], [data-theme="deep"], [data-theme="petrol"]) .dot-field {
  --dot-colour: var(--rule-on-dark);
}

/* ---- controls.css ---- */
/*
  Base controls. Each of the three radii has one reason (brief Part 3):
  --radius-0 for structure and photographs, --radius-1 for inputs and
  buttons, --radius-pill for the chat launcher only.
*/

.button {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--target-min);
  padding: var(--space-2xs) var(--space-m);
  border: var(--rule-hair) solid var(--ink);
  border-radius: var(--radius-1);
  background: var(--ink);
  color: var(--paper);
  font-weight: var(--weight-bold);
  text-decoration: none;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.button:hover {
  background: var(--petrol);
  border-color: var(--petrol);
  color: var(--paper);
}

.button:active {
  background: var(--ink-deep);
  border-color: var(--ink-deep);
}

.field {
  display: grid;
  gap: var(--space-2xs);
  max-inline-size: var(--form-max);
}

.field label {
  font-weight: var(--weight-bold);
}

.input {
  inline-size: 100%;
  min-block-size: var(--target-min);
  padding: var(--space-2xs) var(--space-xs);
  border: var(--rule-hair) solid var(--text-muted);
  border-radius: var(--radius-1);
  background: var(--paper-raised);
  color: var(--ink);
}

textarea.input {
  resize: vertical;
  line-height: var(--leading-caption);
}

.input[aria-invalid="true"] {
  border-color: var(--state-error);
}

.field-error {
  color: var(--state-error);
  font-size: var(--step-0);
}

.field-ok {
  color: var(--state-ok);
  font-size: var(--step-0);
}

.plate {
  border-radius: var(--radius-0);
}

.chat-launcher {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--target-min);
  padding: var(--space-2xs) var(--space-m);
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--ink);
  color: var(--paper);
  font-weight: var(--weight-bold);
}

/* ---- Pills and arrow links (immersive redesign) ----
   Pills are the designed links outside running text: they fill on hover.
   Arrow links carry an arrow that nudges right; underlines stay in body copy. */

.pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-block-size: var(--target-min);
  padding: var(--space-2xs) var(--space-m);
  border: var(--rule-hair) solid transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-weight: var(--weight-bold);
  text-decoration-line: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.pill-amber {
  background: var(--amber);
  color: var(--ink);
}

.pill-amber:hover,
.pill-amber:focus-visible {
  background: var(--paper);
  color: var(--ink);
}

.pill-ink {
  background: var(--ink);
  color: var(--paper);
}

.pill-ink:hover,
.pill-ink:focus-visible {
  background: var(--petrol);
  color: var(--paper);
}

.pill-ghost {
  border-color: currentcolor;
  background: transparent;
  color: inherit;
}

.pill-ghost:hover,
.pill-ghost:focus-visible {
  border-color: var(--amber);
  background: var(--amber);
  color: var(--ink);
}

.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding-block: var(--space-3xs);
  color: inherit;
  font-family: var(--font-structural);
  font-weight: var(--weight-bold);
  text-decoration-line: none;
}

.arrow-link::after {
  display: inline-block;
  transition: transform var(--dur-fast) var(--ease-out);
  content: "→";
  content: "→" / "";
}

.arrow-link:hover::after,
.arrow-link:focus-visible::after {
  transform: translateX(var(--nudge));
}

.arrow-link:hover,
.arrow-link:focus-visible {
  color: var(--petrol-bright);
}

[data-theme="dark"] .arrow-link:hover,
[data-theme="deep"] .arrow-link:hover,
[data-theme="petrol"] .arrow-link:hover,
[data-theme="photo"] .arrow-link:hover,
[data-theme="dark"] .arrow-link:focus-visible,
[data-theme="deep"] .arrow-link:focus-visible,
[data-theme="petrol"] .arrow-link:focus-visible,
[data-theme="photo"] .arrow-link:focus-visible {
  color: var(--amber);
}

/* Small uppercase labels: the split-block and footer labels (3 words or fewer). */
.label {
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

/* ---- imagery.css ---- */
/*
  Imagery (round 4, Phase 3; docs/DESIGN-SYSTEM.md, docs/PHOTOGRAPHY-BRIEF.md).

  One body of work: every photograph passes through the same quiet grade
  (--photo-filter), the four portraits through a shared monochrome
  (--portrait-filter), and every slot crops to one of four ratios
  (--ratio-wide, --ratio-landscape, --ratio-portrait, --ratio-square).
  Illustrations are never graded or cropped. While a file loads, the
  <img> shows the photograph's own average colour (picture.js).

  Captions: the description in Newsreader italic, then the place and month
  as a dated record line, marked by a short rule.
*/

.photo img,
img.photo {
  filter: var(--photo-filter);
}

.team-photo-mono {
  position: absolute;
  inset: 0;
  transition: opacity var(--dur-slow) var(--ease-out);
}

.team-photo-mono img {
  block-size: 100%;
  filter: var(--portrait-filter);
}

@media (hover: none) and (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .team-photo-mono {
      animation: portrait-colour linear both;
      animation-range: var(--portrait-colour-range);
      animation-timeline: view();
    }
  }
}

@keyframes portrait-colour {
  0%,
  100% {
    opacity: 1;
  }

  35%,
  65% {
    opacity: 0;
  }
}

/* ---- Captions ---- */

.caption-text {
  display: block;
}

.caption-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-2xs);
  align-items: center;
  margin-block-start: var(--space-3xs);
  color: var(--ink);
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-stretch: calc(var(--wdth-voice) * 1%);
  font-style: normal;
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-record);
  font-variant-numeric: tabular-nums lining-nums;
}

.caption-meta::before {
  inline-size: var(--space-s);
  border-block-start: var(--rule-strong) solid var(--petrol);
  content: "";
}

/* A dot between place and month: drawn, not typed, so it is never read aloud. */
.caption-date:not(:first-child)::before {
  display: inline-block;
  inline-size: var(--space-3xs);
  block-size: var(--space-3xs);
  margin-inline-end: var(--space-2xs);
  border-radius: var(--radius-pill);
  background: currentcolor;
  vertical-align: middle;
  content: "";
}

.surface-dark .caption-meta,
[data-theme] .caption-meta {
  color: var(--theme-fg, var(--ink));
}

:is([data-theme="dark"], [data-theme="deep"], [data-theme="petrol"], [data-theme="photo"]) .caption-meta::before {
  border-color: var(--amber);
}

/* ---- ledger.css ---- */
/*
  "What we inspected" ledger (brief 6.2, 6.5): a ruled register in record
  style. Statuses are words; the amber mark is decorative and always
  outlined in ink. Used on the homepage and, from Phase 8, in every field
  report. Below 48rem the evidence column is dropped.
*/

.ledger {
  inline-size: 100%;
  border-block: var(--rule-strong) solid var(--rule-heavy);
}

.ledger th,
.ledger td {
  padding: var(--space-xs) var(--space-s) var(--space-xs) 0;
  border-block-start: var(--rule-hair) solid var(--rule-light);
  text-align: start;
  vertical-align: middle;
}

.ledger thead th {
  font-weight: var(--weight-bold);
}

.ledger tbody th {
  font-weight: var(--weight-medium);
}

.ledger-thumb {
  inline-size: var(--mark-m);
  block-size: var(--mark-m);
  object-fit: cover;
}

.mark-inspected {
  display: inline-block;
  inline-size: var(--mark-s);
  block-size: var(--mark-s);
  margin-inline-end: var(--space-2xs);
  border: var(--rule-hair) solid var(--ink);
  background: var(--amber);
}

@media (width < 48rem) {
  .ledger-evidence {
    display: none;
  }
}

/* The same register for survey findings and other cited figures
   (partials/evidence-table.njk): label and note, then the figure. */
.evidence-note {
  display: block;
  font-weight: var(--weight-regular);
}

/* Figures align right, under their heading. (.ledger th/td sets start.) */
.evidence-table .evidence-figure,
.evidence-table thead th:last-child {
  text-align: end;
  white-space: nowrap;
}

/* ---- nav.css ---- */
/*
  SiteNav (PLAN.md, component 1): two floating pills and a full-screen menu.
  The pills are frosted ink over light sections and frosted paper over dark
  ones (data-on, set by core/nav.js). They slide up out of view while the
  reader scrolls down and return on the way up.
*/

.site-nav {
  position: fixed;
  inset-block-start: var(--nav-top);
  inset-inline: var(--page-margin);
  z-index: var(--layer-header);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-s);
  pointer-events: none;
  transition: transform var(--dur-base) var(--ease-out);
}

.site-nav[data-hidden="true"] {
  transform: translateY(calc(-100% - var(--nav-top) - var(--space-s)));
}

.nav-pill {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-block-size: var(--nav-pill-h);
  padding: var(--space-3xs);
  border-radius: var(--radius-pill);
  background: var(--nav-on-light);
  color: var(--paper);
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  pointer-events: auto;
  backdrop-filter: blur(var(--nav-blur));
  transition: background-color var(--dur-base) var(--ease-out);
}

.site-nav[data-on="dark"] .nav-pill {
  background: var(--nav-on-dark);
}

.nav-pill a:not(.pill),
.nav-pill button {
  color: var(--paper);
  text-decoration-line: none;
}

.nav-home {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  min-block-size: calc(var(--nav-pill-h) - 2 * var(--space-3xs));
  padding-inline: var(--space-3xs) var(--space-xs);
  border-radius: var(--radius-pill);
}

.nav-home img {
  inline-size: var(--space-l);
  block-size: auto;
  border-radius: var(--radius-pill);
  background: var(--paper);
}

.nav-wordmark {
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-display);
  letter-spacing: var(--tracking-record);
}

.nav-menu-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  min-block-size: calc(var(--nav-pill-h) - 2 * var(--space-3xs));
  padding-inline: var(--space-s);
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--ink);
  font: inherit;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out);
}

.nav-menu-button:hover {
  background: var(--petrol);
}

.icon-grid {
  inline-size: var(--icon-s);
  block-size: var(--icon-s);
  fill: currentcolor;
}

.nav-right {
  display: none;
  padding-inline-start: var(--space-s);
}

.nav-links {
  display: flex;
  gap: var(--space-s);
  padding: 0;
  list-style: none;
}

.nav-links a {
  position: relative;
  display: inline-block;
  padding-block: var(--space-2xs);
}

/* The underline draws in from the left: a transform, never a layout change. */
.nav-links a::after {
  position: absolute;
  inset-inline: 0;
  inset-block-end: var(--space-3xs);
  block-size: var(--rule-hair);
  background: currentcolor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-base) var(--ease-out);
  content: "";
}

.nav-links a:hover::after,
.nav-links a:focus-visible::after,
.nav-links a[aria-current="page"]::after {
  transform: none;
}

.nav-cta {
  margin-inline-start: var(--space-2xs);
}

@media (width >= 64rem) {
  .nav-right {
    display: flex;
  }
}

/* Without JavaScript the menu cannot open: the links stay visible instead. */
.no-js .nav-menu-button {
  display: none;
}

.no-js .nav-right {
  display: flex;
  flex-wrap: wrap;
}

@media (forced-colors: active) {
  .nav-links a[aria-current="page"] {
    text-decoration-line: underline;
  }
}

/* ---- The full-screen menu ---- */

.site-menu {
  inline-size: 100%;
  max-inline-size: none;
  block-size: 100%;
  max-block-size: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--ink-deep);
  color: var(--paper);
}

.site-menu::backdrop {
  background: var(--ink-deep);
}

.site-menu-inner {
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--space-xl);
  min-block-size: 100%;
  padding: var(--nav-top) var(--page-margin) var(--space-xl);
}

.site-menu-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.site-menu .nav-home {
  color: var(--paper);
  text-decoration-line: none;
}

.site-menu-links {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-3xs);
  padding: 0;
  list-style: none;
}

.menu-mask {
  display: block;
  overflow: hidden;
}

.site-menu-links a {
  display: inline-block;
  color: var(--paper);
  font-family: var(--font-structural);
  font-size: var(--step-5);
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-display);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-display);
  text-decoration-line: none;
  transition: color var(--dur-fast) var(--ease-out);
}

.site-menu-links a:hover,
.site-menu-links a:focus-visible,
.site-menu-links a[aria-current="page"] {
  color: var(--amber);
}

.site-menu-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-l);
  color: var(--silver);
}

.site-menu-meta address {
  font-style: normal;
}

/* ---- footer.css ---- */
/* Footer (PLAN.md, component 16): --ink-deep, a giant wordmark at the foot. */

.site-footer {
  padding-block: var(--space-2xl) var(--space-l);
  overflow: hidden;
}

.site-footer-inner {
  display: grid;
  gap: var(--space-xl);
}

.site-footer-top {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-l);
  padding-block-end: var(--space-xl);
  border-block-end: var(--rule-hair) solid var(--rule-on-dark);
}

.site-footer-lede {
  max-inline-size: var(--measure-standfirst);
  font-size: var(--step-1);
  line-height: var(--leading-ui);
}

.site-footer-grid {
  display: grid;
  gap: var(--space-l) var(--gutter);
}

.site-footer-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  padding: 0;
  list-style: none;
}

.site-footer a:not(.pill) {
  display: inline-block;
  padding-block: var(--space-3xs);
  color: var(--paper);
  text-decoration-line: none;
  transition: color var(--dur-fast) var(--ease-out);
}

.site-footer a:not(.pill):hover,
.site-footer a:not(.pill):focus-visible {
  color: var(--amber);
}

.site-footer-address {
  font-style: normal;
  line-height: var(--leading-ui);
}

/* The wordmark runs the full width of the content column. */
.site-footer-wordmark {
  font-family: var(--font-structural);
  font-size: var(--wordmark-size);
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-heavy);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  color: var(--paper);
  max-inline-size: none;
  opacity: var(--wordmark-opacity);
}

.site-footer-base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-s);
  color: var(--silver);
  font-size: var(--step--1);
}

.site-footer-legal {
  display: flex;
  gap: var(--space-m);
  padding: 0;
  list-style: none;
}

@media (width >= 48rem) {
  .site-footer-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* ---- hero.css ---- */
/*
  PageHero (PLAN.md, component 2; partials/page-hero.njk).

  One structure, four grounds:
    particle   the homepage: Africa in dots on the deepest ink (hero-particles.js)
    photo      an ENKOP photograph under a scrim, slowly drifting (Ken Burns)
    dotgrid    limewash paper with a dot field from the top right
    statement  dark petrol, shorter, for contact, legal and the 404

  The headline is the largest thing on the page and sits at the top left.
  Below it the lede and actions; at the foot the dashed link grid, the meta
  strip and the scroll cue. The H1 has its own row, so a long headline
  pushes the rest down and never overlaps it.

  The headline arrives by CSS alone (a rise from behind its own box), so it
  never waits for the script and is never invisible: it is the LCP element.
*/

.page > .page-hero {
  grid-column: full;
}

.page-hero {
  position: relative;
  isolation: isolate;
  display: grid;
  min-block-size: var(--hero-min);
  overflow: hidden;
}

.hero-statement {
  min-block-size: var(--hero-statement-min);
}

.hero-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.hero-inner {
  display: grid;
  grid-template-rows: auto auto 1fr;
  gap: var(--space-l);
  inline-size: min(100% - 2 * var(--page-margin), var(--content-max));
  margin-inline: auto;
  padding-block: var(--hero-pad-top) var(--space-l);
}

.hero-head {
  overflow: hidden;
  padding-block-end: var(--space-3xs);
}

.hero-title {
  max-inline-size: var(--hero-title-max);
  font-size: min(var(--step-7), var(--h1-cap));
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-display);
  hyphens: manual;
}

:is(.hero-dotgrid, .hero-statement) .hero-title {
  font-size: min(var(--step-6), var(--h1-cap));
}

@media (prefers-reduced-motion: no-preference) {
  .js .hero-title {
    animation: hero-rise var(--dur-hero-rise) var(--ease-out) both;
  }

  .js.is-preloading .hero-title {
    animation-delay: var(--dur-preloader);
  }
}

@keyframes hero-rise {
  from {
    transform: translateY(var(--hero-rise));
  }
}

.hero-lede-wrap {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
}

.hero-lede {
  max-inline-size: var(--hero-lede-max);
  font-size: var(--step-1);
  line-height: var(--leading-ui);
}

.hero-foot {
  display: grid;
  align-self: end;
  gap: var(--space-l);
}

@media (width >= 64rem) {
  .hero-foot {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    align-items: end;
  }

  .hero-links {
    grid-column: 1 / 7;
  }

  .hero-meta {
    grid-column: 1 / 9;
  }

  .hero-controls {
    grid-column: 9 / -1;
    justify-self: end;
  }
}

/* ---- The 2 x 2 dashed link grid ---- */

.hero-links .label {
  margin-block-end: var(--space-xs);
  color: var(--theme-muted);
}

.hero-links ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--link-grid-min), 1fr));
  padding: 0;
  list-style: none;
}

@media (width >= 48rem) {
  .hero-links ul {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.hero-links li {
  border-block-end: var(--rule-hair) dashed var(--theme-rule);
}

/* An odd last item takes the whole row, so the grid never ends on a gap. */
.hero-links li:last-child:nth-child(odd) {
  grid-column: 1 / -1;
}

.hero-links a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  min-block-size: var(--target-min);
  padding-block: var(--space-2xs);
  padding-inline-end: var(--space-s);
  color: inherit;
  font-family: var(--font-structural);
  font-weight: var(--weight-medium);
  text-decoration-line: none;
}

.hero-links a::after {
  transition: transform var(--dur-fast) var(--ease-out);
  content: "→";
  content: "→" / "";
}

.hero-links a:hover,
.hero-links a:focus-visible {
  color: var(--amber);
}

.hero-dotgrid .hero-links a:hover,
.hero-dotgrid .hero-links a:focus-visible {
  color: var(--petrol-bright);
}

.hero-links a:hover::after,
.hero-links a:focus-visible::after {
  transform: translateX(var(--nudge));
}

/* ---- Meta strip: place, date, facts ---- */

.hero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-l);
  padding: 0;
  color: var(--theme-muted);
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  list-style: none;
}

.hero-meta a {
  color: inherit;
}

/* ---- Controls: pause and the scroll cue ---- */

.hero-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s);
}

.hero-pause {
  min-block-size: var(--target-min);
}

.scroll-cue {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-block-size: var(--target-min);
  color: inherit;
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  text-decoration-line: none;
}

.scroll-cue-line {
  position: relative;
  inline-size: var(--rule-hair);
  block-size: var(--scroll-cue-h);
  overflow: hidden;
  background: var(--theme-rule);
}

.scroll-cue-line::after {
  position: absolute;
  inset: 0;
  background: var(--amber);
  transform: translateY(calc(var(--cue-travel) * -1));
  content: "";
}

@media (prefers-reduced-motion: no-preference) {
  .scroll-cue-line::after {
    animation: cue var(--dur-cue) var(--ease-in-out) infinite;
  }
}

@keyframes cue {
  50% {
    transform: none;
  }

  100% {
    transform: translateY(var(--cue-travel));
  }
}

/* ---- Particle ---- */

.hero-canvas,
.hero-particles-fallback,
.hero-particles-fallback img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
}

.hero-particles-fallback img {
  object-fit: contain;
  object-position: right center;
}

.hero-canvas {
  opacity: var(--hero-canvas-opacity-small);
}

@media (width >= 64rem) {
  .hero-canvas,
  .hero-particles-fallback {
    inset-inline-start: var(--hero-canvas-inset);
    inline-size: calc(100% - var(--hero-canvas-inset));
  }

  .hero-canvas {
    opacity: 1;
  }
}

/* The still image is for pages without JavaScript. With it, the canvas
   draws the dots (assembling them, or one still frame under reduced
   motion), so the image is never displayed and, being lazy, never fetched:
   it would otherwise compete with the fonts on a slow connection and
   become the largest paint. */
.js .hero-particles-fallback {
  display: none;
}

/* ---- Photo ---- */

.hero-media,
.hero-media picture,
.hero-media img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
}

.hero-media img {
  object-fit: cover;
}

.hero-media::after {
  position: absolute;
  inset: 0;
  background: var(--scrim-side), var(--scrim-photo);
  content: "";
}

@media (prefers-reduced-motion: no-preference) {
  .hero-media img {
    animation: ken-burns var(--dur-ken-burns) var(--ease-in-out) infinite alternate;
  }
}

@keyframes ken-burns {
  to {
    transform: scale(var(--ken-burns-scale));
  }
}

.hero-caption {
  max-inline-size: var(--measure-standfirst);
}

@media (width >= 64rem) {
  .hero-caption {
    grid-column: 1 / 9;
  }
}

/* ---- blocks.css ---- */
/*
  Blocks (PLAN.md, components 3 to 15; macros/blocks.njk). Every block reads
  the theme roles (themes.css), so it works on any ground unchanged.
*/

/* ---- Section headings shared by the blocks ---- */

.chapter-title {
  max-inline-size: var(--measure-display);
  font-size: min(var(--step-6), var(--display-cap));
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-display);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  hyphens: manual;
}

h1,
h2,
h3,
.display,
.statement {
  hyphens: manual;
  text-wrap: balance;
}

/* ---- 3. SplitBlock: a label on the left, content on the right ---- */

.split {
  display: grid;
  gap: var(--space-m) var(--gutter);
}

.split-label {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--theme-muted);
}

.split-label::before {
  inline-size: var(--space-m);
  block-size: var(--rule-strong);
  background: var(--amber);
  content: "";
}

.split-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
}

.split-body > :is(.pill, .arrow-link, .chip) {
  align-self: flex-start;
}

.split-body > .statement,
.split-body > h2 {
  max-inline-size: var(--measure-title);
}

/* A label column that carries more than the label (contact details). */
.split-label-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-l);
}

@media (width >= 64rem) {
  .split {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }

  .split-label-col {
    grid-column: 1 / 4;
  }

  .split-label {
    grid-column: 1 / 4;
    align-self: start;
    padding-block-start: var(--space-2xs);
  }

  .split-body {
    grid-column: 5 / -1;
  }
}

.split + .split {
  margin-block-start: var(--space-2xl);
}

/* ---- 4. LeadIn: a small sentence, a vertical line, a giant heading ---- */

.lead-in {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-m);
  margin-block-end: var(--space-2xl);
  text-align: center;
}

.lead-in-kicker {
  max-inline-size: var(--measure-standfirst);
  color: var(--theme-muted);
  font-size: var(--step-1);
  line-height: var(--leading-ui);
}

.lead-in-line {
  inline-size: var(--rule-hair);
  block-size: var(--lead-line-h);
  background: var(--theme-rule);
}

.lead-in .chapter-title {
  margin-inline: auto;
}

/* ---- 5. OrbitFeature: a round image in two rings, zigzag ---- */

.orbits {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xl);
  padding: 0;
  list-style: none;
}

.orbit {
  display: grid;
  align-items: center;
  gap: var(--space-xl) var(--gutter);
}

@media (width >= 64rem) {
  .orbit {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }

  .orbit-media {
    grid-column: 1 / 6;
  }

  .orbit-body {
    grid-column: 7 / -1;
  }

  .orbit:nth-child(even) .orbit-media {
    grid-column: 8 / -1;
    grid-row: 1;
  }

  .orbit:nth-child(even) .orbit-body {
    grid-column: 1 / 7;
    grid-row: 1;
  }
}

.orbit-media {
  position: relative;
  display: grid;
  place-items: center;
  justify-self: center;
  inline-size: calc(var(--orbit-size) + 2 * var(--ring-gap));
  max-inline-size: 100%;
  aspect-ratio: var(--ratio-square);
}

.orbit-ring {
  position: absolute;
  inset: 0;
  border: var(--rule-hair) solid var(--theme-rule);
  border-radius: 50%;
  pointer-events: none;
}

.orbit-ring-outer {
  inset: calc(var(--ring-gap) - var(--ring-gap-outer));
  border-style: dashed;
}

@media (width < 64rem) {
  .orbit-ring-outer {
    display: none;
  }
}

.orbit-image {
  inline-size: calc(100% - 2 * var(--ring-gap));
  aspect-ratio: var(--ratio-square);
  overflow: hidden;
  border-radius: 50%;
}

.orbit-image picture,
.row-thumb picture,
.card-media picture,
.fbb-photos picture {
  display: block;
  inline-size: 100%;
  block-size: 100%;
}

.orbit-image img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.orbit-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-s);
}

.orbit-index {
  color: var(--theme-muted);
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-stretch: calc(var(--wdth-record) * 1%);
  font-weight: var(--weight-medium);
}

.orbit-title {
  font-size: min(var(--step-3), var(--display-cap));
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-display);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
}

.orbit-title a {
  color: inherit;
  text-decoration-line: none;
}

/* ---- Evidence chip ---- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--chip-pad);
  border: var(--rule-hair) solid var(--theme-rule);
  border-radius: var(--radius-pill);
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  line-height: var(--leading-ui);
}

.chip::before {
  inline-size: var(--space-2xs);
  block-size: var(--space-2xs);
  border-radius: 50%;
  background: var(--amber);
  content: "";
}

.chip a {
  color: inherit;
}

/* ---- 6. Collage: staggered photographs; a swipe row on phones ---- */

/* position: relative makes the collage the containing block of anything
   positioned inside it, so its scroller clips everything it holds. */
.collage {
  position: relative;
  display: grid;
  grid-auto-columns: minmax(var(--tile-min), var(--collage-peek));
  grid-auto-flow: column;
  gap: var(--gutter);
  margin: 0;
  padding: 0;
  overflow-x: auto;
  list-style: none;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.collage > * {
  scroll-snap-align: start;
}

.collage img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.collage figure {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.collage picture {
  display: block;
  aspect-ratio: var(--ratio-portrait);
  overflow: hidden;
  border-radius: var(--radius-card);
}

@media (width >= 64rem) {
  .collage {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    grid-auto-flow: row;
    align-items: start;
    overflow: visible;
  }

  .collage > :nth-child(1) {
    grid-column: 1 / 6;
  }

  .collage > :nth-child(2) {
    grid-column: 7 / 11;
    margin-block-start: var(--space-3xl);
  }

  .collage > :nth-child(3) {
    grid-column: 2 / 6;
    margin-block-start: calc(var(--space-xl) * -1);
  }

  .collage > :nth-child(4) {
    grid-column: 8 / -1;
  }

  .collage > :nth-child(5) {
    grid-column: 4 / 9;
  }

  .collage > :nth-child(odd) picture {
    aspect-ratio: var(--ratio-landscape);
  }
}

/* ---- 7. FullBleedBand: a frosted panel between two photographs ---- */

.page > .fbb,
.fbb {
  position: relative;
  display: grid;
  grid-column: full;
  min-block-size: var(--band-min);
  overflow: hidden;
}

.fbb-photos {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.fbb-photos picture,
.fbb-photos img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.fbb-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  place-self: center;
  gap: var(--space-m);
  inline-size: min(100% - 2 * var(--page-margin), var(--narrow-max));
  margin-block: var(--space-2xl);
  padding: var(--space-xl) var(--card-pad);
  border-radius: var(--radius-card);
  background: var(--frost);
  color: var(--paper);
  text-align: center;
  backdrop-filter: blur(var(--nav-blur));
}

.fbb-panel > * {
  margin-inline: auto;
}

/* ---- 8. StickyTabs ---- */

.page > .tabs {
  grid-column: full;
}

.tabs {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--layer-raised);
  border-block-end: var(--rule-hair) solid var(--theme-rule);
  background: var(--theme-bg);
}

.tabs ul {
  display: flex;
  gap: var(--space-l);
  inline-size: min(100% - 2 * var(--page-margin), var(--content-max));
  margin-inline: auto;
  padding: 0;
  overflow-x: auto;
  list-style: none;
  scrollbar-width: none;
}

.tabs a {
  position: relative;
  display: flex;
  align-items: center;
  min-block-size: var(--tabs-h);
  color: var(--theme-muted);
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-weight: var(--weight-bold);
  text-decoration-line: none;
  white-space: nowrap;
}

.tabs a::after {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  block-size: var(--rule-strong);
  background: var(--amber);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-base) var(--ease-out);
  content: "";
}

.tabs a:hover,
.tabs a[aria-current="true"] {
  color: var(--theme-fg);
}

.tabs a[aria-current="true"]::after {
  transform: none;
}

/* ---- 9. StatBand: figures that count up, each linked to its source ---- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--stat-min)), 1fr));
  gap: var(--space-xl) var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

.stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-block-start: var(--space-s);
  border-block-start: var(--rule-hair) solid var(--theme-rule);
}

.stat a {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  color: inherit;
  text-decoration-line: none;
}

.stat-figure {
  font-family: var(--font-structural);
  font-size: var(--step-5);
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-display);
  font-variant-numeric: tabular-nums lining-nums;
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
}

.stat-of {
  font-size: var(--step-2);
}

.stat-label {
  max-inline-size: var(--measure-title);
  font-family: var(--font-structural);
  font-weight: var(--weight-medium);
  line-height: var(--leading-ui);
}

.stat-source {
  color: var(--theme-muted);
  font-size: var(--step--1);
}

.stat a:hover .stat-label,
.stat a:focus-visible .stat-label {
  text-decoration-line: underline;
  text-decoration-thickness: var(--underline-hover);
  text-underline-offset: var(--underline-offset);
}

/* ---- 11. ReportRow: thumbnail, date, title, excerpt, round arrow ---- */

.rows {
  margin: 0;
  padding: 0;
  border-block-start: var(--rule-hair) solid var(--theme-rule);
  list-style: none;
}

.row {
  position: relative;
  display: grid;
  grid-template-columns: var(--thumb-row) 1fr;
  align-items: center;
  gap: var(--space-s) var(--gutter);
  padding-block: var(--space-m);
  border-block-end: var(--rule-hair) solid var(--theme-rule);
}

@media (width >= 48rem) {
  .row {
    grid-template-columns: var(--thumb-row) 1fr auto;
  }
}

.row-thumb {
  inline-size: var(--thumb-row);
  aspect-ratio: var(--ratio-square);
  overflow: hidden;
  border-radius: 50%;
  background: var(--theme-rule);
}

/* A row without a photograph (articles) carries a dot field in its circle. */
.row-thumb-mark {
  position: relative;
  display: block;
  inline-size: 100%;
  block-size: 100%;
  background: var(--ground-petrol);
}

.row-thumb-mark .dot-field {
  --dot-colour: var(--petrol-300);

  mask-image: none;
}

.row-thumb img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform var(--dur-base) var(--ease-out);
}

.row-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

.row-meta {
  color: var(--theme-muted);
  font-family: var(--font-structural);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
}

.row-title {
  max-inline-size: var(--measure-title);
  font-size: var(--step-2);
  font-stretch: calc(var(--wdth-heading) * 1%);
  line-height: var(--leading-heading);
}

.row-title a {
  color: inherit;
  text-decoration-line: none;
}

/* The whole row is the link's target; the title stays the accessible name. */
.row-title a::after {
  position: absolute;
  inset: 0;
  content: "";
}

.row-excerpt {
  display: -webkit-box;
  overflow: hidden;
  color: var(--theme-muted);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--clamp-lines);
  line-clamp: var(--clamp-lines);
}

.round-arrow {
  display: none;
  place-items: center;
  inline-size: var(--round-button);
  aspect-ratio: var(--ratio-square);
  border: var(--rule-hair) solid var(--theme-rule);
  border-radius: 50%;
  font-family: var(--font-structural);
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

@media (width >= 48rem) {
  .round-arrow {
    display: grid;
  }
}

.row:hover .round-arrow,
.row:focus-within .round-arrow {
  border-color: var(--amber);
  background: var(--amber);
  color: var(--ink);
  transform: translateX(var(--nudge));
}

.row:hover .row-thumb img {
  transform: scale(var(--hover-zoom));
}

.row:hover .row-title a {
  text-decoration-line: underline;
  text-decoration-thickness: var(--underline-hover);
  text-underline-offset: var(--underline-offset);
}

/* ---- 12. CardGrid ---- */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--tile-min)), 1fr));
  gap: var(--space-xl) var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (width >= 64rem) {
  .cards {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .cards-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .cards-4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-block-start: var(--space-s);
  border-block-start: var(--rule-strong) solid var(--theme-fg);

  /* The lift uses the translate property, not transform: GSAP animates the
     card's transform when it is revealed, and a CSS transition on the same
     property would leave it stranded part way. */
  transition: translate var(--dur-base) var(--ease-out);
}

.card-media {
  order: -1;
  aspect-ratio: var(--ratio-landscape);
  margin-block-end: var(--space-2xs);
  overflow: hidden;
  border-radius: var(--radius-card);
}

.card:has(.card-media) {
  padding-block-start: 0;
  border-block-start: 0;
}

.card-media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform var(--dur-base) var(--ease-out);
}

.card-kicker {
  color: var(--theme-muted);
}

.card-title {
  font-size: var(--step-1);
  font-stretch: calc(var(--wdth-heading) * 1%);
}

.card-title a {
  color: inherit;
  text-decoration-line: none;
}

.card-title a::after {
  position: absolute;
  inset: 0;
  content: "";
}

.card-excerpt {
  display: -webkit-box;
  overflow: hidden;
  color: var(--theme-muted);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--clamp-lines);
  line-clamp: var(--clamp-lines);
}

.card-text {
  color: var(--theme-muted);
}

@media (hover: hover) {
  .card:has(a):hover {
    translate: 0 var(--lift);
  }

  .card:hover .card-media img {
    transform: scale(var(--hover-zoom));
  }

  .card:hover .card-title a {
    text-decoration-line: underline;
    text-decoration-thickness: var(--underline-hover);
    text-underline-offset: var(--underline-offset);
  }
}

/* ---- 13. TeamCard: the emblem's treatment, a portrait in a gold ring ---- */

.team {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--tile-min)), 1fr));
  gap: var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (width >= 64rem) {
  .team {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.team-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--card-pad);
  border: var(--rule-hair) solid var(--theme-rule);
  border-radius: var(--radius-card);
  background: var(--paper-raised);
  text-align: center;
  transition:
    translate var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}

.team-card:hover,
.team-card:focus-within {
  box-shadow: var(--shadow-lift);
  translate: 0 var(--lift);
}

.team-photo {
  position: relative;
  flex: none;
  inline-size: var(--team-photo);
  block-size: var(--team-photo);
  margin-block-end: var(--space-xs);
  padding: var(--space-3xs);
  border: var(--ring-amber) solid var(--amber);
  border-radius: 50%;
}

.team-photo picture,
.team-photo img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  overflow: hidden;
  border-radius: 50%;
  object-fit: cover;
  object-position: center top;
}

/* Two copies of each portrait, stacked: the monochrome one fades away on
   hover or focus to show the colour one (round 4 decision; WebKit does not
   transition filter reliably, so opacity carries the change). */
.team-photo .team-photo-mono {
  position: absolute;
  inset: var(--space-3xs);
  inline-size: auto;
  block-size: auto;
  filter: var(--portrait-filter);
  transition: opacity var(--dur-base) var(--ease-out);
}

.team-card:hover .team-photo-mono,
.team-card:focus-within .team-photo-mono {
  opacity: 0;
}

@media (hover: none) {
  .team-photo .team-photo-mono {
    opacity: 0;
  }
}

.team-name {
  font-size: var(--step-0);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.team-role {
  max-inline-size: var(--measure-title);
  color: var(--theme-muted);
  font-family: var(--font-structural);
  font-size: var(--step--1);
  line-height: var(--leading-ui);
}

.team-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2xs);
  margin-block-start: auto;
  padding-block-start: var(--space-xs);
}

.round-link {
  display: inline-grid;
  place-items: center;
  inline-size: var(--target-min);
  block-size: var(--target-min);
  border: var(--rule-hair) solid var(--theme-rule);
  border-radius: 50%;
  color: inherit;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.round-link svg {
  inline-size: var(--icon-s);
  block-size: var(--icon-s);
  fill: currentcolor;
}

.round-link:hover,
.round-link:focus-visible {
  border-color: var(--amber);
  background: var(--amber);
  color: var(--ink);
}

/* ---- 15. LogoWall ---- */

.logos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--logo-cell), 1fr));
  margin: 0;
  padding: 0;
  border-block-start: var(--rule-hair) solid var(--theme-rule);
  border-inline-start: var(--rule-hair) solid var(--theme-rule);
  list-style: none;
}

.logo {
  display: grid;
  min-inline-size: 0;
  place-items: center;
  min-block-size: var(--logo-cell-h);
  padding: var(--space-m);
  border-block-end: var(--rule-hair) solid var(--theme-rule);
  border-inline-end: var(--rule-hair) solid var(--theme-rule);
}

.logo-pending {
  font-family: var(--font-structural);
  font-weight: var(--weight-bold);
  text-align: center;
}

/* The ink version is the logo's own shape masked onto an ink fill (classes
   generated per partner in src/css.11ty.js); the colour image sits on top
   and appears on hover. content-visibility defers the masks, which are CSS
   images, until the wall nears the viewport. */
.partner-logo {
  display: grid;
  min-inline-size: 0;
  inline-size: 100%;
  block-size: var(--mark-m);
  content-visibility: auto;
}

.partner-logo::before,
.partner-logo img {
  grid-area: 1 / 1;
  inline-size: 100%;
  block-size: 100%;
}

.partner-logo::before {
  background: var(--theme-fg);
  opacity: var(--logo-ink-opacity);
  mask-position: center;
  mask-repeat: no-repeat;
  mask-size: contain;
  transition: opacity var(--dur-base) var(--ease-out);
  content: "";
}

.partner-logo img {
  min-inline-size: 0;
  object-fit: contain;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
}

.logo:hover .partner-logo::before {
  opacity: 0;
}

.logo:hover .partner-logo img {
  opacity: 1;
}

/* ---- Call-to-action band ---- */

.cta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-l);
}

@media (width >= 64rem) {
  .cta {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
  }
}

/* ---- Checklist: "This includes" ---- */

.checklist {
  display: grid;
  gap: var(--space-2xs) var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (width >= 48rem) {
  .checklist {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.checklist li {
  display: flex;
  gap: var(--space-xs);
  padding-block: var(--space-xs);
  border-block-end: var(--rule-hair) solid var(--theme-rule);
  font-family: var(--font-structural);
  line-height: var(--leading-ui);
}

.checklist li::before {
  flex: none;
  color: var(--amber-700);
  content: "✓";
  content: "✓" / "";
}

:is([data-theme="dark"], [data-theme="deep"], [data-theme="petrol"]) .checklist li::before {
  color: var(--amber);
}

/* ---- How we work: four steps, pinned on large screens (modules/steps.js) ----
   Each step is a box. While the reader moves through the section, the step
   in view fills with ink, grows and lifts, and the others recede, so the
   progression is impossible to miss. Without the script, on phones the
   step nearest the middle of the screen is highlighted the same way; under
   reduced motion all four boxes stay equal. */

.steps {
  display: grid;
  gap: var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (width >= 64rem) {
  .steps {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: stretch;
  }
}

.step {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--card-pad);
  border: var(--rule-hair) solid var(--theme-rule);
  border-radius: var(--radius-card);
  background: var(--paper);
  transform-origin: center bottom;
  transition:
    background-color var(--dur-base) var(--ease-out),
    color var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out),
    scale var(--dur-enter) var(--ease-out),
    translate var(--dur-enter) var(--ease-out);
}

.step-number {
  color: var(--amber-700);
  font-family: var(--font-structural);
  font-size: var(--step-5);
  font-stretch: calc(var(--wdth-record) * 1%);
  font-weight: var(--weight-display);
  line-height: var(--leading-display);
  transition: color var(--dur-base) var(--ease-out);
}

.step-name {
  font-size: var(--step-3);
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-display);
  line-height: var(--leading-heading);
}

/* The steps not in view recede to the muted colour on a quiet box: colour,
   not opacity, so every step keeps AA contrast. */
.steps.is-active .step:not(.is-current) {
  border-color: var(--rule-light);
  background: transparent;
  color: var(--theme-muted);
}

.steps.is-active .step:not(.is-current) .step-number {
  color: var(--theme-muted);
}

/* The step in view: ink box, paper text, amber number, larger and lifted. */
.steps.is-active .step.is-current {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
  box-shadow: var(--shadow-lift);
  scale: var(--step-pop);
  translate: 0 var(--lift);
}

.steps.is-active .step.is-current .step-number {
  color: var(--amber);
}

/* Phones and tablets: the box fills the column, so it lifts without growing. */
@media (width < 64rem) {
  .steps.is-active .step.is-current {
    scale: none;
  }
}

.steps-progress {
  display: none;
}

.steps.is-pinned + .steps-progress {
  display: block;
  block-size: var(--rule-strong);
  margin-block-start: var(--space-l);
  background: var(--theme-rule);
}

.steps-progress span {
  display: block;
  block-size: 100%;
  background: var(--amber);
  transform: scaleX(var(--progress, 0));
  transform-origin: left center;
}

/* ---- Profile dialogs (/about; modules/bio-dialog.js) ----
   Without JavaScript the Profile buttons are hidden and each profile shows
   in place below the team, so no content depends on the script. */

.bio-dialog {
  inline-size: min(100% - 2 * var(--page-margin), var(--narrow-max));
  max-block-size: min(100% - 2 * var(--space-l), var(--dialog-max-block));
  margin: auto;
  padding: 0;
  border: 0;
  border-radius: var(--radius-card);
  background: var(--paper);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
}

.bio-dialog::backdrop {
  background: var(--scrim);
  backdrop-filter: blur(var(--nav-blur));
}

.bio-dialog-inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  padding: var(--space-l);
  border-block-start: var(--rule-bold) solid var(--amber);
}

.bio-dialog-name {
  font-size: var(--step-3);
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-display);
}

.bio-dialog-actions {
  justify-content: space-between;
  padding-block-start: var(--space-s);
  border-block-start: var(--rule-hair) solid var(--rule-light);
}

.no-js .team-actions .pill,
.no-js .bio-dialog-head,
.no-js [data-bio-close] {
  display: none;
}

.no-js .bio-dialog {
  position: static;
  display: block;
  inline-size: auto;
  max-block-size: none;
  margin-block-start: var(--space-l);
  box-shadow: none;
}

/* ---- Services: one chapter per service ---- */

.service-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  margin-block-end: var(--space-2xl);
}

.service-figure {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-block-start: var(--space-2xl);
}

.service-figure picture {
  display: block;
  overflow: hidden;
  border-radius: var(--radius-card);
}

.service-figure img {
  inline-size: 100%;
  max-block-size: var(--band-min);
  object-fit: cover;
  object-position: var(--focus-people);
}

.service-figure.is-portrait {
  inline-size: min(100%, var(--form-max));
  margin-block-start: 0;
}

.service-figure.is-portrait img {
  max-block-size: none;
  aspect-ratio: var(--ratio-portrait);
}

.service .split + .split {
  margin-block-start: var(--space-2xl);
}

.service-figure.is-illustration img {
  max-block-size: none;
  object-fit: contain;
}

/* Anchors land below the sticky tabs as well as the nav. */
.service,
#sectors {
  scroll-margin-block-start: var(--tabs-h);
}

.tabs + .service {
  padding-block-start: var(--space-2xl);
}

/* ---- Pager: previous and next report or article ---- */

.pager {
  display: flex;
  flex-direction: column;
  gap: var(--space-l);
}

.pager-list {
  display: grid;
  gap: var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (width >= 48rem) {
  .pager-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .pager-next {
    grid-column: 2;
    text-align: end;
  }
}

.pager-list a {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding-block: var(--space-m);
  border-block-start: var(--rule-strong) solid var(--theme-fg);
  color: inherit;
  text-decoration-line: none;
}

.pager-title {
  font-family: var(--font-structural);
  font-size: var(--step-2);
  font-stretch: calc(var(--wdth-heading) * 1%);
  font-weight: var(--weight-bold);
  line-height: var(--leading-heading);
}

.pager-list a:hover .pager-title,
.pager-list a:focus-visible .pager-title {
  text-decoration-line: underline;
  text-decoration-thickness: var(--underline-hover);
  text-underline-offset: var(--underline-offset);
}

/* A chapter title standing alone at the top of a band. */
.band > .chapter-title {
  margin-block-end: var(--space-xl);
}

/* ---- chrome.css ---- */
/*
  Site chrome that is neither nav nor footer: the preloader, the page wipe
  (core/transitions.js) and the reading progress bar (modules/reading-progress.js).
*/

/* ---- Preloader: once a session, at most --dur-preloader (partials/preloader.njk) ---- */

.preloader {
  display: none;
}

.is-preloading .preloader {
  position: fixed;
  inset: 0;
  z-index: var(--layer-dialog);
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--space-m);
  background: var(--ink-deep);
  color: var(--paper);
  animation: preloader-out var(--dur-base) var(--ease-exit) var(--dur-preloader) both;
  pointer-events: none;
}

.preloader-mark {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.preloader-mark img {
  border-radius: 50%;
  background: var(--paper);
}

.preloader-word {
  font-family: var(--font-structural);
  font-size: var(--step-3);
  font-stretch: calc(var(--wdth-display) * 1%);
  font-weight: var(--weight-display);
  letter-spacing: var(--tracking-record);
  animation: preloader-word var(--dur-enter) var(--ease-out) both;
}

.preloader-line {
  inline-size: var(--orbit-size);
  max-inline-size: var(--preloader-line-max);
  block-size: var(--rule-hair);
  background: var(--amber);
  transform-origin: left center;
  animation: preloader-line var(--dur-preloader) var(--ease-in-out) both;
}

@keyframes preloader-word {
  from {
    clip-path: inset(0 100% 0 0);
  }
}

@keyframes preloader-line {
  from {
    transform: scaleX(0);
  }
}

@keyframes preloader-out {
  to {
    visibility: hidden;
    opacity: 0;
  }
}

/* ---- Page wipe: browsers without cross-document view transitions ---- */

.page-wipe {
  position: fixed;
  inset: 0;
  z-index: var(--layer-dialog);
  background: var(--ink-deep);
  transform: translateY(100%);
  visibility: hidden;
  pointer-events: none;
}

.page-wipe[data-state="entering"],
.page-wipe[data-state="covering"],
.page-wipe[data-state="leaving"] {
  visibility: visible;
}

.page-wipe[data-state="entering"] {
  transform: none;
  transition: transform var(--dur-wipe) var(--ease-in-out);
}

.page-wipe[data-state="covering"] {
  transform: none;
}

.page-wipe[data-state="leaving"] {
  transform: translateY(-100%);
  transition:
    transform var(--dur-wipe) var(--ease-in-out),
    visibility 0s linear var(--dur-wipe);
  visibility: hidden;
}

/* ---- Reading progress: field reports and articles ---- */

.reading-progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--layer-header);
  block-size: var(--progress-h);
  background: var(--amber);
  transform: scaleX(var(--progress, 0));
  transform-origin: left center;
  pointer-events: none;
}

.no-js .reading-progress {
  display: none;
}

/* ---- motion.css ---- */
/*
  Motion in CSS (immersive redesign; PLAN.md, section 3). The scroll-driven
  motion is GSAP (src/js/core/reveal.js and the page modules); this file
  holds what needs no script: counters' steady width, the cross-page view
  transition, the scroll-linked settle of a report's lead plate, and the
  map's line draw-in. All of it is off under reduced motion
  (reduced-motion.css; the scripts check src/js/lib/motion.js).
*/

/* Counters hold a steady width while their figure runs (reveal.js). */
[data-count] {
  display: inline-block;
  font-variant-numeric: tabular-nums lining-nums;
}

/* ---- Between pages: a short crossfade; the header holds still ---- */

@view-transition {
  navigation: auto;
}

::view-transition-group(root) {
  animation-duration: var(--dur-page);
  animation-timing-function: var(--ease-out);
}

.site-nav {
  view-transition-name: site-nav;
}

@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

/* ---- Scroll-linked scale: each field report's lead plate ---- */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* A field report's lead plate settles into place as it arrives. */
    .report > .plate-bleed:first-of-type {
      overflow: hidden;
    }

    .report > .plate-bleed:first-of-type img {
      animation: plate-settle linear both;
      animation-range: entry 0% cover 50%;
      animation-timeline: view();
    }
  }
}

@keyframes plate-settle {
  from {
    transform: scale(var(--plate-scale));
  }

  to {
    transform: none;
  }
}

/* State change in response to an action: position or size via transform. */
.transition-move {
  transition: transform var(--dur-base) var(--ease-out);
}

/*
  Line draw-in for SVG strokes (the map, Phase 4). Set --draw-length to the
  path length (pathLength="1" makes it 1) and --draw-delay per element.
*/
.draw-in {
  stroke-dasharray: var(--draw-length, 1);
  stroke-dashoffset: var(--draw-length, 1);
  animation: draw-in var(--dur-slow) var(--ease-out) var(--draw-delay, 0ms) forwards;
}

@keyframes draw-in {
  to {
    stroke-dashoffset: 0;
  }
}

/* ---- reduced-motion.css ---- */
/*
  The one place !important is allowed (brief Part 9). Every animation and
  transition collapses to an instant change when the reader has asked for
  reduced motion. Scripts check the same query before starting anything
  (src/assets/js/motion.js).
*/

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: var(--dur-none) !important;
    animation-iteration-count: 1 !important;
    transition-duration: var(--dur-none) !important;
    scroll-behavior: auto !important;
  }
}

/* ---- generated from partners.json ---- */
.partner-logo-bayes-consulting { aspect-ratio: 505 / 96; }
.partner-logo-bayes-consulting::before { mask-image: url("/images/partners/bayes-consulting-mono.webp"); }
.partner-logo-fsd-africa { aspect-ratio: 271 / 96; }
.partner-logo-fsd-africa::before { mask-image: url("/images/partners/fsd-africa-colour.webp"); }
.partner-logo-kk-advisors { aspect-ratio: 258 / 96; }
.partner-logo-kk-advisors::before { mask-image: url("/images/partners/kk-advisors-colour.webp"); }
.partner-logo-pure-plant-organics { aspect-ratio: 214 / 88; }
.partner-logo-pure-plant-organics::before { mask-image: url("/images/partners/pure-plant-organics-colour.webp"); }
.partner-logo-verst-carbon { aspect-ratio: 227 / 96; }
.partner-logo-verst-carbon::before { mask-image: url("/images/partners/verst-carbon-colour.webp"); }
.partner-logo-zila-tech { aspect-ratio: 538 / 96; }
.partner-logo-zila-tech::before { mask-image: url("/images/partners/zila-tech-colour.webp"); }