/* ============================================================
 * VHS EFFECTS
 * ------------------------------------------------------------
 * Overlays the .visualizer area with VHS-aesthetic glitches:
 *   - persistent horizontal scanlines (slow downward drift)
 *   - a "tracking" band that rolls down the screen every cycle
 *   - high-frequency static noise
 *   - chromatic aberration on the artist logo (red/cyan ghost)
 *   - periodic glitch jitter on the artist logo wrap
 *
 * USAGE
 * ------------------------------------------------------------
 * 1) Link this file from the page:
 *      <link rel="stylesheet" href="assets/vhs-effects.css">
 *    (use ../assets/ from artist subpages)
 *
 * 2) Inside <main class="visualizer">, AFTER the existing layers
 *    (visualizer-bg, visualizer-tint, visualizer-logo-wrap), add:
 *      <div class="vhs-scanlines" aria-hidden="true"></div>
 *      <div class="vhs-tracking" aria-hidden="true"></div>
 *      <div class="vhs-static"   aria-hidden="true"></div>
 *
 * 3) The chromatic-aberration and glitch effects apply
 *    automatically via existing selectors — no extra elements
 *    needed for those.
 * ============================================================ */


/* -----------------------------------------------------------
 * Z-INDEX STACK
 * -----------------------------------------------------------
 * Inside .visualizer (existing bundle layers):
 *   0   .visualizer-bg            (background image)
 *   1   .visualizer-tint          (dark wash, audio-driven alpha)
 *   3   .visualizer-logo-wrap     (artist portrait + pulse)
 *
 * VHS overlay layers (work both inside .visualizer AND on the
 * landing page — selectors no longer require a .visualizer
 * ancestor):
 *  50   .vhs-scanlines            (constant horizontal lines)
 *  51   .vhs-tracking             (rolling tracking band)
 *  52   .vhs-static               (high-frequency noise)
 * ----------------------------------------------------------- */


/* -------- Scanlines: persistent, slow downward drift --------
 * Selector applies anywhere a .vhs-scanlines element is placed —
 * inside .visualizer it fills the visualizer area; on the landing
 * page (where no positioned ancestor exists nearby) it fills the
 * viewport. position:absolute + inset:0 does the right thing in
 * both contexts. */
.vhs-scanlines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 50;
  /* Pairs of 1px rows: transparent then dark, ~50% duty cycle */
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0)    0px,
    rgba(0, 0, 0, 0)    1px,
    rgba(0, 0, 0, 0.32) 2px,
    rgba(0, 0, 0, 0.32) 3px
  );
  mix-blend-mode: multiply;
  /* Beat-synced: --spin-duration is the per-beat duration (60/bpm), set
     by the bundle from the track's detected/tagged BPM. The scanlines
     drift one cycle per beat so they move in time with the music.
     Falls back to 6s when no track/BPM is active. */
  animation: vhs-scan-drift var(--spin-duration, 6s) linear infinite;
}

/* RGB chromatic lines — thin red / green / blue horizontal stripes laid
 * over the dark scanlines, blended additively (screen) so they read as
 * faint glowing colour fringes like an old CRT/VHS signal. Drifts down
 * slowly alongside the base scanlines. Keep the alpha low so it's a
 * subtle tint, not a rainbow. */
.vhs-scanlines::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(255, 0, 0, 0.06)  0px,
    rgba(255, 0, 0, 0.06)  1px,
    rgba(0, 255, 0, 0.06)  1px,
    rgba(0, 255, 0, 0.06)  2px,
    rgba(0, 0, 255, 0.06)  2px,
    rgba(0, 0, 255, 0.06)  3px,
    rgba(0, 0, 0, 0)       3px,
    rgba(0, 0, 0, 0)       6px
  );
  mix-blend-mode: screen;
  /* Beat-synced drift + energy-reactive intensity: the RGB fringes
     brighten on louder passages (--audio-energy 0→1) and drift in time
     with the beat. */
  opacity: calc(0.55 + var(--audio-energy, 0) * 0.45);
  animation: vhs-scan-drift var(--spin-duration, 6s) linear infinite;
}

@keyframes vhs-scan-drift {
  from { background-position: 0 0; }
  to   { background-position: 0 4px; }
}


/* -------- Tracking band: rolls UP from below, infrequently --------
 * Restructured to use a child ::before element so animation can use
 * `top:` directly — more predictable than animating background-position
 * over a 220% gradient. The band:
 *   - starts BELOW the bottom edge of the visible area
 *   - sweeps upward over ~6 seconds
 *   - exits above the top, then holds off-screen for ~18 seconds
 * Total cycle ~24s, so it shows up much less often than before. */
.vhs-tracking {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 51;
  overflow: hidden;
}

.vhs-tracking::before {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
  height: 14%;
  /* Initial state — below the viewport. Animation overrides this. */
  top: 100%;
  background: linear-gradient(
    to bottom,
    transparent 0%,
    rgba(255, 255, 255, 0.10) 30%,
    rgba(255, 255, 255, 0.28) 50%,
    rgba(255, 255, 255, 0.10) 70%,
    transparent 100%
  );
  mix-blend-mode: screen;
  /* Beat-synced: rolls once every 8 beats (a musical phrase), so the
     tracking band sweeps in time with the music. calc multiplies the
     per-beat --spin-duration by 8. Falls back to 24s. */
  animation: vhs-tracking-roll calc(var(--spin-duration, 3s) * 8) linear infinite;
}

@keyframes vhs-tracking-roll {
  0%   { top: 100%; }    /* below the bottom edge */
  25%  { top: -14%; }    /* fully exited above the top */
  100% { top: -14%; }    /* hold off-screen for remaining 75% */
}


/* -------- Static: high-frequency procedural noise --------
 * SVG fractalNoise filter, alpha cranked to ~60%. Position
 * shifts on a stepped animation so the noise field "swims"
 * frame by frame instead of smoothly drifting. */
.vhs-static {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 52;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='1.2' numOctaves='2' seed='7' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.6 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  background-size: 200px 200px;
  mix-blend-mode: screen;
  opacity: 0.35;
  animation: vhs-static-shift 0.5s steps(6) infinite;
}

