/* ============ fonts ============ */
/* Thin is only reached by the 404's big number (font-weight 100) — browsers
   fetch a face on first use, so every other page never downloads it */
@font-face {
  font-family: "PP Neue Montreal";
  src: url("../fonts/PPNeueMontreal-Thin.otf") format("opentype");
  font-weight: 100;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "PP Neue Montreal";
  src: url("../fonts/PPNeueMontreal-Book.otf") format("opentype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "PP Neue Montreal";
  src: url("../fonts/PPNeueMontreal-Medium.otf") format("opentype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "PP Neue Montreal";
  src: url("../fonts/PPNeueMontreal-MediumItalic.otf") format("opentype");
  font-weight: 500;
  font-style: italic;
  font-display: swap;
}

/* ============ tokens (Figma: Website v6a) ============ */
:root {
  --bg: #0e0e0e;
  --fg: #fff;
  --muted: #777;
  --green: #56d46a;
  --line: #2e2e2e;
  --glass: rgba(14, 14, 14, .85);   /* mobile bar + menu frost (was .65 — Hanson, Sept 15: darker) */
  /* frost recipe: maxed blur + saturation so the smear reads rich, not milky;
     a faint grain tile dithers away the gradient banding heavy blurs cause */
  --frost: saturate(180%) blur(64px);
  --frost-max: saturate(180%) blur(96px);
  --noise: 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='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.05'/%3E%3C/svg%3E");
  --radius: 10px;
  --margin: 50px;   /* grid margin  (Figma layout grid: 16 col / stretch) — vertical rhythm */
  --margin-x: clamp(50px, 5vw, 100px); /* left/right page margin — grows with the screen so on a ~2000px display it matches
                                             the 100px top offset (Hanson: "almost even to the top"); 50px floor on laptops,
                                             ≤860px drops back to 25 (was a fixed 25: tight; a fixed 50 + 1600 cap: too much) */
  --gutter: 15px;   /* grid gutter */
  --maxw: 2000px;   /* site centers beyond this — the margin above does the laptop/desktop breathing room */
  --edge: max(var(--margin-x), calc((100% - var(--maxw)) / 2 + var(--margin-x)));
}
html[data-theme="light"] {
  --bg: #fff;
  --fg: #000;
  --muted: #bbb;
  --line: #e6e6e6;
  --glass: rgba(255, 255, 255, .88);   /* brighter than dark's .65 — the #bbb links blend into a .65 smear (Hanson, Sept 15) */
}

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

html { scroll-behavior: smooth; background: var(--bg); }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: "PP Neue Montreal", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  transition: background .2s ease, color .1s ease;
}

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }

/* ============ 16-column page grid ============ */
.page {
  display: grid;
  grid-template-columns: repeat(16, minmax(0, 1fr));
  column-gap: var(--gutter);
  padding: 0 var(--margin-x);
  max-width: var(--maxw);
  margin: 0 auto; /* centered on wide monitors */
}

/* ============ left rail (cols 1-2) ============ */
.rail {
  grid-column: 1 / 3;
  grid-row: 1;
  display: flex;
  flex-direction: column;
}

/* profile starts aligned with the first row of projects (100px), docks at 50px on
   scroll — js drives --photo-push from 50px down to 0. Fixed so it stays pinned even
   at the very bottom of the page; js grows --park-lift as the toggle+copyright parks,
   and the centered nav re-balances. Slow cubic transitions = smooth, delayed motion. */
.rail-top {
  position: fixed;
  top: var(--margin);
  left: var(--edge);
  width: calc((min(100%, var(--maxw)) - 2 * var(--margin-x) - 225px) / 8 + 15px); /* 2 of 16 columns + 1 gutter (225px = 15 gutters) */
  height: calc(100svh - var(--margin));
  display: flex;
  flex-direction: column;
  padding-top: var(--photo-push, 50px);
  padding-bottom: calc(93px + var(--park-lift, 0px)); /* down to the toggle's top edge (31 + 18px©×1.25 + 23 + 16) */
  /* the fixed box spans the full viewport height — let clicks pass through its
     empty space (e.g. to the hm. logo underneath), links stay interactive */
  pointer-events: none;
  /* above the 3d canvas (z 1) AND the open-menu veil (z 2) at all times —
     gating this on the menu state flashed the rail dark on close, while the
     fading veil still covered it */
  z-index: 6;
}
.rail-top a { pointer-events: auto; }

/* photo + identity tilt together as one card on hover (main.js) */
.phead { flex-shrink: 0; display: flex; flex-direction: column; }

/* the profile is persistent chrome: it lives in the rail on every page and
   folds open (home) or shut (everywhere else) as the route changes. .pslot's
   grid track animates 1fr <-> 0fr, pulling the identity + nav up on real
   layout; the photo — bottom-anchored, UNCLIPPED, full size — rides that
   collapse plus an extra transform, so the text literally pushes it up and
   clean out the top of the window. Coming home it drops back in from above
   and pushes the text down. Leaving accelerates (fly-out curve), arriving
   decelerates (fly-in curve). */
.pslot {
  display: grid;
  grid-template-rows: 1fr;
  flex-shrink: 0;
}
html.ready .pslot { transition: grid-template-rows .8s cubic-bezier(.33, 1, .68, 1); }
.pclip {
  min-height: 0;
  display: grid;
  /* bottom-anchor: the photo travels with the collapsing track, and the
     14px gap below it means it can never overlap the identity. align-items
     must NOT be left at stretch — stretch squashes the aspect-ratio box to
     the collapsed track instead of letting it overflow at full size */
  align-content: end;
  align-items: end;
}
.pclip .profile { margin-bottom: 14px; } /* the identity gap folds away with the photo */
/* transform = the fold; filter = the hover color fade — one shorthand, or the
   fold rule silently clobbers the fade's transition on home */
html.ready .pclip .profile { transition: transform .8s cubic-bezier(.33, 1, .68, 1), filter .35s ease; }

body[data-route]:not([data-route="home"]) .pslot { grid-template-rows: 0fr; }
body[data-route]:not([data-route="home"]) .pclip .profile {
  /* the track collapse alone parks the photo just above the slot; this
     carries it the rest of the way past the top edge of the viewport */
  transform: translateY(-110px);
}
html.ready body[data-route]:not([data-route="home"]) .pslot,
html.ready body[data-route]:not([data-route="home"]) .pclip .profile {
  transition-timing-function: cubic-bezier(.55, 0, .68, 1); /* fly-out */
}
body[data-route]:not([data-route="home"]) .pclip {
  visibility: hidden;             /* unreachable by keyboard once flown out… */
  transition: visibility 0s .8s;  /* …but only after the flight lands */
}

/* the color fade lives on the wrapper, NOT the <video>: the hover scrub's
   per-frame seeks churn the video's compositor layer, which can drop an
   in-flight filter transition (the "sometimes it cuts to color" bug). Both
   endpoints are explicit grayscale() lists so interpolation never falls
   back to a discrete swap. */
.profile {
  display: block;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 190 / 223;
  background: var(--line);
  filter: grayscale(1);
  transition: filter .35s ease;
}
.profile:hover { filter: grayscale(0); }
.profile video,
.profile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.identity {
  display: block;
  margin-top: 0; /* the gap above lives inside the fold (.pclip .profile) */
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1;
  flex-shrink: 0;
}
.identity .name, .identity .role { display: block; white-space: nowrap; } /* never wrap */
.identity .name { color: var(--green); }
.identity .role { color: var(--fg); }

/* nav centered between the identity and the toggle — equal space above and below,
   one blank line between work / play / info / the social block */
.nav {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--nav-gap, 31px); /* compresses when the toggle parks and space runs out */
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
}
.nav > * { flex-shrink: 0; } /* keep natural height so overflow is measurable */
.nav a { width: fit-content; transition: color .12s ease; }
.nav-main:hover { color: var(--green); }
.sublinks { display: flex; flex-direction: column; }
.nav-sub { color: var(--muted); }
.nav-sub:hover { color: var(--green); }

