/* ==========================================================================
   Mohammad Mahdi Azarbeik — site stylesheet
   fonts · tokens · themes · base · atoms · masthead · menu · layout · chapter heads · panels ·
   intro · about · research (fields, project deck) · publications · experience · contact ·
   cursor · glyphs · motion · responsive · reduced motion · print
   ========================================================================== */

/* ---------------------------------------------------------------- fonts --- */
/* Self-hosted, latin subset. Newsreader keeps its optical-size axis; Geist and Geist Mono are variable (100–900). */
@font-face { font-family: "Newsreader"; src: url("../fonts/newsreader-roman.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Newsreader"; src: url("../fonts/newsreader-italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap; }
@font-face { font-family: "Geist"; src: url("../fonts/geist.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist Mono"; src: url("../fonts/geist-mono.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap; }
/* Metric-matched fallbacks keep layout shift near zero while the web fonts load. */
@font-face { font-family: "Newsreader Fallback"; src: local("Georgia"), local("Times New Roman");
  size-adjust: 94%; ascent-override: 78%; descent-override: 28%; line-gap-override: 0%; }
@font-face { font-family: "Newsreader Fallback"; font-style: italic; src: local("Georgia Italic"), local("Times New Roman Italic");
  size-adjust: 86%; ascent-override: 85%; descent-override: 31%; line-gap-override: 0%; }
@font-face { font-family: "Geist Fallback"; src: local("Arial"), local("Helvetica"), local("Liberation Sans");
  size-adjust: 108.7%; ascent-override: 92.5%; descent-override: 27%; line-gap-override: 0%; }

/* --------------------------------------------------------------- tokens --- */
:root {
  --serif: "Newsreader", "Newsreader Fallback", Georgia, serif;
  --sans: "Geist", "Geist Fallback", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  /* type scale */
  --fs-mega: clamp(3.3rem, 15.6vw, 6.4rem);          /* intro name, phones: one word per line */
  --fs-h2: clamp(2.05rem, 1.15rem + 3.5vw, 4.5rem);
  --fs-h3: clamp(1.4rem, 1.18rem + .9vw, 2rem);
  --fs-lede: clamp(1.18rem, 1.04rem + .6vw, 1.55rem);
  --fs-body: clamp(1rem, .97rem + .18vw, 1.125rem);
  --fs-ui: .9375rem;
  --fs-small: .875rem;
  --fs-label: .71875rem;

  /* space */
  --gutter: clamp(18px, 4.6vw, 72px);
  --maxw: 1400px;
  --colgap: clamp(16px, 2.2vw, 32px);
  --mast-h: 64px;
  --panel-m: clamp(6px, 1vw, 14px);
  --panel-r: clamp(22px, 3vw, 44px);
  --section-y: clamp(110px, 14vw, 200px);

  /* motion — curves are named by intent */
  --ease-out: cubic-bezier(.16, 1, .3, 1);       /* entrances: fast start, long settle */
  --ease-in: cubic-bezier(.55, 0, .75, .2);      /* exits */
  --ease-io: cubic-bezier(.76, 0, .24, 1);       /* cinematic: panels, masks, page-level changes */
  --ease-soft: cubic-bezier(.65, 0, .35, 1);     /* state changes in place, ambient drift */
  /* damped springs (sampled analytically): ζ = .76 (2.5 % overshoot) and ζ = .9 */
  --spring: linear(0, 0.029, 0.100, 0.197, 0.306, 0.418, 0.524, 0.622, 0.709, 0.784, 0.846, 0.896, 0.936, 0.967, 0.989, 1.005, 1.015, 1.022, 1.025, 1.025, 1.025, 1.023, 1.020, 1.017, 1.015, 1.012, 1.009, 1.007, 1.005, 1.004, 1.003, 1.002, 1);
  --spring-firm: linear(0, 0.020, 0.072, 0.143, 0.223, 0.308, 0.392, 0.472, 0.547, 0.615, 0.676, 0.730, 0.777, 0.817, 0.852, 0.881, 0.905, 0.925, 0.942, 0.955, 0.966, 0.975, 0.981, 0.987, 0.991, 0.994, 0.996, 0.998, 0.999, 1.000, 1.001, 1.001, 1);
  --t-hover: 280ms;
  --t-ui: 520ms;
  --t-roll: 700ms;                               /* the chapter label's roll, shared by the intro name */

  --i-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2 6h8M6.5 2.5 10 6 6.5 9.5' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --i-out: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.5 8.5 8.5 3.5M4 3.5h4.5V8' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --i-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 1.5v7M2.75 5.5 6 8.75 9.25 5.5M2 10.75h8' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --i-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Crect x='4' y='4' width='6.5' height='6.5' rx='1.2' fill='none' stroke='black' stroke-width='1.3'/%3E%3Cpath d='M8 2.2H3a.8.8 0 0 0-.8.8V8' fill='none' stroke='black' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .55 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (min-width: 760px) { :root { --fs-mega: clamp(4.6rem, 10.4vw, 13rem); } }

/* --------------------------------------------------------------- themes ---
   One identity, built around burgundy. Every colour has a job:
     burgundy   the accent — emphasis, interactive states, focus, the marks that matter, and the wine panels
     ivory      the light page (and the type on dark surfaces)
     rose       the secondary voice — selection, the intro's glow, the accent on wine, the "learning" field
     stone      structure — rules, quiet surfaces, tags, the experience band, the "sensing" field
     aubergine  the dark tone — ink on light, the page itself at night
   Light ("ivory") is the default; dark ("cellar") follows the system unless the visitor picked a theme. Each is
   drawn for its own light rather than inverted: by day burgundy is ink-deep and the panels are wine; by night the
   page sinks to a wine-black, the accent lifts to claret, and rose carries the accent text. .tone-wine and
   .tone-stone are the two panel surfaces. Every text pair meets WCAG AA (most AAA). */
:root {
  color-scheme: light;
  --bg: #f6f1ea; --bg-2: #eee6db; --bg-3: #e4d8c9;
  --ink: #221418; --ink-2: #4b3a3e; --muted: #6e5c63;
  --rule: #e2d6c8; --rule-2: #cbbbab;
  --accent: #7c1d34; --accent-text: #7c1d34; --accent-wash: rgb(124 29 52 / .07); --on-accent: #fbf3ec;
  --f-sense: #a8927f; --f-learn: #c98b92; --f-clin: #7c1d34;
  --sel: rgb(201 139 146 / .4);
  --aura-1: rgb(124 29 52 / .2); --aura-2: rgb(214 150 155 / .3); --aura-3: rgb(176 156 138 / .28);
  --glass: rgb(246 241 234 / .74);
  --shade: rgb(34 20 24 / .1);
  --shadow: 0 1px 0 rgb(34 20 24 / .04), 0 30px 60px -34px rgb(60 20 32 / .4);
  --panel-bg: var(--bg);
}
.tone-wine {
  --bg: #4b1323; --bg-2: #581a2c; --bg-3: #682536;
  --ink: #f8ece3; --ink-2: #ead3ca; --muted: #cfaaa8;
  --rule: #622235; --rule-2: #7d3c4f;
  --accent: #f0b3a5; --accent-text: #f4c3b7; --accent-wash: rgb(240 179 165 / .12); --on-accent: #3a0d19;
  --f-sense: #d6bfa9; --f-learn: #e79aa6; --f-clin: #f6d2c4;
  --sel: rgb(240 179 165 / .3);
  --glass: rgb(75 19 35 / .8);
  --shade: rgb(0 0 0 / .2);
  --shadow: 0 1px 0 rgb(0 0 0 / .2), 0 30px 60px -30px rgb(0 0 0 / .6);
  --panel-bg: radial-gradient(70% 60% at 88% 0%, rgb(240 160 150 / .14), transparent 70%),
    radial-gradient(60% 55% at 0% 100%, rgb(20 4 10 / .38), transparent 70%), var(--bg);
  color: var(--ink);
}
.tone-stone {
  --bg: #ece3d7; --bg-2: #e4d9cb; --bg-3: #d9cbbb;
  --muted: #66555b; --rule: #d8cbbc; --rule-2: #c2b1a0;
  --panel-bg: radial-gradient(60% 60% at 100% 0%, rgb(124 29 52 / .05), transparent 70%), var(--bg);
  color: var(--ink);
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #130c0f; --bg-2: #1c1317; --bg-3: #271b20;
  --ink: #f3e9e0; --ink-2: #d3c3ba; --muted: #a6938c;
  --rule: #2a1e23; --rule-2: #46353b;
  --accent: #c4495f; --accent-text: #eba3ad; --accent-wash: rgb(196 73 95 / .13); --on-accent: #fff5f0;
  --f-sense: #b49f8c; --f-learn: #d98a97; --f-clin: #c4495f;
  --sel: rgb(196 73 95 / .45);
  --aura-1: rgb(150 30 60 / .42); --aura-2: rgb(214 130 145 / .13); --aura-3: rgb(140 110 95 / .14);
  --glass: rgb(19 12 15 / .74);
  --shade: rgb(0 0 0 / .35);
  --shadow: 0 1px 0 rgb(0 0 0 / .3), 0 30px 60px -30px rgb(0 0 0 / .85);
}
:root[data-theme="dark"] .tone-wine {
  --bg: #2b0f18; --bg-2: #361521; --bg-3: #441c2a;
  --ink: #f6eadf; --ink-2: #dec6bd; --muted: #bb9b98;
  --rule: #3f1a27; --rule-2: #5e2f3d;
  --accent: #e47f90; --accent-text: #f2b4bc; --accent-wash: rgb(228 127 144 / .12); --on-accent: #2b0f18;
  --glass: rgb(43 15 24 / .8);
}
:root[data-theme="dark"] .tone-stone {
  --bg: #1b1513; --bg-2: #241c1a; --bg-3: #2e2522;
  --muted: #a6938c; --rule: #2e2522; --rule-2: #4a3d38;
  --panel-bg: radial-gradient(60% 60% at 100% 0%, rgb(196 73 95 / .07), transparent 70%), var(--bg);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #130c0f; --bg-2: #1c1317; --bg-3: #271b20;
    --ink: #f3e9e0; --ink-2: #d3c3ba; --muted: #a6938c;
    --rule: #2a1e23; --rule-2: #46353b;
    --accent: #c4495f; --accent-text: #eba3ad; --accent-wash: rgb(196 73 95 / .13); --on-accent: #fff5f0;
    --f-sense: #b49f8c; --f-learn: #d98a97; --f-clin: #c4495f;
    --sel: rgb(196 73 95 / .45);
    --aura-1: rgb(150 30 60 / .42); --aura-2: rgb(214 130 145 / .13); --aura-3: rgb(140 110 95 / .14);
    --glass: rgb(19 12 15 / .74);
    --shade: rgb(0 0 0 / .35);
    --shadow: 0 1px 0 rgb(0 0 0 / .3), 0 30px 60px -30px rgb(0 0 0 / .85);
  }
  :root:not([data-theme="light"]) .tone-wine {
    --bg: #2b0f18; --bg-2: #361521; --bg-3: #441c2a;
    --ink: #f6eadf; --ink-2: #dec6bd; --muted: #bb9b98;
    --rule: #3f1a27; --rule-2: #5e2f3d;
    --accent: #e47f90; --accent-text: #f2b4bc; --accent-wash: rgb(228 127 144 / .12); --on-accent: #2b0f18;
    --glass: rgb(43 15 24 / .8);
  }
  :root:not([data-theme="light"]) .tone-stone {
    --bg: #1b1513; --bg-2: #241c1a; --bg-3: #2e2522;
    --muted: #a6938c; --rule: #2e2522; --rule-2: #4a3d38;
    --panel-bg: radial-gradient(60% 60% at 100% 0%, rgb(196 73 95 / .07), transparent 70%), var(--bg);
  }
}

/* ----------------------------------------------------------------- base --- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: calc(var(--mast-h) + 20px);
  background: var(--bg);
}
body {
  margin: 0;
  min-height: 100svh;
  background: var(--bg);
  color: var(--ink);
  font: 400 var(--fs-body)/1.62 var(--sans);
  letter-spacing: -.004em;
  font-kerning: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}
h1, h2, h3, h4, p, ol, ul, dl, dd, figure, blockquote { margin: 0; }
h1, h2, h3, h4 { font-weight: 400; }
ol, ul { padding: 0; list-style: none; }
img, svg, video, canvas { display: block; max-width: 100%; }
img, video { height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
i, em { font-style: italic; }
b, strong { font-weight: 600; }
sup { line-height: 0; }
[hidden] { display: none !important; }
::selection { background: var(--sel); color: var(--ink); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
[tabindex="-1"]:focus { outline: none; }
html.is-locked { overflow: hidden; }
.visually-hidden { position: absolute !important; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.skip { position: fixed; left: 12px; top: 10px; z-index: 100; padding: 10px 14px; background: var(--accent); color: var(--on-accent);
  font: 500 var(--fs-small)/1 var(--sans); border-radius: 8px; transform: translateY(-180%); transition: transform var(--t-hover) var(--ease-out); }
.skip:focus { transform: none; }
.serif { font-family: var(--serif); }

/* ---------------------------------------------------------------- atoms --- */
.label {
  font: 500 var(--fs-label)/1.3 var(--mono);
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--muted);
}

/* links with a drawn underline: neutral rule, burgundy rule sweeps in on hover */
.link {
  background:
    linear-gradient(var(--accent), var(--accent)) 0 100% / 0 1px no-repeat,
    linear-gradient(var(--rule-2), var(--rule-2)) 0 100% / 100% 1px no-repeat;
  padding-bottom: 1px;
  transition: background-size var(--t-ui) var(--ease-out), color var(--t-hover);
}
.link:hover, .link:focus-visible { background-size: 100% 1px, 100% 1px; color: var(--accent-text); }

/* pills: the fill grows from the point where the pointer came in, and drains towards where it left (JS sets --hx/--hy) */
.pill {
  position: relative; isolation: isolate; overflow: hidden;
  display: inline-flex; align-items: center; gap: 8px;
  height: 38px; padding: 0 16px;
  font: 500 var(--fs-small)/1 var(--sans); letter-spacing: -.005em; color: var(--ink);
  border: 1px solid var(--rule-2); border-radius: 999px;
  transition: border-color var(--t-hover), color 420ms var(--ease-out), transform var(--t-hover) var(--spring-firm);
}
.pill::before { content: ""; position: absolute; z-index: -1; left: var(--hx, 50%); top: var(--hy, 50%); width: 260%; aspect-ratio: 1;
  border-radius: 50%; background: var(--accent); transform: translate(-50%, -50%) scale(0); transition: transform 560ms var(--ease-out); }
.pill::after { content: ""; width: 11px; height: 11px; background: currentColor;
  -webkit-mask: var(--i-out) center / contain no-repeat; mask: var(--i-out) center / contain no-repeat; transition: transform var(--t-ui) var(--spring); }
.pill[download]::after { -webkit-mask-image: var(--i-down); mask-image: var(--i-down); }
.pill[href^="mailto:"]::after { -webkit-mask-image: var(--i-arrow); mask-image: var(--i-arrow); }
.pill--plain::after { display: none; }
.pill:hover, .pill:focus-visible { border-color: var(--accent); color: var(--on-accent); }
.pill:hover::before, .pill:focus-visible::before { transform: translate(-50%, -50%) scale(1); }
.pill:hover::after { transform: translate(1.5px, -1.5px); }
.pill[download]:hover::after { transform: translateY(1.5px); }
.pill:active { transform: scale(.96); }

.iconbtn {
  display: inline-grid; place-items: center; width: 40px; height: 40px; border-radius: 999px;
  color: var(--ink); transition: background-color var(--t-hover), transform var(--t-hover) var(--spring-firm);
}
.iconbtn:hover { background: var(--accent-wash); }
.iconbtn:active { transform: scale(.9); }
.iconbtn svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; }

/* rolling label: the word slides up, its italic twin follows from below */
.roll { position: relative; display: inline-grid; overflow: hidden; vertical-align: top; }
.roll > span, .roll::after { grid-area: 1 / 1; transition: transform 560ms var(--ease-out); }
.roll::after { content: attr(data-text) / ""; transform: translateY(105%); }
a:hover .roll > span, a:focus-visible .roll > span { transform: translateY(-105%); }
a:hover .roll::after, a:focus-visible .roll::after { transform: none; }

/* small actions (paper, code and data links) */
.acts { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-top: 16px; }
.act { position: relative; display: inline-flex; align-items: center; gap: 6px; font: 500 11px/2.2 var(--mono); letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink); }
.act::after { content: ""; width: 10px; height: 10px; background: var(--muted);
  -webkit-mask: var(--i-out) center / contain no-repeat; mask: var(--i-out) center / contain no-repeat;
  transition: transform var(--t-ui) var(--spring), background-color var(--t-hover); }
.act[href^="#"]::after { -webkit-mask-image: var(--i-arrow); mask-image: var(--i-arrow); }
button.act::after { -webkit-mask-image: var(--i-copy); mask-image: var(--i-copy); }
.act::before { content: ""; position: absolute; left: 0; right: 16px; bottom: 3px; height: 1px; background: var(--accent);
  transform: scaleX(0); transform-origin: right; transition: transform var(--t-ui) var(--ease-out); }
.act:hover, .act:focus-visible { color: var(--accent-text); }
.act:hover::before, .act:focus-visible::before { transform: scaleX(1); transform-origin: left; }
.act:hover::after { transform: translate(2px, -1px); background: var(--accent-text); }
.act[href^="#"]:hover::after { transform: translateX(3px); }
button.act:hover::after { transform: scale(1.12); }

.tag { display: inline-block; padding: 4px 8px; border-radius: 4px; vertical-align: 1px;
  font: 500 10px/1.2 var(--mono); letter-spacing: .07em; text-transform: uppercase; color: var(--ink-2); background: var(--bg-3); white-space: nowrap; }

/* ------------------------------------------------------------- masthead ---
   Glass once the page has moved; takes the wine palette over wine panels; names the current chapter; its
   accent hairline marks where in the page you are (main.js). On phones it steps aside while you read down. */
.mast {
  position: fixed; inset: 0 0 auto 0; z-index: 50; color: var(--ink);
  view-transition-name: mast;
  transition: transform 640ms var(--ease-out), color 400ms;
}
.mast::before {
  content: ""; position: absolute; inset: 0; background: var(--glass);
  -webkit-backdrop-filter: blur(16px) saturate(1.4); backdrop-filter: blur(16px) saturate(1.4);
  border-bottom: 1px solid var(--rule); opacity: 0; transition: opacity 400ms var(--ease-out), background-color 400ms, border-color 400ms;
}
.mast.is-scrolled::before { opacity: 1; }
.mast.is-hidden { transform: translateY(calc(-100% - 3px)); }
.mast__bar {
  position: relative; height: var(--mast-h);
  max-width: calc(var(--maxw) + 2 * var(--gutter)); margin-inline: auto; padding-inline: var(--gutter);
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; align-items: center; column-gap: clamp(14px, 2vw, 32px);
}
.brand { display: inline-flex; align-items: center; gap: 11px; font: 550 15px/1 var(--sans); letter-spacing: -.015em; white-space: nowrap; }
/* The mark is balanced by eye, not by its box: ring and dot carry equal visual weight (the dot is solid, so it is
   drawn smaller; it takes the accent *text* colour, which keeps its contrast — and so its weight — level with the
   ring's in every theme), the bar joins both, and the margins either side match, so the weight sits on the centre. */
.brand__mark { width: 30px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.3; overflow: visible; }
.brand__line { stroke-dasharray: 15; stroke-dashoffset: 0; transition: stroke-dashoffset 600ms var(--ease-out); }
.brand__dot { fill: var(--accent-text); stroke: none; transform-box: fill-box; transform-origin: center; transition: transform 600ms var(--spring); }
.brand:hover .brand__dot { transform: translateX(-3px) scale(1.15); }
.brand:hover .brand__line { stroke-dashoffset: 3; }

.chapter { height: 16px; overflow: hidden; font: 400 12px/16px var(--mono); color: var(--muted); letter-spacing: .02em;
  padding-left: 16px; border-left: 1px solid var(--rule-2); justify-self: start; min-width: 0; max-width: 100%;
  opacity: 0; transition: opacity 400ms var(--ease-out); }
.mast.is-scrolled .chapter { opacity: 1; }
.chapter__track { display: block; transition: transform var(--t-roll) var(--spring-firm); }
.chapter__track > span { display: block; height: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chapter__track b { font-weight: 500; color: var(--accent-text); margin-right: 8px; }

.nav { position: relative; justify-self: end; }
.nav ul { display: flex; gap: clamp(16px, 2.1vw, 34px); }
.nav__ind { position: absolute; left: 0; bottom: 4px; width: 1px; height: 1.5px; border-radius: 2px; background: var(--accent);
  transform-origin: left; transform: translateX(var(--nx, 0)) scaleX(var(--nw, 0)); opacity: 0; pointer-events: none;
  transition: transform 620ms var(--spring), opacity 300ms var(--ease-out); }
.nav__ind.is-on { opacity: 1; }
.nav a { position: relative; display: inline-flex; align-items: center; padding-block: 10px;
  font: 450 var(--fs-ui)/1.1 var(--sans); color: var(--ink-2); letter-spacing: -.01em; transition: color var(--t-hover); }
.nav a:hover, .nav a.is-current { color: var(--ink); }
.nav .roll::after { font-family: var(--serif); font-style: italic; font-size: 1.08em; letter-spacing: -.01em; }

.mast__tools { display: flex; align-items: center; gap: 4px; justify-self: end; grid-column: -2 / -1; }
.mast__tools .pill { height: 34px; margin-left: 4px; }
.theme-toggle svg circle { stroke: currentColor; }
.theme-toggle__half { fill: currentColor; stroke: none; transform-origin: 10px 10px; transition: transform 800ms var(--spring); }
:root[data-theme="dark"] .theme-toggle__half { transform: rotate(180deg); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .theme-toggle__half { transform: rotate(180deg); } }
.menu-toggle { display: none; }

.mast__progress { position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--accent);
  transform-origin: left; transform: scaleX(var(--progress, 0)); }

/* ----------------------------------------------------------------- menu --- */
.menu { position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0;
  background: transparent; color: var(--ink); overflow: hidden; }
.menu::backdrop { background: transparent; }
.menu__inner {
  position: absolute; inset: 0; display: flex; flex-direction: column; overflow: auto;
  padding: 0 var(--gutter) max(28px, env(safe-area-inset-bottom));
  background: var(--panel-bg);
  clip-path: inset(0 0 100% 0 round 0 0 48px 48px);
  transition: clip-path 820ms var(--ease-io);
}
.menu.is-open .menu__inner { clip-path: inset(0 0 0 0 round 0 0 0 0); }
.menu.is-closing .menu__inner { transition-duration: 560ms; }
.menu__head { flex: none; height: var(--mast-h); display: flex; align-items: center; justify-content: space-between; }
/* numbered by a counter, so the list can't drift out of step with the section numbers */
.menu__list { margin-top: clamp(24px, 6vh, 64px); border-top: 1px solid var(--rule); counter-reset: menu; perspective: 900px; }
.menu__list li { counter-increment: menu; position: relative; border-bottom: 1px solid var(--rule); opacity: 0;
  transform: translateY(40%) rotateX(-70deg); transform-origin: 50% 100%;
  transition: opacity 500ms var(--ease-out), transform 900ms var(--ease-out); transition-delay: calc(260ms + var(--i, 0) * 60ms); }
.menu.is-open .menu__list li { opacity: 1; transform: none; }
.menu.is-closing .menu__list li { opacity: 0; transform: translateY(-20%); transition-delay: calc(var(--i, 0) * 25ms); transition-duration: 260ms; }
.menu__list a { display: flex; align-items: baseline; gap: 18px; padding: 16px 0 14px;
  font: 400 clamp(2.4rem, 10.5vw, 4.25rem)/1 var(--serif); letter-spacing: -.025em; transition: translate 600ms var(--ease-out), color var(--t-hover); }
.menu__list a::before { content: counter(menu, decimal-leading-zero); content: counter(menu, decimal-leading-zero) / "";
  font: 500 11px/1 var(--mono); font-style: normal; letter-spacing: .1em; color: var(--accent-text); width: 26px; flex: none; }
.menu__list a:hover, .menu__list a:focus-visible { translate: .18em 0; font-style: italic; color: var(--accent-text); }
.menu__foot { margin-top: auto; padding-top: 40px; display: flex; flex-wrap: wrap; gap: 10px; opacity: 0; transition: opacity 600ms var(--ease-out) 420ms; }
.menu.is-open .menu__foot { opacity: 1; }
.menu.is-closing .menu__foot { opacity: 0; transition-delay: 0ms; transition-duration: 160ms; }

/* --------------------------------------------------------------- layout --- */
.wrap { width: 100%; max-width: calc(var(--maxw) + 2 * var(--gutter)); margin-inline: auto; padding-inline: var(--gutter); }
.grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--colgap); }
/* chapters are spaced by margin, so a jump to one lands on its running head rather than on empty space */
.section { margin-top: var(--section-y); }

/* chapter heads: a running head (number, name, a rule that draws across, the count) opens every chapter; a
   large outlined numeral drifts behind it, against the scroll */
.chead { position: relative; isolation: isolate; display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--colgap);
  margin-bottom: clamp(48px, 6.5vw, 96px); }
