/* ==========================================================================
   Invisible interface, loud surface.

   Grid     16 columns, stretch. Margin 120px, gutter 24px (desktop).
            Tablet and phone keep all 16 columns and scale margin and gutter.
   Spacing  8px base unit. Every margin, padding and gap is a multiple of 8.
   Type     8px baseline. Every line-height is a multiple of 8.
            Row rules are drawn with inset shadows so they never add a pixel.

   Add ?grid to the URL to see the columns and the baseline.
   ========================================================================== */

/* ---------- Tokens ---------- */
:root {
  /* Surface: warm near-black */
  --ink: #0f0e0c;
  --ink-2: #161411;
  --ink-3: #1f1d19;
  --line: #2c2a25;

  /* Text: off-white */
  --paper: #ece7dc;
  --paper-rgb: 236 231 220;
  --accent-text: var(--acid);
  --press-face: #ece7dc;
  --paper-dim: #a39d90;   /* 7.4:1 on --ink */
  --paper-mute: #8a8479;  /* 5.2:1 on --ink */

  /* The one accent */
  --acid: #c6f432;
  --on-acid: #0f0e0c;

  /* Type families */
  --f-display: "Bricolage Grotesque", "Arial Narrow", system-ui, sans-serif;
  --f-body: "Geist", ui-sans-serif, system-ui, sans-serif;
  --f-mono: var(--f-body);

  /* Motion */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --press: 90ms;   /* hover and press feedback: always under 100ms */

  /* 8px scale */
  --s1: 8px;
  --s2: 16px;
  --s3: 24px;
  --s4: 32px;
  --s5: 40px;
  --s6: 48px;
  --s8: 64px;
  --s10: 80px;
  --s12: 96px;
  --s15: 120px;
  --s20: 160px;
  --s30: 240px;

  /* Grid: phone */
  --cols: 16;
  --margin: 24px;
  --gutter: 8px;

  /* Rhythm: phone */
  --section-y: var(--s15);
  --block-y: var(--s6);
  --stack: var(--s8);

  /* Type scale (size / line-height): phone.
     One scale. Each step is 1.2 to 1.5x the one below, so nothing jumps. */
  --label: 12px;      --label-lh: 16px;
  --text: 16px;       --text-lh: 24px;
  --passage: 24px;    --passage-lh: 32px;
  --principle: 24px;  --principle-lh: 32px;
  --chapter: 28px;    --chapter-lh: 32px;
  --mail: 28px;       --mail-lh: 32px;
  --h2: 32px;         --h2-lh: 40px;
  --intro: 32px;      --intro-lh: 40px;
  --project: 32px;    --project-lh: 40px;
  --next: 32px;       --next-lh: 40px;
  --h1: 40px;         --h1-lh: 48px;
  --hero: 48px;       --hero-lh: 48px;

  --rail-h: 240px;

  /* Grain tiles */
  /* Grain tiles. Light and dark specks with their own alpha, so they need no blend mode.
     Rasterised once by the browser; nothing redraws them. */
  --grain-light: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 3 0 0 0 -1.65'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  --grain-dark: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' seed='7' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -3 0 0 0 1.35'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  --grain-surface: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='1.35' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 2.6 0 0 0 -1.35'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");

  color-scheme: dark;

  /* The nav capsule: dark glass on ink, pale glass on paper */
  --capsule: rgb(23 22 20 / 0.72);
  --capsule-edge: var(--line);
}

:root[data-theme="light"] {
  --ink: #fafaf8;
  --ink-2: #f0f1ed;
  --ink-3: #e7e9e2;
  --line: #cdd0c7;
  --paper: #20221d;
  --paper-rgb: 32 34 29;
  --paper-dim: #51564a;
  --paper-mute: #646a5c;
  --accent-text: #4b6500;
  --capsule: rgb(250 250 248 / 0.8);
  --capsule-edge: #c4c8bc;
  color-scheme: light;
}

@media (min-width: 768px) {
  :root {
    --margin: 48px;
    --gutter: 16px;

    --section-y: var(--s20);
    --block-y: var(--s8);
    --stack: var(--s10);

    --label: 14px;      --label-lh: 24px;
    --text: 16px;       --text-lh: 24px;
    --passage: 28px;    --passage-lh: 40px;
    --principle: 28px;  --principle-lh: 32px;
    --chapter: 32px;    --chapter-lh: 40px;
    --mail: 48px;       --mail-lh: 56px;
    --h2: 40px;         --h2-lh: 48px;
    --intro: 40px;      --intro-lh: 48px;
    --project: 40px;    --project-lh: 48px;
    --next: 48px;       --next-lh: 56px;
    --h1: 56px;         --h1-lh: 64px;
    --hero: 72px;       --hero-lh: 72px;

    --rail-h: 320px;
  }
}

@media (min-width: 1280px) {
  :root {
    --margin: 120px;
    --gutter: 24px;

    --section-y: var(--s30);
    --block-y: var(--s12);
    --stack: var(--s15);

    --label: 14px;      --label-lh: 24px;
    --text: 18px;       --text-lh: 32px;
    --passage: 32px;    --passage-lh: 40px;
    --principle: 32px;  --principle-lh: 40px;
    --chapter: 40px;    --chapter-lh: 48px;
    --mail: 64px;       --mail-lh: 72px;
    --h2: 48px;         --h2-lh: 56px;
    --intro: 48px;      --intro-lh: 56px;
    --project: 48px;    --project-lh: 56px;
    --next: 64px;       --next-lh: 72px;
    --h1: 72px;         --h1-lh: 80px;
    --hero: 96px;       --hero-lh: 96px;

    --rail-h: 400px;
  }
}

/* ---------- Fonts (self-hosted, preloaded in each page's head) ----------
   font-display: block with a preload means no fallback text is ever shown,
   so nothing reflows when the real fonts arrive. */
@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 75% 100%;
  font-display: block;
  src: url("../fonts/bricolage-grotesque-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: "Geist";
  font-style: normal;
  font-weight: 400 500;
  font-display: block;
  src: url("../fonts/geist-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;
}

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  background: var(--ink);
}

/* Lenis */
html.lenis, html.lenis body { height: auto; }
.lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
.lenis.lenis-smooth iframe { pointer-events: none; }

body {
  margin: 0;
  min-height: 100svh;
  min-height: round(down, 100svh, 8px);
  background: var(--ink);
  color: var(--paper);
  font-family: var(--f-body);
  font-size: var(--text);
  line-height: var(--text-lh);
  font-feature-settings: "ss01", "cv11";
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

img, svg, video, canvas { display: block; max-width: 100%; }

::selection { background: var(--acid); color: var(--on-acid); }

h1, h2, h3, p, ol, ul, dl, dd, figure { margin: 0; }
ol, ul { padding: 0; list-style: none; }

a {
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* ---------- Focus ---------- */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 8px;
  border-radius: 2px;
}
.link:focus-visible { outline-offset: 0; }
[tabindex="-1"]:focus-visible { outline: none; }

.skip {
  position: fixed;
  top: var(--s2);
  left: var(--s2);
  z-index: 100;
  padding: var(--s2);
  border: 1px solid rgb(15 14 12 / 0.28);
  border-radius: 999px;
  background: var(--acid);
  color: var(--on-acid);
  font: 500 var(--label)/16px var(--f-mono);
  transform: translateY(-200%);
  transition: transform var(--press) var(--ease);
}
.skip:focus-visible { transform: none; outline-color: var(--paper); }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Texture ---------- */
/* The page grain is static: drawn once, never moved or redrawn. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
  background-image: var(--grain-light), var(--grain-dark);
  background-size: 240px 240px, 240px 240px;
  background-position: 0 0, 120px 80px;
  opacity: 0.16;
}

/* The one exception: the opening has living grain. Its tile only moves (transform),
   in steps, and it pauses once the opening is off screen (scripts/main.js). */
.opening-grain {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
.opening-grain::before {
  content: "";
  position: absolute;
  inset: -50%;
  background-image: var(--grain-light);
  background-size: 240px 240px;
  opacity: 0.22;
  animation: grain 0.9s steps(6) infinite;
  will-change: transform;
}
.opening-grain.is-paused::before { animation-play-state: paused; }
[data-theme="light"] .opening-grain::before { background-image: var(--grain-dark); }

@keyframes grain {
  0%   { transform: translate(0, 0); }
  17%  { transform: translate(-3%, 2%); }
  33%  { transform: translate(2%, -4%); }
  50%  { transform: translate(-4%, -1%); }
  67%  { transform: translate(3%, 3%); }
  83%  { transform: translate(-1%, 4%); }
  100% { transform: translate(0, 0); }
}

/* Panels get a finer, heavier grain than the page (static too) */
.surface,
.rail-on .principle {
  position: relative;
  background: var(--ink-2);
  isolation: isolate;
}
.surface::before,
.rail-on .principle::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--grain-surface);
  background-size: 160px 160px;
  opacity: 0.4;
  pointer-events: none;
}

