/* ============================================
   ICEMAN — STYLES
   ============================================ */

:root {
  --black:        #000000;
  --black-soft:   #050608;
  --ice-deep:     #0a1f2e;
  --ice-mid:      #1a4a6b;
  --ice-bright:   #4ba8d4;
  --ice-glow:     #7ec8e3;
  --ice-pale:     #c4e3f0;
  --ice-white:    #e8f4fa;
  --frost-dim:    #5a7a8a;
  --crack:        rgba(126, 200, 227, 0.35);
  --ease:         cubic-bezier(0.2, 0.8, 0.2, 1);
  /* One source of truth for the custom icicle cursor. The -clickable
     variant only differs in its system fallback (pointer vs auto). */
  --ice-cursor:           url("Media/Pointer/IceCursorHov-sm.png") 2 2, auto;
  --ice-cursor-clickable: url("Media/Pointer/IceCursorHov-sm.png") 2 2, pointer;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* ============================================
   CUSTOM CURSOR — frosted ice arrow
   Single cursor everywhere. The * rule covers all elements; the
   second rule explicitly re-asserts the cursor on interactive
   elements so Firefox's UA `:any-link { cursor: pointer }` (and any
   future button-default cursor) can't override it. The fallback is
   `pointer` instead of `auto` only because the system cursor shown
   if the PNG ever fails to load is more semantically right.
   ============================================ */
*                                       { cursor: var(--ice-cursor); }
a, button, summary, label,
[role="button"], .clickable,
.frost-card, .brand-mark                { cursor: var(--ice-cursor-clickable); }

/* Decorative copy: kill text-selection so click-drag never flashes
   the I-beam over the icicle. None of these are useful to copy. */
.hero-blurb-title, .hero-blurb-body,
.hero-caption,
.hero-quote-body, .hero-quote-attribution,
.frost-text,
.tracklist, .tracklist *,
.countdown-headline, .countdown-date,
.count-num, .count-label {
  user-select: none;
  -webkit-user-select: none;
}

/* Decorative imagery & video: kill native drag-export so the OS
   drag-ghost cursor can't take over mid-hover. */
.hero-album-cover,
.hero-album-tile img,
.hero-quote-img,
.scrub-video {
  -webkit-user-drag: none;
  user-select: none;
}

html { scroll-behavior: smooth; }

body {
  background: var(--black);
  color: var(--ice-white);
  font-family: 'JetBrains Mono', monospace;
  font-weight: 300;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* Subtle grain over everything */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  opacity: 0.04;
  pointer-events: none;
  z-index: 999;
  mix-blend-mode: overlay;
}

/* ============================================
   PERSISTENT BRAND MARK
   ============================================ */
.brand-mark {
  position: fixed;
  top: 24px;
  right: 32px;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  font-size: 16px;
  letter-spacing: 0.15em;
  color: var(--ice-white);
  z-index: 100;
  /* mix-blend-mode lives on the child .brand-mark-text now. Putting it
     on the cursor hit-area causes Chromium/WebKit to composite the
     cursor through the blend pipeline, which makes the icicle invisible
     when its pixel values land close to the page-content underneath. */
  cursor: var(--ice-cursor-clickable);
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  outline: none;
  transition:
    letter-spacing 0.4s var(--ease),
    color 0.3s var(--ease);
}

/* Re-assert the icicle in every interaction state. Without these,
   clicking the brand-mark briefly reverts to the OS cursor — the
   smooth-scroll-to-top fired by the click handler triggers a repaint
   in which some browsers drop the variable-resolved cursor. */
.brand-mark:hover,
.brand-mark:active,
.brand-mark:focus,
.brand-mark:focus-visible { cursor: var(--ice-cursor-clickable); outline: none; }

.brand-mark:hover { letter-spacing: 0.22em; }

/* The text itself owns the difference-blend so the wordmark still
   auto-inverts against bright frames without disturbing the parent's
   cursor rendering. inline-block + pointer-events:none keep the parent
   div as the sole cursor target. */
.brand-mark-text {
  display: inline-block;
  mix-blend-mode: difference;
  pointer-events: none;
}

/* ============================================
   SCENE BASE
   ============================================ */
.scene {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 720px;
  overflow: hidden;
}

/* ============================================
   SCRUB SECTIONS (Hero + Countdown)
   250vh wrapper, sticky 100vh inner pin holds
   <video> + <canvas>. Scroll progress 0→1 maps
   to a JPG frame sequence drawn on the canvas.
   ============================================ */