.runhead { grid-column: 1 / -1; display: flex; align-items: center; gap: 14px;
  font: 500 var(--fs-label)/1.3 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--ink); }
.runhead__n { color: var(--accent-text); font-variant-numeric: tabular-nums; }
.runhead__rule { flex: 1; height: 1px; background: var(--rule-2); transform-origin: left; }
.runhead__of { color: var(--muted); font-variant-numeric: tabular-nums; }
.chead__big { position: absolute; right: -.04em; top: .02em; z-index: -1; pointer-events: none;
  font: 400 clamp(7rem, 16vw, 14.5rem)/1 var(--serif); font-variation-settings: "opsz" 72; letter-spacing: -.05em; font-variant-numeric: lining-nums;
  color: transparent; -webkit-text-stroke: 1px var(--rule-2); transform: skewY(calc(var(--skew, 0) * 1deg)); }
.chead__title { grid-column: 1 / span 10; margin-top: clamp(32px, 4.4vw, 64px); font: 400 var(--fs-h2)/1.02 var(--serif);
  font-variation-settings: "opsz" 72; letter-spacing: -.026em; text-wrap: balance; }
.chead__title em, .phead__title em, .about__lede em, .habits__list h4 em { font-style: italic; color: var(--accent-text); }
.chead__lede { grid-column: 7 / -1; margin-top: clamp(24px, 3vw, 44px); font: 400 var(--fs-lede)/1.42 var(--serif);
  font-variation-settings: "opsz" 24; color: var(--ink-2); max-width: 40ch; text-wrap: pretty; }

