/* Scroll-scrubbed header.
   Hard edges only — no rounding, no drop shadows. The copy is anchored to the
   left edge rather than sitting in a centred column, so the header reads as a
   printed page with a photograph bled off it, not as a hero banner. */

.scrub {
  position: relative;
  background: var(--dark);
}

/* The scroll budget. 700svh of runway over a 100svh stage leaves 600svh of
   travel for 400 frames — 1.5svh per frame, which is the pacing the 250-frame
   version had. Deliberately NOT doubled to match the doubled footage: 900svh
   would have made the header nine viewports deep. The sequence runs slightly
   fewer frames per second of footage instead, which is invisible while
   scrubbing because scroll speed, not frame rate, sets the pace. */
.scrub-runway { height: 700svh; }

.scrub-stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  background: var(--dark);
}

.scrub-canvas,
.scrub-still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.scrub-still { object-fit: cover; }

/* The merge. The last frame of the sequence is a flat overhead of the record on
   the platter; this element is that same record, cut out of that same frame, so
   at the handover it is pixel-identical to what the canvas has just painted.
   Fading it in is therefore invisible — what the reader sees is the record
   simply starting to turn. Position and diameter are computed in JS from the
   identical cover-fit maths the canvas draws with, so it lands correctly at any
   viewport rather than being pinned to one breakpoint. */
.scrub-disc {
  position: absolute;
  z-index: 1;
  /* base.css:29 sets img{max-width:100%}. This element is deliberately WIDER
     than its container on narrow screens — the frame is cover-fitted, so a
     portrait viewport crops the sides and the record runs past both edges.
     Without this the width was clamped to the stage while the height was not,
     giving 485x816 where it had to be square, and the merge came apart on
     phones while looking perfect on desktop. */
  max-width: none;
  opacity: 0;
  transition: opacity .28s linear;
  pointer-events: none;
  border-radius: 50%;
  will-change: transform;
  /* --k is the foreshortening, set per-frame in JS. The element is square and
     holds a de-projected circle; this squashes it back to the ellipse the
     camera sees. Rotation is applied INSIDE that, so the record turns in its
     own plane and the result is projected — the transform list reads
     right-to-left, so rotate happens first and scaleY second. */
  transform: scaleY(var(--k, 1));
}

.scrub.is-spun .scrub-disc {
  opacity: 1;
  /* 1.8s per revolution is 33 1/3 rpm, which is the speed this record would
     actually be turning at. */
  animation: scrub-spin 1.8s linear infinite;
}

@keyframes scrub-spin {
  from { transform: scaleY(var(--k, 1)) rotate(0deg); }
  to   { transform: scaleY(var(--k, 1)) rotate(360deg); }
}

/* A rotating photograph carries its own highlight around with it, which reads
   as the whole world spinning rather than the disc. This sheen sits still on
   top so the light stays put and only the record moves under it. */
.scrub-sheen {
  position: absolute;
  z-index: 1;
  opacity: 0;
  transition: opacity .28s linear;
  pointer-events: none;
  border-radius: 50%;
  /* Squashed to match the disc, but never rotated — that is the whole point. */
  transform: scaleY(var(--k, 1));
  background: linear-gradient(115deg,
    rgb(255 255 255 / .16) 0%,
    rgb(255 255 255 / .02) 26%,
    rgb(0 0 0 / .10) 52%,
    rgb(255 255 255 / .09) 74%,
    rgb(0 0 0 / .06) 100%);
  mix-blend-mode: soft-light;
}

.scrub.is-spun .scrub-sheen { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .scrub.is-spun .scrub-disc { animation: none; }
}

/* The still is the no-JS / pre-decode floor. It is only dropped once a real
   frame has actually been painted. */
.scrub.is-live .scrub-still { opacity: 0; }

/* Legibility scrim. Weighted to the left because that is where the type sits;
   the right two thirds of the photograph stay untouched.
   z-index is load-bearing: as ::after this is the stage's LAST child, so
   without it the scrim paints over the type and greys out the very copy it is
   there to make readable. Scrim sits at 1, everything textual at 2. */
.scrub-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(100deg,
    rgb(22 19 15 / .92) 0%,
    rgb(22 19 15 / .80) 26%,
    rgb(22 19 15 / .42) 52%,
    rgb(22 19 15 / .12) 100%);
  pointer-events: none;
}

.scrub-copy {
  z-index: 2;
  position: absolute;
  inset: 0;
  padding: clamp(1.4rem, 5vw, 5rem);
  display: grid;
  align-content: end;
  pointer-events: none;
}

.scrub-mark {
  z-index: 2;
  position: absolute;
  top: clamp(1.4rem, 5vw, 5rem);
  left: clamp(1.4rem, 5vw, 5rem);
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--dark-ink);
  /* .7 measured 4.45:1 against the brightest frame at 390px — 0.05 short of AA
     for 9.9px text. .8 clears it at 5.26:1 with 0/250 frames failing. */
  opacity: .8;
}

/* Beats are stacked in one grid cell and cross-faded, so the block never
   reflows the layout as the reader scrubs. */