.scrub-section {
  position: relative;
  width: 100%;
  height: 250vh;
  background: var(--black);
}

.scrub-pin {
  position: sticky;
  top: 0;
  width: 100%;
  height: 100vh;
  min-height: 720px;
  overflow: hidden;
}

.scrub-video,
.scrub-idle,
.scrub-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.scrub-video,
.scrub-idle {
  object-fit: cover;
  object-position: center;
  z-index: 1;
}

/* Two stacked .scrub-video clones crossfade at loop boundary.
   Both stay at full opacity so black never bleeds through.
   On swap, the incoming clone is snapped to opacity 0 and fades up
   on top while the outgoing clone holds at 1 underneath. */
.scrub-video {
  opacity: 1;
  transition: opacity 1s linear;
}

.scrub-video.is-active { z-index: 2; }

.scrub-canvas {
  z-index: 2;
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
}

.scrub-section.scrubbing .scrub-canvas { opacity: 1; }

/* ============================================
   SCENE 1 — HERO
   ============================================ */
/* Hero scrubs through three clips (4s + 5.04s + 6.04s = 15.08s).
   At 37.5vh/s auto-scroll → 100vh pin + 15.08 × 37.5vh ≈ 666vh. */
.scene-hero { background: var(--black); height: 666vh; }

.hero-glow {
  position: absolute;
  width: 60vw;
  height: 60vw;
  border-radius: 50%;
  filter: blur(120px);
  pointer-events: none;
  opacity: 0.35;
  z-index: 3;
  mix-blend-mode: screen;
}

.hero-glow-1 {
  background: radial-gradient(circle, rgba(75, 168, 212, 0.3) 0%, transparent 70%);
  top: -20vw;
  left: -20vw;
}

.hero-glow-2 {
  background: radial-gradient(circle, rgba(126, 200, 227, 0.2) 0%, transparent 70%);
  bottom: -20vw;
  right: -20vw;
}

/* ============================================
   FROSTED GLASS BUTTON (bottom of hero)
   ============================================ */
.frost-card {
  position: absolute;
  z-index: 6;
  left: 50%;
  bottom: 6vh;
  transform: translateX(-50%);
  width: auto;
  padding: 12px 28px;
  text-align: center;
  font: inherit;
  background: rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 2px;
  box-shadow:
    0 0 0 1px rgba(126, 200, 227, 0.08) inset,
    0 20px 60px rgba(0, 0, 0, 0.45),
    0 0 60px rgba(126, 200, 227, 0.12);
  color: #ffffff;
  overflow: hidden;
  opacity: var(--btn-opacity, 1);
  transition:
    transform 0.5s var(--ease),
    background 0.5s var(--ease),
    border-color 0.5s var(--ease),
    box-shadow 0.5s var(--ease),
    opacity 0.15s linear;
}

/* shimmer sweep on hover */
.frost-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 30%,
    rgba(255, 255, 255, 0.18) 50%,
    transparent 70%
  );
  transform: translateX(-110%);
  transition: transform 0.9s var(--ease);
  pointer-events: none;
}

.frost-card:hover,
.frost-card:focus-visible {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.35);
  transform: translateX(-50%) translateY(-3px);
  box-shadow:
    0 0 0 1px rgba(126, 200, 227, 0.18) inset,
    0 28px 80px rgba(0, 0, 0, 0.55),
    0 0 90px rgba(126, 200, 227, 0.28);
  outline: none;
}

.frost-card:hover::before,
.frost-card:focus-visible::before { transform: translateX(110%); }

.frost-card:hover .frost-text,
.frost-card:focus-visible .frost-text { letter-spacing: 0.7em; }

.frost-card:active { transform: translateX(-50%) translateY(-1px); }

.frost-card.is-armed {
  background: rgba(126, 200, 227, 0.18);
  border-color: rgba(126, 200, 227, 0.5);
  box-shadow:
    0 0 0 1px rgba(126, 200, 227, 0.35) inset,
    0 28px 80px rgba(0, 0, 0, 0.55),
    0 0 120px rgba(126, 200, 227, 0.45);
}

