/* ═══════════════════════════════════════════════════════════════════════════
   Critical Edition the design system for olutosinakinwumi

   The page is set as a printed scholarly edition. Every decision follows from
   that one premise:

   - Paper is the default state, not dark. A critical edition is a book.
   - Rubric red is the single accent. In a manuscript the rubricator worked in
     red after the scribe finished in black: headings, section marks, the
     corrections. It is the colour of apparatus, never of decoration.
   - The display face is a serif, the body a grotesk, the apparatus a mono.
     Three registers, the way a critical edition separates text, translation,
     and the footnote apparatus beneath the rule.
   - Sharp corners everywhere (radius 0). A printed leaf has no rounded corners.
   - The gloss is not a hover trick. It is the organising principle: this site
     annotates itself the way a linguistics paper annotates a term.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Palette values, each declared exactly once ────────────────────────────
   Components never read these directly. They read the semantic tokens mapped
   below, so a colour value can never drift between the two themes. */
:root {
  /* Leaf. White page, with two barely-tinted depths for surfaces and plate
     grounds. The tints stay neutral: warm greys against pure white read as
     stains rather than as paper. */
  --paper:          #FFFFFF;
  --paper-surface:  #F5F5F3;
  --paper-raised:   #E9E9E5;
  --ink:            #15140F;
  --ink-2:          #46433C;
  /* ink-3 and chalk-3 carry the 10.5-12.5px mono apparatus labels. Both are
     pushed past the obvious "dim grey" value so those labels clear WCAG AA
     against the most extreme surface they ever sit on, not just the page.
     ink-3 on --paper-raised is 4.8:1; chalk-3 on --vellum-raised is 4.9:1. */
  --ink-3:          #635C50;
  --rubric:         #A8332A;

  /* Night reading. Not an inversion: a lamp on the same leaf, so the paper
     hues stay warm and the red lifts rather than glares. */
  --vellum:         #14120F;
  --vellum-surface: #1C1916;
  --vellum-raised:  #252119;
  --chalk:          #EDE7DA;
  --chalk-2:        #A9A296;
  --chalk-3:        #948D80;
  --rubric-lit:     #D4695C;

  --rule-light:     rgba(26, 24, 21, 0.20);
  --rule-soft-light:rgba(26, 24, 21, 0.09);
  --rule-dark:      rgba(237, 231, 218, 0.16);
  --rule-soft-dark: rgba(237, 231, 218, 0.08);

  /* Sharp everywhere. This is the whole radius system. */
  --radius: 0px;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --gut: clamp(20px, 5vw, 64px);
  --max: 1400px;

  /* One family across the whole site. The three tokens are kept as separate
     names so components still say which register they mean, and so a second
     face can be reintroduced later by changing one line rather than hunting
     every rule. Noto Sans stays behind each as the per-glyph Yoruba fallback:
     Urbanist has no underdotted forms, and those must never tofu. */
  --display: 'Urbanist', 'Noto Sans', -apple-system, BlinkMacSystemFont, sans-serif;
  --font:    'Urbanist', 'Noto Sans', -apple-system, BlinkMacSystemFont, sans-serif;
  --mono:    'Urbanist', 'Noto Sans', -apple-system, BlinkMacSystemFont, sans-serif;

  /* Width of the hanging marginal column the apparatus labels sit in. */
  --margin-col: clamp(0px, 8vw, 132px);
}

/* ── Theme mapping ─────────────────────────────────────────────────────────
   Paper is the default, because the premise is a printed book. Night reading
   applies when the OS asks for dark AND the visitor has not chosen
   (`:not([data-theme])`), or when they have explicitly chosen it. An explicit
   [data-theme] always outranks the media query, which is how the nav toggle
   overrides the operating system.

   The dark mapping appears twice because plain CSS cannot share one
   declaration block across a media-query boundary. Only the mapping is
   duplicated, never a colour value: keep the two lists identical.

   NOTE: main.js derives its fallback from `(prefers-color-scheme: dark)` so
   that an OS reporting `no-preference` lands on paper in both CSS and JS. If
   the default here ever flips, flip that query with it or the toggle label
   will disagree with the page on first load. */