/* ---------- Light ---------- */
/* One off-center pool per section. Nothing is evenly lit. */
.section {
  position: relative;
  isolation: isolate;
  padding: var(--section-y) var(--margin);
}
.section::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: var(--pool-x, 20%);
  top: var(--pool-y, 30%);
  width: var(--pool-size, 90vmax);
  aspect-ratio: 1;
  translate: -50% -50%;
  background: radial-gradient(closest-side, rgb(var(--paper-rgb) / var(--pool-a, 0.055)), rgb(var(--paper-rgb) / 0) 100%);
  pointer-events: none;
}

/* Cursor light, desktop only (switched on by JS) */
.lamp {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 1;
  width: 560px;
  height: 560px;
  margin: -280px 0 0 -280px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(var(--paper-rgb) / 0.065), rgb(var(--paper-rgb) / 0.02) 55%, rgb(var(--paper-rgb) / 0) 100%);
  pointer-events: none;
  opacity: 0;
  transition: opacity 400ms var(--ease);
  will-change: transform;
}
.lamp.is-on { opacity: 1; }

/* The pools may not stretch the page */
main { position: relative; z-index: 2; overflow: clip; }

/* ---------- Motion starting state (Motion does the rest) ---------- */
.js .reveal {
  opacity: 0;
  transform: translate3d(0, 24px, 0);
}
.js.no-motion-lib .reveal { opacity: 1; transform: none; }

/* ---------- Grid ---------- */
.grid {
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  column-gap: var(--gutter);
}

/* Phone placements keep the desktop asymmetry: labels on the margin, text indented. */
.aside { grid-column: 1 / -1; margin-bottom: var(--s4); }
.body  { grid-column: 3 / -1; }

/* ---------- Type ---------- */
.display {
  font-family: var(--f-display);
  font-weight: 700;
  font-stretch: 80%;
  font-optical-sizing: auto;
  letter-spacing: 0;
  text-wrap: balance;
}

.label {
  display: flex;
  gap: var(--s2);
  align-items: baseline;
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--paper-mute);
}
.label b { font-weight: 500; color: var(--paper-dim); }

/* Placeholders. Remove the span once the text is real.
   Outline instead of border, so it never moves the baseline. */
.todo {
  font-family: var(--f-mono);
  font-size: var(--label);
  font-weight: 400;
  font-stretch: 100%;
  letter-spacing: 0;
  vertical-align: top;  /* a mixed font on the baseline would make the line taller than its line-height */
  color: var(--paper-mute);
  outline: 1px dashed var(--line);
  outline-offset: -1px;
  padding: 0 var(--s1);
  border-radius: 2px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.display .todo,
.passage .todo {
  display: inline-block;
  vertical-align: top;
  max-width: 100%;
  font-size: var(--text);
  line-height: var(--text-lh);
}

/* The highlighter */
.hl {
  position: relative;
  color: var(--on-acid);
  white-space: nowrap;
  z-index: 0;
}
.hl::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0.12em -0.06em 0.02em -0.06em;
  background: var(--acid);
  transform-origin: 0 50%;
}
.js .hl::before {
  transform: scaleX(0);
  transition: transform 450ms var(--ease) 200ms;
}
.js .is-in .hl::before { transform: none; }
.js .hl { color: var(--paper); transition: color 0s linear 380ms; } /* a switch, not an animation */
.js .is-in .hl { color: var(--on-acid); }

/* ---------- Links and controls ---------- */
/* Every hit area is at least 48px tall */
.link {
  --link-pad: var(--s2);
  --arrow-size: var(--s3);
  --arrow-gap: var(--s1);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0;
  min-height: var(--s6);
  padding: 0 var(--link-pad);
  border: 1px solid rgb(var(--paper-rgb) / 0.18);
  border-radius: 999px;
  background-image:
    var(--grain-surface),
    linear-gradient(180deg, rgb(var(--paper-rgb) / 0.055), rgb(var(--paper-rgb) / 0.015));
  background-color: var(--ink);
  background-size: 160px 160px, auto;
  box-shadow:
    0 1px 0 rgb(236 231 220 / 0.08) inset,
    0 -1px 0 rgb(0 0 0 / 0.38) inset;
  color: var(--paper);
  transition:
    transform var(--press) var(--ease),
    color var(--press) var(--ease),
    border-color var(--press) var(--ease),
    background-color var(--press) var(--ease);
  isolation: isolate;
  overflow: hidden;
}
.link:hover {
  border-color: rgb(198 244 50 / 0.72);
  color: var(--paper);
  background-color: var(--ink-2);
}
.link:active { transform: translateY(2px); }
.link:focus-visible { border-color: var(--acid); }
.link .arrow {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  width: 0;
  height: var(--arrow-size);
  margin-inline: 0;
  overflow: hidden;
  border-radius: 50%;
  color: var(--accent-text);
  background: rgb(198 244 50 / 0.08);
  opacity: 0;
  transition:
    width 180ms var(--ease),
    margin-inline-start 180ms var(--ease),
    opacity var(--press) var(--ease);
}
.link:hover .arrow,
.link:focus-visible .arrow {
  width: var(--arrow-size);
  margin-inline-start: var(--arrow-gap);
  opacity: 1;
}
.link--quiet { color: var(--paper-dim); }
.link--quiet:hover { color: var(--paper); }
.end .link,
.side .more .link {
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  letter-spacing: 0.04em;
}
.side .more .link {
  --arrow-gap: var(--s3);
  border-radius: 4px;
  border-color: rgb(var(--paper-rgb) / 0.24);
  background: transparent;
  box-shadow: none;
  font: 400 16px/24px var(--f-body);
  letter-spacing: 0;
  color: var(--paper);
}
.side .more .link .arrow {
  border-radius: 0;
  background: none;
}
.side .more .link:hover {
  border-color: rgb(198 244 50 / 0.5);
  background: transparent;
  color: var(--accent-text);
}

/* ---------- Top bar ---------- */
.bar {
  position: fixed;
  top: var(--s3);
  left: var(--margin);
  right: var(--margin);
  z-index: 20;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s2);
  min-height: var(--s8);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  transition: opacity 180ms var(--ease);
  pointer-events: none; /* the page shows through the gap; only the two pieces take clicks */
}
.bar > * { pointer-events: auto; }
.bar.is-muted { opacity: 0.95; }
.bar.is-muted:hover,
.bar.is-muted:focus-within { opacity: 1; }
.bar-brand {
  position: relative;
  display: grid;
  align-content: center;
  gap: 4px;
  min-height: var(--s6);
  padding-left: var(--s2);
  color: var(--paper);
}
.bar-brand::before {
  content: "";
  position: absolute;
  left: 0;
  top: var(--s1);
  bottom: var(--s1);
  width: 3px;
  background: var(--acid);
}
.bar-brand__name {
  font: 600 18px/24px var(--f-display);
  letter-spacing: 0;
}
.bar-brand__role {
  font-size: 13px;
  line-height: 24px;
  color: var(--paper-mute);
  letter-spacing: 0;
}
.bar-brand:hover .bar-brand__name { color: var(--accent-text); }
/* The links live in one floating capsule, not in a bar across the screen */
.bar-nav {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s1);
  padding: var(--s1) var(--s2);
  border: 1px solid var(--capsule-edge);
  border-radius: 999px;
  background: var(--capsule);
  backdrop-filter: blur(12px) saturate(120%);
  -webkit-backdrop-filter: blur(12px) saturate(120%);
  overflow: hidden;
}

