/* ==========================================================================
   LONGTAKE STUDIO — core stylesheet
   One continuous take: the page is the shot, scroll is the camera move.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* Surface — warm near-black, never pure #000 (OLED smear, and film has no true black) */
  --ink:        #121110;
  --ink-deep:   #0a0908;
  --ink-raise:  #191816;

  /* Ink on surface */
  --bone:       #EFECE4;
  --grey:       #A8A8A0;   /* lifted so body copy clears AA over the projector beam */
  --grey-dim:   #9A9A94;   /* clears AA against the brightest point of the beam */

  /* The tally light — the red lamp that means the camera is rolling */
  --tally:      #E4341C;
  --tally-warm: #FF5A3D;
  /* The brand red only reaches 4.32:1 on ink, which is short of AA for small
     text. Large display type is fine; small labels use this lifted variant. */
  --tally-text: #EC4025;

  /* Hairlines */
  --line:       rgba(239, 236, 228, .13);
  --line-soft:  rgba(239, 236, 228, .06);

  /* Type */
  --font-display: 'Archivo', system-ui, sans-serif;
  --font-body:    'Inter', system-ui, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, monospace;

  /* Motion — expo.out. Everything decelerates like a camera settling on its mark. */
  --ease:      cubic-bezier(.16, 1, .3, 1);
  --ease-soft: cubic-bezier(.4, 0, .2, 1);
  --dur-fast:  .22s;
  --dur-mid:   .45s;
  --dur-slow:  .85s;

  /* Space — spacious scale */
  --s-1: 8px;   --s-2: 16px;  --s-3: 24px;  --s-4: 40px;
  --s-5: 64px;  --s-6: 96px;  --s-7: 144px;

  --gutter: clamp(20px, 5vw, 64px);
  --measure: 1240px;

  --header-h: 68px;
}

/* --------------------------------------------------------------------------
   2. RESET
   -------------------------------------------------------------------------- */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* When the JS scroller is driving, CSS smooth-scroll would animate against
     it and the two would fight over every scrollTo. */
  /* Anchor targets clear the fixed header instead of hiding under it */
  scroll-padding-top: calc(var(--header-h) + var(--s-3));
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--ink);
  color: var(--bone);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  text-rendering: optimizeLegibility;
}

img, video, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
::selection { background: var(--tally); color: var(--bone); }

/* Visible focus for keyboard users only — never remove the ring */
:focus-visible {
  outline: 2px solid var(--tally);
  outline-offset: 3px;
  border-radius: 2px;
}

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

.skip-link {
  position: absolute; top: -100px; left: var(--gutter); z-index: 300;
  background: var(--bone); color: var(--ink);
  padding: 12px 20px; font-family: var(--font-mono); font-size: 12px;
  transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus { top: 12px; }

html.has-smooth { scroll-behavior: auto; }

/* --------------------------------------------------------------------------
   3. PRIMITIVES
   -------------------------------------------------------------------------- */
.wrap { max-width: var(--measure); margin-inline: auto; padding-inline: var(--gutter); }

.mono {
  font-family: var(--font-mono); font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  font-size: 11px; color: var(--grey);
}

.h-display {
  font-family: var(--font-display); font-weight: 800;
  letter-spacing: -.035em; line-height: .95;
}

.accent { color: var(--tally); }

.lede { color: var(--grey); line-height: 1.7; font-size: clamp(15px, 1.5vw, 17px); }

/* --------------------------------------------------------------------------
   THE LEADER — Academy countdown, shown only while JS is alive.
   Hidden by default so a no-JS browser never meets a loading screen it
   cannot dismiss.
   -------------------------------------------------------------------------- */
#leader { display: none; }

.js #leader {
  display: grid; place-items: center;
  position: fixed; inset: 0; z-index: 400;
  background: var(--ink-deep);
  transition: opacity .7s var(--ease), visibility .7s;
}
.js #leader.is-out { opacity: 0; visibility: hidden; pointer-events: none; }

/* Hold the page still underneath so nothing animates behind the leader */
.is-loading body { overflow: hidden; }

.leader__frame { position: relative; display: grid; place-items: center; width: min(46vmin, 300px); aspect-ratio: 1; }

.leader__ring { position: absolute; inset: 0; }
.leader__ring circle, .leader__ring line { fill: none; stroke: var(--line); stroke-width: 1; }
.leader__ring .r-outer { stroke: rgba(239,236,228,.22); }

/* The sweep hand — a clock, not a progress gauge */
.leader__sweep {
  position: absolute; left: 50%; top: 50%;
  width: 1px; height: 50%;
  background: linear-gradient(to top, var(--tally), rgba(228,52,28,0));
  transform-origin: 50% 100%;
  margin-left: -.5px; margin-top: -50%;
}

.leader__count {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(64px, 16vmin, 128px); letter-spacing: -.04em;
  color: var(--bone); font-variant-numeric: tabular-nums; line-height: 1;
}

.leader__meta {
  position: absolute; left: 0; right: 0; bottom: clamp(28px, 7vh, 64px);
  display: grid; gap: 14px; justify-items: center; padding-inline: var(--gutter);
}
.leader__bar {
  width: min(240px, 60vw); height: 1px; background: var(--line);
  overflow: hidden;
}
.leader__bar span {
  display: block; width: 100%; height: 100%;
  background: var(--tally); transform: scaleX(0); transform-origin: left;
}
.leader__pct { font-variant-numeric: tabular-nums; }

.leader__corner { position: absolute; width: 20px; height: 20px; border: 1px solid var(--line); }
.leader__corner:nth-of-type(1) { top: 24px;    left: 24px;  border-right: 0; border-bottom: 0; }
.leader__corner:nth-of-type(2) { top: 24px;    right: 24px; border-left: 0;  border-bottom: 0; }
.leader__corner:nth-of-type(3) { bottom: 24px; left: 24px;  border-right: 0; border-top: 0; }
.leader__corner:nth-of-type(4) { bottom: 24px; right: 24px; border-left: 0;  border-top: 0; }

@media (prefers-reduced-motion: reduce) {
  .js #leader { display: none; }
}

/* --------------------------------------------------------------------------
   PROJECTOR — the WebGL beam sits under the whole page as its base layer.
   Content is lifted above it; if WebGL never starts, the ink background
   underneath is already the finished look.
   -------------------------------------------------------------------------- */
#projector {
  position: fixed; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  display: block;
}
main, .site-footer { position: relative; z-index: 1; }

