/* ==========================================================================
   base — reset, type primitives, the WebGL stage
   ========================================================================== */

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

html, body { margin: 0; padding: 0; }

html {
  background: var(--void);
  -webkit-text-size-adjust: 100%;
}

body {
  min-height: 100svh;
  background: var(--void);
  color: var(--silver);
  font-family: var(--f-body);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.6;
  letter-spacing: -0.005em;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body.is-locked { overflow: hidden; }

h1, h2, h3, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, video, canvas, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }

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

::selection { background: var(--cyan); color: var(--void); }

/* ── the stage ─────────────────────────────────────────────────────────────
   One fixed canvas carries the hero and the reel. They live in a single
   Three.js scene at different depths, and scroll flies one camera through
   both — so there is never a moment where one 3D scene is swapped for
   another. Content scrolls over it.
   ------------------------------------------------------------------------ */

.stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.stage canvas { width: 100%; height: 100%; display: block; }

/* Everything readable sits above the stage. */
.page { position: relative; z-index: 1; }

/* When WebGL is unavailable the poster stack takes over — see .nogl rules in
   sections.css. Nothing on this site is allowed to be blank. */
.nogl .stage { display: none; }

/* ── type primitives ───────────────────────────────────────────────────── */

.display {
  font-family: var(--f-display);
  font-weight: 700;
  font-variation-settings: "wdth" 112;
  line-height: 0.86;
  letter-spacing: -0.035em;
  text-transform: uppercase;
}

.meta {
  font-family: var(--f-mono);
  font-size: var(--t-meta);
  font-weight: 400;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  line-height: 1.6;
  color: var(--silver-3);
}

.lead {
  font-size: var(--t-lead);
  line-height: 1.45;
  letter-spacing: -0.015em;
  color: var(--silver-2);
}

.tnum { font-variant-numeric: tabular-nums; }

/* Split-text spans. Each character/word gets a mask parent so it can be
   swept in from below without a wrapping clip on the whole line. */
.sp-line { display: block; overflow: hidden; }
.sp-w, .sp-c { display: inline-block; will-change: transform; }

/* ── film grain over the whole document ────────────────────────────────────
   The WebGL post pass grains the 3D layers only. This grains the HTML on top
   of it, so type and footage share one emulsion instead of looking like two
   separate images stacked.
   ------------------------------------------------------------------------ */

.emulsion {
  position: fixed;
  inset: -50%;
  z-index: 90;
  pointer-events: none;
  opacity: 0.14;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/></filter><rect width='140' height='140' filter='url(%23n)' opacity='0.55'/></svg>");
  animation: shift 0.7s steps(3) infinite;
}

@keyframes shift {
  0%   { transform: translate3d(0, 0, 0); }
  33%  { transform: translate3d(-1.5%, 1%, 0); }
  66%  { transform: translate3d(1%, -1.5%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* ── accessibility ─────────────────────────────────────────────────────────
   Reduced motion strips every autonomous animation. Scroll-linked movement
   survives, because the reader is the one driving it — but nothing moves on
   its own, and the grain stops flickering.
   ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .emulsion { animation: none; opacity: 0.09; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  top: 0.6rem; left: 0.6rem;
  z-index: 500;
  padding: 0.6rem 1rem;
  background: var(--cyan);
  color: var(--void);
  font-family: var(--f-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transform: translateY(-160%);
  transition: transform 0.3s var(--ease);
}

.skip:focus-visible { transform: translateY(0); }