.frost-text {
  display: block;
  position: relative;
  font-family: 'JetBrains Mono', monospace;
  font-weight: 300;
  font-size: clamp(11px, 1vw, 13px);
  letter-spacing: 0.4em;
  text-transform: lowercase;
  color: #ffffff;
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.35);
  transition: letter-spacing 0.5s var(--ease);
}

@media (max-width: 900px) {
  .frost-card { padding: 10px 22px; bottom: 5vh; }
  .frost-text { font-size: 11px; letter-spacing: 0.3em; }
  .frost-card:hover .frost-text,
  .frost-card:focus-visible .frost-text { letter-spacing: 0.4em; }
}

/* ============================================
   HERO BLURB — frosted intro pair on the idle (FrozeCitySill) frame
   ============================================ */
.hero-blurb {
  position: absolute;
  z-index: 5;
  left: clamp(20px, 4.2vw, 64px);
  top: clamp(72px, 12vh, 140px);
  width: min(38vw, 470px);
  display: flex;
  flex-direction: column;
  gap: 14px;
  opacity: var(--blurb-opacity, 1);
  transition: opacity 0.2s linear;
  pointer-events: none;
}

.hero-blurb-title,
.hero-blurb-body {
  margin: 0;
  background: rgba(8, 14, 22, 0.55);
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  border: 1px solid rgba(126, 200, 227, 0.22);
  border-radius: 2px;
  box-shadow:
    0 0 0 1px rgba(126, 200, 227, 0.1) inset,
    0 20px 60px rgba(0, 0, 0, 0.55),
    0 0 60px rgba(126, 200, 227, 0.12);
  color: #ffffff;
}

.hero-blurb-title {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 600;
  font-size: clamp(22px, 2.2vw, 34px);
  letter-spacing: 0.05em;
  text-align: center;
  padding: 14px 24px;
  width: fit-content;
  align-self: flex-start;
}

.hero-blurb-body {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 500;
  font-size: clamp(15px, 1.25vw, 20px);
  line-height: 1.5;
  letter-spacing: 0.015em;
  padding: 22px 28px;
}

@media (max-width: 900px) {
  .hero-blurb { width: min(70vw, 360px); }
}

/* ============================================
   HERO CAPTION — fades in/out over the IMR2B clip
   ============================================ */
.hero-caption {
  position: absolute;
  z-index: 5;
  left: clamp(32px, 8vw, 120px);
  top: 50%;
  transform: translateY(-50%);
  margin: 0;
  max-width: min(40vw, 520px);
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 300;
  font-size: clamp(20px, 2.4vw, 32px);
  letter-spacing: 0.08em;
  color: var(--ice-white);
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.6);
  opacity: var(--caption-opacity, 0);
  transition: opacity 0.15s linear;
  pointer-events: none;
}

.hero-caption sup { font-size: 0.6em; vertical-align: super; }

/* Frosted OVO pull-quote on the right of the running frame.
   Visible briefly during the EnterIceMan tail, then clears before the
   albums grid takes over the same right-side slot. */