/* Film grain + vignette — the emulsion the whole page sits on */
.grain, .vignette { position: fixed; inset: 0; pointer-events: none; }
.grain {
  z-index: 200; opacity: .04; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.vignette {
  z-index: 199;
  background: radial-gradient(ellipse at center, transparent 42%, rgba(0,0,0,.6) 128%);
}

/* --------------------------------------------------------------------------
   4. SCRUBBER — the signature.
   A hairline playhead across the top. Scene markers double as navigation,
   so the structural device is functional rather than decorative.
   -------------------------------------------------------------------------- */
.scrubber {
  position: fixed; top: 0; left: 0; right: 0; z-index: 120;
  height: 2px; background: var(--line-soft);
}
.scrubber__fill {
  height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--tally), var(--tally-warm));
  transform-origin: left center;
  will-change: width;
}
/* Scene marks are informational, not controls — they show where each scene
   falls in the take. Navigation is the nav's job; making these clickable too
   would put invisible hit-targets on top of the header. */
.scrubber__marks { position: absolute; inset: 0; pointer-events: none; }

.scrubber__mark {
  position: absolute; top: 0; transform: translateX(-50%);
  width: 1px; height: 6px;
  background: var(--grey-dim);
  transition: background var(--dur-mid) var(--ease), height var(--dur-mid) var(--ease);
}
.scrubber__mark[data-active='true'] { background: var(--tally); height: 10px; }

@media (max-width: 760px) { .scrubber__marks { display: none; } }

/* --------------------------------------------------------------------------
   5. HEADER
   -------------------------------------------------------------------------- */
.site-header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 110;
  height: var(--header-h);
  display: flex; align-items: center; justify-content: space-between;
  padding-inline: var(--gutter);
  background: linear-gradient(to bottom, rgba(18,17,16,.94) 20%, rgba(18,17,16,0));
  transition: background var(--dur-mid) var(--ease);
}
.site-header.is-stuck {
  background: rgba(10,9,8,.86);
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid var(--line-soft);
}

.logo {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-display); font-weight: 700;
  font-size: 17px; letter-spacing: -.01em;
}
.logo__tally {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--tally); box-shadow: 0 0 10px var(--tally);
  animation: tally-pulse 2.4s var(--ease-soft) infinite;
}
@keyframes tally-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }

.site-nav { display: flex; gap: clamp(20px, 2.6vw, 34px); align-items: center; }
.site-nav a {
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--grey); position: relative; padding-block: 4px;
  transition: color var(--dur-fast) var(--ease);
}
.site-nav a::after {
  content: ''; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 1px; background: var(--tally);
  transform: scaleX(0); transform-origin: right;
  transition: transform var(--dur-mid) var(--ease);
}
.site-nav a:hover, .site-nav a[aria-current='page'] { color: var(--bone); }
.site-nav a:hover::after { transform: scaleX(1); transform-origin: left; }
.site-nav a[aria-current='page']::after { transform: scaleX(1); }

.timecode {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: .08em; color: var(--grey);
  font-variant-numeric: tabular-nums;
}
.timecode__led {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--tally); box-shadow: 0 0 8px var(--tally);
  animation: tally-pulse 1.6s var(--ease-soft) infinite;
}

/* Mobile menu */
/* Bars are absolutely centred, then offset by transform only. Both arms
   collapse to the exact same point before rotating, so the X is symmetric. */
.nav-toggle { display: none; position: relative; width: 44px; height: 44px; margin-right: -10px; }
.nav-toggle__bar {
  position: absolute; top: 50%; left: 50%;
  width: 22px; height: 2px; margin: -1px 0 0 -11px;
  background: var(--bone);
  transition: transform var(--dur-mid) var(--ease), opacity var(--dur-fast) var(--ease);
}
.nav-toggle__bar:nth-child(1) { transform: translateY(-6px); }
.nav-toggle__bar:nth-child(3) { transform: translateY(6px); }

.nav-toggle[aria-expanded='true'] .nav-toggle__bar:nth-child(1) { transform: rotate(45deg); }
.nav-toggle[aria-expanded='true'] .nav-toggle__bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded='true'] .nav-toggle__bar:nth-child(3) { transform: rotate(-45deg); }

@media (max-width: 860px) {
  .timecode { display: none; }
  .nav-toggle { display: block; z-index: 120; }
  /* A dark halo keeps the bars legible even when they sit over the light
     wordmark frame at the top of the page. */
  .nav-toggle__bar { box-shadow: 0 0 6px rgba(10,9,8,.7); }
  .site-nav {
    position: fixed; inset: var(--header-h) 0 auto 0;
    flex-direction: column; align-items: flex-start; gap: 0;
    background: rgba(10,9,8,.97);
    backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
    border-bottom: 1px solid var(--line);
    padding: var(--s-2) var(--gutter) var(--s-4);
    clip-path: inset(0 0 100% 0);
    transition: clip-path var(--dur-slow) var(--ease);
  }
  .site-nav[data-open='true'] { clip-path: inset(0 0 0 0); }
  .site-nav a { font-size: 15px; letter-spacing: .1em; padding-block: 16px; width: 100%; }
  /* With the menu open, make the whole bar solid so the close (×) always reads,
     whatever is behind it. */
  .site-header:has(.site-nav[data-open='true']) {
    background: rgba(10,9,8,.98);
    backdrop-filter: blur(16px) saturate(1.3); -webkit-backdrop-filter: blur(16px) saturate(1.3);
  }
}

/* --------------------------------------------------------------------------
   6. HERO
   -------------------------------------------------------------------------- */
.hero {
  min-height: 100svh;
  display: flex; flex-direction: column; justify-content: center;
  position: relative; isolation: isolate;
  padding-top: calc(var(--header-h) + var(--s-4));
  padding-bottom: var(--s-6);
}
/* Full-bleed background; the copy stays inside the measure via .hero__inner */
.hero__inner {
  position: relative; z-index: 1;
  width: 100%; max-width: var(--measure);
  margin-inline: auto; padding-inline: var(--gutter);
}

.hero__bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.hero__bg img, .hero__bg video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.hero__bg video { opacity: 0; transition: opacity 1.4s var(--ease); }
.hero__bg video.is-ready { opacity: 1; }

/* The scrim is what makes the headline legible over moving footage. At these
   alpha values even a pure-white frame leaves the bone text above 5.9:1, so
   contrast never depends on what the video happens to be showing. */
.hero__bg::after {
  content: ''; position: absolute; inset: 0;
  background:
    /* vertical: keeps the header readable and sinks the footer edge */
    linear-gradient(180deg, rgba(18,17,16,.66) 0%, rgba(18,17,16,.50) 44%, rgba(18,17,16,.88) 100%),
    /* horizontal: heavy over the column the copy occupies, near-clear on the
       right so the footage is actually visible rather than uniformly muddy */
    linear-gradient(90deg, rgba(18,17,16,.66) 0%, rgba(18,17,16,.52) 34%, rgba(18,17,16,.04) 72%);
}
@media (max-width: 860px) {
  /* copy spans the full width on phones, so the side gradient can't carry it */
  .hero__bg::after {
    background: linear-gradient(180deg, rgba(18,17,16,.80) 0%, rgba(18,17,16,.70) 44%, rgba(18,17,16,.92) 100%);
  }
}
.hero__eyebrow { display: flex; align-items: center; gap: 12px; margin-bottom: var(--s-3); }
.hero__eyebrow .bar {
  width: 34px; height: 1px; background: var(--tally);
  transform: scaleX(0); transform-origin: left;
}
.hero__title { font-size: clamp(42px, 8.8vw, 122px); max-width: 14ch; }
/* Each line is a mask; the text rides up into frame like a title card */
.hero__title .line { display: block; overflow: hidden; padding-bottom: .06em; }
.hero__title .line > span { display: block; transform: translateY(110%); }
.hero__sub { margin-top: var(--s-3); max-width: 48ch; }
.hero__cta { margin-top: var(--s-4); display: flex; gap: 14px; flex-wrap: wrap; }

