/* ---------- EPIK / DATU — scroll-video zine (white theme) ---------- */

:root {
  --paper: #f7f5f0;      /* page background */
  --ink: #17151a;        /* primary text */
  --dim: rgba(23, 21, 26, 0.6);
  --dimmer: rgba(23, 21, 26, 0.38);
  --line: rgba(23, 21, 26, 0.22);
  --accent: #b8446a;     /* pink from the wireframe spirals */
  --ease: cubic-bezier(.22, .61, .36, 1);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  overflow-x: hidden;
}

body { min-height: 100%; }

/* ---------- Stage: fixed full-viewport frame ---------- */

.stage {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100dvh;
  overflow: hidden;
  background: var(--paper);
}

.stage-bg {
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 90% at 50% 30%, #ffffff 0%, #f2efe8 70%);
}

.zine-title {
  position: absolute;
  top: clamp(14px, 2.6vh, 24px);
  left: 50%;
  transform: translateX(-50%);
  font-size: clamp(11px, 1.2vw, 14px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink);
  z-index: 5;
  white-space: nowrap;
}

.scrub-video,
.hold-image {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* 64dvh (was 78): the under-zine stack now carries caption + seek row +
     zine credit, which needs ~62-77px below the video. At 78dvh that stack
     ran past the footer credit and off the viewport on 800-900px-tall
     laptops (measured by tools/qa/seek_layout.mjs). */
  height: 64dvh;
  width: auto;
  max-width: 92vw;
  object-fit: contain;
  filter: drop-shadow(0 24px 50px rgba(23, 21, 26, 0.28));
  border-radius: 2px;
  z-index: 2; /* above the floaters, which pass behind the zine */
}

.hold-image {
  opacity: 0;
  pointer-events: none;
  transition: none; /* opacity driven by JS per-frame */
}

/* Song + tagline pinned right under the zine video */

.now-playing {
  position: absolute;
  left: 50%;
  top: calc(50% + 32dvh + clamp(10px, 1.8vh, 20px));
  transform: translateX(-50%);
  text-align: center;
  z-index: 5;
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
  max-width: 90vw;
}

.now-playing.is-changing {
  opacity: 0;
  transform: translateX(-50%) translateY(4px);
}

.ftr-track-name {
  font-size: clamp(12px, 1.25vw, 15px);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 700;
  margin-bottom: 3px;
  white-space: normal; /* featuring credits can wrap */
}

.ftr-track-line {
  font-size: clamp(10px, 1vw, 12px);
  color: var(--dim);
  font-style: italic;
  letter-spacing: 0.01em;
}

/* ---------- Audio seek bar (under the zine, with the song caption) ------- */

.seek-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 7px;
}

.play-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  flex: none;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.7);
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s, transform 0.15s var(--ease);
}

.play-btn:hover {
  border-color: var(--ink);
  background: rgba(23, 21, 26, 0.05);
  transform: scale(1.08);
}

.play-btn svg { display: block; }
/* the rule above would otherwise defeat the hidden attribute, showing the
   play triangle and the pause bars at the same time */
.play-btn svg[hidden] { display: none; }

.seek-time {
  font-size: 9px;
  letter-spacing: 0.06em;
  color: var(--dimmer);
  font-variant-numeric: tabular-nums;
  min-width: 26px;
}

.seek-time:last-of-type { text-align: right; }

.seek-bar {
  -webkit-appearance: none;
  appearance: none;
  width: clamp(150px, 20vw, 240px);
  height: 3px;
  border-radius: 2px;
  /* elapsed portion inked, remainder faint (--seek-pct set from JS) */
  background: linear-gradient(
    to right,
    var(--ink) 0%,
    var(--ink) var(--seek-pct, 0%),
    var(--line) var(--seek-pct, 0%),
    var(--line) 100%
  );
  outline: none;
  cursor: pointer;
}

.seek-bar::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--ink);
  border: none;
  transition: transform 0.15s var(--ease);
}

.seek-bar:hover::-webkit-slider-thumb,
.seek-bar:active::-webkit-slider-thumb { transform: scale(1.25); }

.seek-bar::-moz-range-thumb {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--ink);
  border: none;
}

.zine-credit {
  margin-top: 7px;
  font-size: clamp(8px, 0.8vw, 10px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dimmer);
}

.vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(130% 100% at 50% 50%, transparent 55%, rgba(23, 21, 26, 0.08) 100%);
}