/* The live index (scripts/nav.js): a track under the names, with the marker sitting on
   the section you are in and filling as you read it. It is a readout, not a control. */
.bar-nav__rail {
  position: absolute;
  left: var(--s2);
  right: var(--s2);
  bottom: 0;
  height: 2px;
  background: var(--line);
  pointer-events: none;
}
.bar-nav__marker {
  position: absolute;
  left: 0;
  top: 0;
  height: 2px;
  width: 1px;
  background: rgb(198 244 50 / 0.22);
  opacity: 0;
  transform-origin: 0 50%;
  transition:
    transform 450ms var(--ease),
    opacity 180ms var(--ease);
}
.bar-nav__fill {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--acid);
  transform-origin: 0 50%;
  transform: scaleX(0);
}
.bar-nav__marker.is-on { opacity: 1; }
.bar-nav__marker.is-hint { opacity: 0.5; }

/* The name of the section you are in says so */
.bar-nav__link[aria-current="true"] {
  color: var(--accent-text);
  background-color: rgb(198 244 50 / 0.07);
}
.bar-nav__link--contact[aria-current="true"] { background-color: rgb(198 244 50 / 0.14); }
.bar-nav__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--s5);
  padding-inline: var(--s2);
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--paper-dim);
  letter-spacing: 0;
  transition:
    color var(--press) var(--ease),
    background-color var(--press) var(--ease),
    border-color var(--press) var(--ease);
}
.bar-nav__link:hover { color: var(--paper); background-color: var(--ink-3); }
.bar-nav__link--contact {
  margin-left: var(--s1);
  border-color: rgb(198 244 50 / 0.3);
  background-color: rgb(198 244 50 / 0.08);
  color: var(--accent-text);
}
.bar-nav__link--contact:hover {
  border-color: var(--acid);
  background-color: var(--acid);
  color: var(--on-acid);
}
.bar-nav__link:active { background-color: var(--line); color: var(--paper); }

/* The theme control is a real light switch. In the dark it sits down, with its little
   locator glowing so you can find it. In the light it is up and the locator goes out.
   The rocker follows the press at once; the room follows behind it. */
.theme-toggle {
  --plate-hi: #5d594f;
  --plate-lo: #3b3830;
  --rocker-lit: #79746a;
  --rocker-mid: #514d44;
  --rocker-dark: #2c2a24;
  --plate-edge: rgb(0 0 0 / 0.6);
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: 6px;
  cursor: pointer;
  touch-action: manipulation;
}
[data-theme="light"] .theme-toggle {
  --plate-hi: #ffffff;
  --plate-lo: #e2e1d9;
  --rocker-lit: #ffffff;
  --rocker-mid: #f0efe9;
  --rocker-dark: #c9c7bd;
  --plate-edge: rgb(32 34 29 / 0.3);
}
.switch {
  position: relative;
  display: block;
  width: 24px;
  height: 36px;
  border-radius: 4px;
  background:
    radial-gradient(circle 1px at 50% 3px, rgb(0 0 0 / 0.45) 99%, transparent),
    radial-gradient(circle 1px at 50% calc(100% - 3px), rgb(0 0 0 / 0.45) 99%, transparent),
    linear-gradient(165deg, var(--plate-hi), var(--plate-lo));
  box-shadow:
    0 0 0 1px var(--plate-edge),
    0 2px 6px rgb(0 0 0 / 0.32),
    inset 0 1px 0 rgb(255 255 255 / 0.14);
  perspective: 44px;
  transition: filter 90ms cubic-bezier(0.16, 1, 0.3, 1);
}
.theme-toggle:hover .switch { filter: brightness(1.14); }
.switch__rocker {
  position: absolute;
  inset: 7px 5px;
  overflow: hidden;
  border-radius: 2px;
  /* Off: the top stands proud and catches the light, the bottom is pressed in */
  background: linear-gradient(180deg, var(--rocker-lit), var(--rocker-mid) 48%, var(--rocker-dark) 52%, var(--rocker-mid));
  box-shadow: 0 0 0 1px var(--plate-edge);
  transform: rotateX(-20deg);
  transition: transform 90ms cubic-bezier(0.16, 1, 0.3, 1);
}
.switch__rocker::before {
  /* On: the same rocker, the other way up */
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, var(--rocker-mid), var(--rocker-dark) 48%, var(--rocker-lit) 52%, var(--rocker-mid));
  opacity: 0;
  transition: opacity 90ms linear;
}
.switch__locator {
  position: absolute;
  left: 50%;
  bottom: 4px;
  width: 4px;
  height: 4px;
  margin-left: -2px;
  border-radius: 50%;
  background: #c6f432;
  box-shadow: 0 0 6px 1px rgb(198 244 50 / 0.85);
  transition: background-color 90ms linear, box-shadow 90ms linear;
}
.theme-toggle[aria-checked="true"] .switch__rocker { transform: rotateX(20deg); }
.theme-toggle[aria-checked="true"] .switch__rocker::before { opacity: 1; }
.theme-toggle[aria-checked="true"] .switch__locator { background: rgb(32 34 29 / 0.22); box-shadow: none; }
.theme-toggle:active .switch__rocker { transform: rotateX(0deg); }
/* Phones: the nav sits at the bottom, where a thumb can reach it.
   The name stays small at the top left. */
@media (max-width: 767px) {
  .bar {
    top: auto;
    bottom: calc(var(--s2) + env(safe-area-inset-bottom));
    min-height: 0;
    justify-content: center;
  }
  .bar-brand {
    position: fixed;
    top: var(--s2);
    left: var(--margin);
  }
  .bar-nav {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr)) var(--s5);
    width: 100%;
  }
  .bar-nav__link { padding-inline: var(--s1); }
  .bar-nav__link--contact { margin-left: 0; }

}

/* ---------- Opening ---------- */
.opening {
  --pool-x: 30%;
  --pool-y: 62%;
  --pool-size: 115vmax;
  --pool-a: 0.085;
  min-height: 100svh;
  min-height: round(down, 100svh, 8px);
  display: grid;
  grid-template-rows: 1fr auto;
  padding-top: var(--s20);
  padding-bottom: var(--s8);
}
.opening h1 {
  align-self: end;
  position: relative;
  top: calc(var(--s4) * -1);
  font-size: var(--hero);
  line-height: var(--hero-lh);
  max-width: 12ch;
}
.opening h1 .w { display: inline-block; }
.opening .foot {
  margin-top: var(--s6);
  align-items: end;
}
.opening .foot p {
  grid-column: 1 / -1;
  max-width: 42ch;
  color: var(--paper);
  font-size: var(--text);
  line-height: 24px;
}

/* The hero's two ways on: the work, or a conversation. Same capsule as the nav. */
.hero-cta {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s5);
}
/* The keys themselves live in styles/cta.css */
.cue {
  grid-column: 13 / -1;
  justify-self: end;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s1);
  min-width: var(--s6);
  min-height: var(--s10);
  padding: var(--s2) var(--s1);
  border: 1px solid rgb(236 231 220 / 0.18);
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgb(236 231 220 / 0.045), rgb(236 231 220 / 0)),
    rgb(15 14 12 / 0.72);
  box-shadow:
    0 1px 0 rgb(236 231 220 / 0.08) inset,
    0 16px 32px rgb(0 0 0 / 0.28);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper-mute);
  transition:
    transform var(--press) var(--ease),
    color var(--press) var(--ease),
    border-color var(--press) var(--ease);
}
.cue:hover {
  color: var(--paper);
  border-color: rgb(198 244 50 / 0.62);
}
.cue:active { transform: translateY(2px); }
.cue:focus-visible { border-color: var(--acid); }
.cue i {
  display: block;
  width: 1px;
  height: var(--s6);
  background: linear-gradient(var(--acid), var(--acid)) 0 0 / 1px 40% no-repeat;
  background-color: var(--line);
}