.hero__meta {
  position: absolute; bottom: var(--s-4); right: var(--gutter);
  text-align: right; display: grid; gap: 4px;
}
.scroll-cue {
  position: absolute; bottom: var(--s-4); left: var(--gutter);
  display: flex; align-items: center; gap: 11px;
}
.scroll-cue .rail {
  width: 1px; height: 38px; overflow: hidden;
  background: var(--line);
}
.scroll-cue .rail::after {
  content: ''; display: block; width: 100%; height: 100%;
  background: linear-gradient(var(--tally), transparent);
  animation: cue-run 2.1s var(--ease-soft) infinite;
}
@keyframes cue-run {
  0%   { transform: translateY(-100%); }
  60%  { transform: translateY(100%); }
  100% { transform: translateY(100%); }
}
@media (max-width: 860px) { .hero__meta { display: none; } }

/* --------------------------------------------------------------------------
   7. BUTTONS
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-mono); font-size: 12px;
  letter-spacing: .1em; text-transform: uppercase;
  padding: 15px 28px; min-height: 48px;      /* comfortable touch target */
  border: 1px solid var(--bone); border-radius: 2px;
  cursor: pointer; position: relative;
  /* The wipe is a background layer rather than a pseudo-element, so it paints
     above the button's own background colour on every variant. */
  background-image: linear-gradient(var(--tally), var(--tally));
  background-repeat: no-repeat;
  background-position: left center;
  background-size: 0% 100%;
  transition:
    background-size var(--dur-mid) var(--ease),
    color var(--dur-mid) var(--ease),
    border-color var(--dur-mid) var(--ease);
}
.btn:hover, .btn:focus-visible { background-size: 100% 100%; }

.btn--primary { background-color: var(--bone); color: var(--ink); }
.btn--primary:hover, .btn--primary:focus-visible { color: var(--bone); border-color: var(--tally); }
.btn--ghost { border-color: var(--line); color: var(--grey); }
.btn--ghost:hover, .btn--ghost:focus-visible { color: var(--bone); border-color: var(--tally); }
.btn__arrow { transition: transform var(--dur-mid) var(--ease); }
.btn:hover .btn__arrow { transform: translateX(4px); }

/* --------------------------------------------------------------------------
   8. SECTION HEADS
   -------------------------------------------------------------------------- */
.section-head {
  padding: var(--s-7) 0 var(--s-4);
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--s-3); flex-wrap: wrap;
}
.section-head h2 { font-size: clamp(30px, 4.4vw, 50px); margin-top: 12px; }
.section-head p { color: var(--grey); max-width: 40ch; font-size: 14.5px; line-height: 1.65; }

/* --------------------------------------------------------------------------
   9. WORK / SCENES
   -------------------------------------------------------------------------- */
.scene { border-top: 1px solid var(--line); padding: var(--s-5) 0; }
.scenes .scene:last-child { border-bottom: 1px solid var(--line); }
.scene__inner {
  display: grid; grid-template-columns: 1.08fr .92fr;
  gap: clamp(32px, 5vw, 64px); align-items: center;
}
/* Alternate which side the frame sits on, so the eye crosses the page */
.scene:nth-child(even) .scene__inner { grid-template-columns: .92fr 1.08fr; }
.scene:nth-child(even) .scene__frame { order: 2; }
.scene:nth-child(even) .scene__text { order: 1; }

.scene__tag {
  margin-bottom: var(--s-2); color: var(--bone);
  display: flex; align-items: center; gap: 10px;
}
.scene__tag::before {
  content: ''; width: 22px; height: 1px; background: var(--tally); flex: none;
}
.scene__title { font-size: clamp(28px, 3.4vw, 42px); margin-bottom: var(--s-2); }
.scene__body { color: var(--grey); line-height: 1.75; font-size: 15px; max-width: 46ch; margin-bottom: var(--s-3); }

.chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: var(--s-3); }
.chip {
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .08em; text-transform: uppercase;
  padding: 7px 13px; border: 1px solid var(--line);
  border-radius: 20px; color: var(--grey);
}

.text-link {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-mono); font-size: 12px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--bone);
  border-bottom: 1px solid var(--tally); padding-bottom: 5px;
  transition: gap var(--dur-mid) var(--ease);
}
.text-link:hover, .text-link:focus-visible { gap: 15px; }

@media (max-width: 900px) {
  .scene__inner,
  .scene:nth-child(even) .scene__inner { grid-template-columns: 1fr; gap: var(--s-3); }
  .scene:nth-child(even) .scene__frame { order: 0; }
  .scene { padding: var(--s-4) 0; }
}

/* --------------------------------------------------------------------------
   10. FRAME — image container with a slate placeholder underneath.
   Until real stills land, the slate reads as an intentional production
   artifact rather than a broken image.
   -------------------------------------------------------------------------- */
.frame {
  position: relative; aspect-ratio: 16 / 9;    /* matches the source captures; reserved space => no CLS */
  border-radius: 5px; overflow: hidden;
  background: var(--ink-deep);
  border: 1px solid var(--line-soft);
  box-shadow: 0 40px 80px -34px rgba(0,0,0,.75);
}
.frame__slate {
  position: absolute; inset: 0;
  display: grid; place-content: center; gap: 10px; text-align: center;
  background:
    repeating-linear-gradient(180deg, rgba(239,236,228,.022) 0 1px, transparent 1px 4px),
    var(--ink-deep);
}
.frame__slate .id {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .22em;
  text-transform: uppercase; color: var(--grey);
}
.frame__slate .status { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .18em; color: var(--grey-dim); }
/* Camera framing marks in the corners */
.frame__slate::before, .frame__slate::after {
  content: ''; position: absolute; width: 18px; height: 18px;
  border: 1px solid var(--line);
}
.frame__slate::before { top: 14px; left: 14px; border-right: 0; border-bottom: 0; }
.frame__slate::after  { bottom: 14px; right: 14px; border-left: 0; border-top: 0; }

/* Real media fades over the slate once decoded */
.frame__media {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0;
  transition: opacity var(--dur-slow) var(--ease);
}
.frame__media.is-ready { opacity: 1; }
.frame::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(10,9,8,0) 58%, rgba(10,9,8,.72) 100%);
}