/* toggle + copyright: sticks to viewport bottom while scrolling,
   parks at the content's end (bottom of the last subtitle) at full scroll */
.rail-park {
  margin-top: auto;
  position: sticky;
  bottom: 31px;
  padding-top: 8px;
  z-index: 6;   /* above the open-menu veil (z 2), like .rail-top */
}

.toggle {
  display: block;
  width: 28px;
  height: 16px;
  border-radius: 3px;
  background: var(--green);
  position: relative;
  margin-bottom: 23px;
}
.toggle .knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: 2px;
  background: #000;
  transition: transform .25s ease, background .25s ease;
}
html[data-theme="light"] .toggle .knob {
  transform: translateX(12px);
  background: #fff;
}

.copyright {
  display: block;
  font-size: 18px;
  letter-spacing: -0.01em;
  color: var(--muted);
  line-height: 1.25;
  white-space: nowrap;
  text-decoration: none;
  transition: color .12s ease;
}
.copyright:hover { color: var(--green); }

/* ============ top-right view switcher ============ */
/* one word — "view" — in the rail nav's voice; clicking it drops the scene
   menu (flat layouts, then the 3d displays) straight below. The menu is out
   of flow so the block stays trigger-sized: every item shares the trigger's
   left edge and overhangs the page margin — harmless, it's only ever seen
   over the veil (html clips the stray horizontal overflow). */
.status {
  position: absolute;
  top: 50px;               /* the rail's top line — clear of the first card row */
  right: var(--edge);
  z-index: 5;
}
.view-dd { position: relative; display: flex; flex-direction: column; }
.view-trigger,
.vm-item {
  display: block;
  width: fit-content;
  text-align: left;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  transition: color .12s ease;
}
.view-trigger {
  position: relative;
  color: var(--muted);
}
.view-trigger::before { content: ""; position: absolute; inset: -10px -12px; } /* bigger hit area */
.view-trigger:hover { color: var(--green); }
.view-menu {
  position: absolute;
  top: calc(100% + 27px);   /* same drop the old margin-top gave */
  left: 0;
  width: max-content;       /* absolute shrink-to-fit would wrap "3 column" */
  display: flex;
  flex-direction: column;
  gap: 12px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition: opacity .18s ease, transform .18s ease, visibility 0s .18s;
}
.view-dd.open .view-menu {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity .18s ease, transform .18s ease;
}
.vm-item { color: #fff; }   /* only ever seen over the veil: black in dark mode… */
html[data-theme="light"] .vm-item { color: #000; }   /* …white in light mode */
.vm-item:hover { color: var(--green); }
.vm-item[aria-pressed="true"] { color: var(--green); }

/* open menu: the page fades under 75% black — content and the 3d stage sit
   beneath the veil (it eats their hovers and clicks; a click on it just
   closes the menu). The nav column and the menu itself stay above, sharp. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 2;                /* over the content and the 3d canvas (z 1) */
  background: #000;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .3s ease, visibility 0s .3s;
}
html[data-theme="light"] body::after { background: #fff; }   /* light mode: a white veil, not grey */
html.vd-open body::after {
  opacity: .75;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .3s ease;
}
/* menu items overhang the right margin (even closed — visibility keeps
   their boxes), so the stray horizontal overflow is clipped for good */
html { overflow-x: clip; }

/* ---- 3d displays: the stage ---- */
/* fixed full-bleed canvas behind the chrome; the rail and switcher float above */
.g3d {
  position: fixed;
  inset: 0;
  z-index: 1;
  opacity: 0;
  transition: opacity .4s ease;
  cursor: grab;
  pointer-events: none;         /* the fading-out husk must not eat clicks */
  touch-action: pinch-zoom;     /* drags are ours, browser zoom stays the user's */
  -webkit-user-select: none;
  user-select: none;
}
.g3d.on { opacity: 1; pointer-events: auto; }
.g3d.cut { transition: none; }   /* grid hand-overs swap between two frames */
.g3d.grabbing { cursor: grabbing; }
.g3d.overcard { cursor: pointer; }
.g3d canvas { display: block; width: 100%; height: 100%; }

html.g3d-lock { overflow: hidden; overscroll-behavior: none; }
/* screen-reader-only announcements (the stage swaps the grid away visually) */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
body[data-g3d] .grid, body[data-g3d] .page-end { display: none; }
/* the exit flight lands media-perfect, but the canvas never drew the type —
   dissolve it back in, the subtitle a beat behind the title (js adds
   .g3d-landed for the swap frame and clears it once the fade is done) */
.grid.g3d-landed .card .num,
.grid.g3d-landed .card .year,
.grid.g3d-landed .card h2 { animation: g3d-text-in .3s ease both; }
.grid.g3d-landed .card .sub { animation: g3d-text-in .3s ease .08s both; }
@keyframes g3d-text-in { from { opacity: 0; } }
/* the page under the stage has no height — park the toggle+copyright where
   a fully-scrolled page would have left it */
body[data-g3d] .rail-park {
  position: fixed;
  bottom: 31px;
  left: var(--edge);
  z-index: 6;
  margin: 0;
}

/* hover lockup: the flat card's title + subtitle, living in the hovered
   card's plane — js anchors it to the card's bottom-left corner and turns it
   with the card (a back-facing card keeps its lockup to itself) */
.g3d-tip {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 7;
  pointer-events: none;
  opacity: 0;
  transition: opacity .16s ease;
  transform-origin: 0 0;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
.g3d-tip.show { opacity: 1; }
.g3d-tip h3 {
  margin: 0;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  white-space: nowrap;
}
.g3d-tip p { margin: 0; font-size: 18px; color: var(--muted); line-height: 1.05; white-space: nowrap; }

/* dev panel (local server only): every display's numbers on sliders */
.g3d-dev {
  position: fixed;
  top: 110px;
  right: var(--edge);
  z-index: 8;
  width: 272px;
  max-height: calc(100vh - 180px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background-color: var(--glass);
  background-image: var(--noise);
  -webkit-backdrop-filter: var(--frost);
  backdrop-filter: var(--frost);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px 12px;
}
.g3d-dev h4 {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--fg);
}
.g3d-dev .row { display: flex; align-items: center; gap: 8px; margin: 5px 0; }
.g3d-dev label { flex: 0 0 84px; font-size: 12px; color: var(--muted); white-space: nowrap; }
.g3d-dev input[type="range"] {
  flex: 1;
  min-width: 0;
  accent-color: var(--green);
  height: 3px;
  cursor: pointer;
}
.g3d-dev .val {
  flex: 0 0 46px;
  font-size: 11px;
  text-align: right;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
}
.g3d-dev .dev-actions { margin-top: 10px; }
.g3d-dev .dev-actions button { font-size: 12px; color: var(--muted); transition: color .12s ease; }
.g3d-dev .dev-actions button:hover { color: var(--green); }
/* mode name flashes bottom-center on switch, then bows out */
.g3d-mode {
  position: fixed;
  z-index: 7;
  left: 50%;
  bottom: 44px;
  transform: translateX(-50%);
  font-size: 18px;
  letter-spacing: 0.02em;
  color: var(--muted);
  opacity: 0;
  transition: opacity .45s ease;
  pointer-events: none;
  white-space: nowrap;
}
.g3d-mode.show { opacity: 1; }

/* ============ main content (cols 5-16 → inner 12-col grid) ============ */
.main {
  grid-column: 5 / -1;
  grid-row: 1;
}

.grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 64px var(--gutter);
  padding-top: 100px;
}

.card { display: block; position: relative; min-width: 0; }
.card .num, .card .year { display: none; }

body[data-view="3"] .card { grid-column: span 4; }
body[data-view="2"] .card { grid-column: span 6; }

.card .media {
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--line);
  transform: translateZ(0);
}
.card .media img,
.card .media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* hover motion: the whole card — media, title, subtitle — leans away from the
   cursor in 3d as one unit; main.js drives it */

/* view switching: main.js FLIPs each media box from its old rect to its new
   one and the text rides the same flight, pinned under its card; only
   elements with no old spot (js tags them .appear) fade in a beat behind */
.grid.view-anim .appear { animation: card-text-in .45s ease .18s backwards; }
@keyframes card-text-in { from { opacity: 0; } }

.card h2 {
  margin: 11px 0 0;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  transition: color .12s ease;
}
.card:hover h2 { color: var(--hc, var(--green)); }

.card .sub {
  margin: 0;
  font-size: 18px;
  font-weight: 400;
  line-height: 1.05;
  color: var(--muted);
}

/* ---- list view ---- */
body[data-view="list"] .grid { gap: 0 var(--gutter); }
body[data-view="list"] .card {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 0 var(--gutter);
  align-items: start;
  border-top: 1px solid var(--line);
  padding: 15px 0 35px;
}
body[data-view="list"] .card .num {
  display: block;
  grid-column: 1 / 3;
  grid-row: 1;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
}
body[data-view="list"] .card .media {
  grid-column: 3 / 5;
  grid-row: 1;
  max-width: 190px;
}
body[data-view="list"] .card h2 { margin: 0; grid-column: 5 / 9; grid-row: 1; }
body[data-view="list"] .card .sub { grid-column: 9 / 11; grid-row: 1; padding-top: 8px; }
body[data-view="list"] .card .year {
  display: block;
  grid-column: 11 / 13;
  grid-row: 1;
  font-size: 18px;
  line-height: 1.05;
  color: var(--muted);
  text-align: right;
  padding-top: 8px;
}

/* ============ page end: hm. cornered bottom-left ============ */
/* bottom spacing matches the grid's side margin */
.page-end {
  grid-column: 1 / -1;
  grid-row: 2;
  display: grid;
  grid-template-columns: repeat(16, minmax(0, 1fr));
  column-gap: var(--gutter);
  align-items: end;
  margin-top: 160px;
  padding-bottom: var(--margin);
}

.end-logo {
  grid-column: 1 / 5;
  display: block;
  color: var(--fg);
  transition: color .12s ease;
}
.end-logo:hover { color: var(--green); }
.end-logo svg { width: 228px; max-width: 100%; height: auto; display: block; }

.fdot {
  /* sized and bottom-aligned to match the hm. logo's period exactly:
     228px logo width × (17.87 dot / 285.73 viewBox) ≈ 14.26px, flush on the baseline */
  width: 14.26px;
  height: 14.26px;
  background: var(--fg);
  display: none;
  position: relative;
  transition: background .12s ease;
}
.fdot::before { content: ""; position: absolute; inset: -10px; } /* bigger hit area */
.fdot:hover { background: var(--green); }
.fdot.d1 { grid-column: 5; }
.fdot.d3 { grid-column: 7; }
.fdot.d5 { grid-column: 9; }
.fdot.d7 { grid-column: 11; }
.fdot.d9 { grid-column: 13; }
.fdot.d12 { grid-column: 16; justify-self: end; }

body[data-view="3"] .fdot.d1, body[data-view="3"] .fdot.d5, body[data-view="3"] .fdot.d9 { display: block; }
body[data-view="2"] .fdot.d1, body[data-view="2"] .fdot.d7 { display: block; }
body[data-view="list"] .fdot.d1, body[data-view="list"] .fdot.d3, body[data-view="list"] .fdot.d5,
body[data-view="list"] .fdot.d9, body[data-view="list"] .fdot.d12 { display: block; }
.page-end .fdot.always { display: block; }

/* ============ about page ============ */
.about {
  grid-column: 5 / -1;
  grid-row: 1;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gutter);
  padding-top: 100px; /* same top rhythm as the home grid */
  align-content: start;
}