.grain {
  position: absolute;
  inset: -50%;
  width: 200%;
  height: 200%;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  animation: grainShift 8s steps(8) infinite;
}

@keyframes grainShift {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-2%, 1%); }
  50%  { transform: translate(1%, -2%); }
  75%  { transform: translate(-1%, -1%); }
  100% { transform: translate(0, 0); }
}

/* ---------- Floating vectors: 2 per side, always fully visible ----------
   All movement is scroll-driven (see UI.updateFloaters): top and bottom
   floaters move in opposite directions, crossing over the About columns and
   ending roughly in each other's starting positions. They sit behind the
   zine video (z-index) so on narrow screens they peek from the margins. */

.floaters { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
.floater {
  position: absolute;
  opacity: 0.9;
  will-change: transform;
}

.floater-left-a  { width: 15vw; max-width: 250px; top: 6%;  left: 2.5vw; }
.floater-left-b  { width: 12vw; max-width: 200px; bottom: 8%; left: 4vw; }
.floater-right-a { width: 12vw; max-width: 200px; top: 9%; right: 4vw; transform: scaleX(-1); }
.floater-right-b { width: 14vw; max-width: 235px; bottom: 7%; right: 2.5vw; }

/* ---------- Scroll instruction beside the zine ---------- */

.scroll-cue {
  position: absolute;
  top: 50%;
  left: calc(50% + 31dvh + 6px);
  transform: translateY(-50%);
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  color: var(--dim);
  animation: hintBob 2.4s ease-in-out infinite;
}

.scroll-cue-label {
  writing-mode: vertical-rl;
  font-size: clamp(9px, 0.9vw, 11px);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  white-space: nowrap;
}

/* ---------- About columns flanking the zine (desktop) ---------- */

.about-col {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: clamp(200px, 19vw, 320px);
  z-index: 4;
  font-size: clamp(10px, 0.82vw, 12.5px);
  line-height: 1.65;
  color: var(--ink);
}

.about-col p { margin: 0 0 1em; }

.about-col-left  { left: clamp(18px, 3.2vw, 48px); }
.about-col-right { right: clamp(18px, 3.2vw, 48px); text-align: right; }

/* ---------- Header ---------- */

.hdr {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: clamp(12px, 2.4vh, 22px) clamp(18px, 4vw, 40px);
  z-index: 6;
}

.hdr-logo {
  height: clamp(18px, 2.2vh, 26px);
  width: auto;
  opacity: 0.92;
}

/* ---------- Header controls (about / mute / volume / spatial) ---------- */

.hdr-controls {
  display: flex;
  align-items: center;
  gap: 8px;
}

.ctrl-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 34px;
  min-width: 34px;
  padding: 0 10px;
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s, opacity 0.2s;
}

.ctrl-btn:hover:not(:disabled) {
  border-color: var(--ink);
  background: rgba(23, 21, 26, 0.05);
}

.ctrl-btn:disabled {
  opacity: 0.35;
  cursor: default;
}

.ctrl-btn[aria-pressed="true"] {
  border-color: var(--accent);
  color: var(--accent);
}

.ctrl-icon { display: block; }
/* same [hidden] guard as .play-btn: without it the speaker-on and
   speaker-off glyphs both render inside the mute pill */
.ctrl-icon[hidden] { display: none; }

.ctrl-btn-text {
  min-width: 0;
  padding: 0 14px;
}

.ctrl-btn-label {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  white-space: nowrap;
}

a.ctrl-btn { text-decoration: none; }

.spatial-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
}

.spatial-hint {
  font-size: 8px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dimmer);
  white-space: nowrap;
}

.vol-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 88px;
  height: 3px;
  border-radius: 2px;
  background: var(--line);
  outline: none;
  cursor: pointer;
}

.vol-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--ink);
  border: none;
}

.vol-slider::-moz-range-thumb {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--ink);
  border: none;
}

/* ---------- Music side rail ---------- */

.music-rail {
  position: absolute;
  left: clamp(14px, 3vw, 32px);
  bottom: clamp(66px, 10vh, 96px);
  z-index: 6;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.rail-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.75);
  color: var(--ink);
  opacity: 0.9;
  text-decoration: none;
  transition: border-color 0.2s, background 0.2s, opacity 0.2s, transform 0.2s;
}

.rail-btn:hover {
  opacity: 1;
  border-color: var(--ink);
  background: rgba(23, 21, 26, 0.05);
  transform: scale(1.06);
}

/* ---------- Footer (page count + hint only) ---------- */