/* Whole-frame link: lifts slightly on hover, like a shot being selected */
a.frame-link { display: block; }
a.frame-link .frame {
  transition: transform var(--dur-slow) var(--ease), border-color var(--dur-mid) var(--ease);
}
a.frame-link:hover .frame,
a.frame-link:focus-visible .frame {
  transform: translateY(-5px); border-color: var(--line);
}

/* --------------------------------------------------------------------------
   11. APPROACH
   -------------------------------------------------------------------------- */
.approach { padding-block: var(--s-4) var(--s-7); border-top: 1px solid var(--line); }
.approach__grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(40px, 6vw, 80px); align-items: start; }
.approach__lead {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(23px, 2.9vw, 34px); line-height: 1.32; letter-spacing: -.018em;
}
.approach__body p { color: var(--grey); line-height: 1.8; font-size: 15px; margin-bottom: var(--s-2); }
.approach__body strong { color: var(--bone); font-weight: 600; }
.footnote { font-size: 12px; color: var(--grey-dim); }

.stats { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3) var(--s-4); margin-top: var(--s-4); }
.stat b {
  display: block; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(30px, 3.8vw, 44px); letter-spacing: -.025em; line-height: 1;
  margin-bottom: 7px;
}
.stat span { color: var(--grey); font-size: 13px; line-height: 1.5; }

@media (max-width: 900px) { .approach__grid { grid-template-columns: 1fr; gap: var(--s-4); } }

/* --------------------------------------------------------------------------
   12. SERVICES — the slate list
   -------------------------------------------------------------------------- */
.services { padding-bottom: var(--s-7); }
.svc-list { border-top: 1px solid var(--line); }
.svc {
  display: grid; grid-template-columns: 68px 1.25fr 1fr;
  gap: var(--s-3); align-items: center;
  padding: var(--s-3) 0; border-bottom: 1px solid var(--line);
  position: relative; transition: padding-left var(--dur-slow) var(--ease);
}
/* A tally wipe sweeps in from the left edge on hover */
.svc::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--tally); transform: scaleY(0); transform-origin: bottom;
  transition: transform var(--dur-mid) var(--ease);
}
.svc:hover { padding-left: 16px; }
.svc:hover::before { transform: scaleY(1); transform-origin: top; }
.svc .idx { font-family: var(--font-mono); font-size: 12px; color: var(--grey-dim); }
.svc h3 { font-family: var(--font-display); font-weight: 600; font-size: clamp(18px, 2vw, 25px); letter-spacing: -.015em; }
.svc p { color: var(--grey); font-size: 13.5px; line-height: 1.6; }

@media (max-width: 720px) {
  .svc { grid-template-columns: 40px 1fr; row-gap: 6px; }
  .svc p { grid-column: 2; }
}

/* --------------------------------------------------------------------------
   12b. THE FOUNDER — the one person the studio actually is
   -------------------------------------------------------------------------- */
.founder { padding-block: var(--s-6) var(--s-6); border-top: 1px solid var(--line); }
/* Portfolio pattern is "visuals first": the portrait carries as much weight as
   the copy, and the copy is kept to two paragraphs so the pairing stays a
   showcase rather than a wall of text next to a thumbnail. */
.founder__grid {
  display: grid; grid-template-columns: 1fr;
  gap: clamp(32px, 5vw, 72px); align-items: center;
}
/* The second column only exists if the portrait does. Without this the figure's
   error fallback removes the image but the empty track keeps its width, leaving
   the copy stranded beside a large blank half. */
.founder__grid:has(> .founder__frame) { grid-template-columns: 1fr 0.58fr; }
.founder__kicker { color: var(--grey-dim); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; }
.founder__name {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(34px, 5vw, 62px); letter-spacing: -.03em; line-height: 1.04;
  margin: 14px 0 10px;
}
.founder__role { color: var(--tally-text); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; margin-bottom: var(--s-3); }
.founder__text p { color: var(--grey); line-height: 1.8; font-size: 15.5px;
  margin-bottom: var(--s-2); max-width: 54ch; }
.founder__text em { color: var(--bone); font-style: italic; }

/* The proof line. A prospect with no client list to check scans facts, so the
   differentiating ones sit here rather than buried mid-paragraph. */
.founder__facts {
  list-style: none; display: flex; flex-wrap: wrap; gap: 8px;
  margin-top: var(--s-3);
}
.founder__facts li {
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--grey-dim);
  border: 1px solid var(--line); border-radius: 2px;
  padding: 7px 11px;
}

/* The portrait is graded to the page, so it needs no card — only the framing
   marks the rest of the site uses, and a floor for the light to fall onto.

   Two motions, which is the per-view budget: the frame opens like a shutter
   when it arrives, and the image drifts inside it as you scroll. The drift is
   why this clips — an unclipped parallax layer bleeds past its own frame. */
.founder__frame {
  position: relative; margin: 0 0 0 auto; overflow: hidden;
  max-width: 360px; width: 100%;
}
/* The shutter is on an inner element, never on the observed one. Clipping the
   figure itself to zero height also zeroes its intersection area, so the
   observer would never fire, is-in would never land, and it would stay shut
   forever — the reveal has to be one layer inside the thing being watched. */
.founder__media {
  display: block;
  clip-path: inset(100% 0 0 0);
  transition: clip-path 1.05s var(--ease);
}
.founder__frame.is-in .founder__media { clip-path: inset(0 0 0 0); }
/* The generic reveal would fade-and-lift this as well; the shutter is enough. */
.founder__frame[data-reveal] { opacity: 1; transform: none; }