/* panels: an inverse surface that opens out into the page as it scrolls in and folds back into a card as it
   leaves (--enter and --leave are set by the scroll engine); its content trails the edge, for depth */
.panel { position: relative; isolation: isolate; margin-inline: var(--panel-m); --enter: 1; --leave: 0;
  --open: min(var(--enter), 1 - var(--leave));
  --edge: inset(0 calc((1 - var(--open)) * 5vw) round calc(var(--panel-r) + (1 - var(--open)) * 26px)); }
.panel::before {
  content: ""; position: absolute; inset: 0; z-index: -2; border-radius: var(--panel-r);
  background: var(--panel-bg); box-shadow: inset 0 0 0 1px var(--rule);
  clip-path: var(--edge);
}
.panel::after { /* film grain: one static tile, almost invisible, adds depth to the flat colour */
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: var(--panel-r); pointer-events: none;
  background: var(--grain); opacity: .07; mix-blend-mode: overlay;
  clip-path: var(--edge);
}
.js .panel > .wrap { translate: 0 calc((1 - var(--enter)) * 90px); }

/* =============================================================== INTRO ===
   The name is the picture. Behind it, a slow aura of burgundy, rose and stone drifts on its own, leans towards
   the pointer and sinks with the scroll; a paper grain keeps the colour from looking digital. The page's
   contents sit at the foot, so every chapter is visible from the first screen. */
.hero { position: relative; min-height: 100svh; display: flex; isolation: isolate; overflow: clip;
  padding: calc(var(--mast-h) + clamp(18px, 4vh, 48px)) 0 clamp(16px, 2.6vh, 28px); }
.hero::before { /* paper grain: dark specks on ivory, light specks at night */
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; background: var(--grain); opacity: .08; filter: invert(1); }
:root[data-theme="dark"] .hero::before { filter: none; opacity: .04; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .hero::before { filter: none; opacity: .04; } }
.hero__aura { position: absolute; inset: 0; z-index: -2; pointer-events: none;
  translate: var(--ax, 0px) calc(var(--ay, 0px) + var(--hp, 0) * 18vh); opacity: calc(1 - var(--hp, 0) * .85); }
.hero__aura i { position: absolute; display: block; aspect-ratio: 1; border-radius: 50%; will-change: transform; }
.hero__aura i:nth-child(1) { width: max(62vw, 640px); right: -14vw; top: -26vw;
  background: radial-gradient(closest-side, var(--aura-1), transparent); animation: aura-a 26s var(--ease-soft) infinite alternate; }
.hero__aura i:nth-child(2) { width: max(44vw, 460px); left: 28vw; top: 30%;
  background: radial-gradient(closest-side, var(--aura-2), transparent); animation: aura-b 32s var(--ease-soft) infinite alternate; }
.hero__aura i:nth-child(3) { width: max(52vw, 520px); left: -20vw; bottom: -26vw;
  background: radial-gradient(closest-side, var(--aura-3), transparent); animation: aura-c 38s var(--ease-soft) infinite alternate; }
.hero.is-off .hero__aura i { animation-play-state: paused; }
@keyframes aura-a { to { transform: translate(-7vw, 9vh) scale(1.12); } }
@keyframes aura-b { to { transform: translate(9vw, -7vh) scale(.86); } }
@keyframes aura-c { to { transform: translate(7vw, -10vh) scale(1.1); } }