:root,
:root[data-theme="light"] {
  color-scheme: light;
  --bg:          var(--paper);
  --surface:     var(--paper-surface);
  --surface-2:   var(--paper-raised);
  --fg:          var(--ink);
  --fg-mute:     var(--ink-2);
  --fg-dim:      var(--ink-3);
  --accent:      var(--rubric);
  --accent-fill: var(--rubric);
  --on-accent:   var(--paper);
  --rule:        var(--rule-light);
  --rule-soft:   var(--rule-soft-light);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    color-scheme: dark;
    --bg:          var(--vellum);
    --surface:     var(--vellum-surface);
    --surface-2:   var(--vellum-raised);
    --fg:          var(--chalk);
    --fg-mute:     var(--chalk-2);
    --fg-dim:      var(--chalk-3);
    --accent:      var(--rubric-lit);
    --accent-fill: var(--rubric);
    --on-accent:   var(--chalk);
    --rule:        var(--rule-dark);
    --rule-soft:   var(--rule-soft-dark);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg:          var(--vellum);
  --surface:     var(--vellum-surface);
  --surface-2:   var(--vellum-raised);
  --fg:          var(--chalk);
  --fg-mute:     var(--chalk-2);
  --fg-dim:      var(--chalk-3);
  --accent:      var(--rubric-lit);
  --accent-fill: var(--rubric);
  --on-accent:   var(--chalk);
  --rule:        var(--rule-dark);
  --rule-soft:   var(--rule-soft-dark);
}

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

/* Deliberately NO `scroll-behavior: smooth` here. It desynchronises
   ScrollTrigger's scrub and pin maths, which the horizontal pan depends on.
   Anchor jumps are instant instead, which is also the more reliable behaviour
   when the target sits inside a pinned section. */

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-variation-settings: 'wdth' 100;
  font-weight: 400;
  font-size: 17px;
  line-height: 1.55;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; text-decoration: none; }
/* Headings are the display register, always. Instrument Serif ships a single
   weight (400), so hierarchy here comes from size and space, never from bolding
   a serif that has no bold. Tracking is far looser than the old grotesk setting:
   a high-contrast serif at -0.05em collides at display sizes. */
h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
p { margin: 0; }
img { display: block; max-width: 100%; height: auto; }
::selection { background: var(--accent-fill); color: var(--on-accent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ── Shared primitives ────────────────────────────────────────────────── */

.eyebrow {
  font-family: var(--mono);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.02em;
  padding: 15px 26px;
  border: 1px solid transparent;
  transition: background 0.35s var(--ease), color 0.35s var(--ease),
              border-color 0.35s var(--ease), transform 0.12s ease;
}
.btn:active { transform: translateY(1px); }
.btn--solid {
  background: var(--accent-fill);
  color: var(--on-accent);
  border-color: var(--accent-fill);
}
.btn--solid:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); }
.btn--ghost { border-color: var(--rule); color: var(--fg); }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); }

/* Link with a rule that wipes in from the left on hover. */
.link {
  position: relative;
  display: inline-block;
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--accent);
  padding-bottom: 4px;
}
.link::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.5s var(--ease);
}
.link:hover::after { transform: scaleX(1); }

/* Image plate. The fallback label shows only when the <img> is absent or
   removed by its onerror handler, so dropping a real file in just works. */
.plate {
  position: relative;
  margin: 0;
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.plate img {
  position: relative;
  z-index: 1;
  width: 100%; height: 100%;
  object-fit: cover;
}
.plate__fallback {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  padding: 20px;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-dim);
}
/* Deliberately a plain surface, not a blueprint grid. These placeholders are
   the largest elements on the page until real screenshots land, so any tiled
   texture here becomes the dominant impression of the whole site. The border,
   the surface tint, and the label are enough to read as "image pending". */
.plate--portrait { aspect-ratio: 4 / 5; }
.plate--case { aspect-ratio: 4 / 3; }