.founder__frame img {
  display: block; width: 100%; height: auto;
  /* Oversized so the drift never exposes an edge, and --py is the only thing
     JS touches, which keeps this on the compositor. */
  transform: translateY(var(--py, 0px)) scale(1.07);
  will-change: transform;
  /* The shot is already near-black on its left edge; fading it into the page
     stops it reading as a pasted rectangle. */
  -webkit-mask-image: linear-gradient(100deg, transparent 0%, #000 16%, #000 100%);
          mask-image: linear-gradient(100deg, transparent 0%, #000 16%, #000 100%);
}
.founder__marks i {
  position: absolute; width: 22px; height: 22px;
  border: 1px solid rgba(239,236,228,.28);
}
.founder__marks i:nth-child(1) { top: 0;    right: 0;   border-left: 0;  border-bottom: 0; }
.founder__marks i:nth-child(2) { bottom: 0; right: 0;   border-left: 0;  border-top: 0; }
.founder__marks i:nth-child(3) { top: 0;    left: 16%;  border-right: 0; border-bottom: 0; }
.founder__marks i:nth-child(4) { bottom: 0; left: 16%;  border-right: 0; border-top: 0; }

/* Without JS the shutter is never opened, so it must not start closed. Same for
   anyone who has asked for less motion — they get the frame, not the reveal. */
.no-js .founder__media,
html:not(.js) .founder__media { clip-path: none; }

@media (prefers-reduced-motion: reduce) {
  .founder__media { clip-path: none !important; transition: none !important; }
  .founder__frame img { transform: none !important; will-change: auto; }
}

@media (max-width: 860px) {
  .founder__grid, .founder__grid:has(> .founder__frame) { grid-template-columns: 1fr; gap: var(--s-4); }
  /* Portrait first on a phone: meet the person, then read about him. */
  .founder__frame { order: -1; max-width: 250px; margin-inline: 0; }
  .founder__frame img { -webkit-mask-image: none; mask-image: none; }
  .founder__marks i:nth-child(3), .founder__marks i:nth-child(4) { left: 0; }
}

/* --------------------------------------------------------------------------
   13. CONTACT + FOOTER
   -------------------------------------------------------------------------- */
.contact { padding-block: var(--s-7) var(--s-6); border-top: 1px solid var(--line); text-align: center; }
.contact h2 { font-size: clamp(36px, 7.2vw, 92px); max-width: 15ch; margin: 12px auto var(--s-4); }
.contact__actions { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-bottom: var(--s-6); }
.contact__note {
  max-width: 56ch; margin: calc(var(--s-4) * -1 + 4px) auto var(--s-6);
  color: var(--grey-dim); font-size: 13.5px; line-height: 1.75;
}
.contact__meta {
  display: flex; justify-content: center; gap: var(--s-2) var(--s-4);
  flex-wrap: wrap; color: var(--grey); font-size: 13.5px;
}
.contact__meta a { transition: color var(--dur-fast) var(--ease); }
.contact__meta a:hover { color: var(--bone); }

.site-footer {
  border-top: 1px solid var(--line);
  padding: var(--s-3) var(--gutter);
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; gap: 12px;
}
.site-footer .mono { font-size: 10px; }

/* 404 — a frame that never made the cut */
.lost {
  min-height: 74svh;
  padding-block: calc(var(--header-h) + var(--s-6)) var(--s-6);
  display: flex; flex-direction: column; justify-content: center;
}
.lost__code { color: var(--grey-dim); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; }
.lost__title {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(40px, 8vw, 104px); line-height: 1.03; letter-spacing: -.03em;
  margin: var(--s-3) 0 var(--s-3);
}
.lost__text { color: var(--grey); line-height: 1.8; font-size: 15.5px; max-width: 52ch; }
.lost__actions { margin-top: var(--s-4); display: flex; gap: 14px; flex-wrap: wrap; }

/* --------------------------------------------------------------------------
   14. CASE STUDY PAGES
   -------------------------------------------------------------------------- */
.case-hero { padding: calc(var(--header-h) + var(--s-6)) 0 var(--s-5); }
.case-hero__back { display: inline-flex; align-items: center; gap: 9px; margin-bottom: var(--s-4); }
.case-hero__back:hover { color: var(--bone); }
.case-hero h1 { font-size: clamp(40px, 7.4vw, 92px); max-width: 13ch; margin: 12px 0 var(--s-3); }
.case-hero__sub { max-width: 52ch; }

.case-facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s-3); margin-top: var(--s-5);
  border-top: 1px solid var(--line); padding-top: var(--s-3);
}
.case-facts dt { font-family: var(--font-mono); font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--grey-dim); margin-bottom: 7px; }
.case-facts dd { font-size: 14.5px; color: var(--bone); line-height: 1.5; }

.case-body { padding-block: var(--s-5) var(--s-6); }
.case-block { border-top: 1px solid var(--line); padding: var(--s-4) 0; display: grid; grid-template-columns: 200px 1fr; gap: var(--s-4); }
.case-block > h2 { font-family: var(--font-mono); font-weight: 500; font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--bone); padding-top: 5px; }
.case-block__text p { color: var(--grey); line-height: 1.8; font-size: 15.5px; margin-bottom: var(--s-2); max-width: 62ch; }
.case-block__text p:last-child { margin-bottom: 0; }
.case-block__text strong { color: var(--bone); font-weight: 600; }

.case-list { list-style: none; display: grid; gap: var(--s-2); max-width: 62ch; }
/* The number sits in the margin; the body flows as one normal block. A grid
   here would auto-place the loose text after <strong> into the number column,
   which is what folded each item into a one-word-per-line strip. */
.case-list li { position: relative; padding-left: 38px; color: var(--grey); line-height: 1.7; font-size: 15px; }
.case-list li::before {
  content: attr(data-n); position: absolute; left: 0; top: 4px;
  font-family: var(--font-mono); font-size: 11px; color: var(--grey);
}
.case-list strong { color: var(--bone); font-weight: 600; }

.case-figure { margin: var(--s-4) 0; }
.case-figure figcaption { margin-top: 12px; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--grey-dim); }

@media (max-width: 860px) {
  .case-block { grid-template-columns: 1fr; gap: var(--s-2); }
}

/* Next-take footer — the reel keeps running */
.next-take { border-top: 1px solid var(--line); padding-block: var(--s-5) var(--s-6); }
.next-take a { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); flex-wrap: wrap; }
.next-take h2 { font-size: clamp(30px, 5.2vw, 60px); transition: color var(--dur-mid) var(--ease); }
.next-take a:hover h2, .next-take a:focus-visible h2 { color: var(--tally); }

/* --------------------------------------------------------------------------
   SPLIT TYPE — words ride up into frame behind a mask
   -------------------------------------------------------------------------- */
.w   { display: inline-block; overflow: hidden; vertical-align: bottom; padding-bottom: .04em; }
.w-i {
  display: inline-block;
  transform: translateY(108%);
  transition: transform .9s var(--ease);
  transition-delay: inherit;
}
.type-in .w-i { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .w-i { transform: none !important; }
}

/* --------------------------------------------------------------------------
   15. REVEAL — scroll-in choreography
   -------------------------------------------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity var(--dur-slow) var(--ease),
    transform var(--dur-slow) var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}
[data-reveal].is-in { opacity: 1; transform: none; }

/* Frames get a touch of scale so they feel like they settle into focus */
[data-reveal='frame'] { transform: translateY(34px) scale(.975); }

/* Hero load sequence — driven by class on <body>, not per-element JS timers */
.hero__eyebrow .bar,
.hero__title .line > span,
.hero__sub,
.hero__cta,
.hero__meta,
.scroll-cue { transition: transform 1s var(--ease), opacity 1s var(--ease); }

.hero__sub, .hero__cta, .hero__meta, .scroll-cue { opacity: 0; transform: translateY(16px); }

.is-loaded .hero__eyebrow .bar   { transform: scaleX(1); transition-delay: .1s; }
.is-loaded .hero__title .line:nth-child(1) > span { transform: translateY(0); transition-delay: .18s; }
.is-loaded .hero__title .line:nth-child(2) > span { transform: translateY(0); transition-delay: .29s; }
.is-loaded .hero__title .line:nth-child(3) > span { transform: translateY(0); transition-delay: .40s; }
.is-loaded .hero__sub    { opacity: 1; transform: none; transition-delay: .52s; }
.is-loaded .hero__cta    { opacity: 1; transform: none; transition-delay: .62s; }
.is-loaded .hero__meta   { opacity: 1; transform: none; transition-delay: .72s; }
.is-loaded .scroll-cue   { opacity: 1; transform: none; transition-delay: .82s; }

