/* ── the faces ────────────────────────────────────────────────
   Self-hosted, the same way three.js is vendored: no runtime call
   out to a font CDN, and nothing to go missing offline. Latin
   subsets only. Inter, Inter Tight and Syne are variable, so one
   file each covers every weight the page asks for.

   These were named throughout the stylesheet long before they were
   ever loaded, so until now every one of them was silently falling
   back to the system sans. */
@font-face {
  font-family: "Geist";
  src: url("../fonts/geist.woff2") format("woff2-variations"),
       url("../fonts/geist.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Source Serif 4";
  src: url("../fonts/source-serif.woff2") format("woff2-variations"),
       url("../fonts/source-serif.woff2") format("woff2");
  font-weight: 200 900; font-style: normal; font-display: swap;
}

@font-face {
  font-family: "Inter";
  src: url("../fonts/inter.woff2") format("woff2-variations"),
       url("../fonts/inter.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Inter Tight";
  src: url("../fonts/inter-tight.woff2") format("woff2-variations"),
       url("../fonts/inter-tight.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Syne";
  src: url("../fonts/syne.woff2") format("woff2-variations"),
       url("../fonts/syne.woff2") format("woff2");
  font-weight: 400 800; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Instrument Serif";
  src: url("../fonts/instrument-serif.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* ═══════════════════════════════════════════════════════════
   r7d.creative — Rahulraj P V
   Near-black ground, oversized light grotesque, and the
   observer crossing in front of the type.
   ═══════════════════════════════════════════════════════════ */

:root {
  --bg:     #131313;
  --type-1: #f0f0f0;
  --type-2: #c9c9c9;
  --type-3: #8a8a8a;
  --muted:  #6d6d6d;
  --accent: #ff5a1f;
}

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

html { background: var(--bg); }

body {
  background: var(--bg);
  overflow-x: hidden;
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  color: var(--type-2);
  -webkit-font-smoothing: antialiased;
}

canvas { display: block; }
ul, ol { list-style: none; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--accent); color: #fff; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* ── hero ─────────────────────────────────────────────────── */
.hero {
  position: relative;
  height: 100vh;
  height: 100svh;
}

/* ── heading layer ────────────────────────────────────────── */
.stage {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; justify-content: center;
  padding: 6vw clamp(1.1rem, 3vw, 4rem);
}

.title {
  display: flex; flex-direction: column;
  /* Inter Tight is drawn for headlines: tighter apertures and less
     side bearing than Inter, which is what lets it hold together at
     this size. Light weight, because the poster is thin huge type. */
  font-family: "Inter Tight", "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
  font-weight: 200;
  font-size: clamp(1.95rem, 7.1vw, 7.4rem);
  line-height: 0.84;
  letter-spacing: -0.045em;
  text-transform: uppercase;
  font-feature-settings: "kern" 1, "liga" 1;

  color: var(--type-2);                 /* fallback where clip is unsupported */
  background: linear-gradient(178deg, var(--type-1) 0%, var(--type-2) 42%, var(--type-3) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .title { background: none; -webkit-text-fill-color: currentColor; }
}

/* ── the observer ─────────────────────────────────────────── */
/* Above the type: the canvas is transparent everywhere but the head, so he
   occludes the words he crosses and the rest reads straight through. */
.observer {
  position: absolute; inset: 0; z-index: 2;
  pointer-events: none;
}
.observer__canvas { width: 100%; height: 100%; }
.observer__canvas > canvas { width: 100% !important; height: 100% !important; }

.observer__status {
  position: absolute; right: 4vw; bottom: 4vw;
  font-size: 0.68rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: #3a3a3a;
}
.observer.is-live .observer__status { display: none; }

/* ── responsive ───────────────────────────────────────────── */
@media (max-width: 700px) {
  .stage { justify-content: flex-end; padding-bottom: 12vh; }
  .title { letter-spacing: -0.025em; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* ── madhuri ──────────────────────────────────────────────── */
.wireless {
  position: relative;
  padding: clamp(4rem, 12vh, 9rem) clamp(1.25rem, 5vw, 4rem) clamp(5rem, 14vh, 10rem);
  max-width: 1460px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.3fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}

/* ── the panel beside the set ─────────────────────────────── */
.about__lede {
  margin-top: 1.2rem;
  font-size: clamp(0.9rem, 1.15vw, 1.02rem);
  line-height: 1.62; color: #b6b6b6;
}
.about__body {
  margin-top: 0.9rem;
  font-size: clamp(0.8rem, 1vw, 0.9rem);
  line-height: 1.75; color: #8c8c8c;
}

/* ── play store ── */
.store {
  margin-top: 1.9rem;
  display: inline-flex; align-items: center; gap: 0.7rem;
  padding: 0.6rem 1.15rem 0.6rem 0.9rem;
  border: 1px solid #3a3a3a; border-radius: 8px;
  background: #1b1b1b;
  transition: border-color 0.25s ease, background 0.25s ease, transform 0.15s ease;
}
.store:hover { border-color: #6a6a6a; background: #232323; transform: translateY(-1px); }
.store:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.store__icon { width: 1.6rem; height: 1.6rem; flex: none; }
.store__text {
  display: flex; flex-direction: column; line-height: 1.15;
  font-size: 0.94rem; font-weight: 500; color: #e8e8e8;
}
.store__text small {
  font-size: 0.56rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: #8a8a8a;
}

@media (max-width: 900px) {
  .wireless { grid-template-columns: 1fr; }
}


/* ── the set ──────────────────────────────────────────────────
   Cutout coordinates come straight from the chassis PNG's alpha
   channel, so the controls land exactly in their holes.
   ─────────────────────────────────────────────────────────── */
.radio {
  position: relative;
  margin: 0;
  width: 100%;
  aspect-ratio: 1536 / 1024;
  user-select: none; -webkit-user-select: none;
}
.radio__chassis {
  position: absolute; inset: 0; z-index: 2;
  width: 100%; height: 100%;
  filter: drop-shadow(0 30px 50px rgba(0,0,0,0.6));
  pointer-events: none;
}
.radio__back { position: absolute; inset: 0; z-index: 1; }

/* ── screen, seen through the grille cutout ── */
.screen {
  position: absolute;
  left: 17.90%; top: 12.79%; width: 64.26%; height: 33.30%;
  overflow: hidden;
  display: grid; place-items: center;
  background: radial-gradient(120% 140% at 50% 40%, #14100c 0%, #070605 70%);
  container-type: inline-size;
}
.screen::after {                       /* glass */
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(160deg, rgba(255,220,170,0.07), transparent 45%);
  pointer-events: none;
}
.screen__vu {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; transition: opacity 0.5s ease;
}
.radio.is-on .screen__vu { opacity: 1; }
.screen__hud {
  position: absolute; inset: 0;
  padding: 4.2% 4.6% 0;
  display: flex; align-items: flex-start; justify-content: space-between;
  pointer-events: none;
}
.screen__mark {
  font-family: "Syne", "Inter", sans-serif;
  line-height: 1;
  opacity: 0.16;
  transition: opacity 0.55s ease, filter 0.55s ease;
}
.radio.is-on .screen__mark { opacity: 0.75; }
.radio.is-playing .screen__mark { opacity: 1; }
.screen__mark-en {
  font-weight: 400;
  font-size: clamp(0.5rem, 1.5cqw, 1.05rem);
  font-weight: 700;
  letter-spacing: 0.3em; text-transform: lowercase;
  background: linear-gradient(96deg, #ff5ea0, #d63cdc 55%, #8b5cf6);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #ff8ec0;
}

.screen__state {
  display: flex; align-items: center; gap: 0.45em;
  font-family: "Inter", sans-serif;
  font-size: clamp(0.36rem, 0.95cqw, 0.72rem);
  letter-spacing: 0.24em; text-transform: uppercase;
  color: rgba(255, 205, 150, 0.45);
  transition: color 0.4s ease;
}
.screen__state i {
  width: 0.5em; height: 0.5em; border-radius: 50%;
  background: currentColor; flex: none;
}
.radio.is-on .screen__state { color: rgba(255, 200, 130, 0.85); }
.radio.is-playing .screen__state { color: #ff6b8f; }
.radio.is-playing .screen__state i { animation: onair 1.6s ease-in-out infinite; }
@keyframes onair { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

.screen__scan {
  position: absolute; inset: 0; opacity: 0;
  background: repeating-linear-gradient(
    to bottom, rgba(255,255,255,0.045) 0 1px, transparent 1px 3px);
  transition: opacity 0.5s ease;
}

.radio.is-on .screen__scan { opacity: 1; }

/* ── tuning scale, seen through the slot ── */
.dialface {
  position: absolute;
  left: 26.95%; top: 50.39%; width: 46.22%; height: 2.93%;
  background: linear-gradient(to bottom, #17120c, #241a10);
  overflow: hidden;
}
.dialface canvas { width: 100%; height: 100%; }

/* ── knobs, filling their holes ── */
.knob {
  position: absolute; z-index: 3;
  border-radius: 50%;
  cursor: grab; touch-action: none;
}
.knob:active { cursor: grabbing; }
.knob--vol  { left: 18.82%; top: 59.18%; width: 11.78%; height: 18.07%; }
.knob--tune { left: 69.40%; top: 59.18%; width: 11.78%; height: 18.16%; }

.knob__face {
  position: absolute; inset: 0; border-radius: 50%;
  background:
    radial-gradient(60% 60% at 34% 28%, #6b6357 0%, #40392f 45%, #241f19 100%);
  box-shadow:
    inset 0 2px 4px rgba(255,225,180,0.28),
    inset 0 -3px 8px rgba(0,0,0,0.75),
    0 2px 6px rgba(0,0,0,0.6);
}
.knob__face::after {                   /* brass collar */
  content: ""; position: absolute; inset: 14%;
  border-radius: 50%;
  background: radial-gradient(60% 60% at 36% 30%, #b99a5e, #6d5730 70%, #3a2e19);
  box-shadow: inset 0 -2px 5px rgba(0,0,0,0.6);
}
.knob__pointer {
  position: absolute; left: 50%; top: 8%;
  width: 6%; height: 34%;
  margin-left: -3%;
  transform-origin: 50% 123%;
  transform: rotate(0deg);
  border-radius: 3px;
  background: linear-gradient(to bottom, #ffd9a0, rgba(255,180,90,0.15));
  box-shadow: 0 0 6px rgba(255,170,80,0.5);
  opacity: 0.25;
  transition: opacity 0.4s ease, box-shadow 0.4s ease;
  pointer-events: none;
}
.radio.is-on .knob__pointer {
  opacity: 1;
  box-shadow: 0 0 10px rgba(255,150,60,0.9);
}
.knob input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0;
  /* the knob handles the rotary drag; the input keeps it keyboard-operable */
  pointer-events: none;
}
.knob:focus-within { box-shadow: 0 0 0 3px var(--accent); }

/* ── the five keys, filling their holes ── */
.radio__keys button {
  position: absolute; z-index: 3;
  top: 71.00%; width: 3.12%; height: 4.79%;
  border: 0; border-radius: 50%;
  font-size: 0; color: transparent;
  cursor: pointer;
  background: radial-gradient(62% 62% at 36% 28%, #efe6d2 0%, #cbbfa4 55%, #8d8168 100%);
  box-shadow: inset 0 -2px 3px rgba(0,0,0,0.45), 0 2px 3px rgba(0,0,0,0.6);
  transition: transform 0.07s ease, box-shadow 0.2s ease, filter 0.2s ease;
}
.radio__keys button:nth-child(1) { left: 36.91%; }
.radio__keys button:nth-child(2) { left: 42.58%; }
.radio__keys button:nth-child(3) { left: 48.37%; }
.radio__keys button:nth-child(4) { left: 54.17%; }
.radio__keys button:nth-child(5) { left: 59.96%; }
.radio__keys button:hover { filter: brightness(1.15); }
.radio__keys button:active { transform: translateY(9%); }
.radio__keys button:focus-visible { box-shadow: 0 0 0 3px var(--accent); }
.radio__keys button[aria-pressed="true"] {
  transform: translateY(9%);
  background: radial-gradient(62% 62% at 36% 28%, #ffd9ea 0%, #f07ab4 55%, #b93b78 100%);
  box-shadow: inset 0 -2px 3px rgba(0,0,0,0.4), 0 0 12px 2px rgba(236,72,153,0.8);
}

@media (max-width: 640px) {
  .radio__chassis { filter: none; }
}

/* the set is on but silent — nudge the play key, since the legend is gone */
.radio.is-on:not(.is-playing) .radio__keys button[data-key="play"] {
  animation: keyhint 1.9s ease-in-out infinite;
}
@keyframes keyhint {
  0%, 100% { box-shadow: inset 0 -2px 3px rgba(0,0,0,0.45), 0 0 0 0 rgba(236,72,153,0); }
  50%      { box-shadow: inset 0 -2px 3px rgba(0,0,0,0.45), 0 0 10px 2px rgba(236,72,153,0.75); }
}
@media (prefers-reduced-motion: reduce) {
  .radio.is-on:not(.is-playing) .radio__keys button[data-key="play"] { animation: none; }
}

.about__tag {
  margin-top: 1.1rem;
  display: inline-block;
  padding: 0.3rem 0.7rem;
  border: 1px solid rgba(236, 72, 153, 0.45);
  border-radius: 999px;
  font-size: 0.58rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: #ff6b9d;
}
.about__tag + .about__lede { margin-top: 1rem; }

/* ── gradient ─────────────────────────────────────────────────
   Mirror of the Madhuri block: set on the left, panel on the
   right, so the two sections alternate down the page.
   ─────────────────────────────────────────────────────────── */
.gradient {
  position: relative;
  padding: clamp(4rem, 12vh, 9rem) clamp(1.25rem, 5vw, 4rem) clamp(5rem, 14vh, 10rem);
  max-width: 1460px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 0.78fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}

.tv {
  container-type: inline-size;
  position: relative;
  width: 100%;
  aspect-ratio: 1422 / 984;
  /* the cabinet is cut out now, so this shadow follows its actual
     silhouette instead of a rectangle */
  filter: drop-shadow(0 26px 42px rgba(0,0,0,0.72));
}
.tv__body { width: 100%; height: 100%; display: block; }

/* screen face, measured off the photograph */
.tv__screen {
  container-type: inline-size;
  position: absolute;
  left: 12.38%; top: 11.69%; width: 56.33%; height: 56.10%;
  border-radius: 4% / 5%;
  overflow: hidden;
  background: #050505;
  display: grid; place-items: center;
}
.tv__frame { width: 100%; height: 100%; border: 0; display: block; }

/* ── panel on the right ── */
.about--right { order: 2; }

.store--plain .store__icon { color: #d8d8d8; }
.about__meta {
  margin-top: 1.1rem;
  font-size: 0.58rem; letter-spacing: 0.12em; line-height: 1.8;
  color: #5c5c5c;
}

@media (max-width: 900px) {
  .gradient { grid-template-columns: 1fr; }
  .about--right { order: 0; }
}

/* ── keep YouTube's furniture off the screen ───────────────── */
.tv__frame {
  /* non-interactive, so hover can never raise the title bar, channel
     avatar, CC button or logo overlay */
  pointer-events: none;
}
/* Once the broadcast is running, the tube takes no mouse at all — no
   clicks, no hover, no cursor change. The set is driven from its knobs. */
/* the tube never takes the mouse at all — the set is worked from its knobs */
.tv__screen { pointer-events: none; }

/* ── the picture fills the tube ─────────────────────────────
   The screen face is 1.451:1 and the video is 16:9, so a
   100%-wide iframe letterboxes. Widen it to 16/9 ÷ 1.451 =
   122.5% and let the screen clip the overspill.
   ─────────────────────────────────────────────────────────── */
.tv__frame {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  /* the tube is 1.451:1 and the video 16:9, so a full-width iframe would
     letterbox. Cover it instead, with a little overscan so no edge shows. */
  height: 106%;
  width: 129.86%;          /* 106% x 16/9 / 1.451 */
  pointer-events: none;
  z-index: 1;
}

/* ── snow ──────────────────────────────────────────────────
   Opaque while it is covering. Screen blending cannot darken —
   a white YouTube logo underneath would stay white however much
   static sat on top — so normal blending is what actually hides
   the branding. Only the leftover grain, once the picture is
   running, blends as screen.
   ─────────────────────────────────────────────────────────── */
.tv__noise {
  position: absolute; inset: 0;
  z-index: 2;                        /* over the picture, always */
  width: 100%; height: 100%;
  pointer-events: none;
  opacity: 0;                        /* a set that is off is simply black */
  transition: opacity 1.1s ease;
  mix-blend-mode: normal;
}
/* once the picture is genuinely running, dissolve to a trace of grain */
.tv__screen.is-live .tv__noise { opacity: 1; }
.tv__screen.is-tuned .tv__noise {
  opacity: 0.05;
  mix-blend-mode: screen;
}

/* ── the set's own knobs, made real ────────────────────────
   Positions read off the artwork. Only an index mark is drawn:
   the knob faces are already in the photograph.
   ─────────────────────────────────────────────────────────── */
.tvknob {
  position: absolute; z-index: 3;
  left: 81.01%; width: 9.00%; height: 13.01%;
  border-radius: 50%;
  cursor: grab; touch-action: none;
}
.tvknob:active { cursor: grabbing; }
.tvknob--power { top: 11.91%; }
.tvknob--vol  { top: 30.64%; }

/* A real control rather than a mark floating on the artwork: dark face,
   amber ring, cream index. Sized to sit inside its painted hole. */
.tvknob__face {
  position: absolute; inset: 0;
  border-radius: 50%;
  border: 2px solid #d97f31;
  background:
    radial-gradient(62% 62% at 36% 28%, #40382f 0%, #241e18 52%, #120f0c 100%);
  box-shadow:
    inset 0 2px 4px rgba(255, 214, 165, 0.18),
    inset 0 -6px 14px rgba(0, 0, 0, 0.8),
    0 3px 9px rgba(0, 0, 0, 0.65);
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.tvknob:hover .tvknob__face { border-color: #f0954a; }

.tvknob__mark {
  position: absolute; left: 50%; top: 12%;
  width: 6.5%; height: 32%;
  margin-left: -3.25%;
  transform-origin: 50% 125%;
  transform: rotate(0deg);
  border-radius: 4px;
  background: linear-gradient(to bottom, #fff4e2, #e8cfa8);
  box-shadow: 0 0 7px rgba(255, 210, 150, 0.75);
  pointer-events: none;
}

.tvknob__state {
  position: absolute; left: 50%; top: 104%;
  transform: translateX(-50%);
  font-size: clamp(0.32rem, 0.85cqw, 0.56rem);
  letter-spacing: 0.22em; text-transform: uppercase;
  color: #8a7660;
  white-space: nowrap; pointer-events: none;
  transition: color 0.3s ease;
}
.tv.is-live .tvknob--power .tvknob__state { color: #ff9d4d; }

/* The input is the keyboard handle and nothing else. Without this it paints
   as a native range slider: a horizontal track with a thumb, straight across
   the cabinet. */
.tvknob input {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  pointer-events: none;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  border: 0;
}

.tvknob:focus-within { box-shadow: 0 0 0 3px var(--accent); }

.about__alt {
  display: inline-block; margin-top: 0.5rem;
  color: #8a8a8a; text-decoration: underline;
  text-underline-offset: 3px; text-decoration-color: #3a3a3a;
}
.about__alt:hover { color: var(--accent); text-decoration-color: var(--accent); }

.about__cue {
  display: inline-block; margin-top: 0.5rem;
  color: #7d7d7d; letter-spacing: 0.06em;
}

/* ── play it on YouTube ───────────────────────────────────── */
.yt {
  margin-top: 1.2rem;
  display: inline-flex; align-items: center; gap: 0.65rem;
  padding: 0.55rem 1.1rem 0.55rem 0.85rem;
  border: 1px solid #3a3a3a; border-radius: 8px;
  background: #1b1b1b;
  font-size: 0.8rem; color: #e2e2e2;
  transition: border-color 0.25s ease, background 0.25s ease, transform 0.15s ease;
}
.yt:hover { border-color: #ff0000; background: #221819; transform: translateY(-1px); }
.yt:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.yt__mark { width: 1.5rem; height: 1.5rem; flex: none; }

/* ── gradient panel ───────────────────────────────────────── */
/* Instrument Serif for the wordmark: an editorial face, which suits a
   research instrument, and it sets it apart from Madhuri's Syne. */
.about__grad {
  font-family: "Instrument Serif", Georgia, serif;
  font-weight: 400;
  font-size: clamp(2.9rem, 6.4vw, 5.4rem);
  line-height: 0.95;
  letter-spacing: -0.005em;
  text-transform: none;
  background: linear-gradient(100deg, #ffffff 0%, #b9c6ff 46%, #7ee0c8 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #e8e8e8;                       /* where background-clip is unsupported */
}

/* the two links sit together */
.links {
  margin-top: 1.5rem;
  display: flex; flex-wrap: wrap; gap: 0.7rem;
  align-items: stretch;
}
.links .store, .links .yt { margin-top: 0; }
.yt__text {
  display: flex; flex-direction: column; line-height: 1.15;
  font-size: 0.94rem; font-weight: 500; color: #e8e8e8;
}
.yt__text small {
  font-size: 0.56rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: #8a8a8a;
}
.yt { padding: 0.6rem 1.15rem 0.6rem 0.9rem; font-size: inherit; }

/* Syne for Madhuri: geometric with enough character to read as a station
   wordmark, and clearly not the serif that Gradient uses. */
.about__mad {
  font-family: "Syne", "Inter", sans-serif;
  font-weight: 800;
  font-size: clamp(2.7rem, 6vw, 5rem);
  line-height: 1;
  letter-spacing: -0.025em;
  background: linear-gradient(96deg, #ff2d78 0%, #d63cdc 48%, #7c3aed 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #ff5ea0;                      /* where background-clip is unsupported */
}


/* ── capability boxes ─────────────────────────────────────── */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* A <use> clones the symbol into a shadow tree, which '.sprite g' cannot
   reach — the clones fell back to fill:black, stroke:none and rendered as
   blobs. fill and stroke are inherited, so they must be set on the svg that
   does the using. */
.icon {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  overflow: visible;
}

.feats {
  margin-top: 1.4rem;
  display: flex; flex-wrap: wrap; gap: 0.45rem;
}
.feats li {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.85rem 0.5rem 0.7rem;
  border: 1px solid #303030; border-radius: 9px;
  background: #1a1a1a;
  font-size: 0.74rem; letter-spacing: 0.01em; color: #cfcfcf;
  transition: border-color 0.25s ease, background 0.25s ease;
}
.feats li:hover { background: #1f1f1f; }
.feats .icon { width: 1.15rem; height: 1.15rem; flex: none; opacity: 0.95; }
.feats--warm li { color: #d9c2cd; }
.feats--warm svg { color: #ff5ea0; }
.feats--warm li:hover { border-color: rgba(255,94,160,0.45); }
.feats--cool li { color: #c2cbd9; }
.feats--cool svg { color: #8fb6ff; }
.feats--cool li:hover { border-color: rgba(143,182,255,0.45); }

/* ── what it is built with ────────────────────────────────── */
.label--kit {
  margin-top: 1.6rem; margin-bottom: 0.7rem;
  font-size: 0.58rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: #6d6d6d; border: 0; padding: 0; font-weight: 400;
}
.kit { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.kit li {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.34rem 0.7rem 0.34rem 0.55rem;
  border: 1px solid #2c2c2c; border-radius: 999px;
  font-size: 0.66rem; letter-spacing: 0.06em; color: #a8a8a8;
  font-variant-numeric: tabular-nums;
}
.kit .icon { width: 0.95rem; height: 0.95rem; flex: none; color: #7ee0c8; stroke-width: 1.9; }

.store--live .store__icon { color: #7ee0c8; }
.store--live:hover { border-color: #7ee0c8; }

.about__tag--cool {
  border-color: rgba(143, 182, 255, 0.45);
  color: #9fc0ff;
}

/* ── text arrives ─────────────────────────────────────────────
   Blocks rise and fade as they are reached. No blur or scale on
   the wordmarks: they are painted with background-clip: text and
   a filter on that can drop the clip in some browsers.
   ─────────────────────────────────────────────────────────── */
.rise {
  opacity: 0;
  transform: translateY(18px);
  will-change: opacity, transform;
}
.rise.is-shown {
  opacity: 1;
  transform: none;
  transition:
    opacity 0.75s cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 0.75s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: auto;
}

/* the wordmarks take a touch longer, and come from a little lower */
.about__title.rise { transform: translateY(26px); }
.about__title.rise.is-shown {
  transition:
    opacity 0.95s cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 0.95s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* the hero name, on load */
.hero .title span.rise { transform: translateY(24px); }
.hero .title span.rise.is-shown {
  transition:
    opacity 0.9s cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 0.9s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Belt and braces. The script already skips animating under reduced motion,
   but the wordmark rules above are more specific than a bare .rise, so if the
   class ever did land there it would leave text sitting 24px low with no
   transition to bring it back. */
@media (prefers-reduced-motion: reduce) {
  .rise, .rise.is-shown,
  .about__title.rise, .about__title.rise.is-shown,
  .hero .title span.rise, .hero .title span.rise.is-shown {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* ── the opening caption ──────────────────────────────────────
   Named under the eyes while they are the only thing on screen,
   then cleared so his name can take the space. Centred on the
   viewport rather than in the heading's column, because the eyes
   it belongs to are centred too. It never takes the pointer. */
.intro-cap {
  position: fixed;
  left: 50%;
  top: 62%;
  transform: translate(-50%, 0) translateY(0.6rem);
  z-index: 3;
  display: grid;
  justify-items: center;
  gap: 0.5rem;
  margin: 0;
  padding: 0 1.5rem;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 620ms ease, transform 620ms ease;
}
.intro-cap.is-up {
  opacity: 1;
  transform: translate(-50%, 0) translateY(0);
}
.intro-cap__what {
  font-size: clamp(1.4rem, 3.4vw, 2.6rem);
  font-weight: 300;
  letter-spacing: 0.01em;
  color: #eaf6ff;
}
.intro-cap__who,
.intro-cap__where {
  font-size: clamp(0.72rem, 1.35vw, 0.94rem);
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(190, 222, 240, 0.62);
}
.intro-cap__where { letter-spacing: 0.1em; color: rgba(190, 222, 240, 0.4); }

/* The name waits for the head to hand off; without this it would be
   on screen through the whole opening. */
.hero .title span { opacity: 0; }
.hero .title span.is-shown { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .intro-cap { display: none; }
}

/* ── who he is, under the name ────────────────────────────────
   Held to a narrow measure on the left so it never runs under the
   head, which sits to the right. It arrives with the name, not
   before: see the head:name hand-off in js/head.js. */
.bio {
  /* 34vw keeps it clear of him on a wide screen and holds the overlap to
     the outermost lines on a short one, where he is bounded by height and
     so sits much wider. */
  max-width: min(31rem, 34vw);
  margin-top: clamp(1.4rem, 3.2vh, 2.6rem);
  display: grid;
  gap: 0.85rem;
}
.bio p {
  margin: 0;
  font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: clamp(0.86rem, 1.05vw, 1.02rem);
  font-weight: 300;
  line-height: 1.62;
  color: rgba(228, 234, 242, 0.68);   /* reads over the mesh where they cross */
  text-wrap: pretty;
}
.bio__lede {
  font-size: clamp(1rem, 1.45vw, 1.35rem) !important;
  font-weight: 400 !important;
  line-height: 1.42 !important;
  color: rgba(238, 244, 250, 0.9) !important;
}
.bio__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.6rem;
  margin-top: 0.5rem !important;
  font-size: clamp(0.64rem, 0.82vw, 0.76rem) !important;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(190, 222, 240, 0.42) !important;
}

/* the bio waits for the head to hand off, exactly as the name does */
.hero .bio > * { opacity: 0; }
.hero .bio > *.is-shown { opacity: 1; }

/* Only once the head fills the frame does the measure go full width.
   Above this he is still beside the text, and widening the column there
   ran the bio underneath him. */
@media (max-width: 640px) {
  .bio { max-width: none; }
  .bio p { color: rgba(226, 232, 240, 0.66); }
}


/* ── the pointer ──────────────────────────────────────────────
   A fixation reticle rather than an arrow. The whole page is built
   around him watching where you point, so the pointer is drawn as
   the thing being watched. Both carry a dark halo under the stroke
   so they stay visible against the mesh as well as the black.

   The knobs keep grab and grabbing: those say "this turns", which
   is worth more than the theme. Every cursor falls back to a real
   keyword, so nothing is lost if the data URI is refused. */
body { cursor: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%3E%20%3Cg%20fill='none'%20stroke='%23000'%20stroke-opacity='.55'%20stroke-width='3.2'%3E%20%3Ccircle%20cx='12'%20cy='12'%20r='7.5'/%3E%3C/g%3E%20%3Ccircle%20cx='12'%20cy='12'%20r='7.5'%20fill='none'%20stroke='%23e9eef7'%20stroke-opacity='.92'%20stroke-width='1.2'/%3E%20%3Ccircle%20cx='12'%20cy='12'%20r='1.6'%20fill='%23e9eef7'%20fill-opacity='.95'/%3E%3C/svg%3E") 12 12, default; }

a, button, summary, label[for], .radio__keys button, .store, .yt {
  cursor: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='28'%20height='28'%3E%20%3Cg%20fill='none'%20stroke='%23000'%20stroke-opacity='.55'%20stroke-width='3.4'%3E%20%3Ccircle%20cx='14'%20cy='14'%20r='7.5'/%3E%20%3Cpath%20d='M14%201.6v3.4M14%2023v3.4M1.6%2014h3.4M23%2014h3.4'/%3E%3C/g%3E%20%3Cg%20fill='none'%20stroke='%23d3a9ff'%20stroke-opacity='.98'%20stroke-width='1.5'%3E%20%3Ccircle%20cx='14'%20cy='14'%20r='7.5'/%3E%20%3Cpath%20d='M14%201.6v3.4M14%2023v3.4M1.6%2014h3.4M23%2014h3.4'/%3E%3C/g%3E%20%3Ccircle%20cx='14'%20cy='14'%20r='2.1'%20fill='%23d3a9ff'/%3E%3C/svg%3E") 14 14, pointer;
}

/* he is not clickable, and the canvas takes no pointer events */
.observer, .observer__canvas { cursor: inherit; }

/* ── the written work ─────────────────────────────────────────
   Editorial two column: the label holds its place on the left
   while the entries run past it on the right. Everything sits on
   hairlines rather than boxes, which suits a page whose only
   other furniture is a wireframe. */
.work {
  display: grid;
  grid-template-columns: minmax(12rem, 26%) 1fr;
  gap: clamp(1.5rem, 5vw, 5rem);
  max-width: 78rem;
  margin: 0 auto;
  padding: clamp(3.5rem, 9vh, 7rem) clamp(1.1rem, 3vw, 4rem);
  border-top: 1px solid rgba(255, 255, 255, 0.09);
}
.work--last { border-bottom: 1px solid rgba(255, 255, 255, 0.09); }

.work__head { position: sticky; top: 2.5rem; align-self: start; }
.work__label {
  font-size: clamp(0.62rem, 0.8vw, 0.72rem);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
}
.work__title {
  margin-top: 0.7rem;
  font-family: "Inter Tight", "Inter", sans-serif;
  font-weight: 200;
  font-size: clamp(1.3rem, 2.25vw, 2.05rem);
  line-height: 1.1;
  letter-spacing: -0.025em;
  color: var(--type-1);
  text-wrap: balance;
}

.work__body { max-width: 46rem; }
.work__lede {
  font-size: clamp(1rem, 1.35vw, 1.22rem);
  font-weight: 300;
  line-height: 1.55;
  color: var(--type-1);
}
.work__note {
  margin-top: 1.1rem;
  font-size: clamp(0.88rem, 1.05vw, 1rem);
  font-weight: 300;
  line-height: 1.68;
  color: var(--type-3);
}
.work__kicker {
  margin: 2.6rem 0 0.4rem;
  font-size: 0.66rem;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--muted);
}
.work__kicker:first-child { margin-top: 0; }

/* the three research strands */
.strands { margin-top: 2.4rem; display: grid; gap: 1.7rem; }
.strands h3 {
  font-size: clamp(0.95rem, 1.15vw, 1.06rem);
  font-weight: 500;
  color: var(--type-1);
  letter-spacing: -0.006em;
}
.strands p {
  margin-top: 0.4rem;
  font-size: clamp(0.85rem, 1.02vw, 0.96rem);
  font-weight: 300;
  line-height: 1.62;
  color: var(--type-3);
}

/* dated entries: systems, posts, degrees, talks */
.entries { display: grid; gap: 0; margin-top: 1.4rem; }
.entries > li {
  display: grid;
  grid-template-columns: minmax(6.5rem, 11rem) 1fr;
  gap: 0.4rem clamp(0.9rem, 2vw, 2rem);
  padding: 1.15rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}
.entries > li:first-child { border-top: 0; }
.entries--refs > li { grid-template-columns: 1fr; }
.entries__when {
  font-size: 0.7rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
  padding-top: 0.25rem;
}
.entries h3 {
  font-size: clamp(0.94rem, 1.12vw, 1.04rem);
  font-weight: 500;
  line-height: 1.34;
  color: var(--type-1);
  letter-spacing: -0.006em;
}
.entries p {
  margin-top: 0.35rem;
  font-size: clamp(0.84rem, 1vw, 0.94rem);
  font-weight: 300;
  line-height: 1.6;
  color: var(--type-3);
}
.entries .cite {
  margin-top: 0.3rem;
  font-size: clamp(0.78rem, 0.95vw, 0.86rem);
  color: var(--muted);
  font-style: normal;
}

.prizes { margin-top: 1.2rem; display: grid; gap: 0.62rem; }
.prizes li {
  position: relative;
  padding-left: 1.05rem;
  font-size: clamp(0.85rem, 1.02vw, 0.95rem);
  font-weight: 300;
  line-height: 1.5;
  color: var(--type-3);
}
.prizes li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.62em;
  width: 5px; height: 5px;
  border: 1px solid var(--muted);
  border-radius: 50%;
}

/* contact */
.mail { margin-top: 1.8rem; }
.mail a {
  font-family: "Inter Tight", "Inter", sans-serif;
  font-size: clamp(1.1rem, 2.6vw, 1.9rem);
  font-weight: 250;
  letter-spacing: -0.02em;
  color: var(--type-1);
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 255, 255, 0.22);
  padding-bottom: 0.1em;
  transition: border-color 0.35s ease, color 0.35s ease;
  word-break: break-word;
}
.mail a:hover { color: #fff; border-bottom-color: rgba(255, 255, 255, 0.6); }

.elsewhere { margin-top: 1.7rem; display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; }
.elsewhere a {
  font-size: 0.74rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--type-3);
  text-decoration: none;
  transition: color 0.3s ease;
}
.elsewhere a:hover { color: var(--type-1); }

.colophon {
  margin-top: 2.6rem;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  color: var(--muted);
}

/* the label cannot hold its place once it is above the text, and the
   column is too narrow to set a heading in much below this */
@media (max-width: 900px) {
  .work { grid-template-columns: 1fr; gap: 1.6rem; }
  .work__head { position: static; }
  .entries > li { grid-template-columns: 1fr; }
  .entries__when { padding-top: 0; }
}

/* festival selections under a film: a plain list, indented under the
   entry it belongs to rather than competing with it */
.laurels { margin-top: 0.7rem; display: grid; gap: 0.34rem; }
.laurels li {
  position: relative;
  padding-left: 0.95rem;
  font-size: clamp(0.78rem, 0.94vw, 0.87rem);
  font-weight: 300;
  line-height: 1.5;
  color: var(--muted);
}
.laurels li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.66em;
  width: 4px; height: 1px;
  background: var(--muted);
}
.entries--film > li { padding: 1.5rem 0; }

/* ── film stills ──────────────────────────────────────────────
   The still leads the entry. It sits dim until you go near it,
   so a column of them reads as a list rather than as a wall of
   competing pictures. */
.film {
  display: block;
  position: relative;
  margin-bottom: 0.95rem;
  max-width: 27rem;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 2px;
  background: #0d0d0d;
  text-decoration: none;
}
.film img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  opacity: 0.66;
  filter: saturate(0.75);
  transform: scale(1.01);
  transition: opacity 0.5s ease, filter 0.5s ease, transform 0.7s ease;
}
.film:hover img, .film:focus-visible img {
  opacity: 1;
  filter: saturate(1);
  transform: scale(1.035);
}
.film__play {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 2.9rem; height: 2.9rem;
  display: grid; place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 50%;
  background: rgba(10, 10, 10, 0.42);
  color: #fff;
  backdrop-filter: blur(2px);
  transition: background 0.4s ease, border-color 0.4s ease, transform 0.4s ease;
}
.film:hover .film__play { background: rgba(10,10,10,0.6); border-color: #fff; transform: translate(-50%,-50%) scale(1.06); }
.film__play .icon { width: 1.15rem; height: 1.15rem; fill: currentColor; stroke: none; }

/* laurels, on the selections and the awards */
.laurels li, .prizes--laurel li {
  display: grid;
  grid-template-columns: 1.3rem 1fr;
  gap: 0.5rem;
  align-items: start;
  padding-left: 0;
}
.laurels li::before, .prizes--laurel li::before { content: none; }
.laurel {
  width: 1.25rem; height: 1.25rem;
  stroke-width: 1.15;
  color: rgba(211, 169, 255, 0.55);
  margin-top: 0.12em;
  flex: none;
}
.prizes--laurel .laurel { color: rgba(211, 169, 255, 0.45); }

/* press: the same film, seen from outside */
.press { display: flex; flex-wrap: wrap; gap: 0.45rem 1.3rem; margin-top: 0.75rem; }
.press a {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-size: clamp(0.74rem, 0.9vw, 0.82rem);
  color: var(--type-3);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 0.3s ease, border-color 0.3s ease;
}
.press a:hover { color: var(--type-1); border-bottom-color: rgba(255,255,255,0.3); }
.press .icon { width: 0.85rem; height: 0.85rem; fill: currentColor; stroke: none; }

/* a paper you can actually go and read */
.paper {
  display: inline-flex; align-items: center; gap: 0.38rem;
  margin-top: 0.5rem;
  font-size: clamp(0.74rem, 0.9vw, 0.82rem);
  letter-spacing: 0.04em;
  color: var(--type-3);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 0.3s ease, border-color 0.3s ease;
}
.paper:hover { color: var(--type-1); border-bottom-color: rgba(255,255,255,0.3); }
.paper .icon { width: 0.86rem; height: 0.86rem; }

.elsewhere a { display: inline-flex; align-items: center; gap: 0.4rem; }
.elsewhere .icon { width: 0.92rem; height: 0.92rem; }

/* ── the three rooms ──────────────────────────────────────────
   The same block sits at the foot of all three sites, with the
   current one marked rather than removed, so you can always see
   how many there are and which one you are standing in. */
.rooms { display: grid; gap: 0; margin-top: 1.4rem; }
.rooms li { border-top: 1px solid rgba(255, 255, 255, 0.07); }
.rooms li:first-child { border-top: 0; }
.rooms a, .rooms__here {
  display: grid;
  grid-template-columns: minmax(7rem, 11rem) 1fr auto;
  gap: 0.3rem clamp(0.9rem, 2vw, 2rem);
  align-items: baseline;
  padding: 1.15rem 0;
  text-decoration: none;
  color: inherit;
}
.rooms a > span:first-child, .rooms__here > span:first-child {
  font-family: "Inter Tight", "Inter", sans-serif;
  font-size: clamp(1rem, 1.5vw, 1.28rem);
  font-weight: 300;
  letter-spacing: -0.012em;
  color: var(--type-1);
  transition: color 0.3s ease;
}
.rooms__blurb {
  font-size: clamp(0.8rem, 0.98vw, 0.9rem);
  font-weight: 300;
  color: var(--type-3);
}
.rooms a:hover > span:first-child { color: #fff; }
.rooms a:hover .rooms__blurb { color: var(--type-2); }
.rooms__here { opacity: 0.5; }
.rooms__you {
  font-size: 0.64rem;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--muted);
}
@media (max-width: 640px) {
  .rooms a, .rooms__here { grid-template-columns: 1fr; }
  .rooms__you { display: none; }
}

/* ── the flagship systems, in full ────────────────────────────
   These carry a subtitle, a paragraph, what the system actually
   does, and what it is built from. Everything is on show rather
   than folded behind a disclosure, because the point of the page
   is the detail. */
.entries--sys > li { padding: 1.7rem 0; }
.sys__sub {
  margin-top: 0.15rem !important;
  font-size: clamp(0.78rem, 0.95vw, 0.87rem) !important;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted) !important;
}
.sys__points { margin-top: 0.85rem; display: grid; gap: 0.42rem; }
.sys__points li {
  position: relative;
  padding-left: 1rem;
  font-size: clamp(0.82rem, 0.98vw, 0.92rem);
  font-weight: 300;
  line-height: 1.55;
  color: var(--type-3);
}
.sys__points li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.68em;
  width: 6px; height: 1px;
  background: rgba(211, 169, 255, 0.5);
}
.sys__methods {
  margin-top: 0.85rem !important;
  padding-top: 0.6rem;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  font-size: clamp(0.75rem, 0.9vw, 0.83rem) !important;
  color: var(--muted) !important;
  letter-spacing: 0.02em;
}

/* the toolchain reads better as a column than as three strands */
.strands--kit { gap: 1.35rem; }
.strands--kit h3 {
  font-size: clamp(0.74rem, 0.9vw, 0.82rem);
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--muted);
}
.strands--kit p { margin-top: 0.4rem; color: var(--type-2); }


/* ── the ground ───────────────────────────────────────────────
   A flat #131313 made the page feel like a slide rather than a
   room. Two very soft lights instead, one warm violet up where
   the head sits and one cold behind the text, over a base that
   darkens down the page. Both are fixed, so they stay put while
   the content moves past them.

   The grain on top is what keeps the gradients from banding: a
   flat dark ramp shows visible steps on an 8 bit display, and a
   little noise breaks them up. It is generated, not fetched. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(52rem 38rem at 78% 0%,  rgba(150, 104, 214, 0.085), transparent 60%),
    radial-gradient(44rem 38rem at 4% 40%,  rgba(70, 106, 178, 0.045),  transparent 58%),
    linear-gradient(178deg, #141318 0%, #111013 44%, #0e0e10 100%);
}
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.35;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='200'%20height='200'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.82'%20numOctaves='3'%20stitchTiles='stitch'/%3E%3CfeColorMatrix%20type='saturate'%20values='0'/%3E%3C/filter%3E%3Crect%20width='200'%20height='200'%20filter='url(%23n)'%20opacity='0.5'/%3E%3C/svg%3E");
  background-size: 200px 200px;
}

/* the head sits over the ground, not on a black plate */
html, body { background: transparent; }
html { background-color: #0f0f11; }

@media (prefers-reduced-motion: reduce) { body::after { opacity: 0.22; } }

/* ═══════════════════════════════════════════════════════════════
   Giving the page a pulse.

   Nine sections all set the same way is a template, not a
   portfolio: after the radio and the television the page went
   flat and stayed flat. Three things fix that without touching a
   word of the copy.

   One, scale. The section titles were 2rem next to a 7rem name,
   so nothing after the hero ever felt like an arrival. They are
   display size now, and each section is counted, with the numeral
   set large and hollow so it reads as structure rather than
   decoration.

   Two, colour. The mesh is violet and the page was grey. The
   numerals, the rules and the section marks take the accent, so
   the head and the writing belong to the same object.

   Three, air. Sections breathe further apart, and each opens with
   a hairline that runs the full width rather than a boxed edge.
   ═══════════════════════════════════════════════════════════════ */

:root { --ink: #c9a6ff; }

body { counter-reset: section; }

.work {
  counter-increment: section;
  grid-template-columns: minmax(13rem, 30%) 1fr;
  padding-top: clamp(5rem, 14vh, 10rem);
  padding-bottom: clamp(4rem, 10vh, 7rem);
  border-top: 0;
  position: relative;
}
/* a full width hairline, not a boxed edge */
.work::before {
  content: "";
  position: absolute;
  left: clamp(1.1rem, 3vw, 4rem);
  right: clamp(1.1rem, 3vw, 4rem);
  top: 0;
  height: 1px;
  background: linear-gradient(90deg,
    rgba(201, 166, 255, 0.34) 0%,
    rgba(255, 255, 255, 0.09) 22%,
    rgba(255, 255, 255, 0.05) 100%);
}
.work--last { border-bottom: 0; }

.work__head { top: 4rem; }

/* the count, set large and hollow */
.work__label {
  display: block;
  font-size: clamp(0.6rem, 0.76vw, 0.7rem);
  letter-spacing: 0.24em;
  color: var(--ink);
  opacity: 0.75;
}
.work__label::before {
  content: counter(section, decimal-leading-zero);
  display: block;
  margin-bottom: 0.55rem;
  font-family: "Inter Tight", "Inter", sans-serif;
  font-size: clamp(2.6rem, 6vw, 5.2rem);
  font-weight: 200;
  line-height: 0.8;
  letter-spacing: -0.05em;
  color: transparent;
  -webkit-text-stroke: 1px rgba(201, 166, 255, 0.42);
  opacity: 0.9;
}

.work__title {
  margin-top: 0.9rem;
  font-size: clamp(1.7rem, 3.4vw, 3.1rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
}

.work__body { max-width: 48rem; }
.work__lede {
  font-size: clamp(1.05rem, 1.6vw, 1.45rem);
  line-height: 1.48;
}

/* the accent reaches the small marks too */
.work__kicker { color: var(--ink); opacity: 0.8; }
.entries__when { color: var(--ink); opacity: 0.62; }
.strands h3::before {
  content: "";
  display: inline-block;
  width: 14px; height: 1px;
  margin-right: 0.6rem;
  vertical-align: middle;
  background: var(--ink);
  opacity: 0.7;
}

@media (max-width: 900px) {
  .work__label::before { font-size: clamp(2.2rem, 12vw, 3.4rem); }
  .work { padding-top: clamp(3.2rem, 9vh, 5rem); }
}

/* ── the films, as a gallery ──────────────────────────────────
   A 27rem still stacked in a single column read as an inventory.
   The work is visual, so it gets shown at size: two across, the
   still filling its cell, the title sitting on the image rather
   than queueing underneath it. */
#film .work__body { max-width: none; }
.entries--film {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: clamp(1.2rem, 2.4vw, 2.2rem);
  margin-top: 2rem;
}
.entries--film > li {
  display: block;
  padding: 0;
  border-top: 0;
}
.entries--film .entries__when {
  display: inline-block;
  margin-bottom: 0.55rem;
  font-size: 0.63rem;
  letter-spacing: 0.19em;
}
.entries--film .film {
  max-width: none;
  margin-bottom: 0.9rem;
  border-radius: 3px;
}
.entries--film .film img { opacity: 0.8; }
.entries--film h3 {
  font-family: "Inter Tight", "Inter", sans-serif;
  font-size: clamp(1.15rem, 1.75vw, 1.55rem);
  font-weight: 300;
  letter-spacing: -0.022em;
  line-height: 1.12;
}
.entries--film p { margin-top: 0.45rem; }

/* the laurels go quiet in a grid; they are supporting detail here */
.entries--film .laurels { margin-top: 0.7rem; gap: 0.26rem; }
.entries--film .laurels li { grid-template-columns: 1rem 1fr; }
.entries--film .laurel { width: 0.92rem; height: 0.92rem; }
.entries--film .laurels li span {
  font-size: clamp(0.72rem, 0.86vw, 0.79rem);
  line-height: 1.45;
}

/* the two with no still should not leave a hole in the grid */
.entries--film > li:not(:has(.film)) {
  align-self: start;
  padding: 1.1rem 1.2rem;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.015);
}

/* ── pitute, on a screen ──────────────────────────────────────
   Set in pitute's own system rather than in an approximation of
   it: the tokens are lifted from its globals.css, the faces are
   the two it actually loads. Geist runs the interface, Source
   Serif carries the names and the prose, which is how pitute sets
   a profile: font-serif on the heading, sans on everything round
   it. Cards are white on a cool near-white ground at its 0.5rem
   radius, and the blue is its --primary rather than one I picked.

   The other two devices are warm wood; this one is glass and
   light, because that is what the product is and because it is
   the current work rather than the archive. */
.pitute {
  position: relative;
  padding: clamp(4rem, 12vh, 9rem) clamp(1.25rem, 5vw, 4rem) clamp(5rem, 14vh, 10rem);
  max-width: 1460px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 0.78fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}

/* straight out of pitute/src/app/globals.css */
.screenbox {
  --background: oklch(0.965 0.003 255);
  --foreground: oklch(0.255 0.005 265);
  --card: oklch(1 0 0);
  --primary: oklch(0.6 0.2 256);
  --muted-fg: oklch(0.51 0.008 265);
  --border: oklch(0.895 0.005 255);
  --accent-fg: oklch(0.46 0.16 256);
  --radius: 0.5rem;
  position: relative;
}



/* ── the machine ──────────────────────────────────────────────
   A lid, a hinge and a base, drawn rather than photographed,
   because the other two are period objects and this one is not.
   The screen holds the real pitute; see js/pitute.js for why it
   takes no pointer. */
.lap { position: relative; }

.lap__lid {
  position: relative;
  padding: 0.62rem 0.62rem 0.9rem;
  border-radius: 14px 14px 5px 5px;
  background: linear-gradient(176deg, #34363c 0%, #212328 62%, #191b1f 100%);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.14) inset,
    0 26px 50px -26px rgba(0, 0, 0, 0.9);
}
.lap__notch {
  position: absolute;
  left: 50%; bottom: 0.34rem;
  transform: translateX(-50%);
  width: 3.2rem; height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.09);
}

.lap__screen {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow-y: auto;             /* the page is 1764px tall; let people read it */
  overflow-x: hidden;
  overscroll-behavior: contain; /* and do not drag the article with it */
  border-radius: 7px;
  background: #0b0c0e;
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,0.22) transparent;
}
.lap__screen::-webkit-scrollbar { width: 7px; }
.lap__screen::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,0.2);
  border-radius: 4px;
}
.lap:not(.is-on) .lap__screen { overflow: hidden; }


/* the real screen, captured from pitute running */
.lap__shot {
  display: block;
  width: 100%;
  height: auto;                 /* taller than the lid, which is the point */
  opacity: 0;
  transition: opacity 0.5s ease;
}
.lap.is-on .lap__shot { opacity: 1; }

/* three dots while it is coming up, and the dark screen when it is off */
.lap__boot {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center; gap: 0.4rem;
  opacity: 0;
  transition: opacity 0.4s ease;
}
.lap:not(.is-on) .lap__boot { opacity: 0; }
.lap__boot span {
  width: 6px; height: 6px; border-radius: 50%;
  background: oklch(0.6 0.2 256);
  animation: lap-pulse 1.15s ease-in-out infinite;
}
.lap__boot span:nth-child(2) { animation-delay: 0.16s; }
.lap__boot span:nth-child(3) { animation-delay: 0.32s; }
@keyframes lap-pulse { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .lap__boot span { animation: none; opacity: 0.7; }
}

.lap__base {
  position: relative;
  height: 1.9rem;
  width: 110%;
  margin-left: -5%;
  border-radius: 0 0 14px 14px;
  background:
    linear-gradient(180deg, #34363c 0%, #26282d 42%, #17191c 100%);
  box-shadow: 0 18px 30px -20px rgba(0, 0, 0, 0.95);
}
.lap__hinge {
  position: absolute;
  left: 50%; top: 0.3rem;
  transform: translateX(-50%);
  width: 6rem; height: 4px;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.45);
}

/* the power key sits on the deck, where a laptop keeps one */
.lap__power {
  position: absolute;
  right: 7%;
  top: 50%;
  transform: translateY(-50%);
  width: 1.5rem; height: 1.5rem;
  display: grid; place-items: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.13);
  border-radius: 6px;
  background: linear-gradient(180deg, #2f3238 0%, #212328 100%);
  color: #6d727c;
  cursor: inherit;
  transition: color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}
.lap__power svg {
  width: 0.82rem; height: 0.82rem;
  fill: none; stroke: currentColor; stroke-width: 2.1;
  stroke-linecap: round;
}
.lap__power:hover { border-color: rgba(255, 255, 255, 0.3); color: #9aa0aa; }
.lap.is-on .lap__power {
  color: oklch(0.72 0.16 256);
  border-color: oklch(0.6 0.2 256 / 0.5);
  box-shadow: 0 0 10px oklch(0.6 0.2 256 / 0.35);
}
.lap__power:focus-visible {
  outline: 2px solid oklch(0.72 0.16 256);
  outline-offset: 2px;
}

.lap__note {
  margin-top: 1.1rem;
  font-family: "Geist", "Inter", sans-serif;
  font-size: clamp(0.7rem, 0.85vw, 0.78rem);
  color: var(--muted);
}

/* the wordmark, as pitute sets its own: the mark, the serif, the
   accent full stop. Lifted from components/brand.tsx rather than
   restyled, so the section is headed by the real thing. */
.about__title--pit { display: block; }
/* At Madhuri's scale. The wordmark was sitting at body size next to a
   5rem one, which is why the two sections did not read as siblings.
   Same size, same weight in its own face, and the mark scales with it. */
.about__pitmark {
  width: clamp(2.6rem, 5.6vw, 4.6rem);
  height: auto;
  flex: none;
}
.about__pit {
  font-family: "Source Serif 4", Georgia, serif;
  font-weight: 600;
  font-size: clamp(3rem, 6.6vw, 5.6rem);
  line-height: 0.95;
  letter-spacing: -0.045em;
  color: #f4f6fa;
}
.about__pitstop {
  color: oklch(0.66 0.21 256);
  margin-left: 0.01em;
  text-shadow: 0 0 26px oklch(0.6 0.2 256 / 0.55);
}

/* pitute's blue, where Madhuri has pink and Gradient has ice */
.feats--pit li { color: #c3d0ea; }
.feats--pit svg { color: oklch(0.72 0.16 256); }
.feats--pit li:hover { border-color: oklch(0.6 0.2 256 / 0.5); }

@media (max-width: 900px) {
  .pitute { grid-template-columns: 1fr; }
}

/* ── the pitute button ────────────────────────────────────────
   It was carrying the generic network glyph, which rendered as
   four loose dots and read as a broken image. It takes pitute's
   own mark instead, the way the Play button takes Google's and
   the source button takes GitHub's. */
.store--pit {
  border-color: oklch(0.6 0.2 256 / 0.32);
  background: oklch(0.6 0.2 256 / 0.07);
}
.store--pit:hover {
  border-color: oklch(0.6 0.2 256 / 0.62);
  background: oklch(0.6 0.2 256 / 0.13);
}
.store__mark {
  width: 1.6rem;
  height: auto;
  flex: none;
  object-fit: contain;
}
.store--pit .store__text { color: var(--type-1); }
.store--pit small { color: oklch(0.74 0.14 256); }

/* the toolchain, tinted like its chips */
.kit--pit li { color: #c3d0ea; }
.kit--pit .icon { color: oklch(0.72 0.16 256); }   /* .kit .icon is 0-2-0, so this has to match it */

/* ── shoo shoo, on two phones ─────────────────────────────────
   Two devices rather than one, because it is a space for two
   people and a single handset would say the wrong thing. The
   screens are the real app, run locally and photographed at 390
   by 844 on a 3x screen.

   The mark is drawn rather than fetched: two overlapping circles,
   the yellow one behind the pink, which is what the app puts
   above its own name. */
.shoo {
  position: relative;
  padding: clamp(4rem, 12vh, 9rem) clamp(1.25rem, 5vw, 4rem) clamp(5rem, 14vh, 10rem);
  max-width: 1460px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}

.pair {
  position: relative;
  display: grid;
  place-items: center;
  min-height: clamp(20rem, 46vw, 34rem);
}
.phone {
  position: absolute;
  width: clamp(9.5rem, 17vw, 14rem);
  aspect-ratio: 390 / 844;
  border-radius: clamp(1.4rem, 2.4vw, 2.1rem);
  padding: 0.42rem;
  background: linear-gradient(168deg, #3a3c42 0%, #232529 55%, #17181c 100%);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.14) inset,
    0 34px 60px -28px rgba(0, 0, 0, 0.92);
}
.phone__screen {
  position: relative;
  height: 100%;
  overflow: hidden;
  border-radius: clamp(1.1rem, 2vw, 1.75rem);
  background: #f7f5ef;
}
.phone__screen img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.phone__notch {
  position: absolute;
  left: 50%; top: 0.78rem;
  transform: translateX(-50%);
  width: 27%; height: 0.32rem;
  border-radius: 99px;
  background: rgba(0, 0, 0, 0.55);
}
.phone--back {
  transform: translate(-26%, -6%) rotate(-8deg) scale(0.94);
  filter: brightness(0.82) saturate(0.9);
}
.phone--front { transform: translate(24%, 6%) rotate(5deg); }

/* the two circles */
.about__shoomark, .store__shoomark { display: inline-flex; align-items: center; flex: none; }
.about__shoomark i, .store__shoomark i {
  display: block;
  border-radius: 50%;
}
.about__shoomark i { width: clamp(1.5rem, 3vw, 2.4rem); aspect-ratio: 1; }
.about__shoomark i:first-child { background: #f2c318; }
.about__shoomark i:last-child { background: #e0416b; margin-left: -0.42em; }
.store__shoomark i { width: 0.85rem; aspect-ratio: 1; }
.store__shoomark i:first-child { background: #f2c318; }
.store__shoomark i:last-child { background: #e0416b; margin-left: -0.34em; }

.about__title--shoo { display: flex; align-items: center; gap: 0.7rem; }
.about__shoo {
  font-family: "Inter Tight", "Inter", sans-serif;
  font-weight: 700;
  font-size: clamp(2.5rem, 5.6vw, 4.6rem);
  line-height: 1;
  letter-spacing: -0.045em;
  color: #f6f2ea;
}

.feats--shoo li { color: #e6d6c6; }
.feats--shoo svg { color: #f2c318; }
.feats--shoo li:hover { border-color: rgba(242, 195, 24, 0.45); }
.kit--shoo .icon { color: #e0416b; }

.store--shoo { border-color: rgba(242, 195, 24, 0.34); background: rgba(242, 195, 24, 0.07); }
.store--shoo:hover { border-color: rgba(242, 195, 24, 0.62); background: rgba(242, 195, 24, 0.12); }
.store--shoo small { color: #f2c318; }

@media (max-width: 900px) {
  .shoo { grid-template-columns: 1fr; }
  .pair { min-height: 22rem; }
}

/* ── DinoSynthesis ────────────────────────────────────────────
   The animal is a downloaded model under CC BY 4.0, credited in the
   colophon. It has a rig and no clips, so the walk is driven bone by
   bone in js/dino.js, and its jaw rides the RMS of the call, which
   makes the movement the envelope of the sound itself. */
.dino {
  position: relative;
  padding: clamp(4rem, 12vh, 9rem) clamp(1.25rem, 5vw, 4rem) clamp(5rem, 14vh, 10rem);
  max-width: 1460px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}

.dino__stage { position: relative; }


.about__dino {
  font-family: "Inter Tight", "Inter", sans-serif;
  font-weight: 250;
  font-size: clamp(2.2rem, 4.8vw, 4rem);
  line-height: 1;
  letter-spacing: -0.04em;
  background: linear-gradient(96deg, #f0b357 0%, #e0704a 52%, #b8564f 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #f0b357;
}
.feats--dino li { color: #e8d3bb; }
.feats--dino svg { color: #e0a04a; }
.feats--dino li:hover { border-color: rgba(224, 160, 74, 0.45); }
.kit--dino .icon { color: #e0a04a; }

.dino__spec {
  display: block;
  width: 100%;
  height: auto;
  margin-top: 1.6rem;
  border-radius: 6px;
  opacity: 0.9;
}

@media (max-width: 900px) {
  .dino { grid-template-columns: 1fr; }
}

/* The model sits on the page, not in a box: no frame, no panel, no
   inner shadow. The waves are behind it and the animal walks in place
   over them, which is what carries the forward motion. */
.dino__field {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
}
.dino__waves,
.dino__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.dino__waves { z-index: 0; pointer-events: none; }
.dino__canvas { z-index: 1; }
.dino__canvas > canvas { width: 100% !important; height: 100% !important; display: block; }

/* Sound starts by itself and loops, so it has to be stoppable: this is
   that switch. Small, out of the way, bars still when it is off. */
@media (max-width: 900px) { .dino__field { aspect-ratio: 1 / 1; } }