/* ── Nav ──────────────────────────────────────────────────────────────── */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  height: 68px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 0 var(--gut);
  background: color-mix(in oklab, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.4s var(--ease), background 0.4s var(--ease);
}
.nav.is-scrolled { border-bottom-color: var(--rule); }
.nav__mark {
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
/* align-items must be explicit. The default `stretch` makes each link fill the
   bar's full height with its text sitting at the top of that box, while the
   toggle centres its icon inside a 34px square, so the icon reads as dropped
   below the link baseline. Centring aligns the icon's optical middle to the
   text. */
.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2.4vw, 32px);
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: 0.04em;
}
.nav__links a { color: var(--fg-mute); transition: color 0.3s var(--ease); }
.nav__links a:hover { color: var(--fg); }

/* Theme toggle. A bare icon, no border or fill box: sun while light is the
   active theme, moon while dark is. Hidden until scripting confirms it can
   work, so it never renders as a dead control. */
.themetoggle {
  position: relative;
  appearance: none;
  -webkit-appearance: none;
  display: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  background: none;
  border: none;
  padding: 0;
  color: var(--fg-mute);
  cursor: pointer;
  opacity: 0.85;
  transition: opacity 0.3s var(--ease), color 0.3s var(--ease);
}
.js .themetoggle { display: inline-flex; }
.themetoggle:hover { opacity: 1; color: var(--accent); }
.themetoggle:active { transform: translateY(1px); }

/* The two icons are stacked and cross-faded, each rotating in from its own
   side, so the switch is one gesture rather than a swap. The icon shows the
   CURRENT theme; aria-label states the action, which is what a screen-reader
   user needs instead. */
.themetoggle__icon {
  position: absolute;
  width: 20px;
  height: 20px;
  opacity: 0;
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.themetoggle__icon--sun  { transform: rotate(-90deg) scale(0.6); }
.themetoggle__icon--moon { transform: rotate(90deg) scale(0.6); }
.themetoggle[data-current="light"] .themetoggle__icon--sun,
.themetoggle[data-current="dark"]  .themetoggle__icon--moon {
  opacity: 1;
  transform: rotate(0deg) scale(1);
}

/* Inner pages: the back link and the toggle share the right end of the bar. */
.subnav__end {
  display: flex;
  align-items: center;
  gap: clamp(12px, 2vw, 22px);
}

.nav__mark-short { display: none; }

@media (max-width: 560px) {
  .nav { height: 60px; }
  .nav__mark { font-size: 13.5px; }
  .nav__links { gap: 11px; font-size: 10.5px; }
  .themetoggle { width: 30px; height: 30px; }
  .themetoggle__icon { width: 18px; height: 18px; }
  /* Four nav links plus the toggle need the ~84px the full wordmark occupies.
     It collapses to initials rather than dropping a navigation item. */
  .nav__mark-full { display: none; }
  .nav__mark-short { display: inline; letter-spacing: 0.02em; }
}

/* ── Hero ─────────────────────────────────────────────────────────────── */

.hero {
  min-height: 100dvh;
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.65fr);
  align-items: center;
  gap: clamp(32px, 6vw, 88px);
  padding: 156px var(--gut) clamp(64px, 9vw, 112px);
  max-width: var(--max);
  margin: 0 auto;
}
.hero__body { display: flex; flex-direction: column; align-items: flex-start; }

.hero__title {
  font-size: clamp(3.6rem, 12vw, 10rem);
  font-weight: 700;
  line-height: 0.82;
  letter-spacing: -0.025em;
  margin: 0 0 clamp(20px, 2.6vw, 30px);
}
.hero__title .line { display: block; overflow: hidden; padding-bottom: 0.02em; }
.hero__title .line > span { display: block; will-change: transform; }

.hero__sub {
  font-size: clamp(1.05rem, 1.7vw, 1.3rem);
  line-height: 1.45;
  letter-spacing: -0.015em;
  color: var(--fg-mute);
  max-width: 34ch;
  margin-bottom: clamp(26px, 3vw, 38px);
}
.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; }
.hero__plate { justify-self: end; width: 100%; max-width: 340px; }