/* --------------------------------------------------------------------------
   16. REDUCED MOTION
   Everything still arrives — it just stops moving. Nothing is left invisible.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  [data-reveal],
  .hero__sub, .hero__cta, .hero__meta, .scroll-cue { opacity: 1 !important; transform: none !important; }
  .hero__title .line > span { transform: none !important; }
  .hero__eyebrow .bar { transform: scaleX(1) !important; }
  .svc:hover { padding-left: 0; }
  .scroll-cue .rail::after { display: none; }
  .frame__media { opacity: 1; }
}

/* --------------------------------------------------------------------------
   17. NO-JS FALLBACK — content must never be trapped behind JS
   -------------------------------------------------------------------------- */
.no-js [data-reveal],
.no-js .hero__sub, .no-js .hero__cta, .no-js .hero__meta, .no-js .scroll-cue { opacity: 1; transform: none; }
.no-js .hero__title .line > span { transform: none; }
.no-js .hero__eyebrow .bar { transform: scaleX(1); }
.no-js .scrubber { display: none; }

/* --------------------------------------------------------------------------
   18. PRINT
   -------------------------------------------------------------------------- */
@media print {
  .grain, .vignette, .scrubber, .site-header, .scroll-cue { display: none !important; }
  body { background: #fff; color: #000; }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* ==========================================================================
   THE REEL — five projects on one horizontal track, driven by vertical scroll.

   A long take is a tracking shot, so panning sideways across the work is the
   same idea as craning down it: the camera never cuts. Scroll stays a normal
   vertical scroll — the page is genuinely tall and the scrollbar is honest —
   and that scroll position is mapped onto the track's X.
   ========================================================================== */
.reel { position: relative; }

.reel__sticky {
  position: sticky; top: 0;
  height: 100svh;
  display: flex; align-items: center;
  overflow: hidden;
  /* No shared `perspective` + `preserve-3d` here. That combination is visually
     nicer in principle but breaks hit-testing in Chromium: 3D-transformed
     slides stop receiving pointer events entirely, so hover dies and — worse —
     the links become unclickable. Each slide carries its own perspective()
     inside its transform instead; at these angles it looks the same. */
}

.reel__track {
  display: flex; align-items: center;
  gap: clamp(20px, 2.6vw, 44px);
  /* Half a viewport minus half a slide, so slide one starts centred and the
     neighbours are already in frame — the reference shows three at a time. */
  padding-inline: calc(50vw - min(26vw, 260px));
  will-change: transform;
}

.slide {
  flex: 0 0 auto;
  width: clamp(240px, 34vw, 520px);
}

.slide__link { display: block; position: relative; }

.slide__frame {
  position: relative; aspect-ratio: 16 / 9;
  border-radius: 6px; overflow: hidden;
  background: var(--ink-deep);
  border: 1px solid var(--line-soft);
  box-shadow: 0 50px 90px -40px rgba(0,0,0,.9);
  /* A real reflection of whatever is rendering, video included, for free on
     the compositor. Firefox ignores it and simply shows no reflection. */
  -webkit-box-reflect: below 10px linear-gradient(transparent 66%, rgba(0,0,0,.34));
  transition: border-color var(--dur-mid) var(--ease);
}

.slide__frame img, .slide__frame video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.slide__frame video { opacity: 0; transition: opacity .55s var(--ease); }
.slide__frame video.is-ready { opacity: 1; }

/* Dim everything that is not the slide you are pointing at. The foot gradient
   is permanent — it is what keeps the caption readable over a bright frame. */
.slide__frame::before {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, rgba(10,9,8,0) 42%, rgba(10,9,8,.62) 74%, rgba(10,9,8,.94) 100%);
}
.slide__frame::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: rgba(10,9,8,.46);
  transition: background var(--dur-mid) var(--ease);
}
.slide.is-near .slide__frame::after { background: rgba(10,9,8,.12); }
.slide.is-hot  .slide__frame::after { background: rgba(10,9,8,0); }
.slide.is-hot  .slide__frame { border-color: var(--line); }

.slide__cap {
  position: absolute; z-index: 2;
  left: clamp(16px, 2.2vw, 30px); right: clamp(16px, 2.2vw, 30px);
  bottom: clamp(14px, 2vw, 26px);
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  pointer-events: none;
}
.slide__num {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .18em;
  color: var(--bone); text-shadow: 0 1px 12px rgba(10,9,8,.9);
  transition: color var(--dur-mid) var(--ease);
}
.slide.is-hot .slide__num { color: var(--tally); }

.slide__title {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(17px, 1.55vw, 25px); letter-spacing: -.035em; line-height: 1;
  text-shadow: 0 2px 22px rgba(10,9,8,.85);
}
/* Per-letter lift on hover — the caption animates with the slide, not after it */
.slide__title .c {
  display: inline-block;
  transform: translateY(0);
  transition: transform .5s var(--ease);
  transition-delay: calc(var(--i) * 22ms);
}
.slide.is-hot .slide__title .c { transform: translateY(-.14em); }

.slide__meta {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--bone); text-shadow: 0 1px 12px rgba(10,9,8,.9);
  opacity: 0; transform: translateY(6px);
  transition: opacity var(--dur-mid) var(--ease), transform var(--dur-mid) var(--ease);
}
.slide.is-hot .slide__meta { opacity: 1; transform: none; }

/* Rail + progress, so the reel says where it is in the take */
.reel__rail {
  position: absolute; left: var(--gutter); right: var(--gutter); bottom: clamp(22px, 4vh, 48px);
  height: 1px; background: var(--line-soft);
}
.reel__rail span {
  display: block; height: 100%; width: 100%;
  background: var(--tally);
  transform: scaleX(0); transform-origin: left;
}
.reel__hint {
  position: absolute; left: var(--gutter); bottom: calc(clamp(22px, 4vh, 48px) + 16px);
}

/* Touch and no-JS both fall back to an ordinary horizontal scroller, which
   works with a swipe and needs no scroll mapping at all. */
.no-js .reel__sticky,
html:not(.has-reel) .reel__sticky {
  position: static; height: auto; overflow-x: auto; overflow-y: hidden;
  padding-block: var(--s-5);
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
}
.no-js .reel, html:not(.has-reel) .reel { height: auto !important; }
.no-js .slide, html:not(.has-reel) .slide { scroll-snap-align: center; }
html:not(.has-reel) .slide__frame::after { background: rgba(10,9,8,.12); }

@media (prefers-reduced-motion: reduce) {
  .slide__title .c { transform: none !important; }
  .slide__meta { opacity: 1; transform: none; }
  .slide__frame::after { background: rgba(10,9,8,.12); }
}