@keyframes vhs-static-shift {
  0%   { background-position:   0px   0px; }
  16%  { background-position:  40px -30px; }
  33%  { background-position: -20px  60px; }
  50%  { background-position:  30px  50px; }
  66%  { background-position: -40px -10px; }
  83%  { background-position:  60px  20px; }
  100% { background-position:   0px   0px; }
}


/* -------- Chromatic aberration on the artist logo --------
 * Red on the left, cyan on the right — classic VHS RGB-shift.
 * Stacks WITH the bundle's existing dark drop-shadow, so we
 * re-declare that here so we don't lose it. !important is
 * needed because the bundle rule also uses !important. */
main.visualizer #visualizer-artist-logo,
.visualizer      #visualizer-artist-logo {
  /* Audio-reactive inversion: white when quiet, inverted to black when
   * loud (driven directly by --audio-energy so it matches the collab
   * images). Chromatic-aberration drop-shadows preserved. */
  filter:
    drop-shadow( 1.5px 0 0 rgba(255,  60,  60, 0.55))
    drop-shadow(-1.5px 0 0 rgba( 60, 200, 255, 0.55))
    drop-shadow(0 4px 24px rgba(0, 0, 0, 0.55))
    invert(var(--audio-energy, 0))
    !important;
}


/* -------- Glitch jitter on the artist-logo wrapper --------
 * Most of the cycle the wrap sits still; near the end it
 * twitches horizontally for a handful of frames. Doesn't
 * conflict with the bundle's earthquake animation, which only
 * activates under html.is-loud-sustained (that one wins under
 * its selector, then mine resumes after it ends). */
@keyframes vhs-glitch-jitter {
  0%, 92%, 100% { transform: translate( 0px,  0px); }
  93%           { transform: translate( 3px, -1px); }
  94%           { transform: translate(-4px,  0px); }
  95%           { transform: translate( 2px,  1px); }
  96%           { transform: translate( 0px,  0px); }
}

main.visualizer > .visualizer-logo-wrap,
.visualizer    > .visualizer-logo-wrap {
  animation: vhs-glitch-jitter 5.2s steps(60, end) infinite !important;
}


/* -------- Subtle full-area flicker --------
 * Tiny opacity dips every ~11s to suggest the unstable
 * luminance of a worn tape. Stays high enough to never
 * impair readability. */
@keyframes vhs-flicker {
  0%, 84%, 100% { opacity: 1;    }
  85%           { opacity: 0.92; }
  86%           { opacity: 1;    }
  87%           { opacity: 0.85; }
  88%           { opacity: 1;    }
}

main.visualizer,
.visualizer {
  animation: vhs-flicker 11s infinite;
}


/* -------- Respect reduced-motion preference -------- */
@media (prefers-reduced-motion: reduce) {
  .vhs-scanlines,
  .vhs-tracking::before,
  .vhs-static,
  main.visualizer > .visualizer-logo-wrap,
  .visualizer > .visualizer-logo-wrap,
  main.visualizer,
  .visualizer,
  .label-panel .header-artist::before,
  #label-panel .header-artist::before {
    animation: none !important;
  }
}


/* ============================================================
 * LATE OVERRIDES — kill frost, invert header logo, center
 * the artist label-panel, and add a VHS divider between names
 * ============================================================ */


/* ---- (a) Frosted-glass overlay on the visualizer (all widths).
 * A subtle, see-through blurred layer over the background image,
 * beneath the artist logo (logo sits at z-index 10 and stays
 * sharp). Kept light so the drip image reads clearly through it.
 * We match the bundle's :has()-based selector and win by source
 * order so this applies whenever the bg image has a src. */
/* Frost + tint are applied directly to the BACKGROUND IMAGE (see the
 * .visualizer-bg rule below), NOT as a full-area ::after overlay, so
 * the artist logo is never dimmed or blurred. This ::after is kept
 * disabled to avoid double-frosting. */
main.visualizer::after,
.visualizer::after,
main.visualizer:has(> .visualizer-bg[src]:not([src=""]))::after {
  display: none !important;
  content: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  background: none !important;
}


/* ---- (b) Force the header wordmark to render inverted (white).
 * Bundle has a conditional `invert(var(--effective-invert)) invert(1)`
 * chain in home-mode that depends on a runtime CSS var, so the result
 * shifts depending on state. Force a clean single invert here. */
.site-title-logo {
  filter: invert(0) !important;
}


/* ---- (c) Center the artist label-panel within the header.
 * Bundle pins it at right:32px / left:auto. We swap to left:50%
 * with a translate to center both axes. We also cap its width to
 * the header's inner area and let items shrink slightly so the
 * last name (e.g. "REVOLVER OSCILLATOR") can't run past the right
 * edge and get clipped. */
.label-panel,
#label-panel {
  left: 50% !important;
  right: auto !important;
  transform: translate(-50%, -50%) !important;
  /* Reduce the native flex gap so the divider below provides
   * the visual separation rather than doubling it. */
  gap: 12px !important;
  /* Never exceed the header width (minus room for the left logo). */
  max-width: calc(100vw - 320px) !important;
  flex-wrap: nowrap !important;
  white-space: nowrap !important;
}
/* Let the nav items shrink a touch on narrower desktop widths so
 * the full roster fits without clipping the last name. */
@media (min-width: 901px) and (max-width: 1200px) {
  .label-panel .header-artist,
  #label-panel .header-artist {
    font-size: 12px !important;
    letter-spacing: 0.03em !important;
  }
  .label-panel,
  #label-panel {
    gap: 8px !important;
  }
}


/* VHS / videogame-style divider between artist names.
 * Grayscale variations of black — muted bar with darker ghosts
 * on each side instead of the red/cyan chromatic aberration.
 * Subtle flicker syncs with the rest of the VHS effects. */
.label-panel .header-artist:not(:first-child)::before,
#label-panel .header-artist:not(:first-child)::before {
  content: '';
  display: inline-block;
  width: 2px;
  height: 1.4em;
  background: rgba(140, 140, 140, 0.55);
  /* Two-tone dark gray ghosts — variations of black */
  box-shadow:
    -1.5px 0 0 rgba(80, 80, 80, 0.75),
     1.5px 0 0 rgba(40, 40, 40, 0.75);
  margin-right: 12px;
  vertical-align: middle;
  animation: vhs-divider-flicker 5s steps(40) infinite;
}