.hero__inner { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--colgap);
  grid-template-rows: auto minmax(24px, 1fr) auto auto minmax(32px, .7fr) auto; }
.hero__top { grid-column: 1 / -1; grid-row: 1; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 8px 32px;
  translate: 0 calc(var(--hp, 0) * -6vh); }
.hero__eyebrow { display: flex; flex-wrap: wrap; gap: 6px 0;
  font: 500 var(--fs-label)/1.4 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-2); }
.hero__eyebrow > span:not(:last-child)::after { content: ""; display: inline-block; width: 5px; height: 5px; margin: 0 .95em; border-radius: 50%;
  background: var(--accent); vertical-align: 2px; }
.hero__loc { font: 400 12px/1.4 var(--mono); color: var(--muted); }
.hero__name {
  grid-column: 1 / -1; grid-row: 3;
  font: 400 var(--fs-mega)/.86 var(--serif); font-variation-settings: "opsz" 72; letter-spacing: -.038em;
  margin-left: -.055em; translate: 0 calc(var(--hp, 0) * -10vh);
}
.hero__line { display: block; white-space: nowrap; translate: calc(var(--hp, 0) * -9vw) 0; }
.hero__line--2 { padding-left: 23%; translate: calc(var(--hp, 0) * 11vw) 0; }
.hero__word { display: inline-block; }
.hero__word--mid { font-style: italic; letter-spacing: -.045em; color: var(--accent-text); }
.hero__line--2 .hero__word::after { /* the brand's burgundy dot: it lands last, once the letters have settled */
  content: ""; display: inline-block; width: .13em; height: .13em; margin-left: .06em; border-radius: 50%; background: var(--accent);
  vertical-align: .02em; transition: transform var(--t-roll) var(--spring-firm), opacity 300ms var(--ease-out); transition-delay: 900ms; }
.js .hero__name:not(.is-in) .hero__line--2 .hero__word::after { transform: translateY(.6em); opacity: 0; }
.hero__aside { grid-column: 7 / -1; grid-row: 4; align-self: end; margin-top: clamp(28px, 5.5vh, 64px); translate: 0 calc(var(--hp, 0) * -22vh); }
.hero__statement { font: 400 var(--fs-lede)/1.3 var(--serif); font-variation-settings: "opsz" 28; letter-spacing: -.01em;
  max-width: 30ch; text-wrap: pretty; }
.hero__statement em {
  font-style: italic; color: var(--accent-text);
  background: linear-gradient(var(--accent), var(--accent)) 0 96% / var(--u, 100%) 1.5px no-repeat;
  transition: background-size 1200ms var(--ease-out) 1500ms;
}
.js .hero:not(.is-in) .hero__statement em { --u: 0%; }
/* research interests: all of them at once, so they can be read at a glance; lines break only after a separator */
.hero__topics { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--rule-2); max-width: 46ch; }
.hero__topics ul { margin-top: 10px; font: 500 var(--fs-ui)/1.6 var(--sans); letter-spacing: -.005em; color: var(--ink); }
.hero__topics li { display: inline; white-space: nowrap; }
.hero__topics li:not(:last-child)::after { content: " ·" / ""; margin-right: .2em; color: var(--accent-text); }
.hero__cue { grid-column: 1 / span 4; grid-row: 4; align-self: end; justify-self: start; display: inline-flex; align-items: center; gap: 14px;
  font: 500 var(--fs-label)/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-2); padding: 10px 0;
  opacity: calc(1 - var(--hp, 0) * 4); }
.cue { position: relative; width: 1px; height: 46px; background: var(--rule-2); overflow: hidden; }
.cue::after { content: ""; position: absolute; left: 0; top: 0; width: 1px; height: 46%; background: var(--accent);
  animation: cue 2.2s var(--ease-io) infinite; }
@keyframes cue { 0% { transform: translateY(-100%); } 60%, 100% { transform: translateY(220%); } }

/* the contents: every chapter, from the first screen */
.hero__index { grid-column: 1 / -1; grid-row: 6; opacity: calc(1 - var(--hp, 0) * 2.2); }
.hero__index ol { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); column-gap: var(--colgap); }
.hero__index li { position: relative; border-top: 1px solid var(--rule-2); }
.hero__index a { position: relative; display: flex; align-items: baseline; gap: 12px; padding: 14px 0 6px;
  font: 450 var(--fs-ui)/1.2 var(--sans); letter-spacing: -.01em; color: var(--ink-2); transition: color var(--t-hover); }
.hero__index a::before { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 1px; background: var(--accent);
  transform: scaleX(0); transform-origin: right; transition: transform 700ms var(--ease-out); }
.hero__index a::after { content: ""; width: 12px; height: 12px; margin-left: auto; align-self: center; background: currentColor;
  -webkit-mask: var(--i-arrow) center / contain no-repeat; mask: var(--i-arrow) center / contain no-repeat;
  transform: rotate(90deg) translateX(-6px); opacity: 0; transition: transform 600ms var(--ease-out), opacity 300ms var(--ease-out); }
.hero__index a:hover, .hero__index a:focus-visible { color: var(--ink); }
.hero__index a:hover::before, .hero__index a:focus-visible::before { transform: scaleX(1); transform-origin: left; }
.hero__index a:hover::after, .hero__index a:focus-visible::after { transform: rotate(90deg); opacity: 1; }
.hero__index-n { font: 500 11px/1 var(--mono); letter-spacing: .1em; color: var(--accent-text); }
.hero__index-t { transition: translate 600ms var(--ease-out); }
/* the contents sweep in with the intro itself (they sit too low on the screen to wait for their own reveal) */
.js .hero__index li { clip-path: inset(-40px); transition: clip-path 1100ms var(--ease-io), translate 1200ms var(--ease-out), opacity 700ms var(--ease-out);
  transition-delay: calc(900ms + var(--i, 0) * 90ms); }
.js .hero:not(.is-in) .hero__index li { clip-path: inset(-40px 100% -40px -40px); translate: -24px 0; opacity: .3; }
.js .hero.is-done .hero__index li { clip-path: none; transition-delay: 0ms; }
.hero__index a:hover .hero__index-t { translate: .2em 0; }

/* ================================================================ ABOUT === */
/* A portrait beside a short introduction. The photo is shown whole: its frame has the image's own (square)
   proportions, so nothing is cropped at any width; srcset serves a size that matches the frame. */
.about__grid { row-gap: 36px; align-items: start; }
.about__portrait { grid-column: 1 / span 4; }
.about__photo { position: relative; isolation: isolate; aspect-ratio: 1; border-radius: clamp(12px, 1.4vw, 20px); overflow: hidden; background: var(--bg-3); }
.about__photo img { width: 100%; height: 100%; object-fit: cover; }
.about__photo::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px var(--rule); pointer-events: none; }
:root[data-theme="dark"] .about__photo img { filter: brightness(.92); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .about__photo img { filter: brightness(.92); } }
.about__body { grid-column: 6 / -1; }
.about__lede { font: 400 clamp(1.6rem, 1.12rem + 1.4vw, 2.45rem)/1.14 var(--serif); font-variation-settings: "opsz" 48;
  letter-spacing: -.02em; text-wrap: balance; max-width: 22ch; }
.about__text { margin-top: clamp(24px, 2.8vw, 36px); max-width: 60ch; color: var(--ink-2); text-wrap: pretty; }
.about__text p + p { margin-top: .9em; }
.glance { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 28px var(--colgap); margin-top: clamp(40px, 5vw, 64px); }
.glance > div { position: relative; padding-top: 18px; }
.glance > div::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--ink); transform-origin: left; }
.glance dt { margin-bottom: 10px; color: var(--accent-text); }
.glance dd { font: 400 var(--fs-ui)/1.45 var(--sans); color: var(--ink); text-wrap: pretty; }
.glance dd span { display: block; margin-top: 8px; font: 400 12px/1.4 var(--mono); color: var(--muted); }

.habits { margin-top: clamp(96px, 12vw, 176px); row-gap: 28px; }
.habits__h { grid-column: 1 / span 2; padding-top: 22px; }
.habits__list { grid-column: 3 / -1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px var(--colgap); }
.habits__list li { position: relative; padding-top: 20px; border-top: 1px solid var(--rule-2); }
.habits__n { font: 500 11px/1 var(--mono); letter-spacing: .1em; color: var(--accent-text); }
.habits__list h4 { margin-top: 18px; font: 400 clamp(1.35rem, 1.1rem + .9vw, 1.95rem)/1.1 var(--serif); font-variation-settings: "opsz" 36;
  letter-spacing: -.016em; text-wrap: balance; }
.habits__list p { margin-top: 14px; max-width: 34ch; font: 400 var(--fs-small)/1.6 var(--sans); color: var(--ink-2); text-wrap: pretty; }

/* ============================================================= RESEARCH === */
.research { margin-top: var(--section-y); }
.research > .panel { padding: clamp(84px, 11vw, 168px) 0 clamp(72px, 9vw, 150px); }
.research .chead { margin-bottom: 0; }

/* fields: a sticky stage whose one signal line changes meaning as the three areas pass (stage.js) */
.fields { margin-top: clamp(40px, 6vw, 88px); align-items: start; }
.fields__stage { grid-column: 1 / span 6; position: sticky; top: calc(var(--mast-h) + max(24px, (100svh - var(--mast-h) - min(44vw, 560px) * .8) / 2)); }
.stage { position: relative; aspect-ratio: 5 / 4; border-radius: clamp(14px, 1.6vw, 22px); overflow: hidden;
  background: var(--bg-2); box-shadow: inset 0 0 0 1px var(--rule-2); }
.stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.stage__labels { position: absolute; left: clamp(14px, 2vw, 22px); top: clamp(14px, 2vw, 20px); right: 14px; height: 18px;
  font: 500 10.5px/18px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2); }
.stage__labels li { position: absolute; left: 0; top: 0; white-space: nowrap; display: flex; align-items: center; gap: 10px;
  opacity: 0; transform: translateY(10px); transition: opacity 500ms var(--ease-out), transform 700ms var(--ease-out); }
