/* ==========================================================================
   leoliebenberg.com — "Editorial Ink"
   Design system / CSS foundation. Dark ink base only. No light mode.
   Order: fonts → tokens → reset → base type → utilities → layout → components
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. FONTS  (self-hosted variable, /fonts/)
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Clash Display';
  src: url('fonts/clash-display-var.woff2') format('woff2-variations'),
       url('fonts/clash-display-var.woff2') format('woff2');
  font-weight: 200 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F,
                 U+2074, U+20AC, U+2122, U+2191, U+2193, U+2192, U+2212, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Satoshi';
  src: url('fonts/satoshi-var.woff2') format('woff2-variations'),
       url('fonts/satoshi-var.woff2') format('woff2');
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'JetBrains Mono';
  src: url('fonts/jetbrains-mono-var.woff2') format('woff2-variations'),
       url('fonts/jetbrains-mono-var.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

/* Metric-matched fallbacks — kills the swap reflow (CLS) while the woff2 loads.
   Numbers are eyeballed against each face's real metrics; close enough that the
   line boxes don't jump. */
@font-face {
  font-family: 'Clash Fallback';
  src: local('Helvetica Neue'), local('Arial'), local('Roboto');
  size-adjust: 104%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Satoshi Fallback';
  src: local('Helvetica Neue'), local('Arial'), local('Roboto');
  size-adjust: 102%;
  ascent-override: 96%;
  descent-override: 24%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Mono Fallback';
  src: local('SFMono-Regular'), local('Menlo'), local('Consolas'), local('Courier New');
  size-adjust: 100%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

/* --------------------------------------------------------------------------
   2. TOKENS
   Primitives first, semantic aliases second. Author against the SEMANTIC ones.
   -------------------------------------------------------------------------- */

:root {
  /* --- palette primitives (locked) --- */
  --ink:        #0B0B0F;
  --ink-2:      #131319;
  --ink-3:      #1C1C24;
  --bone:       #F5F0E8;
  --bone-dim:   #9A968E;
  --accent:     #3E9BFF;   /* azure — 6.86:1 on ink, AA as text AND as a fill */
  --accent-2:   #38BDF8;   /* sky — the evidence/data tone (9.17:1) */
  --logo:       var(--accent);   /* the mark follows the accent */

  /* channel forms, for alpha compositing without a colour library */
  --ink-rgb:       11 11 15;
  --bone-rgb:      245 240 232;
  --accent-rgb:   62 155 255;
  --accent-2-rgb: 56 189 248;

  /* derived tints — the only sanctioned way to get "in-between" colours */
  --bone-08:      rgb(var(--bone-rgb) / 0.08);
  --bone-14:      rgb(var(--bone-rgb) / 0.14);
  --bone-24:      rgb(var(--bone-rgb) / 0.24);
  --bone-60:      rgb(var(--bone-rgb) / 0.60);
  --accent-12: rgb(var(--accent-rgb) / 0.12);
  --accent-24: rgb(var(--accent-rgb) / 0.24);
  --accent-40: rgb(var(--accent-rgb) / 0.40);
  --accent-2-16:     rgb(var(--accent-2-rgb) / 0.16);
  --accent-2-32:     rgb(var(--accent-2-rgb) / 0.32);

  /* --- semantic colour --- */
  --bg:            var(--ink);
  --bg-raised:     var(--ink-2);
  --bg-sunken:     #08080B;         /* one step below base, for inset wells */
  --surface-hover: #17171F;
  --text:          var(--bone);
  --text-muted:    var(--bone-dim);   /* 6.6:1 on --ink — AA for body */
  --text-accent:   var(--accent);  /* 6.4:1 on --ink — AA, links/large only */
  --text-data:     var(--accent-2);      /* 8.1:1 on --ink */
  --accent-ink:    var(--ink);        /* text ON accent fills: 6.9:1 */
  --line:          var(--ink-3);
  --line-strong:   var(--bone-14);
  --focus:         var(--accent);
  --selection-bg:  var(--accent);
  --selection-fg:  var(--ink);

  /* --- type families --- */
  --font-display: 'Clash Display', 'Clash Fallback', ui-sans-serif, system-ui, sans-serif;
  --font-body:    'Satoshi', 'Satoshi Fallback', ui-sans-serif, system-ui, sans-serif;
  --font-mono:    'JetBrains Mono', 'Mono Fallback', ui-monospace, monospace;

  /* --- type scale (fluid, 320 → 1440 viewport; no media-query steps) --- */
  --step--2:  clamp(0.694rem, 0.68rem + 0.07vw,  0.75rem);
  --step--1:  clamp(0.833rem, 0.80rem + 0.16vw,  0.938rem);
  --step-0:   clamp(1rem,     0.95rem + 0.25vw,  1.125rem);   /* body */
  --step-1:   clamp(1.2rem,   1.12rem + 0.40vw,  1.5rem);
  --step-2:   clamp(1.44rem,  1.31rem + 0.65vw,  2rem);
  --step-3:   clamp(1.728rem, 1.52rem + 1.04vw,  2.625rem);
  --step-4:   clamp(2.074rem, 1.75rem + 1.62vw,  3.5rem);
  --step-5:   clamp(2.488rem, 1.99rem + 2.49vw,  4.625rem);
  --step-6:   clamp(2.986rem, 2.23rem + 3.78vw,  6.125rem);
  --step-7:   clamp(3.583rem, 2.44rem + 5.71vw,  8.125rem);   /* hero only */

  /* --- line-height --- */
  --lh-display: 0.95;   /* big Clash — set TIGHT */
  --lh-tight:   1.06;
  --lh-snug:    1.24;
  --lh-body:    1.6;
  --lh-loose:   1.75;

  /* --- tracking --- */
  --track-display: -0.035em;
  --track-head:    -0.02em;
  --track-body:    -0.005em;
  --track-mono:     0.14em;   /* eyebrows / labels */
  --track-mono-sm:  0.06em;   /* inline data, numbers */

  /* --- weights --- */
  --w-light: 300;
  --w-book:  400;
  --w-med:   500;
  --w-semi:  600;
  --w-bold:  700;

  /* --- measure --- */
  --measure:       65ch;
  --measure-short: 48ch;
  --measure-long:  76ch;

  /* --- spacing scale (fluid; each step grows ~1.15× from 320 → 1440) --- */
  --sp-3xs: clamp(0.25rem, 0.24rem + 0.05vw, 0.3125rem);
  --sp-2xs: clamp(0.5rem,  0.48rem + 0.11vw, 0.625rem);
  --sp-xs:  clamp(0.75rem, 0.71rem + 0.21vw, 0.9375rem);
  --sp-s:   clamp(1rem,    0.95rem + 0.25vw, 1.25rem);
  --sp-m:   clamp(1.5rem,  1.43rem + 0.38vw, 1.875rem);
  --sp-l:   clamp(2rem,    1.86rem + 0.71vw, 2.75rem);
  --sp-xl:  clamp(3rem,    2.71rem + 1.43vw, 4.5rem);
  --sp-2xl: clamp(4rem,    3.43rem + 2.86vw, 7rem);
  --sp-3xl: clamp(6rem,    5.14rem + 4.29vw, 10.5rem);
  --sp-4xl: clamp(8rem,    6.57rem + 7.14vw, 15rem);   /* between acts */

  /* --- radii — restrained. This is print, not a phone OS. --- */
  --r-0:    0px;
  --r-xs:   2px;
  --r-s:    4px;
  --r-m:    8px;
  --r-l:    14px;
  --r-pill: 999px;

  /* --- hairlines --- */
  --hair:        1px;
  --hairline:    var(--hair) solid var(--line);
  --hairline-2:  var(--hair) solid var(--line-strong);
  --hairline-hot: var(--hair) solid var(--accent-40);

  /* --- easing + duration --- */
  --ease-out:   cubic-bezier(0.16, 1, 0.30, 1);     /* the house curve */
  --ease-in:    cubic-bezier(0.55, 0, 1, 0.45);
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --t-fast: 140ms;
  --t-base: 260ms;
  --t-slow: 520ms;
  --t-vslow: 900ms;

  /* --- elevation (light, warm, never a grey blur blob) --- */
  --shadow-1: 0 1px 0 rgb(var(--bone-rgb) / 0.04) inset,
              0 8px 24px -12px rgb(0 0 0 / 0.8);
  --shadow-2: 0 1px 0 rgb(var(--bone-rgb) / 0.05) inset,
              0 24px 60px -24px rgb(0 0 0 / 0.9);
  --glow-accent: 0 0 0 1px var(--accent-40),
                 0 12px 40px -16px rgb(var(--accent-rgb) / 0.45);

  /* --- layout --- */
  --gutter:        clamp(1.25rem, 1rem + 2vw, 3rem);
  --container:     78rem;   /* 1248px — main reading/content width */
  --container-wide: 96rem;  /* 1536px — full-bleed-ish editorial rows */
  --container-narrow: 44rem;
  --grid-cols: 12;
  --grid-gap:  clamp(1rem, 0.6rem + 1.6vw, 2rem);
  --nav-h:     clamp(3.75rem, 3.4rem + 1.6vw, 5rem);

  /* --- z layers --- */
  --z-bg:       -1;
  --z-base:      0;
  --z-raised:    10;
  --z-sticky:    100;
  --z-nav:       200;
  --z-menu:      300;
  --z-overlay:   400;
  --z-toast:     500;
  --z-cursor:    9999;
}

/* --------------------------------------------------------------------------
   3. RESET
   -------------------------------------------------------------------------- */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + var(--sp-m));
  /* nothing may push the page sideways */
  overflow-x: clip;
}