/* ---------- Opening scene: the fingerprint (scripts/print.js) ----------
   Four depth layers of ridges, off to the right, behind the headline. */
.print {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: clip;
  pointer-events: none;
}
.print__layer,
.print-clear {
  top: 50%;
  right: -32%;
  width: 150vw;
  aspect-ratio: 1;
  translate: 0 -50%;  /* centring; transform is left free for the scroll parallax */
  transform-origin: 50% 54%;
  will-change: transform, opacity;
}
.print__layer { position: absolute; }
.print__layer svg {
  display: block;
  width: 100%;
  height: 100%;
}
.print__ridges {
  fill: none;
  stroke: var(--paper);
  stroke-linecap: round;
  stroke-width: 1.6;
  vector-effect: non-scaling-stroke;
}
.print__ridges--1 { stroke-opacity: 0.34; }
.print__ridges--2 { stroke-opacity: 0.26; }
.print__ridges--3 { stroke-opacity: 0.18; }
.print__ridges--4 { stroke-opacity: 0.12; }

/* The core light: the same locator light as the light switch and the keys */
.print__core-dot { fill: var(--acid); }
[data-theme="light"] .print__core-dot { fill: #6f8f00; }
.print__core-glow {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0.7;
  transition: transform 300ms var(--ease), opacity 300ms var(--ease);
}
.print.is-inked .print__core-glow { transform: scale(1.9); opacity: 1; }

/* Ink: hovering an opening key, or pressing and holding the print, lights it in acid,
   ring by ring from the core. The core ring answers at once; the rest follow 13ms apart. */
.print__ridges path {
  transition:
    stroke 140ms linear calc(var(--i, 0) * 13ms),
    stroke-opacity 140ms linear calc(var(--i, 0) * 13ms);
}
.print__ridges--1 { --ink-op: 0.78; }
.print__ridges--2 { --ink-op: 0.6; }
.print__ridges--3 { --ink-op: 0.44; }
.print__ridges--4 { --ink-op: 0.3; }
.print.is-inked .print__ridges path {
  stroke: var(--accent-text);
  stroke-opacity: var(--ink-op);
}
/* Email me signs the faint print at the end of the page */
.print-echo.is-inked .print__ridges path {
  stroke: var(--accent-text);
  stroke-opacity: 0.5;
}
/* While a hold is on, the opening can't start a text selection or a phone's callout menu */
html.print-holding .opening { user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
@media (min-width: 768px) {
  .print__layer,
  .print-clear {
    right: -24%;
    width: min(130vh, 90vw);
  }
}
@media (min-width: 1280px) {
  .print__layer,
  .print-clear {
    right: -8%;
    width: min(120vh, 80vw);
  }
}

/* ---------- Interested ---------- */
.interested {
  --pool-x: 78%;
  --pool-y: 40%;
  --pool-size: 100vmax;
}
.interested .body { grid-column: 1 / -1; }
.interested__title {
  max-width: 20ch;
  margin-inline: auto;
  margin-bottom: var(--s8);
  font-size: var(--h1);
  line-height: var(--h1-lh);
  letter-spacing: 0;
  text-align: center;
}
.interested .passage {
  max-width: 48ch;
  margin-inline: auto;
  text-align: center;
}
.passage {
  font-family: var(--f-display);
  font-weight: 500;
  font-stretch: 88%;
  font-size: var(--passage);
  line-height: var(--passage-lh);
  letter-spacing: 0;
  text-wrap: pretty;
}
.passage p + p { margin-top: var(--passage-lh); }
.passage .dim { color: var(--paper-mute); }
.interested .story { max-width: 36ch; letter-spacing: 0; }
.story .story-beat + .story-beat { margin-top: var(--s5); }
.story-line { display: inline; }
.story-word { display: inline-block; }
.story-on .story-word { opacity: 0.65; transform: translateY(20px); }

/* ---------- Behaviour: the tube ----------
   Without JS, or with reduced motion, the verbs are a plain stacked list.
   With .tube-on (set by scripts/main.js) they sit on an 8-sided tube that scroll turns.
   Face radius = line-height / (2 * tan(22.5deg)) = line-height * 1.2071. */
.rotation {
  --pool-x: 60%;
  --pool-y: 50%;
  --pool-size: 100vmax;
  --pool-a: 0.07;
}
/* A real section heading, centred over the words, close enough to read as one unit.
   The gap clears the dimmed word that rolls in above the front one. */
.behaviour__title {
  /* Always one line: on narrow screens the size steps down to fit instead of wrapping */
  font-size: min(var(--h2), 6.4vw);
  line-height: var(--h2-lh);
  text-align: center;
  white-space: nowrap;
}
.rotation .tube { margin-top: var(--s10); }
.rotation .tube .body {
  grid-column: 1 / -1;
  text-align: center;
}
.line {
  font-size: var(--hero);
  line-height: var(--hero-lh);
  white-space: nowrap;
}
.count { margin-top: var(--s6); }

.tube-on .rotation {
  /* About 1.3 screens of scroll for the eight words */
  height: calc(100svh + 7 * 18svh);
  height: round(up, calc(100svh + 7 * 18svh), 8px);
  padding-block: 0;
}
.tube-on .rotation .stage {
  position: sticky;
  top: 0;
  height: 100svh;
  height: round(down, 100svh, 8px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: var(--s8) var(--s20); /* more room below, so the group sits a little above centre */
}
.tube-on .rotation .tube {
  margin-top: var(--s10);
  align-items: center;
}
.tube-on .rotation .tube .body {
  perspective: 800px;
  perspective-origin: 50% 50%;
}
.tube-on .tube__inner {
  position: relative;
  height: var(--hero-lh);
  transform-style: preserve-3d;
  will-change: transform;
}
.tube-on .line {
  position: absolute;
  inset: 0;
  transform: rotateX(calc(var(--i) * -45deg)) translateZ(calc(var(--hero-lh) * 1.2071));
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  opacity: 0;
}
.tube-on .count { margin-top: 0; }

/* ---------- Work ----------
   One screen per project. The stage is pinned and the projects fly a diagonal path
   through it, far bottom right to near top left (scripts/work.js). */
.work {
  --pool-x: 15%;
  --pool-y: 8%;
  --pool-size: 80vmax;
  padding: 0;
}
.reel {
  height: calc((var(--shots, 3) + 0.75) * 100svh);
  height: round(up, calc((var(--shots, 3) + 0.75) * 100svh), 8px);
}
.reel__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  height: round(down, 100svh, 8px);
  perspective: 1400px;
  perspective-origin: 50% 46%;
  overflow: hidden;
}
.reel__label {
  position: absolute;
  z-index: 3;
  top: var(--s12);
  left: var(--margin);
  margin: 0;
  color: var(--accent-text);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.shot {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(46vw, 620px);
  margin: 0;
  transform-style: preserve-3d;
  will-change: transform, opacity;
  pointer-events: none;
}
.shot__card {
  display: block;
  color: var(--paper);
  text-decoration: none;
}
.shot__frame {
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: #171614;
  box-shadow: 0 48px 96px rgb(0 0 0 / 0.45);
}
.shot__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.shot__frame--empty {
  display: grid;
  place-items: center;
  background: #262420;
  color: rgb(236 231 220 / 0.5);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  text-transform: uppercase;
}
.shot__frame--alt { background: #343a35; }
.shot__name {
  display: block;
  margin-top: var(--s3);
  font-size: var(--h2);
  line-height: var(--h2-lh);
  text-transform: uppercase;
}
.shot__meta {
  display: block;
  margin-top: var(--s1);
  color: var(--paper-dim);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  text-transform: uppercase;
}
/* Only the project at the front of the path can be clicked */
.shot.is-front { pointer-events: auto; }
.shot.is-front .shot__card:hover .shot__name { color: var(--accent-text); }

/* ---------- Frames and imagery ---------- */
/* Frame heights snap to the 8px baseline */
.figure { container-type: inline-size; }
.frame {
  position: relative;
  height: round(nearest, 75cqw, 8px);
  overflow: hidden;
  border-radius: 2px;
}
.frame--wide { height: round(nearest, 56.25cqw, 8px); }
@supports not (height: round(nearest, 10px, 8px)) {
  .frame { height: auto; aspect-ratio: 4 / 3; }
  .frame--wide { aspect-ratio: 16 / 9; }
}

.frame .empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--s3);
  text-align: center;
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  color: var(--paper-mute);
}
.frame .empty::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle, rgb(var(--paper-rgb) / 0.09) 1px, transparent 1.5px) 0 0 / 8px 8px;
  -webkit-mask-image: radial-gradient(120% 90% at 30% 20%, #000, transparent 70%);
          mask-image: radial-gradient(120% 90% at 30% 20%, #000, transparent 70%);
}
.frame .empty span { position: relative; max-width: 30ch; }

/* Any img inside .halftone is dithered by scripts/main.js. Hover or focus shows the original. */
.halftone { background: var(--ink); }
.halftone img,
.halftone canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.halftone img { filter: grayscale(1) contrast(1.2); }
.halftone canvas {
  image-rendering: pixelated;
  transition: opacity var(--press) var(--ease);
}
.halftone.is-dithered img { opacity: 0; filter: none; transition: opacity var(--press) var(--ease); }
.halftone.is-dithered:hover img,
.halftone.is-dithered:focus-within img { opacity: 1; }
.halftone.is-dithered:hover canvas,
.halftone.is-dithered:focus-within canvas { opacity: 0; }

.figure figcaption {
  margin-top: var(--s2);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  color: var(--paper-mute);
}

/* ---------- People ---------- */
.people {
  --pool-x: 88%;
  --pool-y: 20%;
  --pool-size: 90vmax;
}
.section-title {
  margin-top: var(--s3);
  font-size: var(--h2);
  line-height: var(--h2-lh);
  max-width: 16ch;
}
.people .section-title {
  margin-inline: auto;
  text-align: center;
}
.principles { margin-top: var(--stack); }
.principle {
  row-gap: var(--s1);
  padding-block: var(--s4);
  box-shadow: inset 0 1px 0 var(--line);
}
.principle h3 {
  grid-column: 3 / -1;
  font-family: var(--f-display);
  font-weight: 650;
  font-stretch: 82%;
  font-size: var(--principle);
  line-height: var(--principle-lh);
  letter-spacing: -0.024em;
}
.principle p {
  grid-column: 3 / -1;
  margin-top: var(--s1);
  color: var(--paper-dim);
  max-width: 38ch;
}

/* The button principle, tested */
.proof {
  margin-top: var(--stack);
  row-gap: var(--s3);
  align-items: center;
}
.proof .pad {
  grid-column: 1 / -1;
  display: grid;
  place-items: center;
  height: 240px;
  border-radius: 2px;
}
/* A still base (the dark side and the shadow) and a cap that moves.
   Pressing only moves the cap and fades overlays: transform and opacity. */
.press {
  position: relative;
  width: 128px;
  height: var(--s8);
  border-radius: 999px;
  touch-action: manipulation;
  filter: drop-shadow(0 22px 32px rgb(0 0 0 / 0.42));
}
.press::before {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateY(8px);
  border-radius: 999px;
  background:
    linear-gradient(180deg, #858075, #504c44),
    #6f6a60;
  box-shadow:
    0 1px 0 rgb(236 231 220 / 0.16) inset,
    0 8px 24px rgb(0 0 0 / 0.55);
}
.press::after {
  /* the hit area is bigger than the thing you see */
  content: "";
  position: absolute;
  inset: calc(var(--s2) * -1);
  border-radius: 999px;
}
.press__cap {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: 999px;
  border: 1px solid rgb(15 14 12 / 0.18);
  background:
    linear-gradient(180deg, #fff9ea, var(--press-face) 54%, #cac3b4),
    var(--press-face);
  box-shadow:
    0 2px 0 rgb(255 255 255 / 0.58) inset,
    0 -4px 0 rgb(0 0 0 / 0.24) inset;
  transition: transform var(--press) var(--ease);
  will-change: transform;
}
.press__cap::before,
.press__cap::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--press) var(--ease);
}
.press__cap::before { background: rgb(255 255 255 / 0.4); }
.press__cap::after { background: var(--acid); }
.press:active .press__cap,
.press.is-down .press__cap { transform: translateY(8px); }
.press.is-pressed .press__cap::after { opacity: 1; }

.press:focus-visible { outline-offset: var(--s2); border-radius: 999px; }

.proof .say {
  grid-column: 2 / -2;
  color: var(--paper-dim);
  max-width: 34ch;
  min-height: calc(var(--passage-lh) * 3 + 40px);
}
.proof .say strong {
  display: block;
  color: var(--paper);
  font-family: var(--f-display);
  font-size: var(--passage);
  line-height: var(--passage-lh);
  font-weight: 600;
  letter-spacing: 0;
  overflow-wrap: anywhere;
}
.proof .say p {
  margin-top: var(--s2);
  font-size: 16px;
  line-height: 24px;
}
.proof .say { grid-row: 2; }
.proof-connection {
  grid-column: 1 / span 8;
  grid-row: 1;
  z-index: 1;
  display: grid;
  align-content: start;
  padding: 0;
  background: var(--ink);
  color: var(--paper);
  pointer-events: none;
  opacity: 1;
  animation: proof-overlay-in 400ms var(--ease);
  transition: opacity 600ms ease;
}
.proof:has(.proof-connection) .pad { grid-column: 9 / -1; grid-row: 1; }
.proof-connection.is-leaving { opacity: 0; }
.proof-connection__panel {
  width: min(100%, 640px);
  padding-block: 24px;
  border-top: 1px solid var(--line);
}
.proof-connection .proof-connection__confirmation {
  margin: 0 0 24px;
  color: var(--accent-text);
}
.proof-connection h3 {
  margin-top: 24px;
  font: 600 24px/32px var(--f-display);
  letter-spacing: 0;
}
.proof-connection p { margin-top: 24px; max-width: 44ch; color: var(--paper-dim); }
@media (max-width: 767px) {
  .proof { position: relative; }
  .proof-connection {
    position: absolute;
    bottom: calc(100% - 32px);
    grid-column: auto;
    grid-row: auto;
    inset-inline: calc(16px - var(--margin));
    width: auto;
    transform: none;
    justify-items: center;
    text-align: center;
  }
  .proof-connection__panel { width: 100%; margin-inline: auto; }
  .proof-connection h3 { text-wrap: balance; }
  .proof-connection p { max-width: none; margin-inline: auto; }
  .proof:has(.proof-connection) .pad { grid-column: 1 / -1; grid-row: 1; }
  .proof:has(.proof-connection) .say { grid-row: 2; }
}
@media (min-width: 768px) {
  .proof .say { grid-row: 1; }
  .proof-connection { grid-column: 1 / span 5; grid-row: 1; }
  .proof:has(.proof-connection) .pad { grid-column: 6 / span 5; }
  .proof:has(.proof-connection) .say { grid-column: 12 / -1; }
}
@media (min-width: 1280px) {
  .proof-connection { grid-column: 1 / span 4; }
  .proof:has(.proof-connection) .pad { grid-column: 5 / span 5; }
  .proof:has(.proof-connection) .say { grid-column: 11 / -1; }
}
@keyframes proof-overlay-in { from { opacity: 0; } to { opacity: 1; } }
/* ---------- Dust: the projects come apart ----------
   scripts/main.js redraws the visible end of the projects onto 16 canvas layers of 2px grain.
   While they drift away, the real projects are hidden; scrolling back restores them. */
.projects { position: relative; }
.dust-layers {
  position: absolute;
  left: 0;
  right: 0;
  pointer-events: none;
  visibility: hidden;
}
.dust-layers canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
  will-change: transform, opacity;
}
.is-dissolving > :not(.dust-layers) { visibility: hidden; }
.is-dissolving > .dust-layers { visibility: visible; }

/* ---------- Principles rail ----------
   Without JS, or with reduced motion, the principles are the vertical list above.
   With .rail-on (scripts/main.js) the stage pins and the six cards move left as you scroll.
   Open panels leave a glimpse of the next principle at each breakpoint. */
.people {
  --card-span: 13;
  --card-offset: 0;
  --card-h: 320px;
  --card-pad: var(--s3);
}
.people .count { display: none; }

.rail-on .people { padding-top: 0; }
.rail-on .people { container-type: inline-size; }
.rail-on .people .pin {
  /* How far the row travels, worked out here so the height is right from the first paint.
     scripts/main.js measures the real value into --rail-run. */
  --col: calc((100cqw - 15 * var(--gutter)) / 16);
  --rail-run-css: calc(var(--card-offset) * (var(--col) + var(--gutter)) + 6 * (var(--card-span) * var(--col) + (var(--card-span) - 1) * var(--gutter)) + 5 * var(--gutter) - 100cqw);
  height: calc(100svh + var(--rail-run, var(--rail-run-css)));
  height: round(up, calc(100svh + var(--rail-run, var(--rail-run-css))), 8px);
}
.rail-on .people .stage {
  position: sticky;
  top: 0;
  height: 100svh;
  height: round(down, 100svh, 8px);
  display: flex;
  flex-direction: column;
  justify-content: center;          /* phone: heading and cards sit in the middle of the screen */
  padding-block: var(--s4);
}
@media (max-width: 767px) {
  .rail-on .people .section-title { margin-top: 0; }
}
.rail-on .people .rail {
  container-type: inline-size;
  margin-top: var(--s8);
}
.rail-on .principles {
  --col: calc((100cqw - 15 * var(--gutter)) / 16);
  margin-top: 0;
  display: flex;
  gap: var(--gutter);
  width: max-content;
  padding-left: calc(var(--card-offset) * (var(--col) + var(--gutter)));
  will-change: transform;
}
.rail-on .principle {
  flex: none;
  display: flex;
  flex-direction: column;
  width: calc(var(--card-span) * var(--col) + (var(--card-span) - 1) * var(--gutter));
  min-height: var(--card-h);
  padding: 0 var(--card-pad) var(--s3) 0;
  background: transparent;
  box-shadow: inset -1px 0 0 var(--line);
  border-radius: 0;
}
.rail-on .principle::before { display: none; }
.rail-on .principle:last-child { box-shadow: none; }
.rail-on .principle h3 {
  margin-top: var(--s3);
  max-width: 19ch;
  font-size: var(--h2);
  line-height: var(--h2-lh);
  letter-spacing: 0;
}
.rail-on .principle p {
  margin-top: var(--s3);
  padding-top: 0;
  max-width: 38ch;
}
.rail-on .people .count {
  display: flex;
  margin-top: auto;
  padding-top: var(--s3);
}

/* ---------- The living background (scripts/field.js) ----------
   Drawn once; only moved. Under everything else. */
.field {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.field canvas {
  position: absolute;
}
.field__live {
  inset: 0;
  width: 100%;
  height: 100%;
}
.field__lines {
  left: -15vw;
  top: -5vh;
  width: 130vw;
  height: 160vh;
}
.field__glow {
  left: 0;
  top: 0;
  width: 180vh;
  height: 180vh;
}

/* A soft dark disc under the fingerprint, so the background lines never tangle with it.
   Fixed, above the background and below the fish; scripts/print.js moves it with the print. */
.print-clear {
  position: fixed;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(closest-side at 50% 54%, var(--ink) 52%, transparent 97%);
  will-change: transform, opacity;
}

/* ---------- The fish (scripts/fish.js) ----------
   Swims anywhere on screen, behind the page content (main is z-index 2).
   Built from pieces that are only ever moved and turned. */
.fish {
  position: fixed;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
}
.fish__swim {
  position: absolute;
  left: 0;
  top: 0;
  will-change: transform;
}
.fish__seg,
.fish__tail,
.fish__fin,
.fish__bubble {
  position: absolute;
  left: 0;
  top: 0;
  will-change: transform;
}
.fish__seg {
  border-radius: 50%;
  background: var(--paper);
}
.fish__tail {
  background: var(--paper-dim);
  clip-path: polygon(0 42%, 100% 0, 58% 50%, 100% 100%, 0 58%);
  transform-origin: 0 50%;
}
.fish__fin {
  background: var(--paper-dim);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
  transform-origin: 0 50%;
}
.fish__eye {
  position: absolute;
  left: 58%;
  width: 11%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--ink);
}
.fish__eye--l { top: 10%; }
.fish__eye--r { bottom: 10%; }
.fish__bubble {
  border: 1px solid var(--paper-mute);
  border-radius: 50%;
  opacity: 0;
  will-change: transform, opacity;
}

/* ---------- Contact ---------- */
.contact {
  --pool-x: 50%;
  --pool-y: 55%;
  --pool-size: 110vmax;
  --pool-a: 0.075;
  min-height: 100svh;
  min-height: round(down, 100svh, 8px);
  display: flex;
  flex-direction: column;
  padding-block: var(--s15) var(--s4);
}
.contact-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding-block: var(--s8);
  text-align: center;
}
.contact-phone {
  display: inline-flex;
  align-items: center;
  min-height: var(--s6);
  margin-top: var(--s2);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  color: var(--paper-dim);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 6px;
}
.contact-phone:hover { color: var(--accent-text); }
.contact .end {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: var(--s2);
  margin-top: var(--s5);
  padding-top: var(--s2);
  box-shadow: inset 0 1px 0 var(--line); /* a rule that adds no height */
}
.contact .end .link { justify-self: end; }
.footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0 var(--s2);
  justify-self: end;
}
@media (min-width: 768px) and (max-width: 1100px) {
  .contact .end { grid-template-columns: 1fr auto; }
  .footer-links { grid-column: 1 / -1; justify-self: center; justify-content: center; }
}
@media (max-width: 767px) {
  .contact .end {
    display: flex;
    justify-content: center;
    gap: var(--s1) var(--s2);
  }
  .contact .end > span { white-space: nowrap; }
  .contact .end > .link { flex-basis: 100%; justify-content: center; }
  .footer-links { justify-self: center; justify-content: center; }
}
/* The seam: a grain band that sweeps up as Contact arrives (scripts/seam.js).
   Above the section it only covers the empty padding. Inside it, it sits behind the text. */