@keyframes vhs-divider-flicker {
  0%, 96%, 100% { opacity: 1;    }
  97%           { opacity: 0.4;  }
  98%           { opacity: 1;    }
  99%           { opacity: 0.65; }
}


/* ============================================================
 * ARTIST PAGES → MATCH LISTEN PAGE LAYOUT
 * ============================================================
 * The bundle styles .home-mode and .artist-mode differently for
 * several elements. To make artist pages look identical to the
 * listen page (just with a different track queue), override the
 * artist-mode rules here so they pick up the home-mode values.
 *
 * NOTE: this only addresses the most visible differences I
 * could identify in the bundle CSS:
 *   1. Visualizer layout (position + sizing)
 *   2. Site header padding + background
 *   3. Site title-logo drop-shadow glow
 * If you spot remaining visual differences between an artist
 * page and listen.html, point them out and I can add targeted
 * overrides for those.
 * ============================================================ */

/* (1) Visualizer layout.
 * .home-mode uses position:fixed anchored to viewport edges with
 *   bottom: calc(64px + var(--sc-section-height, 280px)).
 * .artist-mode uses position:relative with a calc()ed height in
 *   normal flow — visually different from home.
 * Force the home-mode layout on artist pages. DESKTOP ONLY — on
 * mobile the scrolling page-flow rules below handle both modes
 * identically, so this fixed positioning must not apply there. */
@media (min-width: 901px) {
  .artist-mode .visualizer,
  .artist-mode main.visualizer {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    bottom: calc(64px + var(--sc-section-height, 280px)) !important;
    width: auto !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: hidden !important;
    isolation: isolate !important;
  }
}

/* (2) Site header: home-mode strips top/bottom padding and forces
 * a fully black background. Match that on artist pages. */
.artist-mode .site-header {
  background: #000 !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}

/* (3) Title logo: home-mode adds a soft white drop-shadow as a
 * "neon flicker" glow. Add the same on artist pages. Note: my
 * earlier `.site-title-logo { filter: invert(1) !important }`
 * override above wins because it has same specificity but loads
 * later — we re-declare here with the glow included to combine
 * both. The selector below is more specific so it beats the
 * earlier rule on artist pages without affecting home-mode. */
.artist-mode .site-title-logo,
.home-mode .site-title-logo {
  filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.35)) invert(0) !important;
}


/* ============================================================
 * ARTIST LOGO SIZE — 1.5× the bundle default
 * ============================================================
 * Bundle defaults: height:70% / max-height:520px / max-width:70%.
 * Bump each by 1.5 (→ 105% / 780px / 105%). The wrapper
 * (.visualizer-logo-wrap) already uses flex centering, and the
 * visualizer has overflow:hidden, so the larger logo stays
 * vertically and horizontally centered regardless of viewport.
 * The bundle's transform: scale() audio-pulse animation
 * continues to work on top of the new base size.
 * ============================================================ */

@media (min-width: 901px) {
  main.visualizer > .visualizer-logo-wrap > .visualizer-artist-logo,
  .visualizer    > .visualizer-logo-wrap > .visualizer-artist-logo,
  #visualizer-artist-logo {
    /* Size purely by WIDTH so both the logo's size and its position
     * below the drips stay identical across desktop screen sizes
     * (heights vary, widths scale the drips — match that basis). */
    height: auto !important;
    width: 45vw !important;
    max-width: none !important;
    max-height: none !important;
    min-width: 0 !important;
    object-fit: contain !important;
    /* Pulse/breathe with the music using the gently-smoothed
     * --audio-pulse signal (separate from the punchy --audio-energy
     * that drives frost/earthquake) so the logo glides smoothly
     * instead of snapping. */
    /* Scale is a DIRECT function of the amplitude at the current playback
     * position (--audio-energy, 0 quiet .. 1 loud). No animation, no
     * breathing — the size reflects the exact moment in the song, so
     * scrubbing to a point shows that moment's size. Quiet = bigger,
     * loud = smaller. A short transition just smooths between ticks. */
    transform: scale(calc(1.08 - var(--audio-energy, 0) * 0.24)) !important;
    transform-origin: center center !important;
    transition: transform 80ms linear !important;
    will-change: transform !important;
  }
}


/* ============================================================
 * HEADER WORDMARK — always inverted, every page
 * ============================================================
 * The bundle has multiple conditional filter chains on
 * .site-title-logo (home-mode, artist-mode, fx-harsh, etc.).
 * Force a clean invert(1) here for every variant so the
 * wordmark always renders as the white silhouette on the dark
 * header, regardless of which mode/fx state classes the bundle
 * is currently applying.
 *
 * The home-mode/artist-mode rule below adds the bundle's soft
 * white drop-shadow glow on top of the invert — same look as
 * the bundle's default home-mode rule, but guaranteed.
 * ============================================================ */

.site-title-logo,
html .site-title-logo {
  filter: invert(0) !important;
}

html.home-mode .site-title-logo,
html.artist-mode .site-title-logo,
html.home-mode.fx-harsh .site-title-logo,
html.artist-mode.fx-harsh .site-title-logo {
  filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.35)) invert(0) !important;
}


/* ============================================================
 * LOAD TRANSITION OVERLAY
 * ============================================================
 * Instead of fading individual pieces (which let the user see the
 * player assemble — white bars, downward shifts), we cover the
 * whole visualizer + player area with a solid black overlay that
 * shows the Deathspin mark while everything loads and settles
 * underneath. Once the bundle signals readiness
 * (html.player-ready, set by player-ready.js), the overlay fades
 * away to reveal a fully-formed, motionless player.
 *
 * The overlay markup (.load-transition) is in each page's HTML so
 * it paints on the very first frame, before any script runs.
 * player-ready.js enforces a minimum on-screen time so the
 * transition always reads as intentional (a couple seconds) even
 * when loading is instant.
 * ============================================================ */
