/* ==========================================================================
   EITRI — cinematic motion layer
   2D only, deliberately. A WebGL scene would look impressive on a desktop
   review and then cook an iPhone battery, and this site is mobile-first.
   Everything here is transform/opacity — the two properties a browser can
   animate on the compositor without touching layout or paint.

   Every effect is driven by one CSS variable set from JS (--t, 0→1) so the
   browser does the interpolation, not a script running per frame.
   ========================================================================== */

/* ---- 1. Aperture -------------------------------------------------------
   The page opens like a projector gate. Two bars retract from top and bottom
   over ~900ms, once, on load. It is the first thing you see and it says
   "film" before a single word is read. */
.aperture {
  position: fixed; inset: 0; z-index: 300;
  pointer-events: none;
  /* FAILS OPEN. Previously the bars were painted by default and only retracted
     once JS added .open — so if cinematic.js 404'd, threw, or JS was disabled,
     the homepage was a permanently black screen with no escape. Now nothing
     renders until JS explicitly arms it, and the worst case is simply no
     animation. */
  display: none;
}
.aperture.armed { display: block; }
.aperture i {
  position: absolute; left: 0; right: 0;
  height: 50%;
  background: var(--ink);
  transform: translateY(0);
  transition: transform 1.1s cubic-bezier(.16,1,.3,1);
}
.aperture i.top    { top: 0; }
.aperture i.bottom { bottom: 0; }
.aperture.open i.top    { transform: translateY(-100%); }
.aperture.open i.bottom { transform: translateY(100%); }
/* Once retracted the element is inert and must stop compositing. */
.aperture.done { display: none; }

/* ---- 2. Film strip -----------------------------------------------------
   A horizontal strip of frames that scrubs sideways as you scroll past it —
   the page becomes a timeline being played. --t runs 0→1 across the section. */
.reel {
  position: relative;
  overflow: hidden;
  padding-block: var(--sect);
  /* Sprocket holes, top and bottom, drawn rather than imaged. */
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.reel::before,
.reel::after {
  content: ""; position: absolute; left: 0; right: 0; height: 14px;
  background-image: radial-gradient(circle at 7px 7px,
    var(--ink) 0 3.5px, transparent 3.5px);
  background-size: 28px 14px;
  opacity: .5; pointer-events: none;
}
.reel::before { top: 0; }
.reel::after  { bottom: 0; }

.reel-track {
  display: flex;
  gap: clamp(10px, 1.4vw, 20px);
  width: max-content;
  will-change: transform;
  /* The whole scrub is one transform. --t is written by JS; the browser
     interpolates. No layout, no paint, one composited layer. */
  transform: translate3d(calc(var(--t, 0) * -46%), 0, 0);
}

.frame {
  position: relative;
  flex: 0 0 auto;
  width: clamp(150px, 21vw, 280px);
  aspect-ratio: 16 / 9;
  background: var(--ink-2);
  border: 1px solid var(--line);
  overflow: hidden;
}
/* Each frame carries a colour-grade wash keyed to the editing levels, so the
   strip reads as the same shot graded four ways — which is the product. */
.frame::before {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(145deg, var(--fc, var(--surface)), transparent 70%);
  opacity: .55;
}
.frame::after {
  content: attr(data-label);
  position: absolute; left: 10px; bottom: 8px;
  font-family: var(--mono); font-size: var(--fs-micro);
  letter-spacing: .16em; text-transform: uppercase;
  color: rgba(247,242,233,.72);
}
.frame .no {
  position: absolute; right: 10px; top: 8px;
  font-family: var(--mono); font-size: var(--fs-micro);
  letter-spacing: .1em; color: rgba(247,242,233,.42);
  font-variant-numeric: tabular-nums;
}
/* Scan bars suggest footage without shipping a video file. */
.frame .bars { position: absolute; inset: auto 10px 26px 10px; display: grid; gap: 3px; }
.frame .bars i { height: 2px; background: rgba(244,238,227,.14); }
.frame .bars i:nth-child(2) { width: 72%; }
.frame .bars i:nth-child(3) { width: 44%; background: var(--fc, var(--brand)); }

/* ---- 3. Playhead -------------------------------------------------------
   A vermillion rule that sweeps the section as it scrubs. */
.reel-head {
  position: absolute; top: 0; bottom: 0; width: 1px;
  background: var(--brand);
  left: calc(var(--t, 0) * 100%);
  opacity: .7;
  pointer-events: none;
}
.reel-head::before {
  content: ""; position: absolute; top: 0; left: -3px;
  width: 7px; height: 7px; background: var(--brand);
}

/* ---- 4. Timecode -------------------------------------------------------
   Counts up with the scrub. Monospaced and tabular so digits do not jitter. */
.reel-tc {
  font-family: var(--mono); font-size: var(--fs-mono);
  letter-spacing: .16em; color: var(--t-4);
  font-variant-numeric: tabular-nums;
}

/* ---- 5. Scroll-linked grade -------------------------------------------
   The section warms as you move through it: the halation bloom rises from
   nothing to full. Cheap — one opacity on one element. */
.reel-glow {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 70% at 50% 50%, var(--halo), transparent 65%);
  opacity: calc(var(--t, 0) * .16);
}

/* ---- 6. Mobile + accessibility ----------------------------------------
   On a phone the strip still scrubs, but the frames are smaller and the
   travel is shorter so nothing runs off into empty space. */
@media (max-width: 700px) {
  .reel-track { transform: translate3d(calc(var(--t, 0) * -62%), 0, 0); }
}

/* A reader who has asked for less motion gets a static, complete composition
   rather than a broken one: the strip sits at its start, fully legible. */
@media (prefers-reduced-motion: reduce) {
  .aperture { display: none; }
  .reel-track { transform: none !important; }
  .reel-head { display: none; }
  .reel-glow { opacity: .08 !important; }
}