.ftr {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  padding: clamp(14px, 3vh, 26px) clamp(18px, 4vw, 40px);
  z-index: 5;
}

.ftr-count {
  font-size: clamp(11px, 1.1vw, 13px);
  letter-spacing: 0.08em;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  min-width: 60px;
}

.ftr-credit {
  flex: 1;
  text-align: center;
  font-size: clamp(9px, 0.85vw, 11px);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dimmer);
}

.ftr-hint {
  font-size: clamp(10px, 1vw, 12px);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--dimmer);
  min-width: 60px;
  text-align: right;
  transition: opacity 0.8s var(--ease);
  animation: hintBob 2.4s ease-in-out infinite;
}

.ftr-hint.is-hidden { opacity: 0; }

@keyframes hintBob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}

/* ---------- Scroll guide (checkered circle) ---------- */

.guide {
  position: absolute;
  right: clamp(18px, 4vw, 40px);
  bottom: clamp(66px, 11vh, 110px);
  z-index: 6;
  color: var(--ink);
  animation: guideFloat 5s ease-in-out infinite;
}

@keyframes guideFloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}

.guide-ring-track {
  fill: none;
  stroke: rgba(23, 21, 26, 0.15);
  stroke-width: 2.4;
}

.guide-ring-fill {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-dasharray: 289;
  stroke-dashoffset: 289;
  transform-origin: 50px 50px;
  transform: rotate(-90deg);
  transition: stroke-dashoffset 0.05s linear;
}

.guide-checker {
  transform-origin: 50px 50px;
  will-change: transform;
}

.guide.is-pulsing .guide-checker {
  animation: guidePulse 0.5s var(--ease);
}

@keyframes guidePulse {
  0% { transform: scale(1); }
  40% { transform: scale(1.18); }
  100% { transform: scale(1); }
}

/* ---------- About overlay ---------- */

.about-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(247, 245, 240, 0.96);
  overflow-y: auto;
  padding: 24px;
}

/* the display:flex above would otherwise defeat the hidden attribute */
.about-overlay[hidden] { display: none; }

.about-panel {
  position: relative;
  max-width: 640px;
  margin: auto;
  padding: clamp(24px, 5vh, 48px) clamp(20px, 4vw, 40px);
  font-size: clamp(13px, 1.4vw, 15px);
  line-height: 1.75;
  color: var(--ink);
}

.about-panel p { margin: 0 0 1.1em; }

.about-heading {
  font-size: clamp(20px, 3vw, 28px);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 0 0 1em;
}

.about-close {
  position: absolute;
  top: 6px;
  right: 8px;
  width: 40px;
  height: 40px;
  border: none;
  background: none;
  color: var(--ink);
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.7;
}

.about-close:hover { opacity: 1; }

/* ---------- End-of-zine bar ---------- */

.end-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 7;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(14px, 3vw, 32px);
  padding: 14px clamp(16px, 4vw, 40px) 16px;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(6px);
  border-top: 1px solid var(--line);
  transform: translateY(105%);
  transition: transform 0.5s var(--ease);
}

.end-bar.is-visible { transform: translateY(0); }

.end-bar-listen {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: clamp(9px, 0.9vw, 11px);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--dim);
  white-space: nowrap;
}

/* Real listen links inside the bar: js/ui.js (upgradeEndBarListen) clones
   the music rail's Spotify / Apple anchors in after the label, replacing
   the old decorative arrow that pointed at the rail. On mobile the rail
   fades out while the bar is up, so these are the finale's listen path.
   Descendant selector outranks the mobile `.rail-btn { width: 32px }`. */
.end-bar-listen .rail-btn {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
}
.end-bar-listen .rail-btn svg { width: 17px; height: 17px; }

.end-bar-title {
  font-size: clamp(10px, 1vw, 12px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 700;
}

.end-bar-links {
  display: flex;
  gap: 8px;
}

/* While the end bar is up, pulse the music rail (desktop only in effect:
   the mobile rules injected by js/ui.js hide the rail at the end state,
   and the bar's own cloned listen anchors take over there) */
.stage.is-at-end .music-rail .rail-btn {
  animation: railPulse 1.6s ease-in-out infinite;
  border-color: var(--accent);
  color: var(--accent);
}

@keyframes railPulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.12); }
}

/* ---------- Scroll driver ---------- */

.spacer {
  position: relative;
  width: 1px;
  pointer-events: none;
}

/* ---------- Gate / preloader ---------- */

.gate {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--paper);
  transition: opacity 0.9s var(--ease), visibility 0.9s;
}