@media (max-width: 900px) {
  .hero {
    grid-template-columns: 1fr;
    align-content: center;
    padding-top: 104px;
  }
  .hero__plate { justify-self: start; max-width: 220px; order: -1; margin-bottom: 8px; }
}

/* Below 640px the portrait costs ~300px of height and pushes the CTAs under
   the fold. The hero is type-led anyway, so the plate drops out rather than
   the primary action becoming invisible. */
@media (max-width: 640px) {
  .hero { padding-top: 88px; }
  .hero__plate { display: none; }
}

/* Tall viewports, any width. Both columns stay vertically centred — the
   portrait plate is meant to land at the same centre as the name once a real
   photo replaces the placeholder. Only the section height is capped, so a very
   tall monitor doesn't stretch the hero to the full viewport and leave the
   next section showing nothing but a sliver. */
@media (min-height: 980px) {
  .hero { min-height: min(100dvh, 940px); }
}

/* ── The band ────────────────────────────────────────────────────────────
   Set upright, not italic: these are the terms themselves, not citations of
   them. Vertical padding is not decoration — the Yoruba grave sits above the
   ascender and "Sprache"/"Language"/"Design" carry descenders, so the band
   needs clearance at both ends or the overflow clip shaves them. */
.band {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  overflow: hidden;
  padding: clamp(16px, 2.6vw, 30px) 0;
  background: var(--surface);
  /* Half-strength so this reads as ambient texture under the hero rather than
     competing with it for attention. */
  opacity: 0.5;
}
.band__track {
  display: flex;
  gap: clamp(40px, 6vw, 104px);
  width: max-content;
  will-change: transform;
}
.band__track span {
  font-family: var(--display);
  font-style: normal;
  font-size: clamp(1.9rem, 4.6vw, 3.6rem);
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: -0.02em;
  color: var(--accent);
  white-space: nowrap;
  padding: 0.06em 0 0.14em;
}

/* ── Sticky-stack: the three domains ─────────────────────────────────── */

.stack { position: relative; border-top: 1px solid var(--rule); }
.stack__card {
  position: sticky;
  top: 0;
  min-height: 100dvh;
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  align-content: center;
  align-items: start;
  gap: clamp(24px, 5vw, 72px);
  padding: clamp(96px, 12vh, 160px) var(--gut) clamp(64px, 9vh, 120px);
  background: var(--bg);
  border-top: 1px solid var(--rule);
  will-change: transform, opacity;
}
.stack__card:first-child { border-top: 0; }
/* Folio numbers. Set in the display serif, not the mono, so they read as
   printed section numbers rather than as data. Alternating cards rubricate
   theirs, the way a scribe worked red through an otherwise black page. */
.stack__num {
  font-family: var(--display);
  font-size: clamp(2.8rem, 7.5vw, 6rem);
  font-weight: 400;
  font-variant-numeric: lining-nums tabular-nums;
  line-height: 0.85;
  letter-spacing: -0.01em;
  color: var(--fg-dim);
}
.stack__card[data-accent="rubric"] .stack__num { color: var(--accent); }
.stack__text { max-width: 46ch; }
.stack__text h2 {
  font-size: clamp(2.7rem, 8.5vw, 6.8rem);
  line-height: 0.96;
  letter-spacing: -0.02em;
  margin-bottom: clamp(16px, 2vw, 24px);
}
.stack__text p {
  font-size: clamp(1rem, 1.5vw, 1.2rem);
  line-height: 1.5;
  letter-spacing: -0.012em;
  color: var(--fg-mute);
  margin-bottom: 26px;
}

@media (max-width: 760px) {
  .stack__card { grid-template-columns: 1fr; gap: 14px; }
}

/* ── Horizontal pan: six case studies ────────────────────────────────── */

.pan {
  position: relative;
  border-top: 1px solid var(--rule);
  background: var(--surface);
}
.pan__viewport { overflow: hidden; }
.pan__track {
  display: flex;
  align-items: center;
  gap: clamp(24px, 3.5vw, 56px);
  min-height: 100dvh;
  padding: 0 var(--gut);
  width: max-content;
}