body {
  min-height: 100svh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: var(--w-book);
  line-height: var(--lh-body);
  letter-spacing: var(--track-body);
  font-optical-sizing: auto;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

img, picture, video, canvas, svg { display: block; max-width: 100%; height: auto; }
img { background: var(--ink-2); }   /* no white flash before decode */

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

p, h1, h2, h3, h4, h5, h6, li, figcaption, blockquote {
  overflow-wrap: break-word;
  hyphens: manual;
}

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

hr { border: 0; }

::selection { background: var(--selection-bg); color: var(--selection-fg); }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--ink-3); border: 2px solid var(--bg); border-radius: var(--r-pill); }
::-webkit-scrollbar-thumb:hover { background: var(--bone-24); }

/* The WebGL backdrop from bg.js */
#bg {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: var(--z-bg);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   4. TYPE
   Clash = display. Satoshi = body. JetBrains = the credibility layer.
   -------------------------------------------------------------------------- */

h1, h2, h3, h4, .display, .h1, .h2, .h3, .h4 {
  font-family: var(--font-display);
  font-weight: var(--w-med);
  line-height: var(--lh-tight);
  letter-spacing: var(--track-head);
  text-wrap: balance;
  /* optical: Clash's left sidebearing is generous — pull big type back to the grid */
  margin-left: -0.015em;
}

.display, .h1, h1 {
  font-size: var(--step-6);
  font-weight: var(--w-med);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  margin-left: -0.03em;
}

/* the one true hero: bigger, tighter, lighter — it should feel drawn, not typed */
.display--hero {
  font-size: var(--step-7);
  font-weight: var(--w-light);
  line-height: 0.92;
  letter-spacing: -0.045em;
  text-wrap: pretty;
}

.h2, h2 { font-size: var(--step-4); line-height: 0.98; letter-spacing: -0.03em; }
.h3, h3 { font-size: var(--step-3); line-height: 1.05; }
.h4, h4 { font-size: var(--step-2); line-height: 1.15; letter-spacing: -0.015em; }
h5, h6  { font-family: var(--font-body); font-size: var(--step-1); font-weight: var(--w-semi); letter-spacing: -0.01em; margin-left: 0; }

/* italic-substitute: Clash has no italic. Emphasis in headlines = the accent tone, not slant. */
h1 em, h2 em, h3 em, .display em {
  font-style: normal;
  color: var(--accent-2);
  font-weight: var(--w-light);
}

p { max-inline-size: var(--measure); }

.lead {
  font-size: var(--step-1);
  line-height: 1.45;
  color: var(--bone);
  font-weight: var(--w-light);
  letter-spacing: -0.012em;
  max-inline-size: var(--measure-short);
  text-wrap: pretty;
}

.prose > * + * { margin-block-start: var(--sp-s); }
.prose > * + h2, .prose > * + h3 { margin-block-start: var(--sp-l); }
.prose p { color: var(--text-muted); }
.prose strong { color: var(--text); font-weight: var(--w-semi); }
.prose a { color: var(--text); text-decoration-color: var(--accent); }

.muted   { color: var(--text-muted); }
.measure { max-inline-size: var(--measure); }
.measure-short { max-inline-size: var(--measure-short); }
.balance { text-wrap: balance; }

/* --- MONO LAYER — eyebrows, labels, data, metadata. Used deliberately. --- */

.eyebrow,
.label,
.meta,
.mono {
  font-family: var(--font-mono);
  font-variant-ligatures: none;
  font-feature-settings: 'tnum' 1, 'zero' 1;
}

/* the section kicker: mono, uppercase, wide, small, with a rule that leads into it */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2xs);
  font-size: var(--step--2);
  font-weight: var(--w-med);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--text-muted);
  line-height: 1;
}
.eyebrow::before {
  content: '';
  inline-size: clamp(1.25rem, 3vw, 2.5rem);
  block-size: var(--hair);
  background: var(--accent);
  flex: none;
}
.eyebrow--hot   { color: var(--accent); }
.eyebrow--alt { color: var(--accent-2); }
.eyebrow--alt::before { background: var(--accent-2); }
.eyebrow--bare::before  { display: none; }

/* numbered section marker: "03 / SELECTED WORK" */
.eyebrow[data-index]::after {
  content: attr(data-index);
  color: var(--bone-60);
}

.label {
  font-size: var(--step--2);
  font-weight: var(--w-med);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--text-muted);
  line-height: 1.2;
}

.meta {
  font-size: var(--step--1);
  letter-spacing: var(--track-mono-sm);
  color: var(--text-muted);
  line-height: 1.4;
}

.mono-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--track-mono-sm);
  color: var(--accent-2);
}

code, kbd, samp, pre {
  font-family: var(--font-mono);
  font-size: 0.9em;
  letter-spacing: 0;
}
:not(pre) > code {
  background: var(--ink-2);
  border: var(--hairline);
  border-radius: var(--r-xs);
  padding: 0.12em 0.4em;
  color: var(--accent-2);
}
pre {
  background: var(--bg-sunken);
  border: var(--hairline);
  border-radius: var(--r-m);
  padding: var(--sp-s);
  overflow-x: auto;
  line-height: 1.6;
}

/* --------------------------------------------------------------------------
   5. A11Y UTILITIES
   -------------------------------------------------------------------------- */

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

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}
/* mouse users don't get the ring; keyboard users always do */
:focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: fixed;
  top: var(--sp-2xs);
  left: var(--sp-2xs);
  z-index: var(--z-toast);
  transform: translateY(calc(-100% - var(--sp-l)));
  padding: var(--sp-xs) var(--sp-s);
  background: var(--accent);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--w-bold);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  text-decoration: none;
  border-radius: var(--r-s);
  transition: transform var(--t-base) var(--ease-out);
}
.skip-link:focus-visible {
  transform: translateY(0);
  outline-offset: 2px;
  outline-color: var(--bone);
}

/* --------------------------------------------------------------------------
   6. LAYOUT
   Container → grid → editorial (asymmetric) rows.
   -------------------------------------------------------------------------- */