.load-transition {
  position: fixed;
  inset: 0;
  z-index: 2147483646;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 1;
  pointer-events: all;
  transition: opacity 900ms ease-out;
}
/* The bundle hides <body> (opacity:0) during html.is-booting until
 * window 'load', which would also hide this overlay. Since the
 * overlay itself covers the still-assembling player, it's safe (and
 * necessary) to show the body immediately so the transition logo is
 * visible from the first frame. The overlay hides everything behind
 * it until we fade it out. */
html.is-booting body { opacity: 1 !important; }
.load-transition-logo {
  width: min(76vw, 640px);
  height: auto;
  object-fit: contain;
  /* Continuous spin with a gentle breathing pulse while loading.
   * Two animations run together: a steady linear rotation and a
   * slower scale/opacity pulse. They're separate animations so the
   * spin stays perfectly even while the pulse breathes. */
  animation:
    loadTransitionSpin 3.2s linear infinite,
    loadTransitionPulse 1.6s ease-in-out infinite;
  will-change: transform, opacity;
}
@keyframes loadTransitionSpin {
  from { rotate: 0deg; }
  to   { rotate: 360deg; }
}
@keyframes loadTransitionPulse {
  0%, 100% { transform: scale(1);    opacity: 0.85; }
  50%      { transform: scale(1.06); opacity: 1;    }
}
/* Reveal: fade the overlay out, then take it out of the flow so it
 * can't intercept clicks once gone. */
html.player-ready .load-transition {
  opacity: 0;
  pointer-events: none;
}
html.transition-done .load-transition {
  display: none;
}
@media (max-width: 900px) {
  .load-transition-logo { width: min(120vw, 600px); }
}


/* ============================================================
 * MOBILE TRACKLIST — vertical, touch-friendly list
 * ============================================================
 * On desktop the tracklist is a horizontal hover-scroll strip.
 * That's wrong for touch, so on mobile we present a clean
 * VERTICAL list of full-width rows (art · title/artist · time)
 * on BOTH the listen page and artist pages. This overrides the
 * bundle's mobile rules (which hid the list on the listen page
 * and left some spacing/overlap issues). Loaded last, so it wins.
 * ============================================================ */
@media (max-width: 900px) {

  /* Show the inline tracklist on every page, including the listen
   * (home-mode) page where the bundle hid it. */
  .home-mode .track-list,
  .track-list {
    display: block !important;
    width: 100% !important;
    max-width: 100% !important;
    min-height: 0 !important;
    overflow: visible !important;
    padding: 0 !important;
    margin: 0 !important;
  }

  /* The row container must stack, not sit in a horizontal flex or
   * `display:contents` mode. Also kill the desktop left-pad var. */
  .track-list-row {
    display: block !important;
    width: 100% !important;
    padding: 0 !important;
  }

  /* Hide desktop-only occluders that can overlap the list. */
  .tracklist-cover,
  .player-cap,
  .player-cap--left,
  .player-cap--merch-ad {
    display: none !important;
  }

  /* Each track = a full-width horizontal card, comfortably spaced.
   * Grid: [art] [title/artist] [duration]. */
  .track-row {
    display: grid !important;
    grid-template-columns: 52px 1fr auto !important;
    grid-template-areas: "art meta time" !important;
    align-items: center !important;
    gap: 14px !important;
    width: 100% !important;
    padding: 12px 16px !important;
    border-bottom: 0.5px solid var(--line) !important;
    border-radius: 0 !important;
    /* undo any desktop card sizing / active-collapse transitions */
    flex-direction: initial !important;
    opacity: 1 !important;
    overflow: visible !important;
    margin: 0 !important;
  }
  .track-row:last-child { border-bottom: 0 !important; }
  .track-row.is-active {
    width: 100% !important;
    padding: 12px 16px !important;
    margin: 0 !important;
    opacity: 1 !important;
    background: rgba(204, 0, 0, 0.10) !important;
  }

  .track-art {
    grid-area: art !important;
    width: 52px !important;
    height: 52px !important;
    aspect-ratio: 1 / 1 !important;
    border-radius: 3px !important;
  }

  .track-meta {
    grid-area: meta !important;
    min-width: 0 !important;
    width: auto !important;
    text-align: left !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 2px !important;
  }
  .track-title {
    font-size: 14px !important;
    text-align: left !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    margin: 0 !important;
    line-height: 1.25 !important;
  }
  /* Re-show the artist line under the title on mobile (desktop
   * hides it in the compact strip). */
  .track-artist {
    display: block !important;
    grid-area: auto !important;
    font-size: 11px !important;
    color: var(--muted) !important;
    text-transform: uppercase !important;
    letter-spacing: 0.02em !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
  }
  .track-duration {
    display: block !important;
    grid-area: time !important;
    font-size: 12px !important;
    color: var(--muted) !important;
    font-variant-numeric: tabular-nums !important;
    padding-left: 8px !important;
  }
  /* Hide the per-row artist logo strip used by the desktop cards. */
  .track-artist-logo-link,
  .track-artist-logo-group { display: none !important; }

  /* The scrollable list sits above the fixed mini player bar; give
   * it room so the last track isn't hidden behind the bar. */
  .soundcloud-section {
    padding-bottom: calc(var(--player-bar-height, 184px) + env(safe-area-inset-bottom, 0px)) !important;
  }
  .home-mode .soundcloud-section {
    padding-bottom: calc(var(--player-bar-height, 184px) + env(safe-area-inset-bottom, 0px)) !important;
  }
  /* Remove the artificial 1200px min-height placeholder that padded
   * the empty (pre-load) state — the real list sizes itself now. */
  .soundcloud-section:has(.player-content[hidden]) {
    min-height: 0 !important;
  }
}


/* ============================================================
 * MOBILE VISUALIZER LOGO — center over the video
 * ============================================================
 * The artist logo (#visualizer-artist-logo) sits in
 * .visualizer-logo-wrap, which is absolutely positioned to fill
 * the visualizer and flex-centers its child. On desktop we bump
 * the logo to 105% height; on mobile that overflows the shorter
 * viewport and drags the logo's visual mass downward (it ended
 * up low on the screen). Here we constrain it to FIT within the
 * visualizer — bounded on BOTH axes — so wide logos (e.g. Moth)
 * stay fully visible and perfectly centered, vertically and
 * horizontally, over the background video.
 * ============================================================ */