/* ==========================================================================
   THE STAGE — a project's opening. The build runs large in the middle and
   the evidence floats around it at different depths, drifting apart as you
   scroll. It is a contact sheet pinned around a monitor, not a hero banner.
   ========================================================================== */
.stage {
  position: relative;
  display: grid; place-items: center;
  padding: calc(var(--header-h) + var(--s-4)) var(--gutter) var(--s-7);
  overflow: hidden;          /* cards drift; never let them widen the page */
}

.stage__inner { position: relative; width: min(100%, 1180px); }

/* The running build */
.stage__plate { position: relative; width: min(100%, 820px); margin-inline: auto; }

.stage__screen {
  position: relative; z-index: 2; width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 6px; overflow: hidden;
  background: var(--ink-deep);
  border: 1px solid var(--line);
  box-shadow: 0 60px 120px -46px rgba(0,0,0,.95);
}
.stage__screen img, .stage__screen video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.stage__screen video { opacity: 0; transition: opacity .6s var(--ease); }
.stage__screen video.is-ready { opacity: 1; }

/* Floating evidence. Absolute on wide screens, an honest grid on narrow ones. */
.stage__card {
  position: absolute; z-index: 1;
  border-radius: 4px; overflow: hidden;
  border: 1px solid var(--line-soft);
  background: var(--ink-raise);
  box-shadow: 0 30px 60px -28px rgba(0,0,0,.9);
  will-change: transform;
}
.stage__card img { width: 100%; height: 100%; object-fit: cover; display: block; }

.stage__card--a { width: 26%; aspect-ratio: 4/3;   left: -19%; top: -9%; }
.stage__card--b { width: 22%; aspect-ratio: 1;     right: -17%; top: 22%; }
.stage__card--c { width: 29%; aspect-ratio: 16/10; left: -14%; bottom: -17%; }

/* The spec card — type and palette, the way a brand sheet shows them */
.stage__spec {
  position: absolute; z-index: 3; right: -13%; bottom: -13%;
  width: 30%; min-width: 190px;
  padding: 16px 18px 18px;
  border-radius: 4px; border: 1px solid var(--line);
  background: rgba(15,14,13,.92);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 30px 60px -28px rgba(0,0,0,.9);
  will-change: transform;
}
.stage__spec dt { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase; color: var(--grey-dim); }
.stage__spec dd { font-size: 13px; color: var(--bone); margin-bottom: 10px; line-height: 1.35; }
.stage__spec dd:last-child { margin-bottom: 0; }
.stage__swatches { display: flex; gap: 6px; margin-top: 2px; }
.stage__swatches i { width: 18px; height: 18px; border-radius: 2px; border: 1px solid rgba(255,255,255,.12); }

/* Title over the stage */
.stage__head { position: relative; z-index: 4; text-align: center; margin-bottom: var(--s-4); }
.stage__head h1 { font-size: clamp(34px, 5.6vw, 70px); }
.stage__head .mono { display: block; margin-bottom: 14px; }

.stage__logo {
  position: relative; z-index: 4;
  margin: var(--s-5) auto 0; text-align: center;
}
.stage__logo span {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(20px, 2.6vw, 30px); letter-spacing: .04em;
  color: var(--grey);
}

@media (max-width: 900px) {
  .stage { min-height: 0; padding-bottom: var(--s-5); overflow: visible; }
  .stage__card, .stage__spec {
    position: static; width: auto; margin: 0;
    transform: none !important;
  }
  .stage__evidence {
    display: grid; grid-template-columns: repeat(2, 1fr);
    gap: 12px; margin-top: var(--s-3);
  }
  .stage__card--a, .stage__card--b, .stage__card--c { aspect-ratio: 4/3; }
  .stage__spec { grid-column: 1 / -1; }
}
@media (min-width: 901px) { .stage__evidence { display: contents; } }

@media (prefers-reduced-motion: reduce) {
  .stage__card, .stage__spec { transform: none !important; }
}

.case-intro { padding-block: var(--s-5) var(--s-2); }
.case-intro .lede { max-width: 62ch; }

/* Concept work, labelled as such. Stated plainly and up front, because a
   prospect who finds out later stops believing the rest of the page. */
.case-note {
  max-width: 62ch; margin-top: var(--s-3);
  border-left: 2px solid var(--tally);
  padding: 2px 0 2px var(--s-2);
  color: var(--grey); font-size: 14px; line-height: 1.7;
}
.case-note strong { color: var(--bone); font-weight: 600; }
.stage__back { display: inline-flex; align-items: center; gap: 9px; margin-bottom: var(--s-3); }

/* ==========================================================================
   HERO v2 — the first shot.

   The old hero was one blurred loop behind large type: legible, but it said
   nothing. This one opens on the studio's actual work — a slow montage of the
   builds, drifting — and the headline assembles rather than fading in.
   Stills, not video: four simultaneous decodes behind a headline is exactly
   the cost this page cannot afford, and a still holds a slow drift better.
   ========================================================================== */
.hero__plate {
  position: absolute; inset: -6%;
  background-size: cover; background-position: center;
  opacity: 0;
  transform: scale(1.06);
  transition: opacity 2.4s var(--ease-soft);
  will-change: opacity, transform;
}
.hero__plate.is-on {
  opacity: 1;
  /* The drift only runs on the plate that is showing, so there is never more
     than one animated layer at a time. */
  animation: hero-drift 16s linear forwards;
}
@keyframes hero-drift {
  from { transform: scale(1.04) translate3d(0, 0, 0); }
  to   { transform: scale(1.13) translate3d(-1.4%, -1.2%, 0); }
}

/* Headline: per-character, masked, arriving out of soft focus. */
.hero__title .ch {
  display: inline-block;
  /* Inline-blocks sit on the baseline and carry their own ~1.2em box, which
     inflated every line to roughly double height. Pin both so the mask is
     exactly one line tall. */
  line-height: .93;
  vertical-align: top;
  transform: translateY(102%) rotate(2.2deg);
  filter: blur(9px);
  opacity: 0;
  transition:
    transform 1.15s var(--ease),
    filter    .95s var(--ease),
    opacity   .75s var(--ease);
  transition-delay: calc(var(--i) * 26ms);
}
/* The line masks are block-level, so the whitespace between them in the markup
   forms an anonymous line box that inherits the title's line-height and shows
   up as a phantom gap between the two lines. Zero it on the parent and set the
   real leading on the masks themselves. */
.hero__title { line-height: 0; }
.hero__line { display: block; overflow: hidden; line-height: .95; padding-bottom: .12em; }
.is-loaded .hero__title .ch { transform: none; filter: blur(0); opacity: 1; }

/* The eyebrow rule draws itself */
.hero__eyebrow .bar { transform-origin: left; }

/* A single light sweep across the headline as it settles — one gesture, not
   a decoration that repeats. */