.stage__labels li::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.stage__labels li.is-on { opacity: 1; transform: none; transition-delay: 160ms; }
.stage__count { position: absolute; right: clamp(14px, 2vw, 22px); top: clamp(14px, 2vw, 20px); font: 500 10.5px/18px var(--mono);
  letter-spacing: .1em; color: var(--muted); font-variant-numeric: tabular-nums; }
.fields__steps { grid-column: 8 / -1; }
/* every step is fully legible at all times; the one the stage is drawing is marked by its rule */
.step { position: relative; padding-block: clamp(28px, 3.2vw, 44px); border-top: 1px solid var(--rule-2); }
.step:first-child { padding-top: clamp(20px, 2.4vw, 32px); }
.js .step::before { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 1px; background: var(--accent);
  transform: scaleX(0); transform-origin: left; transition: transform 1100ms var(--ease-io); }
.js .step.is-active::before { transform: none; }
.js [data-fields] .stage { scale: calc(.9 + var(--enter, 1) * .1); transform-origin: 50% 0; }
.step__n { display: flex; align-items: center; gap: 12px; font: 500 var(--fs-label)/1 var(--mono); letter-spacing: .12em; color: var(--f, var(--accent-text)); }
.step__n::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--f, var(--accent)); }
.step h3 { margin: 16px 0; font: 400 var(--fs-h3)/1.1 var(--serif); font-variation-settings: "opsz" 36; letter-spacing: -.015em; }
.step p { color: var(--ink-2); max-width: 42ch; text-wrap: pretty; }
.step__tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 20px; }
.step__tags li { font: 400 12px/1 var(--mono); padding: 8px 11px; border: 1px solid var(--rule-2); border-radius: 999px; color: var(--ink-2); }
.step .step__where { margin-top: 18px; color: var(--muted); }
.no-js .fields__stage { display: none; }
.no-js .fields__steps { grid-column: 1 / -1; }

/* projects: a deck. On large screens each card sticks a little lower than the one before and the next slides
   over it; a covered card recedes (main.js sets --cover from 0 to 1). Without room for that, a plain stack. */
.projects { margin-top: var(--section-y); }
.phead { row-gap: 18px; align-items: end; margin-bottom: clamp(40px, 6vw, 80px); }
.phead__label { grid-column: 1 / -1; color: var(--accent-text); }
.phead__title { grid-column: 1 / span 7; font: 400 var(--fs-h2)/1.02 var(--serif); font-variation-settings: "opsz" 72; letter-spacing: -.026em; text-wrap: balance; }
.phead__lede { grid-column: 9 / -1; color: var(--ink-2); max-width: 34ch; text-wrap: pretty; }
.deck { display: grid; gap: clamp(16px, 2.4vw, 28px); }
.card { --cover: 0; }
.card__in { position: relative; isolation: isolate; display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--colgap);
  row-gap: 28px; align-items: center; padding: clamp(14px, 2vw, 28px); border-radius: clamp(20px, 2.4vw, 34px);
  background: var(--bg-2); box-shadow: inset 0 0 0 1px var(--rule), var(--shadow); transform-origin: 50% 0; }
.card__in::after { content: ""; position: absolute; inset: 0; z-index: 4; border-radius: inherit; background: var(--shade); pointer-events: none;
  opacity: var(--cover); }
.card__plate { grid-column: 1 / span 6; }
.card__body { grid-column: 7 / -1; padding: clamp(8px, 1.4vw, 20px) clamp(4px, 2.4vw, 40px) clamp(8px, 1.4vw, 20px) 0; }
.card__n { position: absolute; top: clamp(20px, 2.6vw, 38px); right: clamp(22px, 2.8vw, 42px);
  font: 500 10.5px/1 var(--mono); letter-spacing: .1em; color: var(--muted); font-variant-numeric: tabular-nums; }
.card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 0; padding-right: 64px;
  font: 500 var(--fs-label)/1.4 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.card__meta span:not(:last-child)::after { content: "/"; margin: 0 .8em; color: var(--rule-2); }
.field { display: inline-flex; align-items: center; gap: 9px; color: var(--ink-2); }
.field::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--f, var(--accent)); }
.card__title { margin: 18px 0 18px; font: 400 clamp(1.85rem, 1.15rem + 2.3vw, 3.3rem)/1.04 var(--serif); font-variation-settings: "opsz" 72;
  letter-spacing: -.024em; text-wrap: balance; }
.card__text { max-width: 50ch; color: var(--ink-2); text-wrap: pretty; }
.card__out { position: relative; margin-top: 18px; padding-left: 18px; max-width: 52ch; font: 400 var(--fs-small)/1.55 var(--sans); color: var(--ink); text-wrap: pretty; }
.card__out::before { content: ""; position: absolute; left: 0; top: .25em; bottom: .25em; width: 2px; border-radius: 2px; background: var(--accent); }
.card__out i { font-size: 1.07em; }
.card__body .acts { margin-top: 20px; }

.plate { position: relative; aspect-ratio: 16 / 11; border-radius: clamp(12px, 1.4vw, 20px); overflow: hidden; background: var(--bg);
  box-shadow: inset 0 0 0 1px var(--rule); display: grid; place-items: center;
  transform: perspective(1100px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); transition: transform 900ms var(--ease-out); }
.plate.is-tilting { transition-duration: 160ms; }
.plate::after { /* a soft sheen that follows the pointer while the plate tilts */
  content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none; border-radius: inherit; opacity: 0;
  background: radial-gradient(45% 55% at var(--gx, 50%) var(--gy, 50%), rgb(255 255 255 / .18), transparent 70%);
  transition: opacity 500ms var(--ease-out); }