@media (max-width: 900px) {
  main.visualizer > .visualizer-logo-wrap,
  .visualizer > .visualizer-logo-wrap,
  .visualizer-logo-wrap {
    position: absolute !important;
    inset: 0 !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    pointer-events: none !important;
    /* Sit ABOVE the frost ::after (z-index 1) so the logo stays
     * sharp — only the video behind it is frosted, matching desktop. */
    z-index: 10 !important;
  }
  main.visualizer > .visualizer-logo-wrap > .visualizer-artist-logo,
  .visualizer > .visualizer-logo-wrap > .visualizer-artist-logo,
  #visualizer-artist-logo {
    /* Centered, fits inside the visualizer on both axes. */
    height: auto !important;
    width: auto !important;
    max-width: 96vw !important;
    max-height: 48vh !important;
    object-fit: contain !important;
    margin: 0 auto !important;
  }
}


/* ============================================================
 * MOBILE PAGE FLOW — visualizer → tracklist → player bar
 * ============================================================
 * The bundle's mobile layout locks the body to 100vh /
 * overflow:hidden with a near-full-height visualizer and NO
 * inline tracklist (it expected the tap-to-open now-playing
 * overlay). We want a natural scrolling page: a reasonably-sized
 * visualizer with the artist logo centered over the video, the
 * vertical tracklist directly beneath it, and the fixed mini
 * player bar pinned at the bottom. This block reshapes that.
 * ============================================================ */
@media (max-width: 900px) {
  /* Let the page scroll instead of being a fixed 100vh pane. */
  html, body {
    height: auto !important;
    min-height: 100% !important;
    overflow-x: hidden !important;
    overflow-y: auto !important;
  }

  /* Visualizer: a contained banner-height panel (not the whole
   * viewport), in normal flow, with the video filling it. The
   * artist logo centers over it (handled in the logo block). */
  .home-mode .visualizer,
  .artist-mode .visualizer,
  main.visualizer {
    position: relative !important;
    top: auto !important; left: auto !important;
    right: auto !important; bottom: auto !important;
    width: 100% !important;
    height: 52vh !important;      /* contained, not full-screen */
    min-height: 320px !important;
    margin: 0 !important;
    overflow: hidden !important;
    flex: none !important;
  }
  /* Make the background video actually cover the panel. */
  main.visualizer > .visualizer-bg,
  .visualizer > .visualizer-bg,
  .visualizer-bg,
  #visualizer-bg {
    position: absolute !important;
    inset: 0 !important;
    top: 0 !important;
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
    transform: none !important;
    /* Audio-reactive frost + black tint on the image only. */
    filter: blur(calc(1px + var(--audio-energy, 0) * 30px)) brightness(calc(1 + var(--audio-energy, 0) * 0.9)) !important;
    transition: filter 60ms linear !important;
    z-index: 0 !important;
  }

  /* The player/tracklist section flows right below the visualizer,
   * no giant gap. Kill the 1200px placeholder + fixed positioning. */
  .soundcloud-section {
    position: relative !important;
    bottom: auto !important;
    left: auto !important; right: auto !important;
    width: 100% !important;
    max-height: none !important;
    min-height: 0 !important;
    /* leave room for the fixed mini player bar at the very bottom */
    padding-bottom: calc(var(--player-bar-height, 160px) + env(safe-area-inset-bottom, 0px)) !important;
  }
  .soundcloud-section:has(.player-content[hidden]) {
    min-height: 0 !important;
  }

  /* Ensure the player content (which holds the tracklist) is a
   * normal-flow block directly under the visualizer. Note: we do
   * NOT set position:relative here — the player-bar inside needs to
   * pin to the viewport via position:fixed, and keeping these
   * ancestors static avoids any containing-block surprises. */
  .player-stack,
  .soundcloud-inner,
  .player-content {
    position: static !important;
    width: 100% !important;
    max-width: 100% !important;
    display: block !important;
  }
}


/* ============================================================
 * MOBILE HEADER LOGO + VISUALIZER BORDER FIXES
 * ============================================================ */
/* Remove the leftover red debug border on the visualizer. The drip
 * image is black drips on a light/white ground, scaled to WIDTH so it
 * covers only the top band; the area below shows this matching light
 * background, giving seamless white space beneath the drips for the
 * logo to sit in. */
main.visualizer,
.visualizer {
  border: 0 !important;
  background: #eff1f3 !important;   /* matches the drip PNG's ground */
}

@media (max-width: 900px) {
  /* Match the drip image's light ground so there's no seam. */
  main.visualizer,
  .visualizer {
    background: #eff1f3 !important;
  }
  /* Center the Deathspin wordmark in the mobile header instead of
   * pinning it to the left (where it overhangs into the visualizer).
   * The header is already a flex row with center justification, so
   * we drop the absolute positioning and let it center normally. */
  .site-title-logo {
    position: static !important;
    left: auto !important;
    top: auto !important;
    transform: none !important;
    height: 56px !important;
    max-height: 70% !important;
    width: auto !important;
    margin: 0 auto !important;
  }
  /* Keep the header a centered flex row and clip any overflow so
   * nothing spills into the visualizer below. */
  .site-header {
    justify-content: center !important;
    align-items: center !important;
    overflow: hidden !important;
  }
}


/* ============================================================
 * MOBILE PLAYER BAR — pinned to viewport bottom
 * ============================================================
 * The player-bar is nested deep in the (now-scrolling) soundcloud
 * section. Force it fixed to the viewport bottom so play/pause/
 * skip stay reachable while the tracklist scrolls. High
 * specificity + !important guarantees it wins, and no ancestor in
 * our mobile rules sets transform/filter (which would otherwise
 * re-anchor a fixed element to that ancestor instead of the
 * viewport). */
@media (max-width: 900px) {
  #player-bar.player-bar,
  .player-content > .player-bar,
  .player-bar {
    position: fixed !important;
    bottom: 0 !important;
    left: 0 !important;
    right: 0 !important;
    z-index: 100 !important;
    background: var(--bg) !important;
    border-top: 0.5px solid var(--line) !important;
  }
}


/* ============================================================
 * MOBILE PLAYER BAR — body-pinned variant
 * ============================================================
 * player-ready.js moves the bar to be a direct child of <body>
 * on mobile so position:fixed anchors to the viewport no matter
 * what ancestors do. Style that pinned state here. */