/* clock top-aligns with the photo; intro bottom-aligns with it */
.clock {
  grid-column: 7 / -1;
  grid-row: 1;
  align-self: start;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  margin: 0;
  min-height: 31px;
}
.clock .tz { color: var(--muted); font-weight: 400; }

/* photo spans 5 columns; column 6 stays empty as the gap to the right section */
.about-photo {
  grid-column: 1 / 6;
  grid-row: 1;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 1 / 1;
  align-self: start;
}
.about-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

.intro {
  grid-column: 7 / -1;
  grid-row: 1;
  align-self: end;
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  max-width: 601px;
}
.intro p { margin: 0 0 25px; }
.intro p:last-child { margin-bottom: 0; }
.intro .hl { color: var(--green); }

.about-col { margin-top: 96px; }
.about-col.left { grid-column: 1 / 6; } /* lines end at the image's width */
.about-col.right { grid-column: 7 / -1; }
.about-col.left { grid-row: 2; }
.about-col.right { grid-row: 2; }

.about-section + .about-section { margin-top: 34px; }
.about-section h3 {
  margin: 0 0 5px;
  font-size: 25px;       /* matches the rail nav */
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--muted);
  text-transform: lowercase;
}
.about-section hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 0 0 29px;
}
.about-section p {
  margin: 0;
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  max-width: 601px;
}
.about-section .rg { margin-bottom: 25px; }
.about-section .rg:last-child { margin-bottom: 0; }
.about-section .bk { font-weight: 400; }
.about-section .mut { color: var(--muted); font-weight: 400; transition: color .12s ease; }

/* per-brand hover colors: each group sets --hc inline; links fall back to green */
.about-section a { transition: color .12s ease; }
.about-section a:hover,
.about-section a:hover .mut { color: var(--hc, var(--green)); }

/* ============ play page ============ */
.play {
  grid-column: 5 / -1;
  grid-row: 1;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gutter);
  padding-top: 100px;
  align-content: start;
}

.play-intro {
  grid-column: 7 / -1;
  margin: 0;
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  max-width: 537px;
}
.play-intro .hl { color: var(--green); }

.sec-head {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 72px 0 15px;
}
.sec-head.first { margin-top: 98px; }
.sec-head h2 {
  margin: 0;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
}
.show-more {
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--muted);
  transition: color .12s ease;
}
.show-more:hover { color: var(--green); }
.show-more.less { font-weight: 400; }   /* "show less" reads a step lighter */

.prow {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  /* row gap matches the home grid — matters when cards wrap to 2-up below 1360px */
  gap: 64px var(--gutter);
}

/* skydiving: extra rows hidden until "show more" */
.sky-extra { grid-column: 1 / -1; display: none; }
.sky-extra.expanded { display: block; }
.sky-extra .prow { margin-top: var(--gutter); }

/* row 4 keeps the normal grid: the two 9:16 tiles match every other row,
   and only the square clip breaks the column rhythm (it's as wide as it is tall) */
.prow.mixed > * {
  min-width: 0;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--line);
}
.prow.mixed .r916 { grid-column: span 3; aspect-ratio: 9 / 16; }
.prow.mixed .r11 {
  grid-column: span 6;
  justify-self: start;
  aspect-ratio: 1;
  /* js sizes this to the row height so it's square and matches the other rows;
     without js it still renders square, just wider */
  height: var(--sq, auto);
  width: var(--sq, auto);
}
.prow.mixed video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* tap a skydiving clip to hear it */
.pv, .prow.mixed > *, .collabs .cmedia { cursor: pointer; position: relative; }
.pv, .prow.mixed > *, .collabs .cmedia {
  box-shadow: 0 0 0 0 var(--hc, var(--green));
  transition: box-shadow .35s cubic-bezier(.33, 1, .68, 1),
              transform .35s cubic-bezier(.33, 1, .68, 1);
}
.pv.audio-on, .prow.mixed > .audio-on, .cmedia.audio-on {
  /* ring picks up the tile's hover color (--hc) where one is set */
  box-shadow: 0 0 0 3px var(--hc, var(--green));   /* sits flush on the video — no dark gap */
  transform: scale(1.012);
}
.eq {
  position: absolute;
  left: 14px;
  bottom: 14px;
  display: none;
  align-items: flex-end;
  gap: 2px;
  height: 14px;   /* matches the 15px icon scale used everywhere else */
  pointer-events: none;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .6));
}
.audio-on .eq { display: flex; }
.eq i {
  display: block;
  width: 2px;
  height: 8%;
  border-radius: 1px;
  background: var(--hc, var(--green));
  transform-origin: bottom;
  transition: height .07s linear;   /* live level, smoothed a touch */
}