.container {
  inline-size: 100%;
  max-inline-size: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--wide   { max-inline-size: var(--container-wide); }
.container--narrow { max-inline-size: var(--container-narrow); }
.container--flush  { padding-inline: 0; }   /* only inside an already-padded parent */

.section       { padding-block: var(--sp-3xl); }
.section--tight{ padding-block: var(--sp-2xl); }
.section--act  { padding-block: var(--sp-4xl); }   /* between the big movements */
.section--flush-top { padding-block-start: 0; }

/* raised / inset section — breaks the flat black, cheap and effective */
.section--raised {
  background: var(--bg-raised);
  border-block: var(--hairline);
}
.section--sunken { background: var(--bg-sunken); }

/* --- the grid: 12 cols on desktop, 4 on phone, no media-query cliff --- */
.grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
  gap: var(--grid-gap);
}
@media (max-width: 47.99em) {
  .grid { --grid-cols: 4; }
}

/* span helpers — collapse to full width on small screens automatically */
[class*='col-'] { grid-column: 1 / -1; }
@media (min-width: 48em) {
  .col-1  { grid-column: span 1; }  .col-2  { grid-column: span 2; }
  .col-3  { grid-column: span 3; }  .col-4  { grid-column: span 4; }
  .col-5  { grid-column: span 5; }  .col-6  { grid-column: span 6; }
  .col-7  { grid-column: span 7; }  .col-8  { grid-column: span 8; }
  .col-9  { grid-column: span 9; }  .col-10 { grid-column: span 10; }
  .col-12 { grid-column: 1 / -1; }
  .start-2 { grid-column-start: 2; } .start-3 { grid-column-start: 3; }
  .start-4 { grid-column-start: 4; } .start-5 { grid-column-start: 5; }
  .start-6 { grid-column-start: 6; } .start-7 { grid-column-start: 7; }
  .start-8 { grid-column-start: 8; }
}

/* auto-fit card grid, no breakpoints needed */
.grid-auto {
  display: grid;
  gap: var(--grid-gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

/* --- EDITORIAL ROWS — the rhythm system. Never the same shape twice. ---
   Use them in sequence, e.g. row--offset → row--stack → row--split → row--ledger.
   Each is a distinct silhouette so no two sections read alike. */

/* (a) offset: narrow label rail + wide body, the body starts INSIDE the grid */
.row--offset {
  display: grid;
  gap: var(--grid-gap);
  grid-template-columns: 1fr;
}
@media (min-width: 48em) {
  .row--offset { grid-template-columns: minmax(0, 3fr) minmax(0, 8fr); column-gap: calc(var(--grid-gap) * 2); }
  .row--offset > :first-child { position: sticky; top: calc(var(--nav-h) + var(--sp-m)); align-self: start; }
}

/* (b) stack: headline hangs LEFT of the content well, text drops below-right.
   This is the deliberate grid-break #1 — the heading escapes the text column. */
.row--stack { display: grid; gap: var(--sp-m); grid-template-columns: 1fr; }
@media (min-width: 62em) {
  .row--stack { grid-template-columns: repeat(12, minmax(0, 1fr)); row-gap: var(--sp-l); }
  .row--stack > .row__head { grid-column: 1 / 9; }
  .row--stack > .row__body { grid-column: 6 / 12; }   /* overlaps the head column */
  .row--stack > .row__aside { grid-column: 1 / 5; align-self: end; }
}

/* (c) split: image bleeds off ONE edge. Deliberate grid-break #2. */
.row--bleed { display: grid; gap: var(--sp-l); grid-template-columns: 1fr; }
@media (min-width: 62em) {
  .row--bleed {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    column-gap: var(--sp-2xl);
  }
  /* pull the media past the container edge into the viewport margin */
  .row--bleed > .row__media {
    margin-inline-end: calc(var(--gutter) * -1 - (100vw - min(100vw, var(--container))) / 2);
  }
  .row--bleed.row--bleed-left > .row__media {
    order: -1;
    margin-inline-end: 0;
    margin-inline-start: calc(var(--gutter) * -1 - (100vw - min(100vw, var(--container))) / 2);
  }
}

/* (d) ledger: mono data column pinned right, prose left — reads like a spec sheet */
.row--ledger { display: grid; gap: var(--sp-l); grid-template-columns: 1fr; }
@media (min-width: 48em) {
  .row--ledger {
    grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
    column-gap: var(--sp-2xl);
    align-items: start;
  }
  .row--ledger > :last-child { border-inline-start: var(--hairline); padding-inline-start: var(--sp-m); }
}

/* full-bleed escape hatches. `.bleed` goes edge-to-edge of the viewport (safe:
   html has overflow-x: clip, so a scrollbar can never make 100vw overflow).
   `.full-bleed` only cancels the container's gutter. */
.bleed {
  margin-inline: calc(50% - 50vw);
  inline-size: 100vw;
  max-inline-size: 100vw;
}
.full-bleed {
  margin-inline: calc(var(--gutter) * -1);
  inline-size: calc(100% + var(--gutter) * 2);
}

.stack > * + * { margin-block-start: var(--flow, var(--sp-s)); }
.stack-l > * + * { margin-block-start: var(--sp-l); }
.cluster { display: flex; flex-wrap: wrap; gap: var(--sp-xs); align-items: center; }

/* --------------------------------------------------------------------------
   7. COMPONENTS
   -------------------------------------------------------------------------- */

/* --- 7.1 Buttons ------------------------------------------------------ */

.btn {
  --btn-bg: transparent;
  --btn-fg: var(--text);
  --btn-bd: var(--line-strong);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2xs);
  min-block-size: 48px;                 /* > 44px tap target */
  padding-inline: var(--sp-m);
  padding-block: var(--sp-2xs);
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: var(--hair) solid var(--btn-bd);
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--w-med);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  text-decoration: none;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background-color var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out),
    color var(--t-fast) var(--ease-out),
    transform var(--t-fast) var(--ease-out),
    box-shadow var(--t-base) var(--ease-out);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(1px) scale(0.985); transition-duration: 60ms; }
.btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.btn[disabled], .btn[aria-disabled='true'] {
  opacity: 0.45; pointer-events: none; transform: none;
}