@media (max-width: 900px) {
  body > .player-bar.player-bar--body-pinned {
    position: fixed !important;
    bottom: 0 !important;
    left: 0 !important;
    right: 0 !important;
    z-index: 2147483000 !important;   /* above visualizer + tracklist */
    background: var(--bg) !important;
    border-top: 0.5px solid var(--line) !important;
    /* keep the bundle's mobile grid layout for the bar's inner cells */
  }
}


/* ============================================================
 * MOBILE HEADER — double size
 * ============================================================
 * Double the header height and the wordmark logo on mobile for a
 * bolder presence. Header goes 88px -> 176px; the centered
 * wordmark scales up to match. */
@media (max-width: 900px) {
  .site-header {
    height: 96px !important;
  }
  .site-title-logo {
    height: 56px !important;
    max-height: 70% !important;
  }
}


/* ============================================================
 * REMOVE SOUNDCLOUD COVER-ART BACKGROUND
 * ============================================================
 * #now-playing-art is a full-bleed element populated with the
 * current track's SoundCloud artwork URL and layered behind the
 * player as a background cover. We don't want the SoundCloud
 * cover photo used as a visualizer/player background, so remove
 * it entirely (the small tracklist thumbnails use their own
 * .track-art elements and are unaffected). Applies at all widths. */
#now-playing-art,
.now-playing-art {
  display: none !important;
}


/* ============================================================
 * DESKTOP GUARD — keep player cover art small regardless of
 * where the player-bar currently lives
 * ============================================================
 * The mobile player-bar grid sizes .player-meta-art and
 * .player-current-artist-logo to full width (aspect-ratio 1/1).
 * If a resize leaves the bar transiently outside
 * .desktop-player-bar-host, those mobile sizes could apply on
 * desktop and blow the cover art up to fill the screen. Force
 * them back to the small desktop treatment at desktop widths. */
@media (min-width: 901px) {
  .player-meta-art {
    width: 40px !important;
    height: 40px !important;
    aspect-ratio: auto !important;
  }
  .player-current-artist-logo {
    display: none !important;
  }
  /* The full-bleed SoundCloud cover element stays removed. */
  #now-playing-art,
  .now-playing-art {
    display: none !important;
  }
}


/* ============================================================
 * FLOATING COVER SIZE CLAMP (safety net)
 * ============================================================
 * The floating SoundCloud cover is sized from a JS-set
 * --floating-cover-size variable (measured from the tracklist).
 * If that var ever goes stale/huge (e.g. measured against the
 * tall mobile vertical tracklist during a resize), clamp it here
 * so the cover can never balloon to fill the screen. Also keep it
 * fully hidden on mobile. */
.player-floating-cover[src]:not([src=""]) {
  max-width: 140px !important;
  max-height: 140px !important;
}
@media (max-width: 900px) {
  .player-floating-cover,
  .player-floating-cover[src]:not([src=""]) {
    display: none !important;
  }
}


/* ============================================================
 * VISUALIZER DRIP-IMAGE BACKGROUND + LOGO IN WHITE SPACE
 * ============================================================
 * The visualizer background is now a static drip image (drips
 * along the top, white space below). Anchor the image to the top
 * so the drips stay up top and the white area fills the lower
 * portion, then position the artist logo DOWN in that white space
 * (rather than dead center) so it sits fully within the clear
 * white area below the drips. Applies desktop + mobile. */
/* ---- Consistent drip BACKGROUND across desktop sizes ----
 * The drip image is scaled purely by WIDTH (height auto), anchored
 * to the top. This makes the drips always span the full width and
 * hang down by the SAME proportion of the width on every screen —
 * so the drip pattern looks identical regardless of viewport height.
 * Desktop-only; mobile keeps its own cover-based rule above. */
@media (min-width: 901px) {
  main.visualizer > .visualizer-bg,
  .visualizer > .visualizer-bg,
  .visualizer-bg,
  #visualizer-bg {
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    bottom: auto !important;
    width: 100% !important;
    height: auto !important;
    object-fit: contain !important;
    object-position: top center !important;
    transform: none !important;
    /* Audio-reactive, applied to the IMAGE ONLY (logo unaffected):
     *   - blur: base 2px frost, up to +14px on loud hits
     *   - brightness: 1 at rest → down to ~0.5 at max loudness
     *     (this is the black tint — darkens the image on loud hits)
     * Driven by --audio-energy (0 rest → 1 loudest, from SoundCloud's
     * per-track waveform data). Overrides the bundle's static
     * blur(50px) wash via matching selector specificity. */
    filter: blur(calc(1px + var(--audio-energy, 0) * 30px)) brightness(calc(1 + var(--audio-energy, 0) * 0.9)) !important;
    transition: filter 60ms linear !important;
    z-index: 0 !important;
  }
}

/* ---- Consistent logo POSITION across desktop sizes ----
 * Because the drips now hang down a fixed proportion of the WIDTH,
 * we position the logo at a width-relative offset from the top so it
 * always sits the same distance below the drips on every desktop
 * screen. Combined with the fixed logo SIZE rule above, the logo
 * looks identical across laptop / monitor / ultrawide. */
@media (min-width: 901px) {
  main.visualizer > .visualizer-logo-wrap,
  .visualizer > .visualizer-logo-wrap,
  .visualizer-logo-wrap {
    align-items: center !important;
    justify-content: center !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    box-sizing: border-box !important;
  }
}

@media (max-width: 900px) {
  /* Mobile: center the logo in the visualizer. */
  main.visualizer > .visualizer-logo-wrap,
  .visualizer > .visualizer-logo-wrap,
  .visualizer-logo-wrap {
    align-items: center !important;
    justify-content: center !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
  }
}

/* ============================================================
 * EARTHQUAKE SHAKE on the loudest hits
 * ============================================================
 * The JS adds html.is-loud-sustained when the SoundCloud
 * amplitude crosses ~0.86. The bundle already animates
 * --shake-x/--shake-y via @keyframes loudShake on <html>.
 * Here we make the visualizer background image and the artist
 * logo physically shake with those vars so the whole visualizer
 * lurches on big hits. A slightly larger displacement than the
 * global 2-3px is used for a punchier earthquake. */