.seam {
  position: absolute;
  z-index: -1;
  left: 0;
  top: calc(var(--section-y) * -1);
  width: 100%;
  height: calc(var(--section-y) + 50svh);
  pointer-events: none;
}
.seam canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
}

.contact .lead {
  margin-top: 0;
  color: var(--paper-dim);
  max-width: 34ch;
}



.end {
  margin-top: var(--s6);
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s2);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  color: var(--paper-mute);
}

.contact .end .link {
  gap: var(--s1);
  padding-inline: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  letter-spacing: 0;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 6px;
  text-decoration-color: var(--paper-mute);
}
.contact .end .link:hover {
  color: var(--accent-text);
  text-decoration-color: var(--accent-text);
}
.contact .link .arrow {
  width: var(--arrow-size);
  margin-inline: 0;
  border-radius: 0;
  background: none;
  text-decoration: none;
}

/* Contact: a label, a big heading, a short note, then the details */
.reach {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.reach__label {
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--accent-text);
}
.reach__title {
  margin-top: var(--s3);
  max-width: 14ch;
  font-size: var(--h1);
  line-height: var(--h1-lh);
  text-transform: uppercase;
}
.heading-word { display: inline-block; }
.heading--split .heading-word { opacity: 0; transform: translateY(48px); }
.reach__intro {
  margin-top: var(--s4);
  max-width: 44ch;
  color: var(--paper);
}
.reach__email {
  align-self: flex-start;
  margin-top: var(--s5);
  color: var(--paper);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}