/* volume slider in the section header */
.vol {
  display: none;
  align-items: center;
  gap: 10px;
  margin-left: auto;
  margin-right: var(--gutter);
}
.vol.active { display: flex; animation: vol-in .25s ease; }
@keyframes vol-in { from { opacity: 0; transform: translateX(6px); } }
.vol-ico { width: 15px; height: 15px; display: block; color: var(--muted); }
.vol.active .vol-ico { color: var(--green); }
.vol input {
  -webkit-appearance: none;
  appearance: none;
  width: 96px;
  height: 3px;
  border-radius: 3px;
  background: var(--line);
  outline: none;
  cursor: pointer;
}
.vol input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 3px;
  background: var(--muted);
  cursor: pointer;
  transition: background .12s ease;
}
.vol.active input::-webkit-slider-thumb { background: var(--green); }
.vol input::-moz-range-thumb {
  width: 12px; height: 12px; border: 0; border-radius: 3px; background: var(--muted);
}
.vol.active input::-moz-range-thumb { background: var(--green); }

/* portrait video tiles (skydiving + mc) */
.pv {
  grid-column: span 3;
  min-width: 0;
  aspect-ratio: 9 / 16;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--line);
}
.pv video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* collaborations — the first tile locks to the grid (3 columns of 9:16, the
   same box as a skydiving tile above); the other two share its height at
   their natural aspect ratios with equal gaps, their edges landing off-grid */
.collabs {
  display: flex;
  gap: var(--gutter);
  /* shared tile height: a 3-of-12-column 9:16 box.
     (100% - 11 gutters) / 12 × 3 + 2 gutters, over the 9:16 ratio */
  --ch: calc(((100% - 165px) / 4 + 30px) / 0.5625);
}
.collabs > a { flex: none; min-width: 0; display: block; width: calc(var(--ch) * var(--ar)); }
.collabs .cmedia {
  aspect-ratio: var(--ar);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--line);
}
.collabs .c1 { --ar: 0.5625; }
.collabs .c2 { --ar: 0.75; }
.collabs .c3 { --ar: 0.7758; }
.collabs video { width: 100%; height: 100%; object-fit: cover; display: block; }
.collabs h2 {
  margin: 11px 0 0;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  transition: color .12s ease;
}
.collabs a:hover h2 { color: var(--hc, var(--green)); }
.collabs .sub { margin: 0; font-size: 18px; line-height: 1.05; color: var(--muted); }

/* others — justified rows: every tile keeps its natural aspect ratio,
   rows share a height, edges line up with the grid */
.others { grid-column: 1 / -1; }
.orow { display: flex; gap: var(--gutter); margin-bottom: var(--gutter); }
.orow > * {
  flex: calc(var(--ar) * 100) 1 0;
  min-width: 0;   /* never let the image's intrinsic width widen the row */
  min-height: 0;
  display: block;
  aspect-ratio: var(--ar);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--line);
}
.orow.small { justify-content: flex-start; align-items: flex-start; } /* mixed shapes: no cross-axis stretch */
.orow.small > * { flex: none; width: calc(50% - var(--gutter) / 2); min-width: 0; }
/* negative space: a .shr tile keeps its slot on the grid — the image inside
   renders at --s of it (top-left anchored; .rt hugs the slot's right edge) */
.orow > .shr { background: none; }
.orow > .shr img { width: calc(var(--s, 1) * 100%); height: auto; aspect-ratio: var(--ar); border-radius: var(--radius); }
.orow > .shr.rt img { margin-left: auto; }
.orow.small > .sq { width: calc((50% - var(--gutter) / 2) / 3); } /* half of the 3:2 tile's rendered height */
.orow.small > .park { margin-left: auto; }                        /* parks at the right edge — the gap is the point */
.orow.small > .colr { margin-left: calc(50% + var(--gutter) / 2); width: calc(25% - var(--gutter) / 2); } /* left-aligned on the right half-column */
.others img, .others video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ============ project pages ============ */
/* same content column as the other pages; inside it, the old site's media
   rhythm — full-bleed rows, half-and-half pairs and quarter runs, one
   gutter apart. Each cell declares its shape with --ar (default 16:9) so
   the page has its full height before a single byte of media arrives. */
.project {
  grid-column: 5 / -1;
  grid-row: 1;
  padding-top: 100px;
}
.pgrid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);  /* fulls, halves and quarters share one grid */
  gap: var(--gutter);
}
.pgrid + .pgrid { margin-top: 160px; }   /* chapter break between deliverable sets */
.pgrid > * {
  grid-column: 1 / -1;
  min-width: 0;
  aspect-ratio: var(--ar, 16 / 9);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--line);
  transform: translateZ(0);
}
.pgrid > .half { grid-column: span 2; }
.pgrid > .q { grid-column: span 1; }
.pgrid img, .pgrid video, .pgrid iframe {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border: 0;
}

/* text cells — the media framing (ratio box, hairline fill, clipping) steps
   aside and the site's existing type scale takes over: the title/sub pair
   mirrors the home cards, body copy mirrors the about intro, section labels
   mirror the about page's muted lowercase heads. */
.pgrid > .ptxt {
  aspect-ratio: auto;
  border-radius: 0;
  overflow: visible;
  background: none;
}
/* annotations sit in the right half; when media follows in the same set it
   keeps its distance (section labels already have theirs from the 160px break) */
.pgrid > .ptxt.r { grid-column: 3 / -1; }
.pgrid > .ptxt.l { grid-column: 1 / 3; }   /* some old layouts anchor their notes left */
.pgrid > .ptxt.r:not(:last-child),
.pgrid > .ptxt.l:not(:last-child) { margin-bottom: 50px; }
.ptxt h1 {
  margin: 0;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
}
.ptxt .sub {
  margin: 7px 0 0;
  font-size: 18px;
  font-weight: 400;
  line-height: 1.05;
  color: var(--muted);
}
.ptxt h2 {
  margin: 0;
  font-size: 25px;
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--muted);
  text-transform: lowercase;
}
.ptxt p {
  margin: 0 0 25px;
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  max-width: 601px;
}
.ptxt p:last-child { margin-bottom: 0; }
.ptxt .mut { color: var(--muted); }
.ptxt a { transition: color .12s ease; }
.ptxt a:hover { color: var(--hc, var(--green)); }

/* ============ per-page accents (ported from the old site) ============ */
/* every Webflow page set its own text highlight, and a few tinted their
   links' hover away from green. body[data-route] carries the page identity
   through router swaps, so both travel with the route. Content links read
   --hc — set per route here, or inline on one-off credit links — and fall
   back to green. */