/* primary — azure fill, ink text (6.9:1, AA) */
.btn--primary {
  --btn-bg: var(--accent);
  --btn-fg: var(--ink);
  --btn-bd: var(--accent);
  font-weight: var(--w-bold);
}
.btn--primary:hover  { --btn-bg: #5FB0FF; --btn-bd: #5FB0FF; box-shadow: var(--glow-accent); }
.btn--primary:active { --btn-bg: #2B84DB; --btn-bd: #2B84DB; box-shadow: none; }
.btn--primary:focus-visible { outline-offset: 4px; }

/* ghost — hairline outline, fills on hover */
.btn--ghost:hover  { --btn-bg: var(--bone-08); --btn-bd: var(--bone); }
.btn--ghost:active { --btn-bg: var(--bone-14); }

/* text/arrow link — the tertiary action. Underline grows, arrow steps out. */
.btn--text {
  --btn-bd: transparent;
  min-block-size: 44px;
  padding-inline: 0;
  border-radius: var(--r-xs);
  color: var(--text);
  position: relative;
}
.btn--text::after {
  content: '→';
  font-family: var(--font-mono);
  transition: transform var(--t-base) var(--ease-out);
}
.btn--text::before {
  content: '';
  position: absolute;
  inset-block-end: 10px;
  inset-inline: 0 1.4em;
  block-size: var(--hair);
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-base) var(--ease-out);
}
.btn--text:hover { color: var(--accent); transform: none; }
.btn--text:hover::before { transform: scaleX(1); }
.btn--text:hover::after  { transform: translateX(4px); }
.btn--text:active { transform: none; opacity: 0.7; }

.btn--sm { min-block-size: 44px; padding-inline: var(--sp-s); font-size: var(--step--2); }
.btn--block { display: flex; inline-size: 100%; }

/* --- 7.2 Inline links (body copy) ------------------------------------- */
/* NOTE: body links stay BONE with an azure underline — accent text at
   16px on ink measures 6.4:1 (passes AA) but vibrates optically at long lengths.
   Colour is the hover state, not the resting state. */
a:not([class]) {
  color: var(--text);
  text-decoration: underline;
  text-decoration-color: var(--accent-40);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  transition: color var(--t-fast) var(--ease-out),
              text-decoration-color var(--t-fast) var(--ease-out);
}
a:not([class]):hover { color: var(--accent); text-decoration-color: var(--accent); }

/* --- 7.3 Hairline divider --------------------------------------------- */

.rule {
  block-size: var(--hair);
  background: var(--line);
  border: 0;
  inline-size: 100%;
}
/* a rule with an accent tick at its origin — the house signature */
.rule--tick {
  background: linear-gradient(90deg,
    var(--accent) 0 clamp(1.5rem, 4vw, 3rem),
    var(--line) clamp(1.5rem, 4vw, 3rem));
}
.rule--fade {
  background: linear-gradient(90deg, var(--line-strong), transparent);
}
.rule--vert { inline-size: var(--hair); block-size: 100%; }

/* --- 7.4 Cards --------------------------------------------------------- */

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-xs);
  padding: var(--sp-m);
  background: var(--bg-raised);
  border: var(--hairline);
  border-radius: var(--r-l);
  transition: border-color var(--t-base) var(--ease-out),
              background-color var(--t-base) var(--ease-out),
              transform var(--t-base) var(--ease-out);
}
.card__title { font-family: var(--font-display); font-size: var(--step-2); line-height: 1.1; letter-spacing: -0.02em; }
.card__body  { color: var(--text-muted); font-size: var(--step-0); }
.card__foot  { margin-block-start: auto; padding-block-start: var(--sp-s); border-block-start: var(--hairline); }

/* interactive card — whole surface clickable via a stretched link */
.card--link:hover {
  border-color: var(--bone-24);
  background: var(--surface-hover);
  transform: translateY(-2px);
}
.card--link:has(a:focus-visible) { border-color: var(--accent); }
.card--link .stretch::after { content: ''; position: absolute; inset: 0; border-radius: inherit; }
.card--link .stretch:focus-visible { outline: none; }

/* numbered card — mono index in the corner, editorial */
.card--num::before {
  content: attr(data-num);
  position: absolute;
  inset-block-start: var(--sp-s);
  inset-inline-end: var(--sp-s);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: var(--track-mono);
  color: var(--bone-60);
}
/* accent card — azure top hairline instead of a coloured background */
.card--hot { border-block-start-color: var(--accent); }

/* --- 7.5 Tags / chips -------------------------------------------------- */

.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3xs);
  padding: 0.4em 0.75em;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--w-med);
  letter-spacing: var(--track-mono-sm);
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--ink-2);
  border: var(--hairline);
  border-radius: var(--r-s);
  line-height: 1.2;
  white-space: nowrap;
}
.tag--hot   { color: var(--accent); border-color: var(--accent-24); background: var(--accent-12); }
.tag--alt { color: var(--accent-2);     border-color: var(--accent-2-32);     background: var(--accent-2-16); }
/* live/status chip */
.tag--live::before {
  content: '';
  inline-size: 6px; block-size: 6px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 3px rgb(var(--accent-rgb) / 0.18);
}
a.tag { text-decoration: none; transition: color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out); }
a.tag:hover { color: var(--text); border-color: var(--bone-24); }

/* --- 7.6 Data row (mono) ----------------------------------------------- */
/* The spec-sheet component. Label left, value right, dotted leader between.
   This is what makes the page read "engineer" instead of "agency". */

.data-list { display: flex; flex-direction: column; }

.data-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2xs);
  padding-block: var(--sp-xs);
  border-block-end: var(--hairline);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: var(--track-mono-sm);
}
.data-row:first-child { border-block-start: var(--hairline); }
.data-row__key {
  flex: none;
  text-transform: uppercase;
  letter-spacing: var(--track-mono);
  font-size: var(--step--2);
  color: var(--text-muted);
}
.data-row__dots {
  flex: 1 1 auto;
  min-inline-size: var(--sp-s);
  align-self: center;
  block-size: var(--hair);
  background-image: linear-gradient(90deg, var(--line-strong) 25%, transparent 25%);
  background-size: 4px var(--hair);
  opacity: 0.6;
}
.data-row__val {
  flex: none;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  text-align: end;
}
.data-row__val--hot   { color: var(--accent); }
.data-row__val--alt { color: var(--accent-2); }

/* below 24rem the key/value pair stacks rather than squeezing */
@media (max-width: 26em) {
  .data-row { flex-wrap: wrap; }
  .data-row__dots { display: none; }
  .data-row__val { flex: 1 0 100%; text-align: start; }
}

/* --- 7.7 Blockquote / pull-quote --------------------------------------- */

blockquote, .quote {
  margin: 0;
  padding-inline-start: var(--sp-m);
  border-inline-start: 2px solid var(--accent);
  font-family: var(--font-body);
  font-size: var(--step-1);
  font-weight: var(--w-light);
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--text);
  max-inline-size: var(--measure-short);
}
blockquote cite, .quote cite {
  display: block;
  margin-block-start: var(--sp-s);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-style: normal;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* the real editorial move: a display-size pull-quote with a hanging mark.
   It sits OUTSIDE the text measure and breaks the column on purpose. */
.pullquote {
  position: relative;
  border: 0;
  padding: 0;
  margin-block: var(--sp-xl);
  font-family: var(--font-display);
  font-weight: var(--w-light);
  font-size: var(--step-4);
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-wrap: balance;
  max-inline-size: 22ch;
  color: var(--bone);
}
.pullquote::before {
  content: '\201C';
  position: absolute;
  inset-inline-start: -0.5em;
  inset-block-start: -0.12em;
  color: var(--accent);
  font-size: 1.4em;
  line-height: 1;
  pointer-events: none;
}
@media (max-width: 47.99em) {
  /* on phones the hanging mark would overflow — bring it inline */
  .pullquote { padding-inline-start: 0.6em; }
  .pullquote::before { inset-inline-start: -0.05em; }
}
.pullquote cite {
  display: block;
  margin-block-start: var(--sp-m);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-style: normal;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--accent-2);
}

/* --- 7.8 Figure + caption ---------------------------------------------- */

figure { margin: 0; }
.figure { display: flex; flex-direction: column; gap: var(--sp-xs); }
.figure img {
  inline-size: 100%;
  border-radius: var(--r-m);
  border: var(--hairline);
}
.figure figcaption,
.caption {
  display: flex;
  gap: var(--sp-2xs);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: var(--track-mono-sm);
  line-height: 1.5;
  color: var(--text-muted);
  max-inline-size: 44ch;
}
/* mono index prefix: "FIG. 01 —" */
.figure figcaption::before,
.caption::before {
  content: attr(data-fig);
  flex: none;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: var(--track-mono);
}
.figure--frame img { padding: var(--sp-2xs); background: var(--ink-2); }

/* --- 7.9 Nav ----------------------------------------------------------- */

.nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--z-nav);
  block-size: var(--nav-h);
  display: flex;
  align-items: center;
  border-block-end: var(--hair) solid transparent;
  transition: background-color var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out),
              backdrop-filter var(--t-base) var(--ease-out);
}
/* .is-stuck is toggled by JS on scroll (or drop it and keep the nav always solid) */
.nav.is-stuck {
  background: rgb(var(--ink-rgb) / 0.72);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  border-block-end-color: var(--line);
}

.nav__inner {
  inline-size: 100%;
  max-inline-size: var(--container-wide);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-m);
}