.gate.is-open {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.gate-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
  padding: 24px;
}

.gate-logo {
  height: 34px;
  width: auto;
  margin-bottom: 6px;
  opacity: 0.92;
}

.gate-title {
  font-size: clamp(28px, 6vw, 44px);
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--ink);
}

.gate-sub {
  font-size: clamp(11px, 1.4vw, 13px);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--dim);
  margin-bottom: 22px;
}

.gate-bar-track {
  width: min(280px, 60vw);
  height: 1px;
  background: var(--line);
  margin-bottom: 10px;
}

.gate-bar-fill {
  height: 100%;
  width: 0%;
  background: var(--ink);
  transition: width 0.15s linear;
}

.gate-pct {
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--dimmer);
  font-variant-numeric: tabular-nums;
  margin-bottom: 26px;
}

.gate-enter {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  background: transparent;
  border: 1px solid rgba(23, 21, 26, 0.45);
  color: var(--ink);
  padding: 16px 46px;
  cursor: pointer;
  border-radius: 999px;
  transition: border-color 0.3s, background 0.3s, opacity 0.3s;
}

.gate-enter:disabled {
  opacity: 0.25;
  cursor: default;
}

.gate-enter:not(:disabled):hover {
  border-color: var(--ink);
  background: rgba(23, 21, 26, 0.05);
}

.gate-enter-label {
  font-size: 13px;
  letter-spacing: 0.24em;
  font-weight: 600;
}

.gate-enter-sub {
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}