.hero-quote {
  position: absolute;
  z-index: 5;
  right: clamp(24px, 6vw, 100px);
  top: 50%;
  transform: translateY(-50%);
  width: min(38vw, 460px);
  margin: 0;
  padding: 32px 36px;
  background: rgba(8, 14, 22, 0.55);
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  border: 1px solid rgba(126, 200, 227, 0.22);
  border-radius: 2px;
  box-shadow:
    0 0 0 1px rgba(126, 200, 227, 0.1) inset,
    0 20px 60px rgba(0, 0, 0, 0.55),
    0 0 60px rgba(126, 200, 227, 0.12);
  color: #ffffff;
  opacity: var(--ovo-opacity, 0);
  transform-origin: center center;
  transition:
    opacity 0.15s linear,
    transform 0.5s var(--ease),
    box-shadow 0.5s var(--ease);
  pointer-events: none;
  /* Keep text crisp through the hover rotate. Without these, the
     compositor rasterizes the box at its current size and then rotates
     the bitmap, which softens the text. preserve-3d + backface-visibility
     promote the card to its own GPU layer; will-change tells the browser
     to keep that layer hot rather than re-creating it on hover. */
  transform-style: preserve-3d;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  will-change: transform;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Hover/tilt only fires while the quote is actually on screen,
   otherwise the invisible card would block hover on whatever's behind. */
#scene-hero.ovo-active .hero-quote { pointer-events: auto; }

.hero-quote:hover {
  /* Rotate only — no scale. Scaling a rasterized layer resamples the
     text, which is the main reason the body copy was looking soft on
     hover. The rotate keeps the playful tilt; the box-shadow lift
     gives the depth that scale(1.02) was contributing. */
  transform: translateY(-50%) rotate(-1.5deg);
  box-shadow:
    0 0 0 1px rgba(126, 200, 227, 0.2) inset,
    0 28px 70px rgba(0, 0, 0, 0.6),
    0 0 80px rgba(126, 200, 227, 0.2);
}

.hero-quote-img {
  display: block;
  width: 100%;
  height: auto;
  margin: 0 0 22px 0;
  border-radius: 2px;
  box-shadow:
    0 0 0 1px rgba(126, 200, 227, 0.18),
    0 12px 36px rgba(0, 0, 0, 0.5);
}

.hero-quote-body {
  margin: 0 0 18px 0;
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 500;
  font-size: clamp(16px, 1.4vw, 22px);
  line-height: 1.5;
  letter-spacing: 0.01em;
  text-align: center;
}

.hero-quote-attribution {
  margin: 0;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 400;
  font-size: clamp(13px, 1vw, 16px);
  letter-spacing: 0.12em;
  text-align: center;
  color: var(--ice-pale);
}

/* Mobile: card moves to dead-center and goes nearly full-width so the
   image gets room to breathe and the quote text isn't cramped. */
@media (max-width: 900px) {
  .hero-quote {
    right: auto;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: min(94vw, 500px);
    padding: 22px 22px 20px;
  }
  /* No tilt on touch — keeps the centered transform stable. */
  .hero-quote:hover { transform: translate(-50%, -50%); }
  .hero-quote-img { margin-bottom: 18px; }
  .hero-quote-body { font-size: clamp(15px, 4.2vw, 19px); margin-bottom: 14px; }
  .hero-quote-attribution { font-size: 12px; letter-spacing: 0.1em; }
}

/* Discography grid on the right of the running frame.
   Whole rig fades in/out via --quote-opacity (parent), then each tile
   does its own quick stagger via .albums-in on #scene-hero. */
.hero-albums {
  position: absolute;
  z-index: 5;
  right: clamp(24px, 6vw, 100px);
  top: 50%;
  transform: translateY(-50%);
  width: min(38vw, 460px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(10px, 1vw, 16px);
  opacity: var(--quote-opacity, 0);
  transition: opacity 0.15s linear;
  pointer-events: none;
  /* Shared perspective so child rotateX/rotateY tilts feel consistent. */
  perspective: 900px;
}

.hero-album-tile {
  display: block;
  aspect-ratio: 1 / 1;
  border-radius: 2px;
  overflow: hidden;
  /* Tiles only become clickable/hoverable once .albums-in fires —
     before that the OVO quote owns this slot. */
  pointer-events: none;
  /* Resting state: per-tile stagger releases on .albums-in.
     60ms keeps it "quick" (10 tiles ≈ 600ms full sweep). */
  opacity: 0;
  transform: translateY(8px) scale(0.96);
  transition:
    opacity 0.4s var(--ease),
    transform 0.5s var(--ease),
    box-shadow 0.4s var(--ease);
  transition-delay: calc(var(--i, 0) * 0.06s);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}

#scene-hero.albums-in .hero-album-tile {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

.hero-album-tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.5s var(--ease);
}

.hero-album-tile:hover {
  box-shadow:
    0 14px 36px rgba(0, 0, 0, 0.6),
    0 0 40px rgba(126, 200, 227, 0.25);
}

/* While the cursor is over the tile, JS sets an inline transform every
   mousemove. Override the .hero-album-tile transition so tracking feels
   instant; on mouseleave .is-tilting is removed and the default 0.5s
   transition glides the tile back to its resting state. */
.hero-album-tile.is-tilting {
  transition:
    transform 0.08s linear,
    box-shadow 0.4s var(--ease);
}

.hero-album-tile:focus-visible {
  outline: 2px solid var(--ice-bright);
  outline-offset: 2px;
}

/* 10 tiles in a 3-col grid leaves a lone tile on row 4 — center it
   in the middle column at every width. */
.hero-album-tile:nth-child(10) { grid-column: 2; }

/* Mobile: caption goes top-center, grid goes bottom-center so the
   running figure stays visible between them. */
@media (max-width: 900px) {
  .hero-caption {
    left: 50%;
    top: clamp(86px, 14vh, 140px);
    transform: translateX(-50%);
    max-width: min(86vw, 420px);
    text-align: center;
    font-size: clamp(16px, 4.5vw, 22px);
  }
  .hero-albums {
    right: auto;
    left: 50%;
    top: auto;
    bottom: clamp(96px, 14vh, 140px);
    transform: translateX(-50%);
    width: min(92vw, 360px);
    gap: 8px;
  }
}

/* ============================================
   BLACK BUFFER + STATIC ALBUM COVER
   .hero-blackout fades in over the AlbumFrost final frame so there's no
   overlap, then the album cover (left) and tracklist (right) slide in
   over the plain-black hold.
   ============================================ */
.hero-blackout {
  position: absolute;
  inset: 0;
  background: var(--black);
  z-index: 4;
  opacity: var(--blackout-opacity, 0);
  transition: opacity 0.15s linear;
  pointer-events: none;
}

.hero-album-cover {
  position: absolute;
  left: 0;
  top: 0;
  width: 50%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: 5;
  /* Resting state: nudged off-frame, slightly scaled up, blurred and
     desaturated. The .tracks-in class on #scene-hero releases all of
     these in one luxurious motion. */
  opacity: 0;
  transform: translateX(-9%) scale(1.08);
  filter: blur(16px) saturate(0.5) brightness(0.85);
  transition:
    opacity 1.0s cubic-bezier(0.2, 0.8, 0.2, 1),
    transform 1.2s cubic-bezier(0.2, 0.8, 0.2, 1),
    filter 1.0s cubic-bezier(0.2, 0.8, 0.2, 1);
  pointer-events: none;
}

#scene-hero.tracks-in .hero-album-cover {
  opacity: 1;
  transform: translateX(0) scale(1);
  filter: blur(0) saturate(1) brightness(1);
}