.plate.is-tilting::after { opacity: 1; }
.plate .glyph { width: 100%; height: 100%; }
.plate__cap { position: absolute; left: 16px; bottom: 13px; z-index: 2; font: 500 10px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.no-js .card__plate:has(.plate--glyph) { display: none; }
.no-js .card__plate:has(.plate--glyph) + .card__body { grid-column: 1 / -1; }

/* video (inside a plate) */
.video { width: 100%; height: 100%; position: relative; background: #fff; }
.video__poster, .video video { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.video video { object-fit: contain; background: #fff; }
.video__poster { z-index: 1; }
.video__poster img { width: 100%; height: 100%; object-fit: cover; transform: scale(1.18); transition: transform 1400ms var(--ease-out); }
.video__poster:hover img { transform: scale(1.24); }
.video__play { position: absolute; left: 50%; top: 50%; display: inline-flex; align-items: center; gap: 10px; padding: 0 18px 0 8px; height: 48px;
  translate: -50% -50%; border-radius: 999px; background: #221418; color: #f6f1ea; box-shadow: 0 18px 40px -16px rgb(0 0 0 / .5);
  font: 500 12px/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; transition: transform var(--t-ui) var(--spring); }
.video__play svg { width: 32px; height: 32px; padding: 9px 8px 9px 10px; border-radius: 50%; background: #9b2a44; fill: #fbf3ec; }
.video__poster:hover .video__play, .video__poster:focus-visible .video__play { transform: scale(1.06); }
.video.is-playing .video__poster { pointer-events: none; animation: poster-out 380ms var(--ease-in) forwards; }
@keyframes poster-out { to { opacity: 0; } }
:root[data-theme="dark"] .video { filter: brightness(.92); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .video { filter: brightness(.92); } }
.no-js .video__poster { display: none; }

/* ========================================================= PUBLICATIONS === */
.filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: clamp(32px, 4vw, 52px); }
.filters button { position: relative; display: inline-flex; align-items: center; gap: 8px; height: 38px; padding: 0 16px; border-radius: 999px;
  border: 1px solid var(--rule-2); font: 500 var(--fs-small)/1 var(--sans); color: var(--ink-2); isolation: isolate; overflow: hidden;
  transition: color 400ms, border-color 400ms, transform var(--t-hover) var(--spring-firm); }
.filters button::before { content: ""; position: absolute; inset: 0; z-index: -1; background: var(--accent); transform: translateY(101%);
  transition: transform 520ms var(--ease-io); }
.filters button sup { font: 500 10px/1 var(--mono); color: var(--muted); transition: color 400ms; }
.filters button:hover { border-color: var(--accent); color: var(--ink); }
.filters button:active { transform: scale(.96); }
.filters button[aria-pressed="true"] { color: var(--on-accent); border-color: var(--accent); }
.filters button[aria-pressed="true"]::before { transform: none; }
.filters button[aria-pressed="true"] sup { color: var(--on-accent); opacity: .75; }
.filters__count { margin-left: auto; }
.no-js .filters { display: none; }

.years { border-top: 1px solid var(--ink); }
.year { padding: clamp(22px, 2.6vw, 34px) 0 0; border-bottom: 1px solid var(--rule); }
.year__y { grid-column: 1 / span 3; align-self: start; position: sticky; top: calc(var(--mast-h) + 28px);
  font: 400 clamp(3rem, 2rem + 4vw, 6.2rem)/.9 var(--serif); font-variation-settings: "opsz" 72; letter-spacing: -.045em;
  font-variant-numeric: lining-nums; padding-bottom: 24px; color: var(--ink); transition: color 500ms; }
.year:hover .year__y { color: var(--accent-text); }
.year__list { grid-column: 4 / -1; }
.pub { position: relative; padding: clamp(22px, 2.4vw, 32px) 0; border-top: 1px solid var(--rule); isolation: isolate; }
.year__list .pub:nth-child(1 of :not([hidden])) { border-top: 0; padding-top: 6px; }
.pub::before { content: ""; position: absolute; inset: 0 calc(-1 * clamp(10px, 1.4vw, 20px)); z-index: -1; background: var(--bg-2);
  border-radius: 8px; opacity: 0; transform: scaleX(.97); transition: opacity var(--t-ui) var(--ease-out), transform var(--t-ui) var(--ease-out); }
.pub:hover::before { opacity: 1; transform: none; }
.pub:target::before { opacity: 1; transform: none; background: var(--accent-wash); }
.pub__title { font: 400 clamp(1.15rem, 1.04rem + .5vw, 1.5rem)/1.28 var(--serif); font-variation-settings: "opsz" 28; letter-spacing: -.01em;
  max-width: 60ch; text-wrap: pretty; }
.pub__authors { margin-top: 10px; font: 400 var(--fs-small)/1.5 var(--sans); color: var(--muted); }
.pub__authors b { color: var(--ink); font-weight: 600; }
.pub__venue { margin-top: 3px; font: 400 var(--fs-small)/1.6 var(--sans); color: var(--ink-2); }
.pub__venue i { font-family: var(--serif); font-size: 1.07em; }
.pub__venue .tag { margin-left: 8px; }
.pub__note { margin-top: 12px; font: 400 var(--fs-small)/1.5 var(--sans); color: var(--ink-2); }
.pub__note::before { content: ""; display: inline-block; width: 7px; height: 7px; margin-right: 10px; border-radius: 50%; background: var(--accent); vertical-align: 1px; }

/* =========================================================== EXPERIENCE === */
.experience { margin-top: var(--section-y); }
.experience > .panel { padding: clamp(84px, 11vw, 160px) 0 clamp(72px, 9vw, 140px); }
.path { position: relative; --p: 0; }
.path__places { display: grid; grid-template-columns: 2fr 3fr; column-gap: var(--colgap); margin-bottom: 26px; }
.path__places span { position: relative; padding-bottom: 12px; font: 400 var(--fs-h3)/1 var(--serif); font-style: italic; letter-spacing: -.01em; color: var(--ink-2); }
.path__places span::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 7px; border: 1px solid var(--rule-2); border-bottom: 0; }
.path__rail { position: relative; height: 1px; background: var(--rule-2); }
.path__fill { position: absolute; inset: 0; background: var(--accent); transform-origin: left; transform: scaleX(var(--p)); }
.path__stops { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); column-gap: var(--colgap); }
.stop { position: relative; padding-top: 30px; }
.stop::before { content: ""; position: absolute; left: 0; top: -6px; width: 11px; height: 11px; border-radius: 50%; background: var(--bg);
  box-shadow: inset 0 0 0 1.5px var(--rule-2); transition: box-shadow 400ms, background-color 400ms, transform 600ms var(--spring); }
.stop time { display: block; font: 400 clamp(2rem, 1.3rem + 2vw, 3.4rem)/1 var(--serif); font-variant-numeric: lining-nums; letter-spacing: -.03em;
  color: var(--muted); transition: color 500ms; }
.stop b { display: block; margin-top: 14px; font: 500 var(--fs-ui)/1.3 var(--sans); letter-spacing: -.01em; }
.stop span { display: block; margin-top: 6px; font: 400 var(--fs-small)/1.45 var(--sans); color: var(--ink-2); text-wrap: pretty; }
.stop small { display: block; margin-top: 10px; font: 400 11.5px/1.45 var(--mono); color: var(--muted); }
.js .stop time { transition: color 500ms, translate 900ms var(--spring); }
.js .stop:not(.is-reached) time { translate: 0 .25em; }
.stop.is-reached::before, .no-js .stop::before { background: var(--accent); box-shadow: 0 0 0 5px var(--accent-wash); transform: scale(1.1); }
.stop.is-reached time, .no-js .stop time { color: var(--ink); }
.stop--now.is-reached time { color: var(--accent-text); font-style: italic; }
.stop--now.is-reached::after { content: ""; position: absolute; left: -6px; top: -12px; width: 23px; height: 23px; border-radius: 50%;
  border: 1px solid var(--accent); animation: ping 2.4s var(--ease-out) infinite; }
@keyframes ping { 0% { transform: scale(.5); opacity: 1; } 80%, 100% { transform: scale(1.8); opacity: 0; } }

.trio { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 48px var(--colgap); margin-top: clamp(80px, 10vw, 140px); }
.trio h3 { padding-bottom: 14px; margin-bottom: 6px; border-bottom: 1px solid var(--ink); color: var(--accent-text); }
.plain li { padding: 12px 0; border-bottom: 1px solid var(--rule-2); font-size: var(--fs-ui); line-height: 1.4; }
.plain li span { display: block; margin-top: 3px; font: 400 12px/1.4 var(--mono); color: var(--muted); }
.plain i { font-family: var(--serif); font-size: 1.06em; }
.tools { display: flex; flex-wrap: wrap; gap: 7px; padding-top: 14px; }
.tools li { padding: 7px 11px; border: 1px solid var(--rule-2); border-radius: 999px; font: 400 12px/1 var(--mono); color: var(--ink-2);
  transition: border-color var(--t-hover), color var(--t-hover), background-color var(--t-hover); }
.tools li:hover { border-color: var(--accent); color: var(--accent-text); background: var(--accent-wash); }

/* ============================================================= CONTACT === */
.foot { margin: var(--section-y) var(--panel-m) var(--panel-m); padding: clamp(84px, 11vw, 160px) 0 clamp(22px, 3vw, 32px); }
.next { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--colgap); row-gap: 24px; margin-top: clamp(48px, 6vw, 88px); }
.next__h { grid-column: 1 / span 3; padding-top: 20px; color: var(--accent-text); }
.next__list { grid-column: 4 / -1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 32px var(--colgap); }
.next__list li { padding-top: 18px; border-top: 1px solid var(--rule-2); }
.next__list span { font: 500 11px/1 var(--mono); letter-spacing: .1em; color: var(--accent-text); }
.next__list p { margin-top: 14px; font: 400 clamp(1.25rem, 1.05rem + .8vw, 1.75rem)/1.18 var(--serif); font-variation-settings: "opsz" 36;
  letter-spacing: -.014em; text-wrap: balance; }
.foot__grid { align-items: end; margin-top: clamp(80px, 10vw, 152px); }
.foot__lead { grid-column: 1 / span 7; max-width: 48ch; color: var(--ink-2); font: 400 var(--fs-lede)/1.42 var(--serif);
  font-variation-settings: "opsz" 24; text-wrap: pretty; }
.foot__mailwrap { grid-column: 1 / -1; margin-top: clamp(40px, 6vw, 84px); }
.foot__mail { display: inline-flex; align-items: center; gap: .32em; max-width: 100%;
  font: 400 clamp(1.45rem, .5rem + 4.6vw, 5.1rem)/1.1 var(--serif); font-variation-settings: "opsz" 72; letter-spacing: -.028em;
  background: linear-gradient(var(--accent), var(--accent)) 0 100% / 0 2px no-repeat, linear-gradient(var(--rule-2), var(--rule-2)) 0 100% / 100% 1px no-repeat;
  padding-bottom: .1em; transition: background-size 800ms var(--ease-out), color var(--t-hover), translate 700ms var(--spring); }
.foot__mail:hover, .foot__mail:focus-visible { background-size: 100% 2px, 100% 1px; }
.foot__mail i { flex: none; display: grid; place-items: center; width: 1.05em; height: 1.05em; border-radius: 50%; background: var(--accent);
  transition: transform 800ms var(--spring); }
.foot__mail i::before { content: ""; width: 42%; height: 42%; background: var(--on-accent); -webkit-mask: var(--i-out) center / contain no-repeat; mask: var(--i-out) center / contain no-repeat; }
.foot__mail:hover i { transform: rotate(45deg) scale(1.06); }
.foot__mail .mp { white-space: nowrap; }
.foot__mail .ch { transition: transform 500ms var(--spring), color 300ms; transition-delay: calc(var(--ci, 0) * 14ms); }
.foot__mail:hover .ch { transform: translateY(-.06em); }
.foot__links { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 10px; margin-top: clamp(36px, 5vw, 60px); }
.foot__base { grid-column: 1 / -1; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 10px 32px;
  margin-top: clamp(64px, 10vw, 140px); padding-top: 20px; border-top: 1px solid var(--rule-2); font: 400 12px/1.5 var(--mono); color: var(--muted); }
.foot__base a { color: var(--ink-2); transition: color var(--t-hover); }
.foot__base a:hover, .foot__base a:focus-visible { color: var(--accent-text); }
.foot__top span { display: inline-block; transition: transform 500ms var(--spring); }
.foot__top:hover span { transform: translateY(-3px); }
.clock { color: var(--ink-2); font-variant-numeric: tabular-nums; }
.clock__dot { display: inline-block; width: 6px; height: 6px; margin-right: 8px; border-radius: 50%; background: var(--accent); vertical-align: 1px;
  box-shadow: 0 0 0 3px var(--accent-wash); animation: blink 2.4s var(--ease-soft) infinite; }
@keyframes blink { 50% { opacity: .25; } }

.toast { position: fixed; left: 50%; bottom: 24px; z-index: 60; padding: 11px 18px; border-radius: 999px; background: var(--accent); color: var(--on-accent);
  font: 500 13px/1 var(--sans); box-shadow: var(--shadow); pointer-events: none;
  opacity: 0; transform: translate(-50%, 16px) scale(.98); transition: opacity 260ms var(--ease-in), transform 260ms var(--ease-in); }
.toast.is-on { opacity: 1; transform: translate(-50%, 0); transition: opacity 360ms var(--ease-out), transform 620ms var(--spring); }

/* ============================================================== CURSOR === */
/* Fine pointers only (built by JS): a ring that trails the pointer and locks onto a control — taking its shape —
   while the pointer is over it. The native cursor stays: the ring is an addition, never a replacement. */
.cursor { position: fixed; left: 0; top: 0; z-index: 80; width: 28px; height: 28px; border-radius: 14px; pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent); opacity: 0;
  transition: opacity 300ms var(--ease-out), border-color 300ms, background-color 300ms, scale 400ms var(--spring); }
.cursor.is-on { opacity: 1; }
.cursor.is-locked { border-color: var(--accent); background: var(--accent-wash); }
.cursor.is-down { scale: .86; }
.cursor.is-away { opacity: 0; }