.pan__intro,
.pan__outro {
  flex: 0 0 auto;
  width: min(80vw, 460px);
  padding-right: clamp(16px, 3vw, 40px);
}
/* The outro carries two short lines and one button. At the intro's width it
   leaves a wide void at the very end of the pan, which reads as overshoot on
   the one frame the reader lands on. Sized to its content instead. */
.pan__outro { width: min(80vw, 330px); }
.pan__intro h2,
.pan__outro h2 {
  font-size: clamp(2.5rem, 5.6vw, 4.6rem);
  line-height: 0.98;
  letter-spacing: -0.02em;
  margin: 14px 0 20px;
}
.pan__intro p {
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--fg-mute);
  max-width: 34ch;
}

.case {
  flex: 0 0 auto;
  width: min(84vw, 440px);
  display: block;
  border-top: 1px solid var(--rule);
  padding-top: 18px;
}
.case .plate { transition: border-color 0.45s var(--ease); }
.case .plate img { transition: transform 0.7s var(--ease), filter 0.5s var(--ease); }
.case:hover .plate { border-color: var(--accent); }
.case:hover .plate img { transform: scale(1.04); }
.case__meta { padding-top: 20px; }
.case__num {
  display: block;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.18em;
  color: var(--accent);
  margin-bottom: 10px;
}
.case__meta h3 {
  font-size: clamp(1.6rem, 2.8vw, 2.25rem);
  line-height: 1.12;
  letter-spacing: -0.012em;
  margin-bottom: 10px;
  transition: color 0.35s var(--ease);
}
.case:hover .case__meta h3 { color: var(--accent); }
.case__meta p {
  font-size: 15.5px;
  line-height: 1.5;
  color: var(--fg-mute);
  margin-bottom: 16px;
}
.case__tags { display: flex; flex-wrap: wrap; gap: 6px; }
.case__tags i {
  font-style: normal;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
  border: 1px solid var(--rule);
  padding: 5px 9px;
}

/* Progress rail. Reads as position in a long horizontal read, not decoration. */
.pan__rail {
  position: absolute;
  left: var(--gut); right: var(--gut); bottom: 28px;
  height: 1px;
  background: var(--rule);
}
.pan__rail-fill {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
}

/* Below the pin threshold the pan becomes an ordinary vertical read. */
@media (max-width: 860px) {
  .pan__viewport { overflow: visible; }
  .pan__track {
    display: grid;
    grid-template-columns: 1fr;
    width: auto;
    min-height: 0;
    gap: 56px;
    padding: clamp(64px, 10vw, 96px) var(--gut);
  }
  .pan__intro, .pan__outro, .case { width: 100%; padding-right: 0; }
  .pan__rail { display: none; }
}

/* ── Approach, with the gloss layer ──────────────────────────────────── */

.approach {
  border-top: 1px solid var(--rule);
  padding: clamp(96px, 16vh, 200px) var(--gut);
  max-width: 1100px;
  margin: 0 auto;
}
.approach__quote {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.6rem, 3.6vw, 3.1rem);
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: -0.012em;
  max-width: 32ch;
  text-wrap: pretty;
}

/* The signature interaction: an interlinear gloss, the way a linguistics
   paper annotates a term. The gloss reserves its own space so nothing shifts. */
.gloss {
  position: relative;
  display: inline-block;
  color: var(--accent);
  cursor: help;
  border-bottom: 1px dashed currentColor;
}
/* The slip is ::before purely so that ::after is free to carry the reference
   mark, which must follow the term. Being absolutely positioned, the slip does
   not care which pseudo-element it lives on. */
.gloss::before {
  content: attr(data-gloss);
  position: absolute;
  left: 0;
  top: 100%;
  margin-top: 6px;
  width: max-content;
  max-width: min(78vw, 30ch);
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: 0;
  color: var(--fg-mute);
  background: var(--surface-2);
  border-left: 2px solid var(--accent);
  padding: 7px 11px;
  opacity: 0;
  transform: translateY(-5px);
  pointer-events: none;
  transition: opacity 0.28s var(--ease), transform 0.28s var(--ease);
}
.gloss:hover::before,
.gloss:focus-visible::before { opacity: 1; transform: translateY(0); }