@keyframes dsr-quake-bg {
  0%, 100% { transform: translate3d(0, 0, 0); }
  20% { transform: translate3d(-6px, 4px, 0); }
  40% { transform: translate3d(5px, -5px, 0); }
  60% { transform: translate3d(-5px, 5px, 0); }
  80% { transform: translate3d(6px, -3px, 0); }
}
html.is-loud-sustained main.visualizer > .visualizer-bg,
html.is-loud-sustained .visualizer > .visualizer-bg,
html.is-loud-sustained .visualizer-bg,
html.is-loud-sustained #visualizer-bg {
  animation: dsr-quake-bg 90ms linear infinite !important;
}
/* The logo is intentionally NOT shaken — the earthquake is its own
 * thing on the background. Shaking the logo would override its
 * smooth pulse transform and cause a jerky snap. Also override the
 * bundle's earthquake animation on the logo WRAPPER so the whole
 * logo stays on its smooth pulse during quakes. */
html.is-loud-sustained main.visualizer > .visualizer-logo-wrap,
html.is-loud-sustained .visualizer > .visualizer-logo-wrap,
html.is-loud-sustained .visualizer-logo-wrap {
  animation: none !important;
}


/* Suppress the bundle's white flash lines under the header / at the
 * visualizer edge on loud hits — too busy with the other effects. */
html.is-loud-sustained body::before,
html.is-loud-sustained body::after {
  opacity: 0 !important;
}

/* ============================================================
 * PLAYHEAD — track exactly, no lag
 * ============================================================
 * The bundle sets a 0.1s width transition on the progress fill,
 * which (combined with SoundCloud's ~4/sec progress events) makes
 * the playhead feel delayed. We drive the fill every frame from the
 * interpolated position in JS, so remove the transition for an exact,
 * scannable playhead. (Keep the reset/scrub no-transition states.) */
.player-progress-fill,
.np-progress-fill {
  transition: none !important;
}

/* ============================================================
 * AUDIO-REACTIVE STATIC / DISTORTION on the visualizer
 * ============================================================
 * A film-grain / TV-static layer over the visualizer background
 * whose intensity scales with --audio-energy: more static on the
 * hardest hits, less as it quiets, none at rest. Sits above the
 * drip image (z-index 0) but below the frost (1) and logo (10),
 * and never blocks clicks. */
main.visualizer::before,
.visualizer::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='300'><filter id='s'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' seed='7' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.2 0'/></filter><rect width='100%' height='100%' filter='url(%23s)'/></svg>");
  background-size: 300px 300px;
  background-repeat: repeat;
  mix-blend-mode: multiply;
  /* Intensity tracks the music: 0 at rest, rising with sustained energy
     and spiking on each beat hit (--audio-pulse) so the static crackles
     in time with the drums. */
  opacity: calc(var(--audio-energy, 0) * 0.4 + var(--audio-pulse, 0) * 0.3);
  animation: dsr-static-drift 0.28s steps(6) infinite;
  will-change: background-position, opacity;
}
@keyframes dsr-static-drift {
  0%   { background-position: 0 0; }
  20%  { background-position: -80px 40px; }
  40%  { background-position: 100px -60px; }
  60%  { background-position: -50px -90px; }
  80%  { background-position: 70px 80px; }
  100% { background-position: 0 0; }
}

/* ============================================================
 * HORROR-VHS DISTORTION SET (toggleable layers)
 * ============================================================
 * Each layer is gated by a class on <html> so you can keep the
 * ones you like and drop the rest by removing that class:
 *   dvhs-rgbsplit  — chromatic aberration, grows with --audio-energy
 *   dvhs-tear      — horizontal tracking tears on hard hits
 *   dvhs-scanlines — constant faint scanlines, throb on hits
 *   dvhs-bleed     — color/saturation flash on hard hits
 *   dvhs-vignette  — signal-dropout vignette pulse on hard hits
 * All default ON (added to <html> by the snippet in the HTML). They
 * only affect the visualizer background image, never the logo, and
 * never block clicks. --audio-energy = continuous loudness (0..1);
 * html.is-loud-sustained = the hardest hits. */

/* ---- 1. RGB SPLIT / chromatic aberration (continuous) ---- */
html.dvhs-rgbsplit:not(.audio-at-rest) main.visualizer > .visualizer-bg,
html.dvhs-rgbsplit:not(.audio-at-rest) .visualizer > .visualizer-bg,
html.dvhs-rgbsplit:not(.audio-at-rest) .visualizer-bg,
html.dvhs-rgbsplit:not(.audio-at-rest) #visualizer-bg {
  filter:
    blur(calc(1px + var(--audio-energy, 0) * 30px))
    brightness(calc(1 + var(--audio-energy, 0) * 0.9))
    drop-shadow(calc(var(--audio-energy, 0) * 10px) 0 0 rgba(255, 0, 60, 0.5))
    drop-shadow(calc(var(--audio-energy, 0) * -10px) 0 0 rgba(0, 240, 255, 0.5))
    !important;
}
html.dvhs-rgbsplit.is-loud-sustained main.visualizer > .visualizer-bg,
html.dvhs-rgbsplit.is-loud-sustained .visualizer > .visualizer-bg,
html.dvhs-rgbsplit.is-loud-sustained .visualizer-bg,
html.dvhs-rgbsplit.is-loud-sustained #visualizer-bg {
  filter:
    blur(calc(1px + var(--audio-energy, 0) * 30px))
    brightness(calc(1 + var(--audio-energy, 0) * 0.9))
    drop-shadow(18px 0 0 rgba(255, 0, 60, 0.7))
    drop-shadow(-18px 0 0 rgba(0, 240, 255, 0.7))
    !important;
}