.reach__email:hover { color: var(--accent-text); }
.reach__social {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: var(--s6);
  margin-top: var(--s3);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--paper-dim);
  transition: transform var(--press) var(--ease);
}
.reach__social:hover { color: var(--paper); }
.reach__social:active { transform: translateY(1px); }
.reach__social svg { flex: none; }
/* Email me (primary) and LinkedIn (secondary), with the address written out underneath
   so it can be copied. One click selects all of it. */
.reach__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s5);
}
.reach__address {
  margin-top: var(--s2);
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  letter-spacing: 0.04em;
  color: var(--paper-mute);
  user-select: all;
}

/* ---------- Scrubber ---------- */
/* Sits in the right margin on every screen. Drag, tap, or focus and use the keys. */
.scrub {
  position: fixed;
  right: 0;
  top: 50%;
  z-index: 50;
  width: var(--s3);
  height: var(--rail-h);
  margin-top: calc(var(--rail-h) / -2);
  opacity: 0;
  transition: opacity 450ms var(--ease);
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  cursor: grab;
}
.scrub.is-ready { opacity: 1; }
.scrub.is-dragging { cursor: grabbing; }
.scrub .track {
  position: absolute;
  top: 0;
  bottom: 0;
  right: var(--s1);
  width: 1px;
  background: var(--line);
}
.scrub .tick {
  position: absolute;
  right: var(--s1);
  width: var(--tick-long, 16px);
  height: 1px;
  background: var(--paper);
  opacity: 0.55;
  transform: scaleX(var(--tick-rest, 0.5));
  transform-origin: 100% 50%;
  transition: transform var(--press) var(--ease), opacity var(--press) var(--ease);
}
.scrub .tick.is-current { opacity: 1; transform: none; }
.scrub .handle {
  position: absolute;
  right: 5px;
  top: 0;
  width: 7px;
  height: var(--s4);
  margin-top: calc(var(--s2) * -1);
  will-change: transform;
}
.scrub .handle::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid rgb(15 14 12 / 0.32);
  border-radius: 999px;
  background:
    linear-gradient(180deg, #dcff62, var(--acid) 58%, #9ec51e),
    var(--acid);
  box-shadow:
    0 1px 0 rgb(255 255 255 / 0.32) inset,
    0 8px 18px rgb(0 0 0 / 0.34);
  transform: scaleX(var(--handle-rest, 1));
  transition: transform var(--press) var(--ease);
}
.scrub:hover .handle::before,
.scrub.is-dragging .handle::before,
.scrub:focus-visible .handle::before { transform: none; }
.scrub .name {
  position: absolute;
  right: var(--s4);
  top: 0;
  margin-top: calc(var(--label-lh) / -2);
  padding: 0 var(--s1);
  white-space: nowrap;
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--paper);
  background: var(--ink-3);
  border-radius: 2px;
  opacity: 0;
  transition: opacity var(--press) var(--ease);
  pointer-events: none;
  will-change: transform;
}
.scrub:hover .name,
.scrub.is-dragging .name,
.scrub:focus-visible .name { opacity: 1; }
.scrub:focus-visible { outline-offset: 0; }