/* ============================================================== GLYPHS === */
/* Small schematic drawings for each project (injected by JS). Animations only run while visible (.is-live). */
.glyph { display: grid; place-items: center; color: var(--ink); }
.glyph svg { width: 88%; height: auto; overflow: visible; }
.glyph * { animation-play-state: paused !important; }
.glyph.is-live * { animation-play-state: running !important; }
.g-faint { fill: none; stroke: var(--rule-2); stroke-width: 1; }
.g-grid { fill: var(--rule-2); }
.g-ink { fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.g-acc { fill: none; stroke: var(--accent); stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.g-node { fill: var(--bg); stroke: var(--ink); stroke-width: 1.5; }
.g-anode { fill: var(--accent); }
.g-wash { fill: var(--accent-wash); }
.g-bar { fill: var(--ink); opacity: .16; }
.g-flag { fill: var(--accent); }
.g-text { font: 500 9px var(--mono); letter-spacing: .08em; fill: var(--muted); text-transform: uppercase; }
.g-atext { font: 500 9px var(--mono); letter-spacing: .08em; fill: var(--accent-text); text-transform: uppercase; }
.g-draw { stroke-dasharray: 1 1; stroke-dashoffset: 1; animation: g-draw 4.2s var(--ease-io) infinite; }
@keyframes g-draw { 0% { stroke-dashoffset: 1; } 45%, 82% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: -1; } }
.g-pop { transform-box: fill-box; transform-origin: center; animation: g-pop 4.2s var(--ease-out) infinite; animation-delay: calc(var(--k, 0) * 260ms + 600ms); }
@keyframes g-pop { 0%, 8% { transform: scale(0); } 18%, 78% { transform: scale(1); } 92%, 100% { transform: scale(0); } }
.g-pulse { transform-box: fill-box; transform-origin: center; animation: g-pulse 2.4s var(--ease-out) infinite; animation-delay: calc(var(--k, 0) * 300ms); }
@keyframes g-pulse { 0% { transform: scale(.4); opacity: 0; } 30% { opacity: 1; } 100% { transform: scale(1.25); opacity: 0; } }
.g-scan { animation: g-scan 4.8s var(--ease-soft) infinite; }
@keyframes g-scan { 0% { transform: translateX(0); } 50% { transform: translateX(232px); } 100% { transform: translateX(0); } }
.g-blink { animation: g-blink 4.8s var(--ease-out) infinite backwards; animation-delay: var(--at, 0s); }
@keyframes g-blink { 0% { opacity: 1; } 55%, 100% { opacity: .35; } }
.g-wave { animation: g-wave 2.6s linear infinite; }
@keyframes g-wave { to { transform: translateX(-80px); } }
.g-arc { animation: g-arc 2.4s var(--ease-out) infinite; animation-delay: calc(var(--k, 0) * 240ms); opacity: 0; }
@keyframes g-arc { 0% { opacity: 0; } 30% { opacity: 1; } 70%, 100% { opacity: 0; } }
.g-step { transform: translate(var(--x1), var(--y1)); animation: g-step 6.4s var(--ease-io) infinite; }
@keyframes g-step { 0%, 22% { transform: translate(var(--x0), var(--y0)); } 33%, 55% { transform: translate(var(--x1), var(--y1)); }
  66%, 88% { transform: translate(var(--x2), var(--y2)); } 100% { transform: translate(var(--x0), var(--y0)); } }

/* ============================================================== MOTION ===
   Entrance states apply only with JS (html.js) and are lifted by .is-in (and, ~2.4 s later, .is-done, which drops
   clips and delays so hover states respond at once). One language — things settle into place, the way ink
   settles into paper — spoken in several registers, so chapters don't all arrive on the same beat:
     roll      words roll up out of their masks while their letters decode — the masthead's chapter label, and
               the intro name at display size
     unfold    heading lines rise from their masks word by word; the emphasis drifts in last, softening into focus
     decode    mono labels resolve out of noise, and the running head's rule draws across (main.js)
     wipe      a block is uncovered top to bottom while it settles (ledes, statements)
     aperture  plates open from a small window while their picture settles (project plates)
     sweep     rows are wiped in along their rules, one after another; pill groups pop instead (lists)
     rules     hairlines draw from the left, then their text arrives (facts, the active research area)
     depth     scroll moves layers at different rates; panels open out and fold back; the deck stacks (main.js) */
.js [data-reveal] { transition-delay: calc(var(--d, 0) * 110ms); }

/* fade — small elements that follow a larger entrance */
.js [data-reveal="fade"] { transition: opacity 900ms var(--ease-out) calc(var(--d, 0) * 110ms), translate 1100ms var(--ease-out) calc(var(--d, 0) * 110ms); }
.js [data-reveal="fade"]:not(.is-in) { opacity: 0; translate: 0 10px; }

/* decode — the text itself is scrambled by main.js; here: it appears, and the running head's rule draws */
.js [data-reveal="decode"] { transition: opacity 240ms linear calc(var(--d, 0) * 110ms); }
.js [data-reveal="decode"]:not(.is-in) { opacity: 0; }
.js [data-reveal="decode"] .runhead__rule { transition: transform 1500ms var(--ease-io) calc(var(--d, 0) * 110ms + 200ms); }
.js [data-reveal="decode"]:not(.is-in) .runhead__rule { transform: scaleX(0); }

/* wipe — clipped kinds start merely hidden: the intersection observer honours a target's own clip-path, so a block
   clipped to nothing would never count as visible. main.js adds .is-pre (the clipped start, no transition) and
   .is-in in the next style pass. */
.js [data-reveal="wipe"] { clip-path: inset(-1em); transition: clip-path 1300ms var(--ease-io), translate 1400ms var(--ease-out);
  transition-delay: calc(var(--d, 0) * 110ms); }
.js [data-reveal="wipe"]:not(.is-pre) { opacity: 0; }
.js [data-reveal="wipe"].is-pre:not(.is-in) { clip-path: inset(-1em -1em 100% -1em); translate: 0 22px; transition: none; }
.js [data-reveal="wipe"].is-done { clip-path: none; }

/* aperture (clipped start: see wipe) */
.js [data-reveal="plate"] { clip-path: inset(-24px); transition: clip-path 1500ms var(--ease-io) calc(var(--d, 0) * 110ms); }
.js [data-reveal="plate"]:not(.is-pre) { opacity: 0; }
.js [data-reveal="plate"].is-pre:not(.is-in) { clip-path: inset(22% 26% 22% 26% round 28px); transition: none; }
.js [data-reveal="plate"].is-done { clip-path: none; }
.js [data-reveal="plate"] :is(.plate, .about__photo) > * { transition: transform 2100ms var(--ease-out); }
.js [data-reveal="plate"]:not(.is-in) :is(.plate, .about__photo) > * { transform: scale(1.32); }

/* unfold: headings are split into lines (each its own mask) and words by JS */
.js [data-reveal="lines"] { transition: none; }
.line { display: block; overflow: clip; overflow-clip-margin: .25em; padding-bottom: .08em; margin-bottom: -.08em; }
.js [data-reveal="lines"]:not(.is-split) { visibility: hidden; }
.line__inner { display: block; }
.js [data-reveal="lines"] .w { display: inline-block; transform-origin: 0 100%;
  transition: transform 1150ms var(--ease-out), opacity 700ms var(--ease-out), filter 1000ms var(--ease-out);
  transition-delay: calc(var(--d, 0) * 110ms + var(--li, 0) * 120ms + var(--wi, 0) * 36ms); }
.js [data-reveal="lines"]:not(.is-in) .w { transform: translateY(105%) rotate(4deg); }
.js [data-reveal="lines"] :is(em.w, .w:has(> em)) { transition-delay: calc(var(--d, 0) * 110ms + var(--li, 0) * 120ms + var(--wi, 0) * 36ms + 240ms); }
.js [data-reveal="lines"]:not(.is-in) :is(em.w, .w:has(> em)) { transform: translate(.5em, 30%); opacity: 0; filter: blur(10px); }

/* intro name, roll + decode: the masthead's chapter label, at display size. The name is split into letters (a hidden
   copy keeps the text for assistive tech). Each word rolls up out of its mask on the label's beat (--t-roll,
   --spring-firm) while its letters resolve out of noise from the left — the same scramble as every decode (main.js).
   The noise is drawn over each letter (data-n) rather than in its place, so the name keeps its widths and never
   reflows. The burgundy dot lands once the letters have settled; afterwards, letters near the pointer lift a little
   (--lift, main.js). */
.js [data-split="chars"]:not(.is-split) { visibility: hidden; }
.cw { display: inline-block; overflow: clip; overflow-clip-margin: .22em; padding-bottom: .12em; margin-bottom: -.12em; vertical-align: top; }
.ch { display: inline-block; }
.hero__name .ch { position: relative; translate: 0 calc(var(--lift, 0) * -.1em); }
.js .hero__name .ch { transition: transform var(--t-roll) var(--spring-firm), translate 450ms var(--ease-out), color 300ms;
  transition-delay: calc(var(--wi, 0) * 120ms), 0ms, 0ms; }
.js .hero__name:not(.is-in) .ch { transform: translateY(108%); }
/* like a monospaced cell, each letter's box frames its own noise (clip-path, which leaves the baseline alone) */
.hero__name .ch[data-n] { -webkit-text-fill-color: transparent; clip-path: inset(-.3em 0); }
.hero__name .ch[data-n]::after { content: attr(data-n); position: absolute; left: 50%; top: 0; translate: -50% 0; white-space: pre;
  -webkit-text-fill-color: currentColor; opacity: .5; }
.js .hero__name.is-settled .ch { transition: transform 600ms var(--spring), translate 450ms var(--ease-out), color 300ms; transition-delay: 0ms; }
@media (hover: hover) { .hero__name.is-settled .ch:hover { transform: translateY(-.07em); color: var(--accent-text); } }

/* sweep: list rows are wiped in along their rules, left to right, one after another */
.js [data-reveal="stagger"] { transition: none; }
.js [data-reveal="stagger"] > * { clip-path: inset(-40px); transition: clip-path 1100ms var(--ease-io), translate 1200ms var(--ease-out), opacity 700ms var(--ease-out);
  transition-delay: calc(var(--d, 0) * 110ms + var(--i, 0) * 95ms); }
.js [data-reveal="stagger"]:not(.is-in) > * { clip-path: inset(-40px 100% -40px -40px); translate: -32px 0; opacity: .3; }
.js [data-reveal="stagger"].is-done > * { clip-path: none; transition-delay: 0ms; }
/* groups of pills pop in on a spring instead */
.js :is(.foot__links, .filters)[data-reveal="stagger"] > * { clip-path: none; translate: none;
  transition: opacity 500ms var(--ease-out), scale 950ms var(--spring), border-color var(--t-hover), color 420ms var(--ease-out), transform var(--t-hover) var(--spring-firm);
  transition-delay: calc(var(--d, 0) * 110ms + var(--i, 0) * 60ms), calc(var(--d, 0) * 110ms + var(--i, 0) * 60ms), 0ms, 0ms, 0ms; }
.js :is(.foot__links, .filters)[data-reveal="stagger"]:not(.is-in) > * { opacity: 0; scale: .4; }
.js :is(.foot__links, .filters)[data-reveal="stagger"].is-done > * { transition-delay: 0ms; }

/* rules that draw: borders made of pseudo-elements grow from the left, then their text arrives */
.js [data-reveal="rules"] { transition: none; }
.js [data-reveal="rules"] > * { transition: opacity 900ms var(--ease-out), translate 1000ms var(--ease-out); transition-delay: calc(var(--i, 0) * 90ms + 260ms); }
.js [data-reveal="rules"] > *::before { transition: transform 1100ms var(--ease-io); transition-delay: calc(var(--i, 0) * 90ms); }
.js [data-reveal="rules"]:not(.is-in) > * { opacity: 0; translate: 0 12px; }
.js [data-reveal="rules"]:not(.is-in) > *::before { transform: scaleX(0); opacity: 1; }

/* the intro's aura blooms in, one wash after another */
.js .hero__aura i { transition: opacity 2000ms var(--ease-out), scale 2600ms var(--ease-out); }
.js .hero__aura i:nth-child(2) { transition-delay: 200ms; }
.js .hero__aura i:nth-child(3) { transition-delay: 400ms; }
.js .hero:not(.is-in) .hero__aura i { opacity: 0; scale: .7; }


/* the deck: covered cards recede (only where the cards stack) */
@media (min-width: 1000px) and (min-height: 680px) {
  .deck { gap: 12vh; padding-bottom: 4vh; }
  .card { position: sticky; top: calc(var(--mast-h) + 24px + var(--k) * 14px); }
  .card__in { min-height: min(580px, calc(100svh - var(--mast-h) - 120px)); }
  .js .card__in { scale: calc(1 - var(--cover) * .05); }
  .deck.is-measuring .card, .deck.is-flat .card { position: static; }
  .deck.is-flat { gap: clamp(16px, 2.4vw, 28px); }
}

/* settle content instantly (before in-page jumps) */
.no-anim, .no-anim *, .no-anim *::before, .no-anim *::after { transition: none !important; animation: none !important; }

/* without JavaScript (or if it failed to load): hide controls that need it */
.no-js .theme-toggle, .no-js .menu-toggle, .no-js button[data-cite], .no-js .hero__cue, .no-js .chapter { display: none; }
.no-js .mast { position: absolute; }

/* theme switch: the new theme opens as a circle from the toggle (main.js animates it) */
::view-transition-old(root), ::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
::view-transition-old(mast), ::view-transition-new(mast) { animation: none; }

/* ========================================================== RESPONSIVE === */
@media (max-width: 1180px) {
  .chapter { display: none; }
  .nav ul { gap: 18px; }
}
@media (max-width: 1080px) {
  .hero__aside { grid-column: 6 / -1; }
  .about__portrait { grid-column: 1 / span 5; }
  .about__body { grid-column: 6 / -1; }
  .glance { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .habits__h { grid-column: 1 / -1; padding-top: 0; }
  .habits__list { grid-column: 1 / -1; }
  .next__h { grid-column: 1 / -1; padding-top: 0; }
  .next__list { grid-column: 1 / -1; }
  .card__plate { grid-column: 1 / span 5; }
  .card__body { grid-column: 6 / -1; }
  .phead__title { grid-column: 1 / span 8; }
}
@media (max-width: 860px) {
  .nav { display: none; }
  .menu-toggle { display: inline-grid; }
  .mast__bar { grid-template-columns: auto minmax(0, 1fr) auto; }
  .no-js .nav { display: block; grid-column: 1 / -1; overflow-x: auto; }
  .no-js .mast { position: static; }
  .no-js .mast__bar { height: auto; padding-block: 14px; row-gap: 10px; }
  .no-js .hero { padding-top: 40px; }

  .chead__title { grid-column: 1 / -1; }
  .chead__lede { grid-column: 1 / -1; }
  .phead__title, .phead__lede { grid-column: 1 / -1; }
  /* one column: the stage introduces the three areas instead of following them down the page */
  .fields__stage { grid-column: 1 / -1; position: relative; top: auto; margin-bottom: 12px; }
  .stage { aspect-ratio: 16 / 10; }
  .fields__steps { grid-column: 1 / -1; }

  .card__in { row-gap: 22px; }
  .card__plate, .card__body { grid-column: 1 / -1; }
  .card__body { padding: 4px clamp(4px, 2vw, 12px) 12px; }
  .card__n { top: auto; bottom: clamp(20px, 3vw, 28px); }

  .habits__list, .next__list { grid-template-columns: minmax(0, 1fr); gap: 28px; }

  .path__places { display: none; }
  .path { padding-left: 34px; }
  .path__rail { position: absolute; left: 5px; top: 6px; bottom: 6px; width: 1px; height: auto; }
  .path__fill { transform-origin: top; transform: scaleY(var(--p)); }
  .path__stops { grid-template-columns: minmax(0, 1fr); row-gap: 34px; }
  .stop { padding-top: 0; }
  .stop::before { left: -34px; top: .35em; }
  .stop--now.is-reached::after { left: -40px; top: calc(.35em - 6px); }
  .stop time { font-size: 2.1rem; }

  .trio { grid-template-columns: minmax(0, 1fr); }
  .year__y { grid-column: 1 / -1; position: static; padding-bottom: 8px; }
  .year__list { grid-column: 1 / -1; }
  .foot__lead { grid-column: 1 / -1; }
}
@media (max-width: 760px) {
  :root { --mast-h: 58px; }
  .mast__tools .pill { display: none; }
  .hero__inner { grid-template-rows: auto minmax(20px, 1fr) auto auto auto minmax(28px, .5fr) auto; }
  .hero__loc { display: none; }
  .hero__name { line-height: .9; }
  .hero__word { display: block; }
  .hero__line { translate: calc(var(--hp, 0) * -14vw) 0; }
  .hero__line--2 { translate: calc(var(--hp, 0) * 14vw) 0; }
  .hero__word--mid { padding-left: 15%; }
  .hero__line--2 { padding-left: 4%; }
  .hero__aside { grid-column: 1 / -1; grid-row: 4; margin-top: 30px; }
  .hero__cue { display: none; }
  .hero__index { grid-row: 7; }
  .hero__index ol { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hero__index li:last-child { grid-column: 1 / -1; }
  .hero__index a { padding-block: 12px 10px; }
  .about__portrait, .about__body { grid-column: 1 / -1; }
  .about__portrait { width: min(100%, 340px); }
  .card__meta { padding-right: 0; }
  .pub__venue .tag { display: table; margin: 8px 0 0; }
  .filters__count { width: 100%; margin: 6px 0 0; }
  .foot__base { flex-direction: column; }
  .chead__big { display: none; }
  .stage__count { display: none; }
  .stage__labels { font-size: 9.5px; letter-spacing: .08em; }
  .runhead__of { display: none; }
}
@media (min-width: 861px) { .menu { display: none; } }

/* ======================================================= REDUCED MOTION === */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 1ms !important; animation-iteration-count: 1 !important; animation-delay: 0ms !important;
    transition-duration: 1ms !important; transition-delay: 0ms !important; }
  .js [data-reveal], .js [data-reveal] > *, .js .line__inner, .js .w, .js .ch, .js :is(.plate, .about__photo) > *, .js .hero__aura i, .js .hero__index li {
    opacity: 1 !important; transform: none !important; filter: none !important; clip-path: none !important; translate: none !important; scale: none !important; }
  .js [data-reveal="rules"] > *::before, .js [data-reveal] .runhead__rule, .js .hero__line--2 .hero__word::after { transform: none !important; opacity: 1 !important; }
  .js .hero__statement em { --u: 100% !important; }
  .hero__name, .hero__line, .hero__top, .hero__aside, .hero__aura, [data-parallax], .panel > .wrap, .stage, .card__in {
    translate: none !important; scale: none !important; }
  .hero__aura, .hero__cue, .hero__index { opacity: 1 !important; }
  .hero__aura i { animation: none !important; }
  .chead__big { transform: none !important; }
  .panel::before, .panel::after { clip-path: inset(0 round var(--panel-r)) !important; }
  .card__in::after { opacity: 0 !important; }
  .menu__inner { clip-path: none !important; }
  .menu__list li { opacity: 1 !important; transform: none !important; }
  .cursor { display: none; }
  .js .stop time { translate: none; }
  .cue::after, .stop--now.is-reached::after, .clock__dot { animation: none !important; }
  .glyph * { animation: none !important; }
  .g-draw { stroke-dashoffset: 0; }
  .g-pop { transform: none; }
  .g-blink { opacity: 1; }
  .g-arc { opacity: .8; }
}

/* ================================================================ PRINT === */
@media print {
  :root, .tone-wine, .tone-stone { --bg: #fff; --bg-2: #fff; --bg-3: #eee; --ink: #000; --ink-2: #222; --muted: #555; --rule: #ccc; --rule-2: #aaa;
    --accent: #7c1d34; --accent-text: #7c1d34; --panel-bg: #fff; }
  .mast, .menu, .toast, .skip, .cursor, .hero__aura, .hero__index, .fields__stage, .filters, .hero__cue, .video__poster, .card__plate, .chead__big { display: none !important; }
  .hero::before { display: none; }
  [data-reveal], [data-reveal] > *, .w, .plate > *, .about__photo > * { clip-path: none !important; translate: none !important; scale: none !important; }
  .about__portrait { width: 42mm; }
  .panel::before, .panel::after, .card__in::after { display: none; }
  .hero { min-height: 0; }
  .section, .research, .experience, .projects { margin-top: 32px; }
  [data-reveal], [data-reveal] > *, .line__inner, .w, .ch, .about__photo > * { opacity: 1 !important; transform: none !important; filter: none !important; }
  .card { position: static !important; }
  .card__body { grid-column: 1 / -1 !important; }
  .pub, .card, .stop { break-inside: avoid; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; color: #555; }
}