/* ---- 2. TRACKING TEARS on hard hits ---- */
@keyframes dvhs-tracking-tear {
  0%, 100% { clip-path: inset(0); transform: translate3d(0,0,0); }
  15% { clip-path: inset(8% 0 80% 0); transform: translate3d(-14px,0,0); }
  30% { clip-path: inset(35% 0 52% 0); transform: translate3d(12px,0,0); }
  45% { clip-path: inset(60% 0 28% 0); transform: translate3d(-9px,0,0); }
  60% { clip-path: inset(20% 0 68% 0); transform: translate3d(16px,0,0); }
  75% { clip-path: inset(78% 0 10% 0); transform: translate3d(-6px,0,0); }
  90% { clip-path: inset(45% 0 40% 0); transform: translate3d(8px,0,0); }
}
html.dvhs-tear.is-loud-sustained main.visualizer > .visualizer-bg,
html.dvhs-tear.is-loud-sustained .visualizer > .visualizer-bg,
html.dvhs-tear.is-loud-sustained .visualizer-bg,
html.dvhs-tear.is-loud-sustained #visualizer-bg {
  animation: dvhs-tracking-tear 240ms steps(7) infinite !important;
}

/* ---- 3. SCANLINES — constant, throb on hits ---- */
html.dvhs-scanlines:not(.audio-at-rest) main.visualizer::after,
html.dvhs-scanlines:not(.audio-at-rest) .visualizer::after {
  content: '' !important;
  display: block !important;
  position: absolute !important;
  inset: 0 !important;
  z-index: 2 !important;             /* above static(1)/bg(0), below logo(10) */
  pointer-events: none !important;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0,0,0,0) 0px,
    rgba(0,0,0,0) 2px,
    rgba(0,0,0,calc(0.18 + var(--audio-energy, 0) * 0.30)) 3px,
    rgba(0,0,0,calc(0.18 + var(--audio-energy, 0) * 0.30)) 4px
  ) !important;
  mix-blend-mode: multiply !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* ---- 4. COLOR BLEED / saturation flash on hard hits ---- */
@keyframes dvhs-bleed {
  0%   { filter: saturate(1) hue-rotate(0deg); }
  20%  { filter: saturate(3.2) hue-rotate(-18deg); }
  50%  { filter: saturate(2.2) hue-rotate(14deg); }
  100% { filter: saturate(1) hue-rotate(0deg); }
}
html.dvhs-bleed.is-loud-sustained main.visualizer,
html.dvhs-bleed.is-loud-sustained .visualizer {
  animation: dvhs-bleed 260ms ease-out !important;
}

/* ---- 5. SIGNAL-DROPOUT VIGNETTE pulse on hard hits ---- */
html.dvhs-vignette:not(.audio-at-rest) main.visualizer::before,
html.dvhs-vignette:not(.audio-at-rest) .visualizer::before {
  /* reuse the ::before (static) box — layer a vignette via box-shadow */
  box-shadow: inset 0 0 calc(60px + var(--audio-energy, 0) * 240px)
              calc(var(--audio-energy, 0) * 60px) rgba(0,0,0,0.85) !important;
}

/* Remove the audio-driven black tint overlay entirely. */
.visualizer-tint,
main.visualizer > .visualizer-tint,
.visualizer > .visualizer-tint {
  display: none !important;
}

/* Tracks with no SoundCloud artwork: show a neutral dark placeholder
 * instead of a broken-image icon. */
.track-art--noart {
  background: #1a1a1a !important;
  visibility: visible;
}
.track-art.is-broken-image {
  opacity: 0.15;
}

/* ============================================================
 * MOBILE PLAYER TWEAKS
 * - Hide the "NOW PLAYING" label (desktop-only feature)
 * - Hide the audio-energy LED indicator (the small colored dot)
 * - Center the play/skip controls in the mobile player bar
 * ============================================================ */
@media (max-width: 900px) {
  .now-playing-label { display: none !important; }
  .audio-led { display: none !important; }
  .player-controls {
    grid-area: controls !important;
    justify-content: center !important;
    justify-self: center !important;
    width: 100%;
  }
}

/* Belt-and-suspenders: ensure the native volume range slider (which
 * renders with a default blue accent on mobile browsers) is never
 * visible on mobile, even if it escapes its .volume-control wrapper. */
@media (max-width: 900px) {
  .volume-slider,
  input[type="range"].volume-slider { display: none !important; }
}

/* ============================================================
 * MOBILE PLAYER BAR — logo + like button layout
 * - Left artist logo: bigger, centered in the left column
 * - Remove play-count, show a bigger fire like-button on the right,
 *   centered in the right column
 * ============================================================ */
@media (max-width: 900px) {
  /* Left logo: enlarge the column and the logo, center it between the
   * prev button and the left edge. */
  .player-bar {
    grid-template-columns: 120px 1fr 120px !important;
  }
  .player-current-artist-logo {
    width: 96px !important;
    height: 96px !important;
    justify-self: center !important;
  }

  /* Right side: place the stats/like group in the current-cover column,
   * centered between the next button and the right edge. */
  .player-end-group {
    grid-area: current-cover !important;
    display: flex !important;
    align-items: center;
    justify-content: center;
    justify-self: center !important;
    align-self: center !important;
    width: 100%;
    height: 100%;
  }
  /* Hide the volume control and the play count on mobile. */
  .player-end-group .volume-control { display: none !important; }
  .player-end-group .stat-plays { display: none !important; }

  /* Bigger fire like button, centered. */
  .player-end-group .track-stats:not([hidden]) {
    display: flex !important;
    align-items: center;
    justify-content: center;
    width: 100%;
  }
  .player-end-group .like-btn {
    display: flex !important;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
  }
  .player-end-group .like-fire {
    font-size: 44px !important;
    line-height: 1;
  }
  .player-end-group .like-btn .stat-num {
    font-size: 12px;
  }
}

/* The like button now occupies the current-cover column on mobile, so
 * hide the small now-playing cover thumbnail there to avoid overlap. */
@media (max-width: 900px) {
  .player-bar > .player-meta-art { display: none !important; }
}




/* Breathing keyframe for the artist logo — one gentle in/out per beat
   (duration = --spin-duration = 60/bpm). The swell depth is scaled by
   --audio-pulse so the breath is deeper when the music is louder and
   almost still when it's quiet. Base breath is subtle (a few %). */
@keyframes logo-breathe {
  0%, 100% {
    transform: scale(calc(1 + var(--audio-pulse, 0) * 0.02));
  }
  50% {
    transform: scale(calc(1 - 0.03 - var(--audio-pulse, 0) * 0.10));
  }
}