/* ---------- Case study pages ---------- */
.case-head {
  --pool-x: 25%;
  --pool-y: 55%;
  --pool-size: 110vmax;
  --pool-a: 0.08;
  min-height: 88svh;
  min-height: round(down, 88svh, 8px);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-top: var(--s20);
  padding-bottom: var(--block-y);
}
.case-head h1 {
  margin-top: var(--s3);
  font-size: var(--h1);
  line-height: var(--h1-lh);
  max-width: 16ch;
}
.facts {
  margin-top: var(--s6);
  row-gap: var(--s3);
}
.facts > div { grid-column: span 8; }
.facts dt {
  font: 400 var(--label)/var(--label-lh) var(--f-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--paper-mute);
}
.facts dd { margin-top: var(--s1); }

.case {
  --pool-x: 80%;
  --pool-y: 20%;
  padding-block: 0;
}
.chapter {
  padding-block: var(--block-y);
  box-shadow: inset 0 1px 0 var(--line);
}
.chapter h2 {
  font-size: var(--chapter);
  line-height: var(--chapter-lh);
  max-width: 16ch;
}
.chapter .prose {
  margin-top: var(--s3);
  max-width: 60ch;
  color: var(--paper-dim);
}
.chapter .prose p + p { margin-top: var(--s3); }
.chapter .prose strong { color: var(--paper); font-weight: 500; }
.chapter .figure { margin-top: var(--s5); }
.pair {
  margin-top: var(--s5);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s4) var(--gutter);
}
.pair .figure { margin-top: 0; }

.next {
  --pool-x: 70%;
  --pool-y: 60%;
  min-height: 72svh;
  min-height: round(down, 72svh, 8px);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--s6);
}
.next-link {
  position: relative;
  display: inline-block;
  margin-top: var(--s3);
  padding: var(--s2) var(--s8) var(--s2) 0;
  font-size: var(--next);
  line-height: var(--next-lh);
  max-width: min(14ch, 100%);
  color: var(--paper);
  transition:
    transform var(--press) var(--ease),
    color var(--press) var(--ease);
}
.next-link::after {
  content: "";
  position: absolute;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--press) var(--ease);
}
.next-link:hover::after,
.next-link:focus-visible::after { opacity: 1; }
.next-link::after {
  content: "→";
  right: 0;
  top: 50%;
  width: var(--s6);
  height: var(--s6);
  margin-top: calc(var(--s3) * -1);
  display: grid;
  place-items: center;
  border: 1px solid rgb(198 244 50 / 0.46);
  border-radius: 50%;
  background:
    linear-gradient(180deg, rgb(198 244 50 / 0.14), rgb(198 244 50 / 0.035)),
    var(--ink);
  box-shadow: 0 1px 0 rgb(236 231 220 / 0.08) inset;
  font: 400 var(--text)/var(--text-lh) var(--f-mono);
  color: var(--accent-text);
}

[data-theme="light"] .link:hover { border-color: var(--accent-text); }
[data-theme="light"] .link:focus-visible { border-color: var(--accent-text); }
[data-theme="light"] .link { box-shadow: none; }
[data-theme="light"] .press { filter: drop-shadow(0 16px 24px rgb(0 0 0 / 0.18)); }
.next-link:hover {
  color: var(--accent-text);
  transform: translateX(var(--s1));
}
.next-link:active { color: var(--paper-dim); transform: translateX(var(--s1)) translateY(2px); }

/* Redirect stubs */
.stub {
  min-height: 100svh;
  min-height: round(down, 100svh, 8px);
  display: grid;
  place-content: center start;
  gap: var(--s2);
}