::selection { color: #fff; background: var(--green); }   /* the old site's default */
body[data-route="watch25"] ::selection { color: #fff; background: #388851; }
body[data-route="imac24"] ::selection,
body[data-route="noctua"] ::selection { color: #000; background: #e5ff00; }
body[data-route="watch-series9"] ::selection { color: #fff; background: #e875a5; }
body[data-route="mirage"] ::selection { color: #000; background: #ffd400; }
body[data-route="mercurial"] ::selection { color: #fff; background: #f20533; }
body[data-route="tiptree"] ::selection { color: #fff; background: #dc2229; }
body[data-route="drteals"] ::selection { color: #fff; background: #818ee0; }
body[data-route="forward"] ::selection,
body[data-route="nike-seg"] ::selection { color: #000; background: #fff; }
body[data-route="darksphere"] ::selection { color: #000; background: #ead446; }   /* the DARK yellow — matches its play-card hover; fusion/mama8 ran the default green */

body[data-route="noctua"] { --hc: #e5ff00; }
body[data-route="drteals"] { --hc: #818ee0; }
body[data-route="mercurial"] { --hc: #f20533; }
body[data-route="mirage"] { --hc: #ffd400; }     /* Jeffrey Su's credit overrides inline */
body[data-route="tiptree"] { --hc: #c83732; }    /* Janine's credit overrides inline */

/* project prev/next — the old site's bottom nav, now pointing both ways.
   Nav links hover green on every page (the old .btt did), so this sits
   after the accent block to win the tie with .ptxt a:hover. */
.pnav a:hover { color: var(--green); }
/* the row shares its baseline with the parked © at full scroll: the park's
   bottom lands on main's bottom, so the cells drop their trailing margin
   (the box ends exactly where the © box ends) and the text nudges 1.9px —
   the baseline sits 6.79px up a 25px/1.25 box but 4.89px up the ©'s 18px one */
.pgrid.pnav > .ptxt.l:not(:last-child) { margin-bottom: 0; }
.pnav p {
  font-size: 25px;      /* the rail nav's scale, over .ptxt's 20px body copy */
  position: relative;
  top: 1.9px;
}

/* ============ playbook-page extras (darksphere / fusion / mama8) ============ */
/* thirds — the modding pages run three-across rows; a 6-col variant keeps
   halves possible in the same grid (span 3) while .t takes a third */
.pgrid.g6 { grid-template-columns: repeat(6, 1fr); }
.pgrid.g6 > .half { grid-column: span 3; }
.pgrid.g6 > .t { grid-column: span 2; }
.pgrid.g6 > .ptxt.l { grid-column: 1 / 4; }
.pgrid.g6 > .ptxt.r { grid-column: 4 / -1; }
.pgrid.g6 > .ptxt.t:not(:last-child) { margin-bottom: 50px; }   /* same air as .l/.r rows */
/* terminology table — the old site ran two abbreviation/meaning column
   pairs with air on the right; .tc cells flow into the shaped tracks */
.pgrid.terms { grid-template-columns: .25fr 1fr .25fr 1fr 1.5fr; }
.pgrid.terms > .tc { grid-column: auto; }
/* featured-play tabs — the old Webflow tabs verbatim: green active chip,
   gray idle chips (fixed colors on both themes, like the old dark pages) */
.pgrid > .ptabs { aspect-ratio: auto; border-radius: 0; overflow: visible; background: none; }
.ptabs .tabbar { display: flex; flex-wrap: wrap; margin-bottom: 10px; }
.ptabs .tabbar button {
  padding: 9px 30px;
  border: 0;
  cursor: pointer;
  background: #ddd;
  color: #222;
  font-family: inherit;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  transition: background .12s ease, color .12s ease;
}
.ptabs .tabbar button[aria-pressed="true"] { background: var(--green); color: #fff; }
.ptabs .tpane { display: none; }
.ptabs .tpane.on { display: block; animation: tab-in .2s ease; }
@keyframes tab-in { from { opacity: 0; } }
.ptabs .tmedia {
  aspect-ratio: var(--ar, 16 / 9);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--line);
}
.ptabs .tmedia img, .ptabs .tmedia iframe {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  border: 0;
}
.ptabs .cap {
  margin: 15px 0 0;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  max-width: 601px;
}

/* ============ password gate ============ */
/* a few projects are share-gated, like the old site's PHP password pages.
   html[data-locked] hides the content regions — set synchronously by the
   gated page's own head script on direct loads (nothing flashes before the
   deferred js runs) and by the router on swaps — while main.js drops a bare
   password row into the content column: no card, no title, no messaging,
   no footer (the hm. logo stays a reward for reaching the real page's end),
   just an unboxed field and a plain submit word on the project grid's own
   tracks, baseline-aligned with the rail's "work" (main.js measures the
   rail and pads the gate to match). sessionStorage remembers the unlock
   for the tab's lifetime, the way the old PHP session did. */
html[data-locked] .page > main,
html[data-locked] .page > .page-end { display: none; }
html[data-locked] .page { min-height: 100svh; }

.gate {
  grid-column: 5 / -1;
  grid-row: 1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));  /* the project grid's tracks */
  column-gap: var(--gutter);
  align-content: start;   /* js sets the top pad that lands the row on "work" */
}
.gate-form {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));  /* same tracks — field and submit land on gridlines */
  column-gap: var(--gutter);
}
.gate-form input {
  grid-column: 1;
  min-width: 0;
  font: inherit;
  font-size: 25px;      /* the rail nav's scale — the row reads as a peer of "work" */
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--fg);
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  outline: none;
  caret-color: var(--green);
}
.gate-form input::placeholder { color: var(--muted); }
.gate-form button {
  grid-column: 2;
  justify-self: start;
  font: inherit;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--fg);
  background: none;
  border: 0;
  padding: 0 20px 0 0;   /* right pad = hit area; the word stays on the gridline */
  cursor: pointer;
  transition: color .12s ease;
}
.gate-form button:hover { color: var(--green); }
.gate-form.no { animation: gate-no .4s ease; }
@keyframes gate-no {
  20%, 60% { transform: translateX(-6px); }
  40%, 80% { transform: translateX(6px); }
}

/* after the password lands the page enters from above and settles down —
   the way the old site's post-password load moved */
.gate-reveal { animation: gate-reveal .5s cubic-bezier(.33, 1, .68, 1) both; }
@keyframes gate-reveal { from { opacity: 0; transform: translateY(-14px); } }

/* route-driven theme flips (a project page can present its own mode — watch25
   arrives light) fade the chrome instead of snapping. Scoped to html.theming,
   which the router wears for ~.7s around a swap, so the bottom-left toggle
   keeps its instant feel. The profile fold and swap fades are untouched:
   their own transitions out-specify these. */
html.theming { transition: background .6s ease; }
html.theming body { transition: background .6s ease, color .6s ease; }
html.theming a,
html.theming button { transition: color .6s ease, background-color .6s ease, border-color .6s ease; }
html.theming .mb { transition: background-color .6s ease; }

/* ============ page transitions (spa router) ============ */
/* main.js swaps the content regions in place and pushes the url — the rail
   never reloads and NEVER fades: only the content regions transition, and
   the profile fold above carries the big motion. (No body-level load fade
   either — on file:// every click is a full load, and a global fade made
   the nav column flash on every page switch.) */
.swap-leave { opacity: 0; transition: opacity .16s ease; }
.swap-enter { animation: swap-in .5s cubic-bezier(.33, 1, .68, 1) both; }
@keyframes swap-in { from { opacity: 0; transform: translateY(14px); } }

/* ============ video player (js/player.js) ============ */
/* a Vimeo-style chrome over a plain <video>: 50% black chips a gutter in
   from the edges, a 3px track that fattens on hover, and the page accent
   (--hc, the route's colour) only on what moves — the played fill, the
   knob, the volume level, the chosen quality. Everything else is white on
   black; the timecode rides in a white bubble like Vimeo's. The player is
   a .pgrid cell, so the ratio box, radius and clipping come from there;
   the film letterboxes inside on black. Every tone is a custom property so
   .hmv-light can flip the chrome (Vimeo's inverted theme: white chips,
   black glyphs, black time bubble) on films that were set up that way. */
.hmv {
  --hmv-accent: var(--hc, var(--green));
  --hmv-accent-ink: #fff;                   /* the play glyph on its accent-filled hover */
  --hmv-ink: #fff;                          /* glyphs and menu text */
  --hmv-chip: rgba(0, 0, 0, .5);
  --hmv-rail: rgba(255, 255, 255, .28);     /* unplayed track, volume rail */
  --hmv-buf: rgba(255, 255, 255, .3);
  --hmv-ring: rgba(255, 255, 255, .18);     /* preview frame edge, buffering ring */
  --hmv-tag: #fff;                          /* timecode bubble */
  --hmv-tag-ink: #000;
  --hmv-panel: rgba(18, 18, 18, .94);       /* volume + quality panels */
  --hmv-panel-hover: rgba(255, 255, 255, .08);
  --hmv-dim: rgba(255, 255, 255, .5);       /* menu heading */
  --hmv-dim2: rgba(255, 255, 255, .45);     /* menu bitrates */
  --hmv-thumb-w: 176px;
  position: relative;
  color: var(--hmv-ink);
  outline: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}
.hmv-light {
  --hmv-ink: #000;
  --hmv-chip: rgba(255, 255, 255, .5);
  --hmv-rail: rgba(0, 0, 0, .28);
  --hmv-buf: rgba(0, 0, 0, .3);
  --hmv-ring: rgba(0, 0, 0, .18);
  --hmv-tag: #000;
  --hmv-tag-ink: #fff;
  --hmv-panel: rgba(255, 255, 255, .94);
  --hmv-panel-hover: rgba(0, 0, 0, .06);
  --hmv-dim: rgba(0, 0, 0, .5);
  --hmv-dim2: rgba(0, 0, 0, .45);
}
.pgrid > .hmv { background: #000; }
.pgrid > .hmv > video, .hmv > video {
  width: 100%; height: 100%;
  display: block;
  object-fit: contain;
  background: #000;
  border: 0;
}
.hmv:focus-visible { box-shadow: inset 0 0 0 2px var(--hmv-accent); }
.hmv-ui { position: absolute; inset: 0; }
.hmv.hmv-idle { cursor: none; }

/* -- the bar: three chips, one gutter apart -- */
.hmv-bar {
  position: absolute; left: 10px; right: 10px; bottom: 10px;
  display: flex; gap: 6px;
  height: 34px;
  transition: opacity .25s ease, transform .25s ease;
}
.hmv-idle .hmv-bar { opacity: 0; transform: translateY(6px); pointer-events: none; }
.hmv-chip { display: flex; align-items: center; height: 100%; background: var(--hmv-chip); border-radius: 8px; }
.hmv-left { width: 52px; flex: none; }
.hmv-mid { flex: 1; min-width: 0; padding: 0 14px; }
.hmv-right { flex: none; padding: 0 2px; }
.hmv-btn {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  color: var(--hmv-ink);
  opacity: .92;
  border-radius: 6px;
  transition: opacity .12s ease;
}
.hmv-btn:hover { opacity: 1; }
.hmv-btn:focus-visible { box-shadow: inset 0 0 0 2px var(--hmv-accent); }
.hmv-btn svg { width: 18px; height: 18px; display: block; }
.hmv-left .hmv-btn { width: 100%; }
/* hovers: the play chip fills with the accent (glyph stays white); the
   gear and fullscreen glyphs turn accent */
.hmv-left { transition: background .12s ease; }
.hmv-left:hover { background: var(--hmv-accent); }
.hmv-left:hover .hmv-btn { opacity: 1; color: var(--hmv-accent-ink); }
.hmv-gear, .hmv-full, .hmv-mute { transition: color .12s ease, opacity .12s ease; }
.hmv-gear:hover, .hmv-full:hover { color: var(--hmv-accent); opacity: 1; }
/* the play chip swaps its glyph with the state */
.hmv-play svg { display: none; }
.hmv-play .i-play { display: block; }
.hmv-playing .hmv-play .i-play { display: none; }
.hmv-playing .hmv-play .i-pause { display: block; }
.hmv-ended .hmv-play svg { display: none; }
.hmv-ended .hmv-play .i-replay { display: block; }
.hmv-full .i-unfull { display: none; }
.hmv-fs .hmv-full .i-full { display: none; }
.hmv-fs .hmv-full .i-unfull { display: block; }

/* -- timeline -- */
.hmv-track { position: relative; flex: 1; height: 100%; cursor: pointer; touch-action: none; }
.hmv-rail {
  position: absolute; left: 0; right: 0; top: 50%;
  height: 3px; margin-top: -1.5px;
  border-radius: 3px;
  background: var(--hmv-rail);
  overflow: hidden;
  transition: height .12s ease, margin-top .12s ease;
}
.hmv-track:hover .hmv-rail, .hmv-scrub .hmv-rail { height: 5px; margin-top: -2.5px; }
.hmv-buf, .hmv-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 100%;
  transform-origin: 0 50%;
  transform: scaleX(0);
}
.hmv-buf { background: var(--hmv-buf); }
.hmv-fill { background: var(--hmv-accent); }
.hmv-knob {
  position: absolute; left: 0; top: 50%;
  width: 12px; height: 12px; margin: -6px 0 0 -6px;
  pointer-events: none;
}
.hmv-knob::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: 50%;
  background: var(--hmv-accent);
  opacity: 0;
  transform: scale(.5);
  transition: opacity .12s ease, transform .12s ease;
}
.hmv-track:hover .hmv-knob::after, .hmv-scrub .hmv-knob::after { opacity: 1; transform: scale(1); }
/* the timecode bubble rides the playhead: the duration until the first
   play, the current time after. Hovering the track hands the spot to the
   preview instead. */
.hmv-bubble, .hmv-peek { position: absolute; left: 0; bottom: calc(50% + 11px); pointer-events: none; }
.hmv-bubble { transition: opacity .12s ease; }
.hmv-peeking .hmv-bubble { opacity: 0; }
.hmv-bubble > span, .hmv-peek-time {
  position: relative;
  display: block;
  transform: translateX(-50%);
  padding: 3px 6px 2px;
  border-radius: 4px;
  background: var(--hmv-tag);
  color: var(--hmv-tag-ink);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.hmv-peek-time { transform: none; }
.hmv-bubble > span::after, .hmv-peek-time::after {
  content: '';
  position: absolute; left: 50%; bottom: -4px; margin-left: -4px;
  border: 4px solid transparent;
  border-top-color: var(--hmv-tag);
  border-bottom: 0;
}
/* hover preview: a frame from the sheet with its time under it */
.hmv-peek { display: none; }
.hmv-peeking .hmv-peek { display: block; }
.hmv-peek-in { display: flex; flex-direction: column; align-items: center; gap: 6px; transform: translateX(-50%); }
.hmv-thumb {
  position: relative;
  display: none;
  width: var(--hmv-thumb-w);
  aspect-ratio: var(--fw, 240) / var(--fh, 102);
  border-radius: 6px;
  overflow: hidden;
  background: #000;
  box-shadow: 0 0 0 1px var(--hmv-ring), 0 8px 24px rgba(0, 0, 0, .45);
}
.hmv-sheet-ok .hmv-thumb { display: block; }
.hmv .hmv-thumb img {
  position: absolute; left: 0; top: 0;
  width: calc(var(--cols, 12) * 100%); height: auto;
  max-width: none;
  object-fit: fill;
  display: block;
  will-change: transform;
}
.hmv > .hmv-sheet { display: none; }   /* before mount (or without js) the sheet stays out of sight */

/* -- volume: the level slides out of the speaker on hover -- */
.hmv-vol { position: relative; display: flex; align-items: center; height: 100%; }
.hmv-vol:hover .hmv-mute, .hmv-vol.hmv-vdrag .hmv-mute, .hmv-vol.hmv-pin .hmv-mute { color: var(--hmv-accent); opacity: 1; }
/* the level lives in a small panel that pops up over the speaker; the
   wrapper reaches down to the chip so the pointer can travel up into it */
.hmv-vslider {
  position: absolute; left: 50%; bottom: 100%;
  width: 34px; padding-bottom: 6px;
  transform: translateX(-50%);
  display: none;
  cursor: pointer;
  touch-action: none;
}
.hmv-vol:hover .hmv-vslider, .hmv-vol.hmv-vdrag .hmv-vslider, .hmv-vol.hmv-pin .hmv-vslider { display: block; }
.hmv-vpanel {
  position: relative;
  height: 112px;
  border-radius: 8px;
  background: var(--hmv-panel);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
}
.hmv-vrail {
  position: absolute; left: 50%; top: 14px; bottom: 14px;
  width: 3px; margin-left: -1.5px;
  border-radius: 3px;
  background: var(--hmv-rail);
  overflow: hidden;
}
.hmv-vfill { position: absolute; inset: 0; background: var(--hmv-accent); transform-origin: 50% 100%; }
.hmv-vknob {
  position: absolute; left: 50%; bottom: 14px;   /* js walks it up the rail */
  width: 10px; height: 10px; margin: 0 0 -5px -5px;
  border-radius: 50%;
  background: var(--hmv-accent);
  pointer-events: none;
}
.hmv-mute svg path { transition: opacity .12s ease; }
.hmv-mute .wx { opacity: 0; }
.hmv-v0 .hmv-mute .w1, .hmv-v0 .hmv-mute .w2 { opacity: 0; }
.hmv-v0 .hmv-mute .wx { opacity: 1; }
.hmv-v1 .hmv-mute .w2 { opacity: 0; }
@media (pointer: coarse) { .hmv-vslider { display: none; } }   /* phones: hardware volume; the speaker still mutes */

/* -- quality menu: hover opens, a click pins. It spans the right chip
      exactly (same left edge as the three buttons) and sits one chip-gap
      above; the wrapper reaches down to the chip so the pointer can cross -- */
.hmv-right { position: relative; }
.hmv-set { display: flex; align-items: center; height: 100%; }
.hmv-menu {
  position: absolute; left: 0; right: 0; bottom: 100%;
  padding-bottom: 6px;
  display: none;
}
.hmv-set:hover .hmv-menu, .hmv-set.hmv-pin .hmv-menu { display: block; }
.hmv-set:hover .hmv-gear, .hmv-set.hmv-pin .hmv-gear { color: var(--hmv-accent); opacity: 1; }
.hmv-menu-in {
  padding: 4px;
  border-radius: 8px;
  background: var(--hmv-panel);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: -0.01em;
}
.hmv-menu-h {
  padding: 6px 8px 6px;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--hmv-dim);
}
.hmv-menu button {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; height: 30px;
  padding: 0 8px;
  border-radius: 6px;
  color: var(--hmv-ink);
  text-align: left;
  transition: background .12s ease;
}
.hmv-menu button:hover, .hmv-menu button:focus-visible { background: var(--hmv-panel-hover); }
.hmv-menu button[aria-checked="true"] { color: var(--hmv-accent); }
.hmv-menu button .r { display: flex; align-items: center; margin-left: 8px; }
.hmv-menu button em { font-style: normal; color: var(--hmv-dim2); font-variant-numeric: tabular-nums; }
.hmv-menu button svg { width: 14px; height: 14px; opacity: 0; }
.hmv-menu button[aria-checked="true"] svg { opacity: 1; }

/* -- buffering ring + the held frame under a quality switch -- */
.hmv-spin {
  position: absolute; left: 50%; top: 50%;
  width: 44px; height: 44px; margin: -22px 0 0 -22px;
  border-radius: 50%;
  border: 2px solid var(--hmv-ring);
  border-top-color: var(--hmv-accent);
  opacity: 0;
  transition: opacity .2s ease;
  pointer-events: none;
}
.hmv-wait .hmv-spin { opacity: 1; animation: hmv-spin .8s linear infinite; }
@keyframes hmv-spin { to { transform: rotate(360deg); } }
.hmv-freeze { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; display: none; pointer-events: none; }
.hmv-frozen .hmv-freeze { display: block; }

/* -- fullscreen: the cell's ratio box steps aside (the prefixed twin is
      kept apart — an unknown selector in a list would void the whole rule) -- */
.hmv:fullscreen { width: 100%; height: 100%; aspect-ratio: auto; border-radius: 0; background: #000; }
.hmv:-webkit-full-screen { width: 100%; height: 100%; aspect-ratio: auto; border-radius: 0; background: #000; }
.hmv:fullscreen .hmv-bar { left: 16px; right: 16px; bottom: 16px; }
.hmv:-webkit-full-screen .hmv-bar { left: 16px; right: 16px; bottom: 16px; }

.pgrid > .hmv, .tmedia.hmv { background: #000; }
/* compact chrome: phones, and any player narrower than 480px (js sets
   .hmv-sm — the third-width cells on the playbook pages) */
.hmv-sm { --hmv-thumb-w: 128px; }
.hmv-sm .hmv-bar { left: 8px; right: 8px; bottom: 8px; height: 30px; gap: 4px; }
.hmv-sm .hmv-left { width: 44px; }
.hmv-sm .hmv-mid { padding: 0 10px; }
.hmv-sm .hmv-btn { width: 30px; height: 30px; }
.hmv-sm .hmv-left .hmv-btn { width: 100%; }   /* the play glyph stays centred in its wider chip */
.hmv-sm .hmv-btn svg { width: 16px; height: 16px; }
.hmv-sm .hmv-vslider { width: 30px; }
@media (max-width: 600px) {
  .hmv { --hmv-thumb-w: 128px; }
  .hmv-bar { left: 8px; right: 8px; bottom: 8px; height: 30px; gap: 4px; }
  .hmv-left { width: 44px; }
  .hmv-mid { padding: 0 10px; }
  .hmv-btn { width: 30px; height: 30px; }
  .hmv-left .hmv-btn { width: 100%; }
  .hmv-btn svg { width: 16px; height: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .hmv-bar, .hmv-bubble, .hmv-rail, .hmv-knob::after, .hmv-spin, .hmv-btn { transition: none; }
}

/* ============ motion preferences ============ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .profile, .profile video, .profile img { transition: none; }
  html.ready .pslot { transition: none; }
  html.ready .pclip .profile { transition: none; }
  .swap-enter { animation: none; }
  .swap-leave { transition: none; }
  .gate-form.no { animation: none; }
  .gate-reveal { animation: none; }
  .view-menu, .g3d, .g3d-tip, .g3d-mode { transition: none; }
  body::after { transition: none; }
}

/* ============ responsive ============ */
@media (max-width: 1360px) {
  body[data-view="3"] .card { grid-column: span 6; }
  body[data-view="3"] .fdot.d5, body[data-view="3"] .fdot.d9 { display: none; }
  body[data-view="3"] .fdot.d7 { display: block; }
  /* 3-column view doesn't exist at this width — drop its menu item,
     and show 2-column as the active state if it was selected */
  .vm-item[data-view="3"] { display: none; }
  body[data-view="3"]:not([data-g3d]) .vm-item[data-view="2"] { color: var(--green); }
  body[data-view="list"] .card .sub { display: none; }
  body[data-view="list"] .card h2 { grid-column: 5 / 11; }
  /* single column: the base rules pin these to shared rows, so re-stack
     them explicitly or they pile up on top of each other */
  /* the time drops below the picture at this width — only the mobile page
     (block layout, source order) keeps it on top */
  .about-photo { grid-column: 1 / -1; grid-row: 1; max-width: 500px; }
  .clock { grid-column: 1 / -1; grid-row: 2; margin-top: 48px; }
  .intro { grid-column: 1 / -1; grid-row: 3; margin-top: 48px; }
  .about-col.left, .about-col.right { grid-column: 1 / -1; margin-top: 64px; }
  .about-col.left { grid-row: 4; }
  .about-col.right { grid-row: 5; }
}

@media (max-width: 1060px) {
  .rail { grid-column: 1 / 4; }
  .main, .about, .project { grid-column: 5 / -1; }
}

/* ============ mobile chrome (Figma: v6a MOBILE) ============ */
/* rail.js renders the bar + menu on every page; they only exist ≤860px */
.mnav, .mmenu, .end-copy { display: none; }

@media (max-width: 860px) {
  :root { --margin-x: 25px; }   /* phone/tablet keep the tighter edge — the mobile chrome is built around it */
  .page { display: block; padding: 0 24px; }

  /* the desktop rail and view switcher are replaced by the bar + menu —
     mobile is one linear scroll, no layout views */
  .rail-top, .rail-park, .status { display: none; }

  /* ---- fixed bar: transparent at rest, glass once scrolled ---- */
  .mnav {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 65px;
    z-index: 100;
  }
  .mnav::before {   /* the glass itself — fades in as the bar condenses */
    content: "";
    position: absolute;
    inset: 0;
    background-color: var(--glass);
    background-image: var(--noise);
    -webkit-backdrop-filter: var(--frost);
    backdrop-filter: var(--frost);
    opacity: 0;
    transition: opacity .35s ease;
    pointer-events: none;
  }
  html.mnav-scrolled .mnav::before { opacity: 1; }
  html.menu-open .mnav::before { opacity: 0; }   /* the menu's own frost covers the screen */

  .m-id {
    position: absolute;
    top: 22px;
    left: 24px;
    font-size: 25px;
    font-weight: 500;
    letter-spacing: -0.01em;
    line-height: 1;
  }
  .m-id .name, .m-id .role { display: block; white-space: nowrap; }
  /* the name is the wiper: on scroll the role slides UP behind it and fades
     away underneath (same move as the live site, inverted to keep the name
     on its Figma line) */
  .m-id .name { position: relative; z-index: 1; color: var(--green); transition: color .35s ease; }
  .m-id .role { color: var(--fg); transition: opacity .35s ease, transform .35s ease; }
  html.mnav-scrolled:not(.menu-open) .m-id .name { color: var(--fg); }
  html.mnav-scrolled:not(.menu-open) .m-id .role { opacity: 0; transform: translateY(-25px); }

  .menu-btn {
    position: absolute;
    top: 25px;
    right: 24px;
    display: flex;
    flex-direction: column;
    gap: 5px;
  }
  .menu-btn::before { content: ""; position: absolute; inset: -14px; } /* bigger hit area */
  .menu-btn .mb {
    display: block;
    width: 25px;
    height: 3px;
    border-radius: 8px;
    background: var(--fg);
    transition: background .25s ease;
  }
  html.menu-open .menu-btn .mb { background: var(--green); }

  /* ---- full-screen frosted menu ---- */
  html.menu-open { overflow: hidden; }

  .mmenu {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    position: fixed;
    inset: 0;
    z-index: 90;
    background-color: var(--glass);
    background-image: var(--noise);
    -webkit-backdrop-filter: var(--frost-max);
    backdrop-filter: var(--frost-max);
    padding: 133px 24px 25px;
    /* short/landscape viewports: the body is scroll-locked, so the menu
       itself must scroll to keep the last links and the toggle reachable */
    overflow-y: auto;
    overscroll-behavior: contain;
    visibility: hidden;
    opacity: 0;
    transition: opacity .3s ease, visibility 0s .3s;
  }
  html.menu-open .mmenu { visibility: visible; opacity: 1; transition: opacity .3s ease; }

  .mmenu-nav { display: flex; flex-direction: column; align-items: flex-start; margin-bottom: 55px; }
  .mmenu-nav a {
    font-size: 50px;
    font-weight: 500;
    letter-spacing: -0.01em;
    line-height: 1.1;
    transition: color .12s ease;
  }
  .mmenu-nav .mm-main { color: var(--fg); margin-bottom: 55px; } /* one blank line between the mains */
  .mmenu-nav .mm-sub { color: var(--muted); }
  .mmenu-nav a:hover, .mmenu-nav a:active { color: var(--green); }

  /* the menu's theme toggle — the desktop toggle at 2.25x. In flow: margin-top
     auto pins it bottom-left on tall viewports, and it scrolls into reach
     (never overlapping the links) on short ones */
  .mmenu .toggle {
    flex: none;
    width: 63px;
    height: 36px;
    border-radius: 6px;
    margin: auto 0 0;
  }
  .mmenu .toggle .knob { top: 4.5px; left: 4.5px; width: 27px; height: 27px; border-radius: 5px; }
  html[data-theme="light"] .mmenu .toggle .knob { transform: translateX(27px); }

  /* ---- content: one linear scroll under the bar ---- */
  .main, .about, .play, .project { display: block; padding-top: 99px; }
  /* locked pages: no rail nav to align with here, so the row just centers
     in the viewport under the bar */
  html[data-locked] .page { display: flex; flex-direction: column; min-height: 100svh; }
  .gate { flex: 1; align-content: center; }
  .gate-form { grid-template-columns: 1fr; row-gap: 10px; }   /* stacked: field on its own line, submit under it */
  .gate-form button { grid-column: 1; }
  /* project media: one linear scroll — pairs stack like everything else */
  .pgrid { display: block; }
  .pgrid > *, .pgrid > .half { margin-bottom: 12px; }
  .pgrid > .ptxt.r:not(:last-child),
  .pgrid > .ptxt.l:not(:last-child) { margin-bottom: 24px; } /* the 50px desktop push is too airy stacked */
  /* terminology keeps its table shape — abbreviations mean nothing without
     their meanings beside them (the old mobile ran the same two-up) */
  .pgrid.terms { display: grid; grid-template-columns: .5fr 1.5fr; column-gap: var(--gutter); }
  .pgrid.terms > .tc { margin-bottom: 12px; }
  .ptabs .tabbar button { padding: 9px 15px; }   /* tighter chips; the bar wraps like the old one did */
  /* stacked prev/next keeps the 24px rhythm (the desktop baseline rule zeroes it) */
  .pgrid.pnav > .ptxt.l:not(:last-child) { margin-bottom: 24px; }
  .pgrid + .pgrid { margin-top: 56px; }
  .grid { display: block; padding-top: 0; }

  /* every card is the same full-width stack, whatever view was saved on desktop */
  .card, body[data-view="list"] .card {
    display: block;
    margin-bottom: 38px;
    border-top: 0;
    padding: 0;
  }
  .card .num, .card .year,
  body[data-view="list"] .card .num, body[data-view="list"] .card .year { display: none; }
  body[data-view="list"] .card .media { max-width: none; }
  .card h2, body[data-view="list"] .card h2 { margin: 7px 0 0; font-size: 25px; }
  .card .sub, body[data-view="list"] .card .sub { display: block; padding-top: 0; }

  .clock .tz { display: block; }   /* time zone on its own line — no mid-word wrap */
  .about-photo, .intro, .about-col.left, .about-col.right { margin-top: 40px; }
  .about-photo { max-width: none; }   /* the 1360px 500px cap would otherwise leak in */

  .play-intro { margin-top: 90px; }

  .sec-head, .sec-head.first { margin: 56px 0 16px; }
  .prow { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  .pv { grid-column: auto; }
  .collabs { display: block; width: 100%; }
  .collabs > a { margin-bottom: 38px; width: auto; }
  .collabs .cmedia { height: auto; width: 100%; }
  .collabs h2 { margin: 7px 0 0; }
  .prow .card { grid-column: 1 / -1; margin-bottom: 38px; }
  .sky-extra .prow { margin-top: 12px; }
  .prow.mixed { grid-template-columns: 1fr 1fr; }
  /* height:auto lets aspect-ratio 1 re-apply to the square clip — the js-set
     --sq is measured against the 2-up mobile cells and would render it 9:16 */
  .prow.mixed .r916, .prow.mixed .r11 { grid-column: auto; width: 100%; height: auto; }
  .vol input { width: 70px; }
  /* others gallery: the justified-row flex (basis 0) never wraps on its own —
     pin tiles to 2-up so they stay usable at phone widths */
  .orow { flex-wrap: wrap; }
  .orow > * { flex: none; width: calc(50% - var(--gutter) / 2); }
  .orow.small > * { width: 100%; }
  /* negative-space tweaks read as gaps only on the wide grid — undo them here */
  .orow > .shr img { width: 100%; height: 100%; }
  .orow.small > .sq { width: calc(50% - var(--gutter) / 2); }
  .orow.small > .park { margin-left: 0; }
  .orow.small > .colr { margin-left: 0; width: 100%; }

  /* ---- page end: hm. cornered bottom-left, copyright bottom-right ---- */
  .page-end {
    display: flex;
    justify-content: space-between;
    /* the copyright never breaks mid-line: when it can't fit beside the
       logo it takes the whole line ABOVE it (wrap-reverse stacks upward),
       a clear row of air in between. wrap-reverse flips the cross axis,
       so flex-START is the visual bottom here */
    flex-wrap: wrap-reverse;
    align-items: flex-start;
    row-gap: 56px;
    margin-top: 80px;
    padding-bottom: 25px;
  }
  .end-logo svg { width: 176px; }
  .fdot { display: none !important; }
  .end-copy {
    display: block;
    font-size: 18px;
    font-weight: 500;
    letter-spacing: -0.01em;
    line-height: 1.25;
    white-space: nowrap;
    color: var(--fg);
    transition: color .12s ease;
  }
  .end-copy:hover { color: var(--green); }
}