/* On touch widths the hover tooltip is both unreachable and too wide to fit
   beside a word, so the gloss becomes an inline annotation that is simply
   always visible. Same information, no hover affordance to discover, and it
   wraps with the text instead of running off the right edge. */
@media (max-width: 760px) {
  .gloss {
    display: inline;
    border-bottom: 0;
    cursor: auto;
  }
  /* The touch-width gloss is rebuilt in THE APPARATUS block at the end of this
     file, which has to win on source order because it redefines both pseudo-
     elements. Nothing about the gloss slip belongs here. */
  .approach__note { display: none; }
}
.approach__note {
  margin-top: clamp(32px, 5vw, 56px);
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-dim);
}

/* ── Marquee (one per page, by rule) ─────────────────────────────────── */

.marquee {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  overflow: hidden;
  padding: 22px 0;
  background: var(--surface);
}
.marquee__track {
  display: flex;
  gap: 40px;
  width: max-content;
  will-change: transform;
}
.marquee__track span {
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-dim);
  white-space: nowrap;
}
.marquee__track span::after {
  content: "/";
  margin-left: 40px;
  color: var(--accent);
}

/* ── Contact ─────────────────────────────────────────────────────────── */

.contact {
  padding: clamp(96px, 15vh, 180px) var(--gut) clamp(64px, 8vw, 96px);
  max-width: var(--max);
  margin: 0 auto;
}
.contact__title {
  font-size: clamp(3.2rem, 11vw, 9rem);
  line-height: 0.92;
  letter-spacing: -0.025em;
  margin-bottom: clamp(48px, 7vw, 84px);
}
.contact__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: clamp(28px, 4vw, 56px);
  border-top: 1px solid var(--rule);
  padding-top: 28px;
  margin-bottom: clamp(40px, 5vw, 64px);
}
.contact__label {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-bottom: 10px;
}
.contact__col p { font-size: 16px; line-height: 1.5; color: var(--fg-mute); }
.contact__cta { display: flex; flex-wrap: wrap; gap: 12px; }

.foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px;
  padding: 26px var(--gut);
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.08em;
  color: var(--fg-dim);
}

/* ═══ Inner pages: Academia, About, portfolio index, case studies ═══════ */

.subnav {
  position: sticky;
  top: 0;
  z-index: 100;
  height: 68px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 0 var(--gut);
  background: color-mix(in oklab, var(--bg) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--rule);
}
.subnav__mark { font-size: 15px; font-weight: 500; letter-spacing: -0.01em; }
.subnav__back {
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: 0.04em;
  color: var(--fg-mute);
  transition: color 0.3s var(--ease);
}
.subnav__back:hover { color: var(--accent); }

/* Article page shell */
.page {
  max-width: 1400px;
  margin: 0 auto;
  padding: clamp(56px, 10vh, 120px) var(--gut) clamp(80px, 12vh, 160px);
}
.page__head {
  border-bottom: 1px solid var(--rule);
  padding-bottom: clamp(32px, 5vw, 56px);
  margin-bottom: clamp(40px, 6vw, 72px);
}
.page__title {
  font-size: clamp(3rem, 9.5vw, 7.4rem);
  line-height: 0.94;
  letter-spacing: -0.025em;
  margin: 14px 0 0;
}
.page__lede {
  font-size: clamp(1.15rem, 2vw, 1.55rem);
  line-height: 1.4;
  letter-spacing: -0.02em;
  color: var(--fg-mute);
  max-width: 40ch;
  margin-top: clamp(20px, 3vw, 30px);
}

/* Two-column prose: sticky label rail on the left, body on the right. */
.prose { display: grid; grid-template-columns: minmax(0, 200px) minmax(0, 1fr); gap: clamp(24px, 5vw, 80px); }
.prose__rail {
  position: sticky;
  top: 108px;
  align-self: start;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-dim);
}
.prose__body { max-width: 62ch; }
.prose__body p {
  font-size: clamp(1.02rem, 1.4vw, 1.15rem);
  line-height: 1.65;
  letter-spacing: -0.01em;
  color: var(--fg-mute);
}
.prose__body p + p { margin-top: 1.4em; }
.prose__body strong { color: var(--fg); font-weight: 500; }