.nav__brand {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5ch;
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: var(--w-semi);
  letter-spacing: -0.02em;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}
.nav__brand span { font-family: var(--font-mono); font-size: 0.7em; letter-spacing: var(--track-mono); color: var(--accent); }

.nav__links {
  display: none;                /* mobile-first: hidden until the menu breakpoint */
  align-items: center;
  gap: var(--sp-m);
  list-style: none;
  margin: 0; padding: 0;
}
.nav__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--w-med);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--text-muted);
  text-decoration: none;
  transition: color var(--t-fast) var(--ease-out);
}
.nav__link::after {
  content: '';
  position: absolute;
  inset-block-end: 10px;
  inset-inline: 0;
  block-size: var(--hair);
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-base) var(--ease-out);
}
.nav__link:hover,
.nav__link.is-active { color: var(--text); }
.nav__link:hover::after,
.nav__link.is-active::after { transform: scaleX(1); }

/* hamburger — a real 48px target, two lines that become an X */
.nav__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 48px;
  block-size: 48px;
  margin-inline-end: -12px;      /* optical: pull the icon flush to the gutter */
  background: none;
  border: 0;
  border-radius: var(--r-s);
  cursor: pointer;
  color: var(--text);
}
.nav__toggle-box { position: relative; inline-size: 22px; block-size: 12px; }
.nav__toggle-box::before,
.nav__toggle-box::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  block-size: 1.5px;
  background: currentColor;
  transition: transform var(--t-base) var(--ease-out), inset-block-start var(--t-fast) var(--ease-out);
}
.nav__toggle-box::before { inset-block-start: 0; }
.nav__toggle-box::after  { inset-block-start: 10px; }
.nav__toggle[aria-expanded='true'] .nav__toggle-box::before { inset-block-start: 5px; transform: rotate(45deg); }
.nav__toggle[aria-expanded='true'] .nav__toggle-box::after  { inset-block-start: 5px; transform: rotate(-45deg); }

/* mobile menu — full-screen ink sheet, staggered items */
.nav__menu {
  position: fixed;
  inset: 0;
  z-index: var(--z-menu);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-s);
  padding: calc(var(--nav-h) + var(--sp-l)) var(--gutter) var(--sp-xl);
  background: var(--ink);
  overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition: opacity var(--t-base) var(--ease-out),
              transform var(--t-base) var(--ease-out),
              visibility 0s linear var(--t-base);
}
.nav__menu[data-open='true'] {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}
.nav__menu ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.nav__menu li { border-block-end: var(--hairline); }
.nav__menu li:first-child { border-block-start: var(--hairline); }
.nav__menu a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-s);
  min-block-size: 64px;
  padding-block: var(--sp-xs);
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: var(--w-med);
  letter-spacing: -0.025em;
  color: var(--text);
  text-decoration: none;
  transition: color var(--t-fast) var(--ease-out), padding-inline-start var(--t-base) var(--ease-out);
}
.nav__menu a::after {
  content: attr(data-num);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: var(--track-mono);
  color: var(--bone-60);
}
.nav__menu a:hover,
.nav__menu a:focus-visible { color: var(--accent); padding-inline-start: var(--sp-2xs); }
.nav__menu-foot { margin-block-start: var(--sp-l); }

/* lock the page while the sheet is open (JS adds .is-locked to <body>) */
body.is-locked { overflow: hidden; }

@media (min-width: 56em) {
  .nav__links  { display: flex; }
  .nav__toggle { display: none; }
  .nav__menu   { display: none; }
}

/* --- 7.10 Footer -------------------------------------------------------- */

.footer {
  border-block-start: var(--hairline);
  padding-block: var(--sp-2xl) var(--sp-l);
  background: var(--bg-sunken);
}
.footer__grid {
  display: grid;
  gap: var(--sp-xl) var(--grid-gap);
  grid-template-columns: 1fr;
}
@media (min-width: 48em) {
  .footer__grid { grid-template-columns: minmax(0, 5fr) minmax(0, 3fr) minmax(0, 3fr); }
}
.footer__cta {
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: var(--w-light);
  line-height: 1;
  letter-spacing: -0.035em;
  text-decoration: none;
  color: var(--text);
  display: inline-block;
  transition: color var(--t-base) var(--ease-out);
}
.footer__cta:hover { color: var(--accent); }
.footer__col { display: flex; flex-direction: column; gap: var(--sp-2xs); align-items: flex-start; }
.footer__col a {
  min-block-size: 44px;
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: var(--track-mono-sm);
  color: var(--text-muted);
  text-decoration: none;
  transition: color var(--t-fast) var(--ease-out);
}
.footer__col a:hover { color: var(--accent); }
.footer__bottom {
  margin-block-start: var(--sp-xl);
  padding-block-start: var(--sp-s);
  border-block-start: var(--hairline);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-xs) var(--sp-m);
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: var(--track-mono-sm);
  color: var(--bone-60);
}

/* --- 7.11 Misc ---------------------------------------------------------- */

/* scroll progress rail (motion.js drives [data-progress]) */
.progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 2px;
  z-index: var(--z-toast);
  background: var(--accent);
  pointer-events: none;
}

/* mono marquee/ticker strip — CVE-feed energy without the noise */
.strip {
  display: flex;
  gap: var(--sp-m);
  align-items: center;
  padding-block: var(--sp-xs);
  border-block: var(--hairline);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--text-muted);
  overflow-x: auto;
  scrollbar-width: none;
}
.strip::-webkit-scrollbar { display: none; }
.strip > * { flex: none; }

/* --------------------------------------------------------------------------
   8. MOTION — reduce
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
  /* anything motion.js hid pre-reveal must still be visible */
  [data-reveal], [data-split], .nav__menu[data-open='true'] {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
  }
  .btn:hover, .card--link:hover { transform: none; }
}

/* ==========================================================================
   8. PAGE PARTS — hero, brand mark, nav CTA, footer links
   Added after the system: these are the compositions, not the primitives.
   ========================================================================== */

/* --- 8.1 The hero --------------------------------------------------------
   Mobile: a single honest column.
   >=64em: a deliberate grid break — the name runs wide, the portrait hangs
   into its right shoulder, and the body text sits under the name's left edge.
   The overlap is the whole point; don't "fix" it back into two tidy columns. */

.hero {
  position: relative;
  padding-block-start: calc(var(--nav-h) + var(--sp-2xl));
  padding-block-end: var(--sp-3xl);
}

.hero__eyebrow { margin-block-end: var(--sp-m); }

.hero__title {
  margin-block-end: var(--sp-s);
  /* the name is the loudest thing on the site — let it breathe to the edges */
  max-inline-size: 14ch;
}

.hero__role {
  color: var(--accent);
  margin-block-end: var(--sp-l);
}

.hero__hook {
  font-family: var(--font-display);
  font-weight: var(--w-med);
  font-size: var(--step-2);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text);
  margin-block-start: var(--sp-xs);
  text-wrap: balance;
}

.hero__cta { margin-block-start: var(--sp-l); }

.hero__figure {
  margin-block-start: var(--sp-xl);
  margin-inline: 0;
}
.hero__figure img {
  inline-size: 100%;
  block-size: auto;
  border-radius: var(--r-l);
  border: var(--hairline);
  /* the stage photo is very dark; lift it just enough to read on ink */
  filter: contrast(1.04) brightness(1.06);
}

@media (min-width: 64em) {
  .hero .container {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--grid-gap);
    align-items: start;
  }
  .hero__eyebrow { grid-column: 1 / -1; }

  .hero__title  { grid-column: 1 / 9;  max-inline-size: none; }
  .hero__role   { grid-column: 1 / 8; }
  .hero__body   { grid-column: 1 / 7; }

  /* the grid break: pull the portrait up into the headline's shoulder */
  .hero__figure {
    grid-column: 8 / -1;
    grid-row: 2 / span 3;
    margin-block-start: calc(var(--sp-xl) * -1);
    z-index: 1;
  }
}