@media (max-width: 900px) {
  .hero-album-cover { width: 100%; height: 50%; top: 0; }
}

/* ============================================
   TRACKLIST OVERLAY (right side, staggered reveal)
   The parent is just a positioning container — each .track inside fades,
   slides and unblurs one after the other, keyed off the .tracks-in class
   on #scene-hero plus a per-track delay derived from --i (set in JS).
   ============================================ */
.hero-tracklist {
  position: absolute;
  z-index: 7;
  top: 0;
  right: 0;
  width: 50%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.hero-tracklist .tracklist {
  /* Stays unclickable until the end-of-hero reveal so it can't intercept
     clicks on overlapping elements (e.g. the OVO quote carousel arrows). */
  pointer-events: none;
  width: 100%;
  align-self: auto;
}

#scene-hero.tracks-in .hero-tracklist .tracklist {
  pointer-events: auto;
}

#scene-hero .hero-tracklist .track {
  opacity: 0;
  transform: translateX(36px);
  filter: blur(8px);
  transition:
    opacity 0.7s cubic-bezier(0.2, 0.8, 0.2, 1),
    transform 0.8s cubic-bezier(0.2, 0.8, 0.2, 1),
    filter 0.7s cubic-bezier(0.2, 0.8, 0.2, 1);
  /* --i is set by script.js (0..N-1) */
  transition-delay: calc(var(--i, 0) * 0.13s);
}

#scene-hero.tracks-in .hero-tracklist .track {
  opacity: 1;
  transform: translateX(0);
  filter: blur(0);
}

@media (max-width: 900px) {
  .hero-tracklist {
    width: 100%;
    top: 50%;       /* sit below the cover */
    height: 50%;
    align-items: flex-start;
    justify-content: stretch;
    overflow: hidden;
  }
  .hero-tracklist .tracklist {
    padding: 14px 22px 10px;
    gap: 10px;
    width: 100%;
  }
  #scene-hero .hero-tracklist .track {
    grid-template-columns: 32px 1fr;
    gap: 14px;
    padding: 0 4px 8px 4px;
  }
  #scene-hero .hero-tracklist .track-num {
    font-size: 13px;
    padding-top: 2px;
  }
  #scene-hero .hero-tracklist .track-title {
    font-size: 14px;
    margin-bottom: 4px;
  }
  #scene-hero .hero-tracklist .track-title .feat {
    display: block;
    margin-left: 0;
    margin-top: 2px;
    font-size: 0.85em;
  }
  #scene-hero .hero-tracklist .track-status {
    font-size: 9px;
    letter-spacing: 0.3em;
  }
}