.hero__inner::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 42%, rgba(239,236,228,.09) 50%, transparent 58%);
  transform: translateX(-120%);
  opacity: 0;
}
.is-loaded .hero__inner::after { animation: hero-sweep 1.9s var(--ease-soft) .55s 1; }
@keyframes hero-sweep {
  0%   { transform: translateX(-120%); opacity: 0; }
  35%  { opacity: 1; }
  100% { transform: translateX(120%);  opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .hero__plate.is-on { animation: none; transform: scale(1.02); }
  .hero__title .ch { transform: none !important; filter: none !important; opacity: 1 !important; }
  .is-loaded .hero__inner::after { animation: none; }
}

/* ==========================================================================
   HERO v3 — the aperture.

   A port of the smooth-scroll-hero pattern to vanilla: a sticky full-screen
   layer whose clip-path window opens from a small rectangle to the full frame
   as you scroll, while the media inside scales 170% -> 100%.

   The original is React + framer-motion; this site has no build step, so the
   two animated values are mapped from scroll directly. Same two values, same
   curve, none of the dependencies.

   The media is the studio's own title card, so the window opening on it reads
   as an aperture rather than a stock parallax trick.
   ========================================================================== */
.aperture { position: relative; width: 100%; }

.aperture__sticky { position: sticky; top: 0; height: 100svh; width: 100%; }

.aperture__stage {
  position: absolute; inset: 0;
  background: var(--ink-deep);
  overflow: hidden;
  /* Set by JS; this is the closed state so the first paint is already right. */
  clip-path: polygon(25% 25%, 75% 25%, 75% 75%, 25% 75%);
  will-change: clip-path;
}

.aperture__media { position: absolute; inset: 0; overflow: hidden; }
.aperture__media img,
.aperture__media video {
  position: absolute; top: 50%; left: 50%;
  width: 100%; height: 100%;
  object-fit: cover;
  /* 1.35 rather than the reference's 1.7: this media is a wordmark, and at
     1.7 the closed aperture crops it mid-letter, which reads as a bug. */
  transform: translate(-50%, -50%) scale(1.35);
  transform-origin: center;
  will-change: transform;
}
.aperture__media video { opacity: 0; transition: opacity .7s var(--ease); }
.aperture__media video.is-ready { opacity: 1; }

/* Framing marks on the aperture itself — the same corner language as the
   leader and the project stages. */
.aperture__marks { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.aperture__marks i {
  position: absolute; width: 26px; height: 26px;
  border: 1px solid rgba(239,236,228,.45);
}
.aperture__marks i:nth-child(1) { top: 22px;    left: 22px;  border-right: 0; border-bottom: 0; }
.aperture__marks i:nth-child(2) { top: 22px;    right: 22px; border-left: 0;  border-bottom: 0; }
.aperture__marks i:nth-child(3) { bottom: 22px; left: 22px;  border-right: 0; border-top: 0; }
.aperture__marks i:nth-child(4) { bottom: 22px; right: 22px; border-left: 0;  border-top: 0; }

/* Outside the stage: inside it, the clip hides the cue exactly when it is
   the only instruction on screen. */
.aperture__cue {
  position: absolute; left: 50%; bottom: clamp(24px, 5vh, 54px);
  transform: translateX(-50%); z-index: 3;
  display: flex; align-items: center; gap: 10px;
  transition: opacity .5s var(--ease);
}

/* No JS, or reduced motion: the aperture is simply open. */
.no-js .aperture__stage,
html:not(.has-aperture) .aperture__stage { clip-path: none; }
.no-js .aperture__sticky,
html:not(.has-aperture) .aperture__sticky { position: relative; }
.no-js .aperture__media img, .no-js .aperture__media video,
html:not(.has-aperture) .aperture__media img,
html:not(.has-aperture) .aperture__media video { transform: translate(-50%, -50%) scale(1); }
.no-js .aperture, html:not(.has-aperture) .aperture { height: auto !important; }

@media (prefers-reduced-motion: reduce) {
  .aperture__stage { clip-path: none !important; }
  .aperture__media img, .aperture__media video { transform: translate(-50%,-50%) scale(1) !important; }
}

/* Phones and other touch/coarse pointers: no scroll hijack, no clip window.
   The stage becomes a full-width 16:9 band showing the whole wordmark frame,
   so nothing is cropped and the hero is one tidy screen-width card. */
@media (hover: none), (pointer: coarse) {
  .aperture { height: auto !important; }
  .aperture__sticky { position: relative; height: auto; }
  .aperture__stage {
    position: relative; inset: auto;
    aspect-ratio: 16 / 9; width: 100%;
    clip-path: none !important;
  }
  .aperture__media img,
  .aperture__media video { transform: translate(-50%, -50%) scale(1) !important; }
  /* The band ends mid-screen with copy already visible beneath it, so the cue
     has nothing to explain — and it sat on the bright frame at poor contrast. */
  .aperture__cue { display: none; }
  /* The footage carries its own framing marks, and at this size the CSS pair
     lands right on top of them — one set of corners, not two. */
  .aperture__marks { display: none; }
}

/* ==========================================================================
   LAYERED TEXT — an isometric stack where each row slides to reveal the next.

   Ported from the GSAP original. The stagger is a CSS transition-delay per
   row rather than a timeline, which needs no library and cannot desync.
   ========================================================================== */
.layered {
  --row-h: clamp(34px, 5.4vw, 60px);
  --row-step: 35px;
  display: flex; flex-direction: column; align-items: center;
  padding: var(--s-7) 0;
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(30px, 6.2vw, 72px);
  letter-spacing: -.02em; text-transform: uppercase;
  color: var(--bone);
  cursor: pointer; list-style: none;
  -webkit-font-smoothing: antialiased;
}

.layered__row {
  height: var(--row-h);
  overflow: hidden;
  position: relative;
}
/* Alternating shears are what make the stack read as an isometric solid. */
.layered__row:nth-child(odd)  { transform: translateX(var(--x)) skew(60deg, -30deg) scaleY(.66667); }
.layered__row:nth-child(even) { transform: translateX(var(--x)) skew(0deg, -30deg)  scaleY(1.33333); }

.layered__row p {
  height: var(--row-h);
  line-height: calc(var(--row-h) - 5px);
  padding-inline: 15px;
  margin: 0; white-space: nowrap;
  transform: translateY(0);
  transition: transform .8s var(--ease);
  transition-delay: calc(var(--i) * 80ms);
}
.layered:hover .layered__row p,
.layered:focus-within .layered__row p { transform: translateY(calc(var(--row-h) * -1)); }

.layered__row p:first-child { color: var(--grey); }
.layered__row p:last-child  { color: var(--bone); }

@media (max-width: 760px) { .layered { --row-step: 20px; } }

@media (prefers-reduced-motion: reduce) {
  .layered__row p { transition: none; }
  .layered:hover .layered__row p { transform: none; }
}