@media (min-width: 80em) {
  .hero__figure { grid-column: 9 / -1; }
}

/* --- 8.2 Brand mark ------------------------------------------------------ */

.brand-mark,
.nav__brand svg {
  flex: none;
  color: var(--logo);
  transition: color var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-out);
}
.nav__brand:hover svg { transform: translateY(-1px); }

/* --- 8.3 Nav CTA ---------------------------------------------------------
   Hidden on mobile: the hamburger sheet already carries the actions, and two
   competing CTAs in a 375px bar is how you get a 40px tap target. */
.nav__cta { display: none; }
@media (min-width: 56em) { .nav__cta { display: inline-flex; } }

/* --- 8.4 Footer links ---------------------------------------------------- */

.footer__links {
  list-style: none;
  margin: var(--sp-xs) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs, 0.375rem);
}
.footer__links a {
  display: inline-flex;
  align-items: center;
  min-block-size: 32px;
  color: var(--text-muted);
  text-decoration: none;
  transition: color var(--t-fast) var(--ease-out);
}
.footer__links a:hover { color: var(--accent); }

.footer__fine {
  margin-block-start: var(--sp-2xl);
  padding-block-start: var(--sp-m);
  border-block-start: var(--hairline);
  color: var(--text-muted);
}

/* --- 8.5 The pinned sequence --------------------------------------------
   [data-pin] is a tall scroll track. Its [data-pin-inner] sticks to the
   viewport while the track passes, and motion.js writes --p (0..1) on the
   track and toggles .is-active on each [data-step].
   No-JS / reduced-motion: every step is .is-active, the track collapses to
   auto height, and it reads as a plain numbered list. That is the point. */

.pin { position: relative; }

.pin__inner {
  position: sticky;
  inset-block-start: var(--nav-h);
  min-block-size: calc(100svh - var(--nav-h));
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: var(--sp-xl);
}

/* the track only needs its extra height when JS is actually scrubbing it */
.motion .pin { block-size: 320svh; }
.motion .pin__inner { block-size: calc(100svh - var(--nav-h)); }
html.reduce .pin,
html:not(.motion) .pin { block-size: auto; }
html.reduce .pin__inner,
html:not(.motion) .pin__inner { position: static; block-size: auto; min-block-size: 0; }

.pin__rail {
  block-size: var(--hair);
  background: var(--line);
  margin-block-end: var(--sp-l);
  position: relative;
  overflow: hidden;
}
.pin__rail::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform: scaleX(var(--p, 0));
  transform-origin: 0 50%;
}
html.reduce .pin__rail::after,
html:not(.motion) .pin__rail::after { transform: scaleX(1); }

.pin__steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-m);
}

.step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-s);
  align-items: baseline;
  opacity: 0.28;
  transform: translate3d(0, 0, 0);
  transition: opacity var(--t-slow) var(--ease-out), transform var(--t-slow) var(--ease-out);
}
.step.is-active { opacity: 1; }
.step__num {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: var(--w-bold);
  letter-spacing: var(--track-mono);
  color: var(--bone-60);
  transition: color var(--t-base) var(--ease-out);
}
.step.is-active .step__num { color: var(--accent); }
.step__title {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--w-med);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0 0 var(--sp-3xs, 0.375rem);
}
.step__body { color: var(--text-muted); margin: 0; }

/* every step is lit when there's no scrubbing to light them */
html.reduce .step,
html:not(.motion) .step { opacity: 1; }
html.reduce .step__num,
html:not(.motion) .step__num { color: var(--accent); }

@media (min-width: 56em) {
  .pin__steps { gap: var(--sp-l); }
  .step { grid-template-columns: 4rem minmax(0, 1fr); }
}

/* --- 8.6 Story masthead --------------------------------------------------- */
.story__head { padding-block-start: calc(var(--nav-h) + var(--sp-2xl)); }
.story__head .lead { margin-block-start: var(--sp-m); }
.story__head .meta { margin-block-start: var(--sp-s); }

/* ==========================================================================
   9. FIXES — found by driving the real page, not by reading the CSS
   ========================================================================== */

/* 9.1 The mobile sheet covered the nav, which meant it covered its own close
   button: once open, the only ways out were Escape or picking a link. The bar
   has to sit ABOVE the sheet. (The sheet already pads its content down by
   --nav-h, so nothing hides underneath.) */
.nav { z-index: calc(var(--z-menu) + 1); }

/* keep the bar legible against the opaque sheet behind it */
.nav:has(+ .nav__menu[data-open='true']) { background: transparent; backdrop-filter: none; border-block-end-color: transparent; }

/* 9.2 `.nav__menu a` was styling EVERY link in the sheet — including the
   LinkedIn button in the footer, which came out as a 40px display-face word
   in a pill. Scope the big editorial type to the list only. */
.nav__menu-foot a.btn {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--w-med);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  min-block-size: 48px;
  justify-content: center;
}
.nav__menu-foot a.btn::after { content: none; }

/* 9.3 Four principle cards in an auto-fit grid broke 3 + 1 orphan on desktop.
   Four items want a 2x2. */