/* ---------- Tablet: 16 columns, 48px margin, 16px gutter ---------- */
@media (min-width: 768px) {
  .aside { grid-column: 1 / span 4; margin-bottom: var(--s5); }
  .body  { grid-column: 4 / -1; }
  .pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .opening { padding-top: var(--s20); padding-bottom: var(--s10); }
  .opening .foot { margin-top: var(--s4); }
  .opening .foot p { grid-column: 1 / span 12; font-size: 18px; line-height: 32px; }

  .principle { padding-block: var(--s5); }
  .principle h3  { grid-column: 4 / span 7; }
  .principle p   { grid-column: 11 / -1; margin-top: 0; }

  .proof .pad { grid-column: 3 / span 6; height: 280px; }
  .proof .say { grid-column: 10 / -1; }

.end { margin-top: var(--s8); }

  .facts { margin-top: var(--s8); }
  .facts > div { grid-column: span 4; }

  .rotation .tube,
  .tube-on .rotation .tube { margin-top: var(--s15); }

  .people { --card-span: 10; --card-offset: 0; --card-h: 360px; --card-pad: var(--s4); }
  .rail-on .people .stage { justify-content: flex-start; padding-block: var(--s12) var(--s5); }
  .chapter .figure,
  .pair { margin-top: var(--s6); }

  .scrub { width: var(--s5); right: 0; }
  .scrub .track { right: var(--s2); }
  .scrub .tick { right: var(--s2); }
  .scrub .handle { right: 10px; width: 13px; --handle-rest: 0.69; }
  .scrub .name { right: var(--s5); }
}

/* ---------- Desktop: 16 columns, 120px margin, 24px gutter ---------- */
@media (min-width: 1280px) {
  .aside { grid-column: 1 / span 4; margin-bottom: 0; }
  .body  { grid-column: 6 / span 10; }

  .opening { padding-top: var(--s20); padding-bottom: var(--s12); }
  .opening .foot { margin-top: var(--s4); }
  .opening .foot p { grid-column: 1 / span 10; }

  .shot__name { font-size: var(--chapter); line-height: var(--chapter-lh); }

  .principle h3 { grid-column: 6 / span 5; }
  .principle p  { grid-column: 12 / span 4; }

  .proof .pad { grid-column: 5 / span 5; height: 320px; }
  .proof .say { grid-column: 11 / -1; }

.end { margin-top: var(--s12); }

  .facts { margin-top: var(--s10); }

  .rotation .tube,
  .tube-on .rotation .tube { margin-top: var(--s20); }

  .people { --card-span: 9; --card-offset: 0; --card-h: 360px; }
  /* Desktop: heading and cards sit in the middle of the screen */
  .rail-on .people .stage { justify-content: center; padding-block: var(--s12); }
  .rail-on .people .section-title { margin-top: 0; }

  .scrub { width: var(--s6); right: var(--s4); }
  .scrub .track { right: var(--s3); }
  .scrub .tick { right: var(--s2); --tick-long: 24px; --tick-rest: 0.667; }
  .scrub .handle { right: 18px; }
  .scrub .name { right: var(--s6); }
}

/* Short screens (laptops at 720px and below 800px tall): the pinned principles must fit */
@media (min-width: 768px) and (max-height: 800px) {
  .people { --card-h: 360px; }
  .rail-on .people .stage { padding-block: var(--s8) var(--s4); }
  .rail-on .people .rail { margin-top: var(--s5); }
}

@media (max-width: 390px) {
  .link { --arrow-size: var(--s2); }
  .cue {
    min-width: var(--s5);
    padding-inline: 6px;
  }
}

/* ---------- Reduced motion: no transforms, fades stay ---------- */
@media (forced-colors: active) {
  .grain, .opening-grain, .lamp, .seam, .dust-layers, .fish, .print, .print-clear, .field, .section::before { display: none; }
  .is-dissolving > :not(.dust-layers) { visibility: visible; }
  .hl { color: inherit; }
  .scrub .handle::before { background: Highlight; }
}

@media print {
  .grain, .opening-grain, .lamp, .seam, .fish, .print, .print-clear, .field, .scrub, .skip { display: none; }
  .js .reveal { opacity: 1; transform: none; }
  body { background: #fff; color: #000; }
}

/* ---------- Grid overlay (?grid) ---------- */
.grid-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  padding-inline: var(--margin);
  background: repeating-linear-gradient(to bottom, rgb(198 244 50 / 0.16) 0 1px, transparent 1px 8px);
}
.grid-overlay .grid { height: 100%; }
.grid-overlay .grid span { background: rgb(198 244 50 / 0.08); }

/* Phones: keep content clear of the bottom nav bar (see .bar).
   Last in the file on purpose, so it wins over the base section spacing. */
@media (max-width: 767px) {
  main { padding-bottom: calc(var(--s12) + env(safe-area-inset-bottom)); }
  .opening,
  .case-head { padding-top: var(--s12); }
  .opening { padding-bottom: calc(var(--s12) + var(--s2) + env(safe-area-inset-bottom)); }
  .rail-on .people .stage {
    padding-top: var(--s10);
    padding-bottom: calc(var(--s12) + env(safe-area-inset-bottom));
  }
  .rail-on .people .rail { margin-top: var(--s4); }
  .reel__label { top: var(--s8); }
  .shot { width: min(74vw, 420px); }
  /* The bar lives at the bottom here, so the marker goes above the links */
  .bar-nav__rail { bottom: auto; top: calc(var(--s2) * -1 - 1px); }
  /* The ruler keeps to the outer half of the margin, clear of the content.
     It stays 24px wide, so a thumb still finds it. */
  .scrub .track { right: 4px; }
  .scrub .tick { right: 4px; --tick-long: 8px; --tick-rest: 0.5; }
  .scrub .handle { right: 1px; width: 7px; }
}

@media (max-width: 767px) and (max-height: 740px) {
  .people { --card-span: 14; --card-offset: 0; --card-h: 0px; --card-pad: var(--s2); }
  .rail-on .people .section-title { font-size: 28px; line-height: 32px; }
  .rail-on .people .rail { margin-top: var(--s3); }
}

/* Short desktop screens: keep the principles centred with even spacing */
@media (min-width: 1280px) and (max-height: 800px) {
  .rail-on .people .stage { padding-block: var(--s6); }
  .shot { width: min(78vw, 380px); }
  .shot__name { font-size: var(--mail); line-height: var(--mail-lh); }
}

/* Rolling labels on calls to action (scripts/main.js) */
.roll {
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}
.roll__out,
.roll__in {
  display: block;
  white-space: nowrap;
  will-change: transform;
}
.roll__in {
  position: absolute;
  inset: 0;
  transform: translateY(-100%);
}
/* The email keeps its underline on both copies */
.reach__email .roll__out,
.reach__email .roll__in {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}

/* Theme switch curtain. Tiles only animate opacity. */
.theme-pixels {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: grid;
  pointer-events: none;
}
.theme-pixels > div { background: var(--acid); opacity: 0; }

/* Avoid an additional browser cross-fade if another feature uses view transitions. */
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

/* ---------- Contact: main column, local time, and the fingerprint echo ---------- */
.reach__main {
  display: flex;
  flex-direction: column;
}
.reach__side {
  margin-top: var(--s6);
}
.reach__time {
  margin-top: var(--s1);
  font-size: var(--h2);
  line-height: var(--h2-lh);
  font-variant-numeric: tabular-nums;
}
.reach__zone,
.reach__diff {
  color: var(--paper-dim);
}
.reach__zone { margin-top: var(--s1); }

.print-echo { display: none; }

@media (min-width: 1024px) {
  /* Time on the left, place and difference beside it, so the block stays one line tall */
  .reach__side {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: var(--s3);
    align-items: center;
    margin-top: var(--s4);
  }
  .reach__side .reach__label { grid-column: 1 / -1; }
  .reach__time { grid-row: 2 / 4; }
  .reach__zone { grid-row: 2; align-self: end; margin-top: 0; }
  .reach__diff { grid-row: 3; align-self: start; }

  /* A soft dark disc of its own keeps the background lines out of the echo */
  .print-echo {
    display: block;
    position: absolute;
    z-index: -1;
    top: 50%;
    right: -6%;
    width: min(88vh, 56vw);
    aspect-ratio: 1;
    translate: 0 -50%;
    pointer-events: none;
    background: radial-gradient(closest-side at 50% 54%, var(--ink) 52%, transparent 97%);
  }
  .print-echo svg {
    display: block;
    width: 100%;
    height: 100%;
  }
  .print__echo { stroke-opacity: 0.12; }
}
