/* ============================================================
   film.css — the desktop-only opening film and its handoff.

   EVERY rule here is scoped under body.is-film. That class is set by the PC
   gate in index.html and only there, so on a phone, a tablet, or with reduced
   motion on, this whole stylesheet is inert and the page falls back to exactly
   what terminal.css describes: one fixed, single-screen terminal.

   Tokens (--bg, --accent, --mono, …) all come from terminal.css, which is
   loaded first. Do not redeclare them here — scene.js overwrites --fg/--cmd/
   --accent at runtime to follow the saved theme, and a second :root block
   would fight that.
   ============================================================ */

/* The film layers only exist for the film path. Hidden by default rather than
   injected by script, so the terminal path never flashes a stray <video> or a
   loose caption before JS runs. */
#film,#crt,#grain,#hud{display:none}
body.is-film #film,
body.is-film #crt,
body.is-film #grain,
body.is-film #hud{display:block}

/* terminal.css sets body{overflow:hidden} for the single-screen site.
   The film is a scrolling document, so that has to be undone — but only here. */
body.is-film{
  background:var(--bg);
  color:var(--fg);
  font:400 15px/1.6 var(--mono);
  -webkit-font-smoothing:antialiased;
  height:auto;
  min-height:100%;
  overflow-x:hidden;
  overflow-y:visible;
}
body.is-film,
body.is-film .stage{background:var(--bg)}

/* ---------- scroll track ---------- */
/* the track's height IS the scroll budget: film 0-70%, pull 70-82%, dwell to 100% */
body.is-film .track{height:520vh;position:relative}

body.is-film .stage{
  position:sticky;top:0;
  height:100vh;height:100svh;
  overflow:hidden;
}

/* layering inside the one stage: film, CRT treatment, terminal, hud */
body.is-film .film{z-index:1}
body.is-film .crt,
body.is-film .grain{z-index:2}
body.is-film .handoff{z-index:3}
body.is-film .hud{z-index:4}

/* The focus pull. The clip ends on a blank glowing screen, so the blur is a
   look rather than a cover-up — just enough to sell the screen coming into
   focus as the terminal takes it over. */
body.is-film .film{
  position:absolute;inset:0;overflow:hidden;
  filter:blur(calc(var(--pull,0) * 5px));
  will-change:filter,transform,opacity;
}
body.is-film .handoff{
  filter:blur(calc((1 - var(--focus,0)) * 6px));
  transform:scale(calc(1.03 - var(--focus,0) * 0.03));
  transform-origin:50% 46%;
  will-change:filter,transform,opacity;
}

/* ---------- HUD ---------- */
body.is-film .hud{
  position:absolute;inset:0;
  pointer-events:none;
  padding:clamp(18px,3.5vw,40px);
  display:grid;
  grid-template-rows:1fr auto;
  font-size:12px;letter-spacing:.04em;
}

/* The small corner wordmark is gone: it duplicated the centred one, and its
   blinking caret made it flicker in and out in the corner. */

body.is-film .hud-hint{
  grid-row:2;
  justify-self:center;
  display:flex;align-items:center;gap:9px;
  color:var(--dim);
  transition:opacity .5s ease;
}
body.is-film .hud-hint-txt{text-transform:uppercase;letter-spacing:.22em;font-size:10px}
body.is-film .hud-wheel{animation:wheel 1.9s ease-in-out infinite}
@keyframes wheel{
  0%,100%{transform:translateY(0);opacity:1}
  50%{transform:translateY(5px);opacity:.35}
}

/* ---------- beat 2: the handed-off terminal ---------- */
body.is-film .handoff{
  position:absolute;inset:0;
  opacity:0;
  /* visibility:hidden also makes focus() a no-op, which is what stops
     terminal.js's boot-time focusInput() from yanking the scroll position. */
  visibility:hidden;
}
body.is-film .handoff.live{visibility:visible}

/* terminal.css pins .wt to the viewport; inside the stage it must be a child.
   (On the terminal-only path it stays position:fixed, which is correct there.) */
body.is-film .handoff .wt{
  position:absolute;
  inset:0;
  /* the CRT underneath already supplies the glow */
  background-image:none;
}

/* ---------- CRT treatment over the film ---------- */
/* The film's last frame IS the terminal, so the two layers match and the
   handoff can be optical — a focus pull — instead of a wipe hiding a
   mismatch. */
body.is-film .crt{
  position:absolute;inset:0;
  pointer-events:none;
  background:
    repeating-linear-gradient(
      to bottom,
      rgba(0,0,0,0) 0px,
      rgba(0,0,0,0) 2px,
      rgba(0,0,0,.30) 3px),
    radial-gradient(120% 90% at 50% 50%, rgba(0,0,0,0) 42%, rgba(0,0,0,.58) 100%);
  mix-blend-mode:multiply;
}

/* Film grain, as SVG turbulence rather than an image request. Sits with the
   CRT treatment and dissolves with it, so the terminal stays clean. */
body.is-film .grain{
  position:absolute;inset:-50%;
  pointer-events:none;
  opacity:.14;
  background-image:url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'>\
<filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/>\
<feColorMatrix type='saturate' values='0'/></filter>\
<rect width='220' height='220' filter='url(%23n)' opacity='0.55'/></svg>");
  animation:grainshift 900ms steps(4) infinite;
  will-change:transform;
}
@keyframes grainshift{
  0%{transform:translate(0,0)}
  25%{transform:translate(-2%,1.5%)}
  50%{transform:translate(1.5%,-2%)}
  75%{transform:translate(-1%,-1%)}
  100%{transform:translate(0,0)}
}

/* ---------- beat 1: the film, a layer inside the shared stage ---------- */
body.is-film #scrubvid{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:cover;
  /* No mix-blend-mode. screen-blending lifted the blacks (0 -> ~12/255) and
     flattened contrast; the clip is already graded to true black and
     object-fit:cover leaves no edge to hide. */
}
body.is-film .scrub-veil{
  position:absolute;inset:0;
  background:
    radial-gradient(120% 80% at 80% -10%,rgba(255,77,90,.10),transparent 60%),
    linear-gradient(to bottom,rgba(12,12,12,.85),transparent 26%,transparent 62%,rgba(12,12,12,.92));
  pointer-events:none;
}
body.is-film .scrub-cap{
  position:absolute;left:0;right:0;bottom:clamp(28px,7vh,80px);
  display:flex;flex-direction:column;align-items:center;gap:10px;
  text-align:center;padding:0 20px;pointer-events:none;
}
body.is-film .scrub-kicker{
  color:var(--accent);
  font-size:clamp(28px,6.5vw,64px);
  font-weight:700;letter-spacing:.01em;line-height:1;
  text-shadow:0 0 34px rgba(255,77,90,.5);
}
body.is-film .scrub-line{color:var(--dim2);font-size:clamp(11px,1.5vw,14px);letter-spacing:.06em}

/* ---------- visitor counter ---------- */
/* It is position:fixed, so during the film it would sit in the corner of the
   shot looking like a bug. Hold it until the handoff lands. Specificity here
   (1 id, 1 class, 1 element) already beats #visitcount.show, so no !important. */
body.is-film #visitcount{opacity:0}
body.is-film.handed #visitcount{opacity:1}

/* ---------- reduced motion ---------- */
/* The gate already sends reduced-motion visitors to the terminal, so this is
   belt-and-braces for anyone who flips the setting mid-session. */
@media (prefers-reduced-motion: reduce){
  body.is-film .hud-wheel,
  body.is-film .grain{animation:none}
}