.block { margin-bottom: clamp(48px, 7vw, 88px); }
.block:last-child { margin-bottom: 0; }

@media (max-width: 760px) {
  .prose { grid-template-columns: 1fr; gap: 12px; }
  .prose__rail { position: static; }
}

/* Reveal-on-scroll, applied by main.js. Content is visible without JS. */
.js .reveal { opacity: 0; transform: translateY(26px); }

/* ═══════════════════════════════════════════════════════════════════════════
   THE APPARATUS
   Everything below is what makes this a printed edition rather than a page
   with a serif on it: the grain of the leaf, the rubricator's marks, the
   hanging margin, and the gloss promoted from hover-trick to footnote system.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── The leaf ──────────────────────────────────────────────────────────────
   Paper is not a flat colour: it is laid lines and fibre. Two SVG layers in
   one fixed overlay.

   1. Laid lines. Handmade paper is formed on a wire mould, which leaves close
      horizontal wire lines and a heavier vertical chain line. That is the
      texture, and it tiles at 32px.
   2. Fibre. Fractal noise over the top, so the lines never read as a clean
      digital grid.

   It has to be an overlay rather than a background on <body>, because several
   sections paint their own opaque ground (.stack__card, .pan, .marquee) and
   would cover a body background entirely.

   The strokes are neutral grey, which darkens white and lifts vellum, so one
   asset serves both themes with no second file and no filter.

   Deliberately NOT mix-blend-mode: a blended fixed layer forces a full-viewport
   recomposite on every scroll frame, and this page pins and scrubs a horizontal
   pan. Plain opacity costs nothing. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  opacity: 0.16;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg fill='none' stroke='%236E6A60' stroke-width='0.4' opacity='0.5'%3E%3Cpath d='M0 4h32M0 8h32M0 12h32M0 16h32M0 20h32M0 24h32M0 28h32'/%3E%3C/g%3E%3Cg fill='none' stroke='%236E6A60' stroke-width='0.8' opacity='0.32'%3E%3Cpath d='M0 0v32'/%3E%3C/g%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  background-size: 32px 32px, 200px 200px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) body::after { opacity: 0.13; }
}
:root[data-theme="dark"] body::after { opacity: 0.13; }

/* ── The rubricator's mark ─────────────────────────────────────────────────
   Section heads carry a short red rule instead of sitting as bare small-caps.
   In a manuscript the red went on after the black, as a finding aid. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 10px;
  letter-spacing: 0.18em;
}
.eyebrow::before {
  content: "";
  width: 22px;
  height: 2px;
  background: var(--accent);
  flex: 0 0 auto;
}

/* ── Hanging margin ────────────────────────────────────────────────────────
   The approach section is the one place the page shows its own structure: the
   note about the gloss moves out of the flow and into the left margin, level
   with the first line of the quote, exactly where a marginal note belongs. */
.approach {
  display: grid;
  grid-template-columns: var(--margin-col) minmax(0, 1fr);
  gap: clamp(16px, 3vw, 44px);
  align-items: start;
}
.approach__quote { grid-column: 2; }
.approach__note {
  grid-column: 1;
  grid-row: 1;
  margin-top: 0.55em;
  text-align: right;
  line-height: 1.5;
  border-right: 1px solid var(--rule);
  padding-right: clamp(10px, 1.6vw, 18px);
}

@media (max-width: 860px) {
  .approach { grid-template-columns: 1fr; }
  .approach__quote { grid-column: 1; }
  .approach__note {
    grid-column: 1;
    grid-row: 2;
    text-align: left;
    border-right: 0;
    border-left: 1px solid var(--rule);
    padding-right: 0;
    padding-left: clamp(10px, 1.6vw, 18px);
    margin-top: clamp(32px, 5vw, 56px);
  }
}