.gate-muted {
  margin-top: 16px;
  background: none;
  border: none;
  color: var(--dimmer);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.gate-muted:disabled { opacity: 0.25; cursor: default; }

/* ---------- Landing feedback ----------
   One-shot glow pulse when the scroll settles on a page still. Applied to
   the video element only (the hold image shares the same box, so the glow
   halos the zine regardless of which layer is on top). Accent-tinted, low
   alpha over the white theme, 600ms ease-out. */

@keyframes land-glow {
  0%   { box-shadow: 0 0 0 0 rgba(184, 68, 106, 0); }
  22%  { box-shadow: 0 0 22px 2px rgba(184, 68, 106, 0.26),
                     0 0 68px 16px rgba(184, 68, 106, 0.11); }
  100% { box-shadow: 0 0 0 0 rgba(184, 68, 106, 0); }
}

.stage.is-landed .scrub-video {
  animation: land-glow 0.6s ease-out;
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .grain, .guide, .ftr-hint, .floater { animation: none; }
  .now-playing { transition: none; }
  .stage.is-landed .scrub-video { animation: none; }
}

/* Short desktop/laptop viewports: give the under-zine stack more room so the
   zine credit keeps clear of the footer credit (1280x800 page 1, with the
   longest track name, was down to 3.6px — measured by tools/qa/seek_layout). */
@media (min-width: 701px) and (max-height: 860px) {
  .scrub-video, .hold-image { height: 60dvh; }
  .now-playing { top: calc(50% + 30dvh + clamp(10px, 1.8vh, 20px)); }
}

/* ---------- Small screens ---------- */

@media (max-width: 700px) {
  /* title drops below the header controls row so nothing overlaps it */
  .zine-title {
    top: 54px;
    font-size: 10px;
    letter-spacing: 0.14em;
    white-space: normal;
    text-align: center;
    width: 90vw;
  }
  /* Video gives back 6dvh vs the pre-seek-bar layout: the caption block now
     carries the seek row + zine credit under the song lines, so the stack
     below the zine needs the height. */
  .scrub-video, .hold-image { height: 60dvh; max-width: 94vw; top: 51%; }
  .now-playing { top: calc(51% + 30dvh + 6px); max-width: 94vw; }
  .ftr-track-name { font-size: 10px; }
  .ftr-track-line { font-size: 9px; }
  .seek-row { margin-top: 5px; gap: 6px; }
  .play-btn { width: 22px; height: 22px; }
  .seek-bar { width: 40vw; }
  .seek-time { font-size: 8px; min-width: 22px; }
  .zine-credit { margin-top: 5px; font-size: 8px; letter-spacing: 0.1em; }
  .ftr-credit { font-size: 8px; letter-spacing: 0.08em; }
  /* Footer stack breathing room: with 3-line track names (pages 1-3) the
     caption box bottom (y=801.3 at 390x844) overlapped the credit line's
     top (798.7) by 2.6px, and pages 7-8 left only ~8px of air. Dropping
     the footer's bottom padding from clamp(...)=25.3px to 14px lowers the
     credit ~11px, giving >=8.7px clearance on every page while its bottom
     edge (830) keeps 14px off the viewport edge. */
  .ftr { padding-bottom: 14px; }
  /* Guide tucks into the bottom-right corner, smaller: the taller under-zine
     stack (caption + seek row + credit) reaches ~y780 at 320w, and the old
     60px ring at bottom:76px started at y708 -- a measured 21.6px overlap. */
  .guide { bottom: 8px; }
  .guide svg { width: 44px; height: 44px; }
  .music-rail { left: 10px; bottom: 60px; gap: 8px; }
  .rail-btn { width: 32px; height: 32px; }

  /* keep every control usable on mobile: shorter labels, tighter pills */
  .hdr { padding: 10px 12px; }
  .hdr-controls { gap: 4px; }
  .ctrl-btn { height: 30px; padding: 0 7px; }
  .ctrl-btn-label { font-size: 8px; letter-spacing: 0.08em; }
  .vol-slider { width: 48px; }
  .spatial-hint { display: none; } /* no room; the button label carries it */

  /* floaters stay visible on mobile: smaller, hugging the edges; they sit
     behind the zine video (occluded, never viewport-cropped).
     floater-left-a (the pyramid) is additionally capped at 60px wide so its
     bottom edge (top 8% + 60px * 0.778 aspect = ~114px at 844h) stays above
     the top scroll cue row (~119px incl. 3px bob) at every mobile width --
     at 26vw it reached x=86.4/y=132 at 320w and sat under the cue arrow. */
  .floater-left-a  { width: 26vw; max-width: 60px; top: 8%;  left: 1vw; }
  .floater-left-b  { width: 22vw; bottom: 10%; left: 1vw; }
  .floater-right-a { width: 22vw; top: 10%; right: 1vw; }
  .floater-right-b { width: 24vw; bottom: 8%; right: 1vw; }

  /* About columns are desktop furniture */
  .about-col { display: none; }

  /* Scroll cue moves to the TOP on mobile: horizontally laid out, centered
     in the gap between the zine title (top 84px via the ui.js layout-fix
     sheet, ~12px tall) and the video's top edge (53% - 34dvh). The .stage
     prefix outranks the injected ui.js sheet's `.scroll-cue { left: ... }`
     bottom-position override. Centering uses left/right auto margins (not
     translateX) because the hintBob animation replaces `transform`. */
  .stage .scroll-cue {
    top: calc((96px + 51% - 30dvh) / 2 - 14px);
    bottom: auto;
    left: 0;
    right: 0;
    transform: none;
    justify-content: center;
    align-items: center;
    flex-direction: row;
    gap: 8px;
  }
  /* Arrow stays VERTICAL (natural up/down orientation) beside the label;
     shortened so the taller row still clears title above and video below. */
  .scroll-cue svg {
    width: 11px;
    height: 28px;
  }
  .scroll-cue-label {
    writing-mode: horizontal-tb;
    font-size: 9px;
    letter-spacing: 0.18em;
  }
}

/* Very narrow phones (<=360px): the floater-free center band between the
   left pyramid and the right cylinder wireframe is only ~160px at 320w
   (right floater starts at 320 - 1vw - 22vw = x246.4). Slim the cue --
   smaller arrow, tighter label -- so its content (~150px) centers inside
   that band with >=10px of air on each side. */
@media (max-width: 360px) {
  .stage .scroll-cue { gap: 6px; }
  .scroll-cue svg { width: 9px; height: 24px; }
  .scroll-cue-label { font-size: 8px; letter-spacing: 0.12em; }
}

/* Short phones (e.g. 320x568): the title->video gap shrinks to ~12px --
   too small to hold the cue with designed spacing (round-2 QA measured
   1.0px / 0.9px clearances). Hide it; the footer's SCROLL hint carries
   the same instruction on these screens. */
/* Narrow phones (<=360px): long track names wrap to more lines, so the
   under-zine stack is taller and reached the guide ring (measured 7.8px
   overlap at 320x844 on page 1). Give the stack another 2dvh of room. */
@media (max-width: 360px) {
  .scrub-video, .hold-image { height: 56dvh; }
  .now-playing { top: calc(51% + 28dvh + 6px); }
  .stage .scroll-cue { top: calc((96px + 51% - 28dvh) / 2 - 14px); }
}

@media (max-width: 700px) and (max-height: 620px) {
  .stage .scroll-cue { display: none; }
}