/* ============================================
   TRACKLIST
   ============================================ */
.tracklist {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 36px;
  align-self: center;
  padding: 64px clamp(32px, 5vw, 80px);
}

.track {
  display: grid;
  grid-template-columns: 60px 1fr;
  gap: 32px;
  align-items: start;
  position: relative;
  padding: 0 16px 24px 16px;
  border-bottom: 1px solid rgba(126, 200, 227, 0.06);
  transition: all 0.5s var(--ease);
}

.track::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  background: linear-gradient(90deg, rgba(75, 168, 212, 0.08) 0%, transparent 100%);
  transition: width 0.6s ease;
  pointer-events: none;
}

.track:hover::before { width: 100%; }
.track:hover .track-title { color: var(--ice-white); }

.track-num {
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 300;
  font-size: 16px;
  color: var(--frost-dim);
  padding-top: 6px;
}

.track-title {
  font-family: 'JetBrains Mono', monospace;
  font-weight: 500;
  font-size: clamp(18px, 1.6vw, 22px);
  color: var(--ice-pale);
  letter-spacing: 0;
  margin-bottom: 10px;
  transition: color 0.4s ease;
}

.track-title .feat {
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 300;
  font-size: 0.78em;
  color: var(--frost-dim);
  margin-left: 12px;
}

.track-status {
  font-family: 'JetBrains Mono', monospace;
  font-weight: 400;
  font-size: 10px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--frost-dim);
}

.track-status.out { color: var(--ice-bright); }

/* ============================================
   SCENE 4 — COUNTDOWN
   ============================================ */
.scene-countdown {
  background: var(--black);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 32px;
  padding: 48px 32px;
  position: relative;
  height: auto;
  min-height: 100vh;
}

.countdown-globe {
  display: block;
  width: clamp(280px, 38vw, 520px);
  height: auto;
  position: relative;
  z-index: 1;
  object-fit: contain;
  background: #000;
  user-select: none;
  -webkit-user-drag: none;
}

.countdown-headline {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  font-size: clamp(20px, 2.4vw, 32px);
  letter-spacing: 0.04em;
  color: var(--ice-white);
  text-align: center;
  position: relative;
  z-index: 1;
  margin-top: 16px;
}

.countdown {
  display: flex;
  gap: clamp(32px, 5vw, 64px);
  flex-wrap: wrap;
  justify-content: center;
  position: relative;
  z-index: 1;
}

.count-block { text-align: center; }

.count-num {
  display: block;
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 300;
  font-size: clamp(40px, 5.5vw, 64px);
  line-height: 1;
  background: linear-gradient(180deg, var(--ice-pale) 0%, var(--ice-bright) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter:
    drop-shadow(0 0 8px rgba(126, 200, 227, 0.7))
    drop-shadow(0 0 24px rgba(75, 168, 212, 0.5))
    drop-shadow(0 0 48px rgba(75, 168, 212, 0.25));
  margin-bottom: 12px;
  font-variant-numeric: tabular-nums;
}

.count-label {
  display: block;
  font-family: 'JetBrains Mono', monospace;
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.5em;
  text-transform: uppercase;
  color: var(--frost-dim);
}

.countdown-date {
  font-family: 'JetBrains Mono', monospace;
  font-weight: 300;
  font-size: 11px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--frost-dim);
  position: relative;
  z-index: 1;
}

/* ============================================
   ENTRANCE / IN-VIEW REVEALS
   Triggered by IntersectionObserver in script.js
   ============================================ */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}

.reveal.in-view {
  opacity: 1;
  transform: translateY(0);
}

.track.reveal { transition-delay: calc(var(--i, 0) * 0.1s); }

/* ============================================
   RESPONSIVE
   ============================================ */
@media (max-width: 900px) {
  .brand-mark { top: 16px; right: 20px; font-size: 13px; }

  /* Scene 2 — Drake portrait: bigger, more portrait-ish on phones */
  .drake-frame { width: 88vw; aspect-ratio: 4 / 5; }
  .drake-caption { margin-top: 24px; font-size: 16px; }

  /* Scene 3 — album cover stacks above tracklist.
     Force the breathing room via grid row-gap (can't be eaten by alignment),
     and keep tracklist padding modest. */
  .scene-album { height: auto; padding: 0; }
  .album-grid { grid-template-columns: 1fr; gap: clamp(64px, 10vh, 120px) 0; }
  .album-left { aspect-ratio: 1 / 1; height: auto; }
  .tracklist { padding: 24px 24px 48px; }

  /* Scene 4 — countdown */
  .scene-countdown { gap: 24px; padding: 40px 20px; }
  .countdown-globe { width: min(75vw, 320px); }
  .countdown { gap: 22px; }
}