@media (min-width: 60em) {
  #principles .grid-auto { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 9.4 The pinned sequence overflowed the pinned viewport: 5 steps at the global
   type scale come to ~940px of content in an ~820px well, so step 05 was cut
   off — and a pinned section is the one place you cannot scroll to see the rest.
   Size the sequence off viewport HEIGHT (vh) rather than width, so it always
   fits the well it's pinned into, on any screen. */
.pin__steps { gap: clamp(0.75rem, 0.1rem + 1.7vh, 2rem); }

.pin .step__title {
  font-size: clamp(1.05rem, 0.55rem + 1.5vh, 1.7rem);
  margin-block-end: clamp(0.15rem, 0.4vh, 0.5rem);
}
.pin .step__body {
  font-size: clamp(0.85rem, 0.62rem + 0.75vh, 1.05rem);
  line-height: 1.45;
  max-inline-size: 60ch;
}
.pin__rail { margin-block-end: clamp(1rem, 2.5vh, 2.5rem); }
.pin__inner { padding-block: clamp(1rem, 3vh, 3rem); }

/* short landscape screens: drop the bodies of the inactive steps entirely rather
   than clipping the list. The active step always keeps its explanation. */
@media (max-height: 640px) and (min-width: 56em) {
  .pin .step:not(.is-active) .step__body { display: none; }
}

/* 9.5 The inactive pin steps were opacity .28 — which computes to a 1.5:1
   contrast ratio against the ink. That isn't "dimmed", it's unreadable, and it
   fails WCAG AA outright (Lighthouse a11y 96).
   Keep the same read — one step forward, the rest receded — but buy the
   hierarchy with an accent marker and weight instead of near-invisible text.
   .62 measures ~5.6:1 on bone/ink, comfortably AA for body copy. */
.step {
  opacity: 0.62;
  padding-inline-start: var(--sp-s);
  border-inline-start: 2px solid transparent;
  transition: opacity var(--t-slow) var(--ease-out),
              border-color var(--t-slow) var(--ease-out),
              transform var(--t-slow) var(--ease-out);
}
.step.is-active {
  opacity: 1;
  border-inline-start-color: var(--accent);
}
@media (min-width: 56em) {
  .step.is-active { transform: translate3d(var(--sp-2xs), 0, 0); }
}

/* 9.6 ...and the .62 was compounding: the step number and body were ALREADY set
   to muted tones, so the container opacity dimmed a dim colour (3.14:1, still a
   fail). Base them on full bone and let the one opacity do all the dimming —
   .62 of bone on ink measures ~6.9:1. The active step still reads as active via
   the azure number (6.9:1) and the accent rule. */
.step__num,
.step__body { color: var(--text); }
.step.is-active .step__num { color: var(--accent); }

/* 9.7 .row--stack is a 12-column grid that places its children by .row__head /
   .row__body / .row__aside. An untagged child auto-places into a SINGLE column —
   i.e. 1/12 of the width — which shatters the text into one word per line rather
   than failing loudly. (That shipped. It's how the Speaking section broke.)
   Make the degradation safe: anything untagged spans the full row. */
@media (min-width: 62em) {
  .row--stack > *:not([class*='row__']) { grid-column: 1 / -1; }
}

/* ==========================================================================
   10. POLISH PASS — from the four-critic audit (rhythm / composition /
   surface / GEO). Each block names the finding it answers.
   ========================================================================== */

/* 10.1 Head-stack rhythm. Outside .prose the eyebrow→headline→lede→content
   stack had 0-2.3px of separation — the reset ate everything and only .prose
   restored it. One global system, same tokens everywhere. */
.eyebrow + :is(h1, h2, h3, .h2, .h3, .display) { margin-block-start: var(--sp-s); }
:is(h1, h2, .h2, .display) + .lead { margin-block-start: var(--sp-m); }
.lead + :is(p, .data-list, .grid, .grid-auto, .cluster) { margin-block-start: var(--sp-l); }
:is(h2, .h2) + :is(.grid, .grid-auto) { margin-block-start: var(--sp-xl); }
/* long-form had the inversion: 43px above its own headline, 19px below it */
.prose .eyebrow + h2,
.prose .eyebrow + h3 { margin-block-start: var(--sp-s); }
/* the hero used a third value for the same element — unify on the system */
.hero__eyebrow { margin-block-end: var(--sp-s); }

/* 10.2 The pull-quote was a pixel-clone of the chapter h2 (same size, same
   tracking) — hierarchy collapsed exactly where the editorial move should
   sing. Demote it to an aside that breaks the column, not a rival headline. */
.pullquote { font-size: var(--step-3); line-height: 1.15; max-inline-size: 28ch; }
.pullquote em,
blockquote em { font-style: normal; color: var(--accent-2); }
body { font-synthesis: none; }   /* Clash has no italic; never fake one */

/* 10.3 .measure (65ch) silently overrode .lead's own 48ch cap — ledes were
   running ~95 characters per line. Ledes keep the short measure. */
.lead.measure { max-inline-size: var(--measure-short); }

/* 10.4 Story chapters fell off a cliff: 54px display straight into 18px grey
   body. Chapter openers get the same lede tier the homepage has. */
.prose h2 + p {
  font-size: var(--step-1);
  line-height: 1.5;
  font-weight: var(--w-light);
  color: var(--text);
  letter-spacing: -0.012em;
}

/* 10.5 Sub-1.0 leading is a poster setting for 3.5rem+; at the mobile clamp
   floor it left ascenders brushing descenders. Size-aware leading. */
@media (max-width: 47.99em) {
  .h2, h2 { line-height: 1.06; }
  .display, h1 { line-height: 1.02; }
}

/* 10.6 The story title at step-7 ragged into a three-line staircase. One step
   down + balance; step-7 stays exclusive to the name on the homepage. */
.story__head .display--hero {
  font-size: var(--step-6);
  text-wrap: balance;
  max-inline-size: 16ch;
}

/* 10.7 Ledger optical alignment: the right column top-aligned with the tiny
   eyebrow instead of the headline's cap height. */
@media (min-width: 48em) {
  .row--ledger > :last-child { margin-block-start: calc(var(--step--2) + var(--sp-s)); }
}

/* 10.8 Card internals were flat: body copy at full-brightness bone competing
   with the titles, and the designed ghost numeral never wired up. */
.card > p:not([class]) { color: var(--text-muted); font-size: var(--step-0); }
.card--num::before {
  font-size: clamp(2.25rem, 3vw, 3.5rem);
  font-weight: 400;
  line-height: 1;
  color: rgb(var(--bone-rgb) / 0.10);
  inset-block-start: var(--sp-xs);
  inset-inline-end: var(--sp-xs);
}

/* 10.9 Surface modulation was imperceptible (~1% luminance delta) and the
   section padding stacked into 400px voids. Deepen the sunken tone, edge the
   bands, tighten the rhythm. */
:root { --bg-sunken: rgb(3 3 6 / 0.62); }
.section { padding-block: clamp(5rem, 4rem + 3.5vw, 8rem); }
.section--act { padding-block: clamp(6rem, 5rem + 5vw, 11rem); }
.section--sunken {
  border-block: var(--hairline);
  box-shadow: inset 0 1px 0 rgb(var(--bone-rgb) / 0.03);
}

/* 10.10 Mono tracking adds a phantom space after the last glyph, so every
   pill label sat ~1px left of optical centre. Absorb it on the right. */
.btn { padding-inline: var(--sp-m) calc(var(--sp-m) - 0.14em); }
.btn--sm { padding-inline: var(--sp-s) calc(var(--sp-s) - 0.14em); }
.btn--text { padding-inline: 0; }

/* 10.11 Two azure mono lines sandwiched the hero headline — the single accent
   spent twice in 300px. The role recedes; one hot word remains. */
.hero__role { color: var(--text-muted); }
.hero__role .hot { color: var(--accent); }

/* 10.12 The hero portrait hugged the top-right and left the hero's lower
   third uncomposed. Commit the column: full-height editorial split. */
@media (min-width: 64em) {
  .hero__figure { align-self: stretch; margin-block-start: calc(var(--sp-xl) * -1); }
  .hero__figure picture { display: block; block-size: 100%; }
  .hero__figure img { block-size: 100%; object-fit: cover; }
}

/* 10.13 Full-bleed figure captions floated unanchored at the viewport edge.
   Tie them to a rule and give the line two ends. */
.figure.bleed figcaption,
.figure.full-bleed figcaption {
  border-block-start: var(--hairline);
  padding-block-start: var(--sp-2xs);
  margin-inline: var(--gutter);
  justify-content: space-between;
}

/* 10.14 The product screenshots pre-date the current brand tones. Grade them
   into the system rather than letting raw dashboard chrome shout. */
.figure--frame img { filter: saturate(0.72) contrast(1.05) brightness(0.97); }

/* 10.15 Story chapters: an oversized mono numeral hangs in the margin as the
   eye's anchor — the 72%-empty canvas gets a landmark per chapter. */
.chapter .container--narrow { position: relative; }
.chapter-num {
  position: absolute;
  inset-block-start: -0.08em;
  inset-inline-start: -16vw;
  font-family: var(--font-mono);
  font-size: clamp(4rem, 8vw, 8rem);
  font-weight: 400;
  line-height: 1;
  color: rgb(var(--bone-rgb) / 0.05);
  pointer-events: none;
  user-select: none;
}
@media (max-width: 63.99em) { .chapter-num { display: none; } }

/* 10.16 Contact: the email becomes the hero object of the closing section. */
.contact-mail {
  display: inline-block;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 4.5vw, 4rem);
  font-weight: var(--w-light);
  letter-spacing: -0.03em;
  line-height: 1.1;
  color: var(--text);
  text-decoration: none;
  border-block-end: 2px solid var(--accent);
  padding-block-end: 0.1em;
  overflow-wrap: anywhere;
  transition: color var(--t-base) var(--ease-out);
}
.contact-mail:hover,
.contact-mail:focus-visible { color: var(--accent); }

/* 10.17 The pinned sequence left 40% of the viewport black for three
   viewport-heights. Two-column frame: steps left, a graded product still
   right, drifting subtly with the scrub via the pin's own --p. */
@media (min-width: 64em) {
  .pin__grid {
    display: grid;
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
    column-gap: calc(var(--grid-gap) * 2);
    align-items: center;
  }
  .pin__fig {
    transform: translateY(calc((0.5 - var(--p, 0.5)) * 3rem));
    will-change: transform;
  }
}
@media (max-width: 63.99em) { .pin__fig { display: none; } }