/* ── The gloss, promoted ───────────────────────────────────────────────────
   This is the organising principle of the whole skin, so it stops looking like
   a tooltip and starts looking like apparatus: the term carries a superscript
   reference mark in red, and the gloss itself arrives as a slip of footnote
   set under a hairline, in the mono register the apparatus always uses. */
.gloss {
  color: inherit;
  border-bottom: 1px solid var(--accent);
  counter-increment: gloss;
}
/* The reference mark FOLLOWS the term, as a footnote marker always does. */
.gloss::after {
  content: counter(gloss);
  font-family: var(--mono);
  font-size: 0.34em;
  font-weight: 500;
  line-height: 1;
  vertical-align: super;
  color: var(--accent);
  margin-left: 0.14em;
  letter-spacing: 0;
}
.approach { counter-reset: gloss; }

.gloss::before {
  background: var(--surface);
  border-left: 0;
  border-top: 2px solid var(--accent);
  box-shadow: 0 10px 30px -12px rgba(26, 24, 21, 0.28);
  padding: 9px 13px 11px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .gloss::before { box-shadow: 0 10px 30px -12px rgba(0, 0, 0, 0.6); }
}
:root[data-theme="dark"] .gloss::before { box-shadow: 0 10px 30px -12px rgba(0, 0, 0, 0.6); }

/* Touch widths: there is no hover to discover, so the slip is dropped entirely
   and the gloss becomes an inline parenthetical that wraps with the text. The
   reference mark goes with it, because a marker pointing at nothing is noise. */
@media (max-width: 760px) {
  .gloss { border-bottom: 0; }
  .gloss::before { display: none; }
  .gloss::after {
    content: " (" attr(data-gloss) ")";
    font-family: var(--font);
    font-size: 0.62em;
    font-weight: 400;
    vertical-align: baseline;
    color: var(--fg-dim);
    margin-left: 0;
  }
}

/* ── Case numerals ─────────────────────────────────────────────────────────
   Already roman in the markup. Set them as printed folios, not as data. */
.case__num {
  font-family: var(--display);
  font-size: 1.15rem;
  letter-spacing: 0.06em;
  line-height: 1;
}

/* ── Plates ────────────────────────────────────────────────────────────────
   A figure in a book that has not been pasted in yet still reserves its bed
   and still carries its caption. The caption sits under a hairline, which is
   what tells you the space is intentional rather than broken. */
.plate { background: var(--surface-2); }
.plate__fallback {
  align-items: flex-end;
  letter-spacing: 0.2em;
}
.plate__fallback::before {
  content: "";
  position: absolute;
  left: 20px; right: 20px;
  bottom: 44px;
  height: 1px;
  background: var(--rule);
}

/* ── Printed labels ────────────────────────────────────────────────────────
   Buttons and tags are the one place the mono register meets a filled ground.
   Tighter, letterpress-ish, with the press feedback the old skin already had. */
.btn { letter-spacing: 0.08em; text-transform: uppercase; font-size: 11.5px; padding: 16px 24px; }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); background: var(--surface); }
.case__tags i { letter-spacing: 0.12em; }

/* ── Running head ──────────────────────────────────────────────────────────
   The inner-page rail is already a marginal column; this makes it look like
   one, hung off a hairline the way a running head sits above a text block. */
.prose__rail {
  border-left: 1px solid var(--accent);
  padding-left: 12px;
  letter-spacing: 0.2em;
}
@media (max-width: 760px) {
  .prose__rail { border-left: 0; padding-left: 0; }
}

/* ── Reduced motion: everything collapses to static ──────────────────── */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .js .reveal { opacity: 1 !important; transform: none !important; }
  /* The band stops travelling and the repeats collapse, leaving the terms
     stated once rather than a clipped row frozen mid-scroll. */
  .band__track { transform: none !important; flex-wrap: wrap; }
  .band__track span[aria-hidden] { display: none; }
  .stack__card { position: static; min-height: 0; }
  .marquee__track { animation: none; transform: none; }
  .themetoggle__icon { transition: none; }
}