/* Tighter phones — countdown blocks become a 2×2 grid so the four
   units fit predictably on narrow screens instead of relying on
   flex-wrap, which can leave a stray block on its own line. */
@media (max-width: 520px) {
  .countdown {
    display: grid;
    grid-template-columns: auto auto;
    justify-content: center;
    gap: 18px 20px;       /* row gap untouched; slight column breathing room */
    width: auto;
    max-width: none;
  }
  .countdown-headline { font-size: 18px; }

  /* Scene 3 tracklist: trim padding + per-track spacing on small phones */
  .scene-album .tracklist { padding: 18px 18px 32px; gap: 24px; }
  .scene-album .track { grid-template-columns: 40px 1fr; gap: 18px; }
  .scene-album .track-title { font-size: 16px; }
}


/* ========== SCORE TOGGLE ==========
   Mirrors .brand-mark across the top of the viewport (mark sits top-right,
   this sits top-left). Same frosted-glass vocabulary as .frost-card, at
   a smaller weight so it reads as chrome rather than a call to action.
   Stays [hidden] until script.js confirms the track actually loaded. */
.sound-toggle {
  position: fixed;
  top: 20px;
  left: 32px;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  font-family: 'JetBrains Mono', monospace;
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: lowercase;
  color: var(--ice-pale);
  background: rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 2px;
  box-shadow:
    0 0 0 1px rgba(126, 200, 227, 0.06) inset,
    0 12px 40px rgba(0, 0, 0, 0.35);
  cursor: var(--ice-cursor-clickable);
  user-select: none;
  -webkit-user-select: none;
  transition:
    background 0.5s var(--ease),
    border-color 0.5s var(--ease),
    color 0.5s var(--ease),
    box-shadow 0.5s var(--ease);
}
.sound-toggle[hidden] { display: none; }

.sound-toggle:hover,
.sound-toggle:focus-visible {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.3);
  color: var(--ice-white);
  outline: none;
}

/* Four bars that idle flat and animate only while the score is audible,
   so the control doubles as a playback indicator. */
.sound-bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 11px;
}
.sound-bars i {
  display: block;
  width: 2px;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
  transition: height 0.35s var(--ease);
}

.sound-toggle[aria-pressed="true"] {
  color: var(--ice-glow);
  border-color: rgba(126, 200, 227, 0.4);
  box-shadow:
    0 0 0 1px rgba(126, 200, 227, 0.16) inset,
    0 12px 40px rgba(0, 0, 0, 0.35),
    0 0 40px rgba(126, 200, 227, 0.2);
}
.sound-toggle[aria-pressed="true"] .sound-bars i {
  animation: sound-bar 1s var(--ease) infinite;
}
.sound-toggle[aria-pressed="true"] .sound-bars i:nth-child(1) { animation-delay: 0s;    }
.sound-toggle[aria-pressed="true"] .sound-bars i:nth-child(2) { animation-delay: 0.18s; }
.sound-toggle[aria-pressed="true"] .sound-bars i:nth-child(3) { animation-delay: 0.36s; }
.sound-toggle[aria-pressed="true"] .sound-bars i:nth-child(4) { animation-delay: 0.54s; }

@keyframes sound-bar {
  0%, 100% { height: 2px;  }
  50%      { height: 11px; }
}

/* Pulse while the score is armed but still waiting on the gesture that
   browsers require before audible playback is allowed. */
.sound-toggle.is-waiting { animation: sound-wait 2.4s var(--ease) infinite; }
@keyframes sound-wait {
  0%, 100% { border-color: rgba(255, 255, 255, 0.14); }
  50%      { border-color: rgba(126, 200, 227, 0.45); }
}

@media (prefers-reduced-motion: reduce) {
  .sound-toggle,
  .sound-toggle .sound-bars i { animation: none !important; }
}

@media (max-width: 640px) {
  .sound-toggle { top: 18px; left: 18px; padding: 7px 11px; font-size: 9px; }
  .sound-label  { display: none; }
}