.beat {
  grid-area: 1 / 1;
  max-width: 20ch;
  opacity: 0;
  transform: translateY(.6rem);
  transition: opacity .45s ease, transform .45s ease;
}

.beat.on {
  opacity: 1;
  transform: none;
}

.beat h1,
.beat h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.4rem, 7vw, 5.2rem);
  line-height: .94;
  letter-spacing: -.01em;
  color: var(--dark-ink);
  margin: 0 0 .7rem;
}

/* NOT recoloured. pages.css:37 records the coloured second line as a dated
   AI-site tell two reviewers already had removed; putting it back in a new file
   only moved it out of reach of that override. It also could not pass contrast:
   #c1272d is 3.17:1 on --dark and 3.60:1 on pure black, so it can never reach
   4.5:1 on a dark ground, and over the brighter frames of this footage it
   measured 1.00:1 — the word vanished completely. Emphasis comes from
   Redaction's ink grade, not from recolouring half a sentence. */
.beat em {
  font-style: normal;
  color: inherit;
}

.beat p {
  font-family: var(--font-body);
  font-size: clamp(.9rem, 1.6vw, 1.02rem);
  line-height: 1.5;
  color: var(--dark-ink);
  opacity: .8;
  margin: 0;
  max-width: 34ch;
}

/* No CTA in the header, and no eyebrow above the headings. Both were removed
   rather than restyled:
   - A bordered .beat-link next to .btn-primary one section below rebuilt the
     solid/ghost button pair doctrine 1 L31 bans, and put the same "Browse the
     crate" decision on screen twice within one viewport of itself. The hero
     below carries the CTA; this header is the opening statement, not a second
     ask.
   - A numbered mono eyebrow above all four beats is doctrine 1 L30 verbatim
     ("uppercase letter-spaced eyebrow above every heading"), and pages.css:27
     already calls .hero-kicker "The ONE eyebrow on the site". Four more is not
     one. Its accent red also could not pass: 9.9px text needs 4.5:1 and the red
     tops out at 3.60:1 on black, before the photograph is even considered. */

/* Scrub position, as a printed rule across the foot of the stage.
   The track is solid ink, not translucent paper: over the brighter frames a
   16%-paper track and the red fill were both washing out to about 1:1, and this
   is the only scrub-position feedback on the page. Red on --dark is 3.17:1,
   which clears the 3:1 that non-text indicators need. */
.scrub-progress {
  z-index: 2;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: var(--dark);
}

.scrub-progress span {
  display: block;
  height: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* Sits bottom-right, where the scrim is only ~.12 — so over the sand-coloured
   frames this measured 1.28:1 and raising its opacity to 1 still left it at
   1.56:1. That is a location problem, not an alpha problem, so it gets its own
   ink plate: 16.1:1 on every frame, and it reads as a printed stamp. */
.scrub-cue {
  z-index: 2;
  opacity: 0;
  transition: opacity .4s ease;
  position: absolute;
  right: clamp(1.4rem, 5vw, 5rem);
  bottom: clamp(1.6rem, 5vw, 5rem);
  padding: .42rem .7rem;
  background: var(--dark);
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--dark-ink);
}

.scrub.is-cued .scrub-cue { opacity: .55; }

/* Reduced motion: no runway, no travel, one frame, all copy visible at once.
   The stage must stop being a fixed-height sticky viewport here. Left sticky at
   100svh with overflow:hidden it CLIPS the stacked beats — the fourth one was
   cut off mid-word — so height goes auto and the copy returns to normal flow. */
.scrub.is-static .scrub-runway { height: auto; }
.scrub.is-static .scrub-stage {
  position: relative;
  height: auto;
  min-height: 100svh;
}
.scrub.is-static .scrub-cue { display: none; }
/* Absolutely positioned in the top-left corner, it lands on top of the first
   heading once the copy is in flow. It is decoration; drop it. */
.scrub.is-static .scrub-mark { display: none; }
.scrub.is-static .scrub-progress { display: none; }
.scrub.is-static .scrub-copy {
  position: relative;
  inset: auto;
  display: block;
  padding-block: clamp(3rem, 8vw, 6rem);
}
.scrub.is-static .beat {
  position: static;
  opacity: 1;
  transform: none;
}
.scrub.is-static .beat + .beat { margin-top: 2.2rem; }
.scrub.is-static .beat h1,
.scrub.is-static .beat h2 { font-size: clamp(1.8rem, 4vw, 2.6rem); }

@media (max-width: 700px) {
  .scrub-runway { height: 560svh; }
  /* Deliberately still bounded and still asymmetric. Full-width copy over a
     symmetric top-and-bottom gradient is the stock phone hero — the exact
     centered-column look the desktop layout is built to avoid, arriving on the
     device most people will actually use. The measure stays short and the
     gradient stays weighted to the corner the type sits in. */
  .beat { max-width: 20ch; }
  .beat p { max-width: 30ch; }
  .scrub-stage::after {
    background: linear-gradient(165deg,
      rgb(22 19 15 / .74) 0%,
      rgb(22 19 15 / .40) 38%,
      rgb(22 19 15 / .72) 72%,
      rgb(22 19 15 / .92) 100%);
  }
}