/* 10.18 Vikelus hero: bind the empty centre-bottom with a baseline strip. */
.hero-foot {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-m);
  flex-wrap: wrap;
  border-block-start: var(--hairline);
  margin-block-start: var(--sp-xl);
  padding-block-start: var(--sp-xs);
}

/* 10.19 Footer craft: the CTA the CSS always defined finally renders; links
   join the mono layer; the fine-print line gets two ends. */
.footer__links a {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: var(--track-mono-sm);
}
.footer__fine {
  max-inline-size: none;
  display: flex;
  justify-content: space-between;
  gap: var(--sp-m);
  flex-wrap: wrap;
}

/* 10.20 Ledger values may wrap instead of overflowing their dotted leader. */
.data-row__val { flex: 0 1 auto; min-inline-size: 0; }

/* 10.21 The ticker strip: duplicate track, slow drift; still a plain
   scrollable row under reduced motion or no-JS. */
.strip { overflow: hidden; }
.strip__track {
  display: flex;
  gap: var(--sp-l);
  flex: none;
  min-inline-size: 100%;
  justify-content: space-around;
  animation: strip-drift 48s linear infinite;
}
@keyframes strip-drift { to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) {
  .strip__track { animation: none; }
  .strip__track + .strip__track { display: none; }
}

/* 10.22 Visible FAQ — answer engines cite visible text, not hidden JSON-LD. */
.faq-item { padding-block: var(--sp-m); border-block-end: var(--hairline); }
.faq-item:first-of-type { border-block-start: var(--hairline); }
.faq-item dt {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--w-med);
  letter-spacing: -0.02em;
}
.faq-item dd { margin: var(--sp-2xs) 0 0; color: var(--text-muted); max-inline-size: var(--measure); }

/* 10.23 The hero figure is now a flex column (image + caption): the picture
   takes the stretch, the caption keeps its own height. */
@media (min-width: 64em) {
  .hero__figure picture { flex: 1; min-block-size: 0; }
}
/* the hero portrait keeps its lift — it must NOT inherit the screenshot grade */
.hero__figure img { filter: contrast(1.04) brightness(1.06); }

/* the pin figure's caption reads as a live annotation, not a Fig. plate */
.pin__fig figcaption::before { color: var(--accent-2); }

/* 10.24 The pin's report still: crop the print-dialog banner baked into the
   capture's top edge, and sink the white page into the scene so it reads as a
   lit document, not a hole in the dark. */
.pin__fig img {
  clip-path: inset(4.6% 0 0 0);
  margin-block-start: -4.3%;
  filter: saturate(0.78) contrast(1.02) brightness(0.86);
}

/* ==========================================================================
   11. LEO'S ROUND — 12 Jul afternoon feedback
   ========================================================================== */

/* 11.1 Captions retired site-wide (Leo: "remove the text under each image").
   The elements are gone from the HTML; this guards any strays. */
.figure figcaption { display: none; }

/* 11.2 The rule cards: icon, lift, and a bling sweep on hover.
   data-tilt is OFF these cards now — the JS transform would fight the lift. */
.card__icon {
  inline-size: 30px;
  block-size: 30px;
  color: var(--accent);
  flex: none;
}
.card--num {
  overflow: hidden;
  transition: transform var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out),
              box-shadow var(--t-base) var(--ease-out);
}
/* the bling: a light blade that sweeps the card once per hover */
.card--num::after {
  content: '';
  position: absolute;
  inset-block: -40%;
  inset-inline-start: -30%;
  inline-size: 34%;
  transform: rotate(22deg) translateX(-320%);
  background: linear-gradient(90deg,
    transparent,
    rgb(var(--bone-rgb) / 0.06) 40%,
    rgb(var(--accent-rgb) / 0.14) 55%,
    transparent);
  pointer-events: none;
}
.card--num:hover,
.card--num:focus-within {
  transform: translateY(-6px);
  border-color: rgb(var(--accent-rgb) / 0.5);
  box-shadow:
    0 18px 44px rgb(0 0 0 / 0.45),
    0 0 0 1px rgb(var(--accent-rgb) / 0.22),
    0 0 36px rgb(var(--accent-rgb) / 0.13);
}
.card--num:hover::after { animation: card-bling 0.85s var(--ease-out); }
@keyframes card-bling { to { transform: rotate(22deg) translateX(560%); } }

/* 11.3 The product shot is a LINK to vikelus.com: contained, framed, and it
   announces itself on hover instead of with a caption. */
.vk-shot {
  display: block;
  position: relative;
  max-inline-size: 56rem;
  margin: var(--sp-xl) auto 0;
  border: var(--hairline);
  border-radius: var(--r-l);
  overflow: hidden;
  transition: transform var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out),
              box-shadow var(--t-base) var(--ease-out);
}
.vk-shot img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  /* the dashboard already wears the real product brand — no grade */
  filter: none;
}
.vk-shot::after {
  content: 'vikelus.com ↗';
  position: absolute;
  inset-block-end: var(--sp-xs);
  inset-inline-end: var(--sp-xs);
  padding: 0.45em 0.9em;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink);
  background: var(--accent);
  border-radius: var(--r-pill);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-out);
}
.vk-shot:hover,
.vk-shot:focus-visible {
  transform: translateY(-4px);
  border-color: rgb(var(--accent-rgb) / 0.5);
  box-shadow: 0 20px 50px rgb(0 0 0 / 0.5), 0 0 40px rgb(var(--accent-rgb) / 0.12);
}
.vk-shot:hover::after,
.vk-shot:focus-visible::after { opacity: 1; transform: none; }

/* 11.4 People photos were shipping full-bleed at viewport width — "why is the
   image so big". Contained editorial plates instead. */
.photo-plate {
  max-inline-size: 56rem;
  margin-inline: auto;
}
.photo-plate img {
  inline-size: 100%;
  block-size: auto;
  max-block-size: 34rem;
  object-fit: cover;
  border-radius: var(--r-l);
  border: var(--hairline);
}

/* 11.5 Spacing round (Leo): images must sit on a column's axis, not float
   between two. Inside the prose column the shot takes the column's width and
   rhythm; the speaking plate joins the headline's left axis with real air. */
.prose .vk-shot {
  max-inline-size: none;
  margin: var(--sp-l) 0;
}
.photo-plate { margin-block-start: var(--sp-xl); }
.photo-plate--start {
  margin-inline: 0;
  margin-block-start: var(--sp-2xl);
}

/* 11.6 The display-size email was breaking mid-word ("…@icloud.c/om") and
   overrunning its ledger column. Size it to the column, never split a word. */
.contact-mail {
  font-size: clamp(1.4rem, 0.55rem + 2.5vw, 2.85rem);
  overflow-wrap: normal;
  word-break: keep-all;
  max-inline-size: 100%;
}

/* 11.7 Two stacked plain sections were pooling up to ~26rem of empty field
   between their content ("fix the spacing of the entire page"). Any section
   that follows another gives back half its top padding. */
.section + .section { padding-block-start: clamp(2.5rem, 2rem + 2vw, 4.5rem); }

/* 11.8 Story chapters: an icon leads each eyebrow, and the watermark numeral
   drifts on scroll (data-parallax) so the margin feels alive. */
.chapter-icon {
  display: block;
  inline-size: 30px;
  block-size: 30px;
  color: var(--accent);
  margin-block-end: var(--sp-xs);
}

/* 11.9 `.footer__col a` (mono, small) outspecifies `.footer__cta`, so the
   footer's big sign-off rendered as a tiny mono link. Reassert the CTA. */
.footer__col a.footer__cta {
  min-block-size: 0;
  display: inline-block;
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: var(--w-light);
  letter-spacing: -0.035em;
  line-height: 1.05;
  text-transform: none;
}
