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

:root {
  --c1: #111827;
  --c2: #0b1020;
  --c3: #171717;
  --c4: #0f172a;
  --c5: #05070d;
  --home-tint: transparent;
}

@property --home-tint {
  syntax: '<color>';
  inherits: true;
  initial-value: transparent;
}

html,
body {
  width: 100%;
  min-height: 100%;
}

body {
  min-height: 100dvh;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #05070d;
  font-family:
    -apple-system,
    BlinkMacSystemFont,
    "Inter",
    "Segoe UI",
    sans-serif;
  color: #fff;
}

/* Home glows from the first card's real artwork (see --home-art in index.html):
   soft top-left wash flowing down. No albums -> flat dark (#05070d), same
   as About. The live album gradient belongs to Now Playing only. */
body.view-home {
  background: #05070d;
}

/* Only paint the tint wash once album art has set a real color. */
body.view-home.has-home-glow {
  background:
    linear-gradient(
      180deg,
      color-mix(in srgb, var(--home-tint) 62%, transparent) 0%,
      color-mix(in srgb, var(--home-tint) 30%, transparent) 28%,
      transparent 60%
    ),
    linear-gradient(180deg, #0a0d16 0%, #05070d 60%);
}

body.view-home::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: var(--home-art, none);
  background-size: 52rem auto;
  background-position: -8rem -14rem;
  background-repeat: no-repeat;
  filter: blur(6rem) saturate(1.35) brightness(1.18);
  opacity: 0;
  transition: opacity 0.8s ease;
}

/* Blurred art shows the moment a cover URL exists (CSS backgrounds have no
   CORS probe, so this never flickers). Empty grids keep it at 0 = flat dark. */
body.view-home.has-home-art::before {
  opacity: 0.85;
}

body.view-about {
  background: #05070d;
}

[class~="album-background"] {
  position: fixed;
  inset: -35%;
  z-index: 0;
  cursor: pointer;

  background:
    radial-gradient(
      ellipse at 20% 20%,
      var(--c1) 0%,
      transparent 42%
    ),
    radial-gradient(
      ellipse at 80% 20%,
      var(--c2) 0%,
      transparent 45%
    ),
    radial-gradient(
      ellipse at 80% 80%,
      var(--c3) 0%,
      transparent 45%
    ),
    radial-gradient(
      ellipse at 20% 80%,
      var(--c4) 0%,
      transparent 45%
    ),
    radial-gradient(
      ellipse at 50% 50%,
      var(--c5) 0%,
      #05070d 65%
    );

  background-size:
    55% 55%,
    60% 60%,
    55% 55%,
    60% 60%,
    100% 100%;

  background-position:
    0% 0%,
    100% 0%,
    100% 100%,
    0% 100%,
    50% 50%;

  background-repeat: no-repeat;

  filter: blur(3.4375rem) saturate(1.25);

  transform:
    translate3d(0, 0, 0)
    scale(1.15);

  opacity: 0.95;

  transition:
    background 2.5s ease,
    filter 1.5s ease;

  will-change: transform;

  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;

  transform-style: preserve-3d;

  animation:
    albumColorFlow
    30s
    ease-in-out
    infinite;
}

[class~="album-background"]::after {
  content: "";
  position: absolute;
  inset: 0;

  background:
    radial-gradient(
      circle at center,
      transparent 10%,
      rgba(0, 0, 0, 0.18) 55%,
      rgba(0, 0, 0, 0.45) 100%
    );
}

@keyframes albumColorFlow {
  0% {
    transform:
      translate3d(-1.5%, -1%, 0)
      scale(1.15);
  }

  25% {
    transform:
      translate3d(1%, -1.5%, 0)
      scale(1.17);
  }

  50% {
    transform:
      translate3d(1.5%, 1%, 0)
      scale(1.16);
  }

  75% {
    transform:
      translate3d(-1%, 1.5%, 0)
      scale(1.17);
  }

  100% {
    transform:
      translate3d(-1.5%, -1%, 0)
      scale(1.15);
  }
}

body[class~="paused-gradient"]
[class~="album-background"] {
  animation-play-state: paused;
}

/* About keeps a flat backdrop and the live album gradient is Now Playing
   only. Hiding (rather than removing) the layer keeps the Now Playing
   colors fresh when the listener navigates back. Home paints its own
   artwork tint via body.view-home above. */
body.view-home [class~="album-background"],
body.view-about [class~="album-background"] {
  opacity: 0;
  visibility: hidden;
  animation: none;
  transition:
    opacity 0.3s ease,
    visibility 0s linear 0.3s;
}

body.view-now-playing [class~="album-background"] {
  opacity: 0.95;
  visibility: visible;
  transition: opacity 0.3s ease;
}

body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;

  background:
    linear-gradient(
      180deg,
      rgba(0, 0, 0, 0.08),
      rgba(0, 0, 0, 0.32)
    );
}

[class~="app"] {
  width: 100%;
  max-width: 26.25rem;
  padding: 1.375rem;

  text-align: center;

  position: relative;
  z-index: 2;

  overflow: visible;

  transition:
    transform 0.45s ease;
}

.now-playing-heading {
  display: none;
}

.desktop-nav {
  display: none;
}

.desktop-profile-button {
  position: fixed;
  z-index: 6;
  top: 0.45rem;
  right: 1.25rem;
  display: none;
  width: 2.35rem;
  height: 2.35rem;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  cursor: pointer;
  backdrop-filter: blur(1.25rem) saturate(150%);
  -webkit-backdrop-filter: blur(1.25rem) saturate(150%);
}

.desktop-profile-button:hover,
.desktop-profile-button:focus-visible {
  background: rgba(255, 255, 255, 0.22);
}

.desktop-profile-button:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.75);
  outline-offset: 2px;
}

.desktop-profile-button svg {
  width: 1.3rem;
  height: 1.3rem;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-width: 1.7;
}

.desktop-profile-button .profile-avatar {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}

.profile-dialog {
  width: min(26rem, calc(100vw - 2rem));
  padding: 2.25rem;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 1.25rem;
  background: linear-gradient(145deg, #302d1e, #172a25);
  color: #fff;
  box-shadow: 0 1.5rem 5rem rgba(0, 0, 0, 0.5);
}

.profile-dialog h2 {
  margin: 0.75rem 0 1.15rem;
  font-size: 1.6rem;
}

.profile-dialog-copy,
.profile-dialog-status {
  color: rgba(255, 255, 255, 0.78);
  line-height: 1.55;
}

.profile-dialog-copy {
  margin: 0 0 1.25rem;
}

.profile-dialog-status {
  padding: 0.75rem 0.9rem;
  border-radius: 0.7rem;
  background: rgba(255, 255, 255, 0.08);
  font-size: 0.88rem;
}

.profile-dialog.is-signed-in > #profileDialogTitle {
  display: none;
}

.profile-dialog.is-signed-in > .site-page-eyebrow {
  display: block;
  margin: 0 0 1rem;
  text-align: center;
}

.profile-account-avatar {
  display: block;
  width: 6rem;
  height: 6rem;
  margin: 0 auto 0.75rem;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-radius: 50%;
  object-fit: cover;
}

.profile-account-name {
  margin: 0;
  text-align: center;
  font-size: 1rem;
  font-weight: 600;
}

.profile-account-email {
  margin: 0.25rem 0 1.25rem;
  color: rgba(255, 255, 255, 0.68);
  text-align: center;
  font-size: 0.8rem;
}

.profile-sync-status {
  margin: -0.5rem 0 1rem;
  color: rgba(255, 255, 255, 0.68);
  text-align: center;
  font-size: 0.76rem;
  line-height: 1.4;
}

.profile-logout-button {
  width: 100%;
  margin-top: 0;
  padding: 0.8rem 1rem;
  border: 0;
  border-radius: 999px;
  background: #fff;
  color: #111;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.profile-logout-button:hover,
.profile-logout-button:focus-visible {
  background: rgba(255, 255, 255, 0.86);
}

.profile-dialog-action {
  width: 100%;
  margin-top: 0.25rem;
  padding: 0.9rem 1rem;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.75);
  color: #25251e;
  font: inherit;
  font-weight: 600;
}

.profile-dialog-action:disabled {
  cursor: not-allowed;
  opacity: 0.72;
}

.profile-dialog::backdrop {
  background: rgba(8, 12, 10, 0.62);
  backdrop-filter: blur(0.25rem);
}

.site-page {
  display: none;
}

/* Tab switches slide the incoming page in from the side of the tab it came
   from (direction published by applyView() as --view-slide-dir). Only the
   `from` frame is declared, so the implicit `to` frame uses each page's own
   styles and the animation cannot clobber transforms such as the has-lyrics
   layout. */
@keyframes viewSlideIn {
  from {
    opacity: 0;
    transform: translateX(calc(var(--view-slide-dir, 1) * 3.5rem));
  }
}

body.view-home .site-page.home-page,
body.view-about .site-page.about-page {
  /* 0.8s + a gentle ease-in-out keeps the glide slow and even: the old
     easeOutQuint curve covered most of the distance in the first frames,
     which read as a snap rather than a slide. */
  animation: viewSlideIn 0.8s cubic-bezier(0.4, 0, 0.2, 1);
}

/* The desktop Now Playing heading is pinned to the page outside the sliding
   containers (see .now-playing-heading), so it needs its own copy of the
   slide or it stays behind while the player and lyrics glide in. Its resting
   transform carries the translateX(-50%) centring, so the keyframe repeats
   that base and adds the same directional offset as viewSlideIn. */
@keyframes nowPlayingHeadingSlideIn {
  from {
    opacity: 0;
    transform: translateX(calc(-50% + var(--view-slide-dir, 1) * 3.5rem));
  }
}

@media (min-width: 56.25rem) and (pointer: fine) {
  /* Now Playing's sideways slide is desktop-only. On mobile the page is the
     launch view — showView() no-ops there and the body class never switches —
     so the global rule only made the player card and lyrics slide in on every
     load/refresh for no reason. */
  body.view-now-playing .player-view {
    animation: viewSlideIn 0.8s cubic-bezier(0.4, 0, 0.2, 1);
  }

  body.view-now-playing #nowPlayingHeading {
    animation: nowPlayingHeadingSlideIn 0.8s cubic-bezier(0.4, 0, 0.2, 1);
  }
}

/* Tapping the docked mini player's artwork/title opens Now Playing like a
   sheet rising off the bar, so those entries skip the sideways viewSlideIn
   and travel straight up from below the viewport instead. showView() arms
   this by adding `view-rises-from-mini` to the body; the class lives for the
   whole Now Playing stay and is stripped only by the next view switch (in the
   same style recalc that removes `view-now-playing`) because dropping it any
   earlier would restart the sideways slide on top of the rise. Nav-tab
   switches never carry the class and keep the horizontal slide. The distance
   is a full viewport (rather than 100% of each element) on purpose: the
   heading, player card and lyrics panel are three separate elements with very
   different heights, and a shared absolute distance keeps them rigidly in
   step so the page reads as one sheet moving. */
@keyframes nowPlayingRiseIn {
  from {
    opacity: 0;
    transform: translateY(100vh);
  }
}

body.view-rises-from-mini.view-now-playing .player-view {
  animation: nowPlayingRiseIn 0.8s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Same idea for the pinned heading; the keyframe repeats its resting
   translateX(-50%) centring, exactly like nowPlayingHeadingSlideIn. */
@keyframes nowPlayingHeadingRiseIn {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(100vh);
  }
}

@media (min-width: 56.25rem) and (pointer: fine) {
  body.view-rises-from-mini.view-now-playing #nowPlayingHeading {
    animation: nowPlayingHeadingRiseIn 0.8s cubic-bezier(0.4, 0, 0.2, 1);
  }
}

.mini-player {
  display: none;
}

@media (min-width: 56.25rem) and (pointer: fine) {
  .desktop-profile-button {
    position: fixed;
    z-index: 6;
    top: 0.45rem;
    right: 1.25rem;
    display: none;
    width: 2.35rem;
    height: 2.35rem;
    place-items: center;
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    cursor: pointer;
    backdrop-filter: blur(1.25rem) saturate(150%);
    -webkit-backdrop-filter: blur(1.25rem) saturate(150%);
  }

  body.view-home .desktop-profile-button {
    display: grid;
  }

  .desktop-profile-button:hover,
  .desktop-profile-button:focus-visible {
    background: rgba(255, 255, 255, 0.22);
  }

  .desktop-profile-button:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.75);
    outline-offset: 2px;
  }

  .desktop-profile-button svg {
    width: 1.3rem;
    height: 1.3rem;
    fill: none;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-width: 1.7;
  }

  .desktop-profile-button .profile-avatar {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    object-fit: cover;
  }

  .profile-dialog {
    width: min(26rem, calc(100vw - 2rem));
    padding: 2rem;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 1.25rem;
    background: linear-gradient(145deg, #302d1e, #172a25);
    color: #fff;
    box-shadow: 0 1.5rem 5rem rgba(0, 0, 0, 0.5);
  }

  .profile-dialog h2 {
    margin: 0.35rem 0 0.75rem;
    font-size: 1.6rem;
  }

  .profile-dialog-copy,
  .profile-dialog-status {
    color: rgba(255, 255, 255, 0.78);
    line-height: 1.55;
  }

  .profile-dialog-status {
    padding: 0.75rem 0.9rem;
    border-radius: 0.7rem;
    background: rgba(255, 255, 255, 0.08);
    font-size: 0.88rem;
  }

  .profile-dialog-action {
    width: 100%;
    margin-top: 0.5rem;
    padding: 0.8rem 1rem;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.75);
    color: #25251e;
    font: inherit;
    font-weight: 600;
  }

  .profile-dialog-action:disabled {
    cursor: not-allowed;
    opacity: 0.72;
  }

  .profile-dialog::backdrop {
    background: rgba(8, 12, 10, 0.62);
    backdrop-filter: blur(0.25rem);
  }
  body.view-home .site-page.home-page,
  body.view-about .site-page.about-page {
    position: fixed;
    inset: 0;
    z-index: 2;
    display: grid;
    width: 100%;
    min-height: 100dvh;
    place-items: center;
    padding: 4rem 2rem 2rem;
    text-align: center;
  }

  body.view-home .site-page.home-page {
    /* Anchor the Browse-music column to the top padding. Centering made the
       header float to mid-screen whenever the grid was short (first paint,
       "Choose a genre", empty or error states) and snap back up once album
       cards rendered. The section now holds its position and only the inner
       scroll area changes with card count. Horizontal centring is unchanged
       (justify-items stays centre from place-items above). */
    align-items: start;
  }

  body.view-home .player-view,
  body.view-about .player-view,
  body.view-home .site-page.about-page,
  body.view-about .site-page.home-page,
  body.view-now-playing .site-page {
    display: none;
  }

  body.mini-player-visible.view-home .mini-player,
  body.mini-player-visible.view-about .mini-player {
    position: fixed;
    z-index: 7;
    right: 0;
    bottom: 0;
    left: 0;
    display: grid;
    width: 100%;
    min-height: 4.5rem;
    margin: 0;
    padding: 0.5rem clamp(1rem, 4vw, 3rem);
    /* Side clusters keep their natural width (buttons can never clip); the
       center is the only flexible track, so it absorbs the remainder and
       shrinks gracefully (seek shortens, title ellipsizes) at any width. */
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-rows: auto;
    align-items: center;
    column-gap: 0;
    /* Safety net: the center track is flexible so nothing should ever spill,
       but this guarantees the fixed bar never forces a horizontal scrollbar. */
    overflow: hidden;
    border: 0;
    border-top: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 0;
    background: #05070d;
    box-shadow: none;
    color: #fff;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    animation: miniPlayerDockIn 0.35s ease-out both;
  }

  @keyframes miniPlayerDockIn {
    from { opacity: 0; transform: translateY(0.65rem); }
    to { opacity: 1; transform: translateY(0); }
  }

  body.mini-player-visible.view-home .site-page-content,
  body.mini-player-visible.view-about .site-page-content {
    padding-bottom: 8rem;
  }

  .mini-player button {
    color: inherit;
    font: inherit;
  }

  .mini-player-transport,
  .mini-player-actions {
    display: flex;
    align-items: center;
    gap: 0.35rem;
  }

  .mini-player-transport {
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
  }

  .mini-player-actions {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
  }

  /* Center unit: artwork + track info + seek bar grouped and truly centered
     in the middle `auto` grid column, regardless of the side-cluster widths. */
  .mini-player-center {
    display: flex;
    min-width: 0;
    align-items: center;
    gap: clamp(0.75rem, 2.5vw, 1.75rem);
    grid-column: 2;
    grid-row: 1;
    /* Fill the middle track (instead of sizing to content) so a narrow window
       shrinks the center rather than spilling it over the side clusters. */
    justify-self: stretch;
    justify-content: center;
    width: 100%;
    padding: 0 clamp(0.5rem, 2vw, 1.5rem);
  }

  .mini-player-art,
  .mini-player-track,
  .mini-player button.mini-player-toggle,
  .mini-player button.mini-player-ctrl {
    border: 0;
    cursor: pointer;
  }

  /* Round icon controls (prev/next/like/info). */
  .mini-player-ctrl {
    display: grid;
    width: 2.3rem;
    height: 2.3rem;
    place-items: center;
    padding: 0;
    border-radius: 50%;
    background: transparent;
    color: rgba(255, 255, 255, 0.72);
    transition: color 0.18s ease, background 0.18s ease, transform 0.18s ease;
  }

  .mini-player-ctrl svg {
    width: 1.15rem;
    height: 1.15rem;
    fill: currentColor;
  }

  /* Volume toggle: show the "speaker" icon by default, the "muted" icon when
     the button carries .is-muted. Both SVGs live in the button; only one shows. */
  .mini-player-volume .icon-off {
    display: none;
  }

  .mini-player-volume.is-muted .icon-on {
    display: none;
  }

  .mini-player-volume.is-muted .icon-off {
    display: block;
  }

  .mini-player-volume.is-muted {
    color: #ff5c7a;
  }

  /* Visible volume slider (0-100) sitting right after the speaker icon. */
  .mini-player-volume-slider {
    width: clamp(4.5rem, 9vw, 7rem);
    height: 1rem;
    margin: 0 0.25rem;
    padding: 0;
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    cursor: pointer;
  }

  .mini-player-volume-slider::-webkit-slider-runnable-track {
    height: 0.25rem;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.22);
  }

  .mini-player-volume-slider::-moz-range-track {
    height: 0.25rem;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.22);
  }

  .mini-player-volume-slider::-webkit-slider-thumb {
    width: 0.7rem;
    height: 0.7rem;
    margin-top: -0.225rem;
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.35);
  }

  .mini-player-volume-slider::-moz-range-thumb {
    width: 0.7rem;
    height: 0.7rem;
    border: 0;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.35);
  }

  .mini-player-volume-slider:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.6);
    outline-offset: 2px;
    border-radius: 999px;
  }

  /* Numeric volume readout sitting right after the slider. */
  .mini-player-volume-value {
    min-width: 2ch;
    margin-left: 0.15rem;
    color: rgba(255, 255, 255, 0.72);
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
    user-select: none;
    transition: color 0.18s ease;
  }

  .mini-player-volume-value.is-muted {
    color: #ff5c7a;
  }

  .mini-player-ctrl:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    transform: scale(1.06);
  }

  .mini-player-ctrl:active:not(:disabled) {
    transform: scale(0.94);
  }

  .mini-player-ctrl:disabled {
    cursor: default;
    opacity: 0.32;
  }

  .mini-player-glyph {
    font-size: 1.05rem;
    font-style: normal;
    font-weight: 700;
    line-height: 1;
  }

  /* Active (pressed) transport/action state, e.g. a liked track. */
  .mini-player-ctrl.is-active {
    color: #fff;
  }

  .mini-player-like.is-active svg {
    fill: #ff5c7a;
  }

  .mini-player-art {
    width: 3rem;
    height: 3rem;
    flex: 0 0 auto;
    overflow: hidden;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 0.6rem;
    background: rgba(255, 255, 255, 0.1);
    box-shadow: 0 0.35rem 1rem rgba(0, 0, 0, 0.32);
  }

  .mini-player-art img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.25s ease;
  }

  .mini-player-art img.is-wide-thumbnail {
    transform: scale(1.35);
  }

  .mini-player-art:hover img {
    transform: scale(1.05);
  }

  .mini-player-now {
    display: flex;
    min-width: 0;
    flex: 0 1 auto;
    align-items: center;
    gap: 0.85rem;
  }

  .mini-player-meta {
    display: flex;
    min-width: 0;
    flex: 0 1 auto;
    flex-direction: column;
    gap: 0.3rem;
    max-width: 16rem;
  }

  .mini-player-track {
    display: flex;
    min-width: 0;
    flex-direction: column;
    align-items: flex-start;
    overflow: hidden;
    padding: 0;
    background: transparent;
    text-align: left;
  }

  .mini-player-title,
  .mini-player-artist {
    position: relative;
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .mini-player-title.is-marquee,
  .mini-player-artist.is-marquee {
    text-overflow: clip;
  }

  .mini-player-title.is-marquee::before,
  .mini-player-artist.is-marquee::before {
    position: absolute;
    top: 0;
    left: 0;
    width: max-content;
    white-space: nowrap;
    color: inherit;
    content: attr(data-marquee-text) "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0" attr(data-marquee-text) "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0";
    animation: miniPlayerMarquee var(--mini-marquee-duration, 24s) linear infinite;
  }

  .mini-player-title.is-marquee,
  .mini-player-artist.is-marquee {
    color: transparent;
  }

  .mini-player-title.is-marquee::before {
    color: #fff;
  }

  .mini-player-artist.is-marquee::before {
    color: rgba(255, 255, 255, 0.68);
  }

  .mini-player:not(.is-playing) .mini-player-title.is-marquee::before,
  .mini-player:not(.is-playing) .mini-player-artist.is-marquee::before {
    animation-play-state: paused;
  }

  @keyframes miniPlayerMarquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
  }

  .mini-player-title {
    font-size: 0.95rem;
    font-weight: 700;
  }

  .mini-player-artist {
    margin-top: 0.15rem;
    color: rgba(255, 255, 255, 0.68);
    font-size: 0.78rem;
  }

  .mini-player-seek {
    display: flex;
    flex: 0 1 auto;
    align-items: center;
    gap: 0.25rem;
    /* Sits right after the artwork/track info; capped so the bar keeps a
       moderate size instead of stretching across the whole player. */
    width: clamp(16rem, 34vw, 30rem);
    min-width: 0;
    max-width: 100%;
  }

  .mini-player-clock {
    flex: 0 0 auto;
    min-width: 2rem;
    color: rgba(255, 255, 255, 0.62);
    font-size: 0.68rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
  }

  .mini-player-clock:last-child {
    text-align: right;
  }

  .mini-player-progress {
    position: relative;
    flex: 1 1 auto;
    height: 0.25rem;
    overflow: hidden;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.18);
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
  }

  .mini-player-progress-fill {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 0%;
    border-radius: inherit;
    background: linear-gradient(to right, rgba(255, 255, 255, 0.6), #fff);
    box-shadow: 0 0 0.6rem rgba(255, 255, 255, 0.28);
    transition: width 0.25s linear;
  }

  /* When the track length is unknown (e.g. live radio before a song length is
     known), render the seek as a static full "LIVE" indicator instead of a
     moving bar. JS toggles .is-live on the progress container for this. */
  .mini-player-progress.is-live .mini-player-progress-fill {
    width: 100%;
    background: linear-gradient(to right, rgba(255, 255, 255, 0.32), rgba(255, 255, 255, 0.5));
    box-shadow: none;
  }

  .mini-player-progress.is-live {
    cursor: default;
  }

  /* Seekable only for on-demand tracks (JS toggles .is-seekable). */
  .mini-player-progress.is-seekable {
    cursor: pointer;
  }

  /* Freeze the fill while dragging so it snaps to the pointer. */
  .mini-player-progress.is-scrubbing .mini-player-progress-fill {
    transition: none;
  }

  /* Transparent hit area so the 4px track is still easy to grab. */
  .mini-player-progress::after {
    position: absolute;
    top: -0.5rem;
    right: 0;
    bottom: -0.5rem;
    left: 0;
    content: "";
  }

  .mini-player-toggle {
    position: relative;
    display: grid;
    width: 2.6rem;
    height: 2.6rem;
    place-items: center;
    border-radius: 50%;
    background: transparent;
    color: #fff;
    transition: transform 0.18s ease, background 0.18s ease, color 0.18s ease;
  }

  .mini-player-toggle:hover {
    transform: scale(1.06);
    background: rgba(255, 255, 255, 0.12);
  }

  .mini-player-toggle:focus-visible,
  .mini-player-art:focus-visible,
  .mini-player-track:focus-visible,
  .mini-player-ctrl:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.82);
    outline-offset: 3px;
  }

  .mini-player-toggle svg {
    width: 1.5rem;
    height: 1.5rem;
    fill: currentColor;
  }

  .mini-player-toggle.is-loading {
    opacity: 1;
  }

  .mini-player-toggle.is-loading svg {
    opacity: 0;
  }

  .mini-player-toggle.is-loading::after {
    position: absolute;
    width: 1.35rem;
    height: 1.35rem;
    border: 0.15rem solid rgba(255, 255, 255, 0.28);
    border-top-color: #fff;
    border-radius: 50%;
    content: '';
    animation: streamSpinner 0.8s linear infinite;
  }

  @media (prefers-reduced-motion: reduce) {
    .mini-player,
    .mini-player-art img,
    .mini-player-toggle {
      animation: none;
      transition: none;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .mini-player-title.is-marquee::before,
    .mini-player-artist.is-marquee::before {
      animation: none;
    }
  }

  /* Shorter desktop windows: the buttons keep their full size and breathing
     room; only the flexible center (title + seek bar) gives up space. */
  @media (max-width: 75rem) {
    .mini-player-transport,
    .mini-player-actions {
      gap: 0.6rem;
    }

    .mini-player-volume-slider {
      width: clamp(4.5rem, 9vw, 6rem);
      margin: 0 0.25rem;
    }

    .mini-player-meta {
      max-width: 10rem;
    }

    .mini-player-seek {
      width: clamp(8rem, 30vw, 20rem);
    }
  }

  @media (max-width: 62rem) {
    body.mini-player-visible.view-home .mini-player,
    body.mini-player-visible.view-about .mini-player {
      padding-right: 1rem;
      padding-left: 1rem;
    }

    .mini-player-transport,
    .mini-player-actions {
      gap: 0.5rem;
    }

    .mini-player-center {
      gap: 0.75rem;
      padding: 0 1rem;
    }

    .mini-player-now {
      gap: 0.7rem;
    }

    .mini-player-meta {
      max-width: 8rem;
    }

    .mini-player-seek {
      width: clamp(6rem, 24vw, 14rem);
    }

    .mini-player-volume-slider {
      width: 4.5rem;
      margin: 0 0.25rem;
    }
  }

  .site-page-content {
    width: min(42rem, 100%);
    padding: clamp(2rem, 6vw, 5rem);
  }

  .site-page-eyebrow {
    margin-bottom: 1rem;
    color: rgba(255, 255, 255, 0.66);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.18em;
  }

  .site-page h1 {
    margin: 0;
    color: #fff;
    font-size: clamp(2.5rem, 6vw, 5rem);
    font-weight: 700;
    letter-spacing: -0.045em;
    line-height: 1.05;
  }

  .site-page-copy {
    margin: 1rem 0 0;
    color: rgba(255, 255, 255, 0.75);
    font-size: clamp(1rem, 1.5vw, 1.2rem);
    line-height: 1.6;
  }

  .site-page-action {
    min-height: 2.75rem;
    margin-top: 2rem;
    padding: 0 1.35rem;
    border: 1px solid rgba(255, 255, 255, 0.24);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    backdrop-filter: blur(0.75rem);
    -webkit-backdrop-filter: blur(0.75rem);
    transition: background 0.2s ease, transform 0.2s ease;
  }

  .site-page-action:hover,
  .site-page-action:focus-visible {
    background: rgba(255, 255, 255, 0.18);
    transform: translateY(-1px);
  }

  .site-page-action:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.7);
    outline-offset: 3px;
  }

  /* The About page is a full article, so its column scrolls inside the fixed
     overlay the way the home catalog does. */
  .about-page .site-page-content {
    width: min(84rem, 100%);
    max-height: 100%;
    overflow-x: hidden;
    overflow-y: auto;
    scrollbar-width: none;
  }

  .about-page .site-page-content::-webkit-scrollbar {
    display: none;
  }

  .about-story {
    max-width: 68rem;
    margin-top: 1.75rem;
    font-size: 1.08rem;
    line-height: 1.75;
    text-align: left;
  }

  .about-story .site-page-copy {
    max-width: 68ch;
    margin: 0 0 1.15rem;
    color: rgba(255, 255, 255, 0.8);
    font-size: inherit;
    line-height: inherit;
  }

  /* Every paragraph of the story shares one type size, one line rhythm and one
     colour, so the article reads as a single block of text. */

  .about-story .site-page-copy:last-child {
    margin-bottom: 0;
  }

  .about-voices {
    margin-top: 3.25rem;
    padding-top: 2rem;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
  }

  .about-voices-heading {
    margin-bottom: 1.25rem;
  }

  .about-voices-heading .site-page-eyebrow {
    margin: 0;
  }

  .about-voices-intro {
    margin: 0.65rem 0 0;
    color: rgba(255, 255, 255, 0.68);
    font-size: 0.95rem;
  }

  .about-voices-grid {
    display: grid;
    /* A flat 14rem floor wrapped the four artists 3+1 on every width below
       ~1145px (the whole tablet range). The clamp keeps 14rem on wide
       screens but lets the minimum shrink with the viewport, so all four
       portraits share one row on smaller tablets; the cards — and their
       photos, sized by the media's 4/4.5 aspect ratio — simply get smaller
       as the window narrows. Below ~640px the grid falls back to 2x2 and
       then single column like before. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, clamp(7rem, 17vw, 14rem)), 1fr));
    gap: 1rem;
    list-style: none;
  }

  .about-voice {
    display: grid;
    gap: 0.55rem;
    justify-items: stretch;
    padding: 0 0 1rem;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 0.7rem;
    background: rgba(255, 255, 255, 0.035);
    overflow: hidden;
    box-shadow: 0 0.8rem 2rem rgba(0, 0, 0, 0.18);
  }

  .about-voice-media {
    display: grid;
    width: 100%;
    aspect-ratio: 4 / 4.5;
    place-items: center;
    background: linear-gradient(145deg, rgba(255, 255, 255, 0.17), rgba(255, 255, 255, 0.025));
    overflow: hidden;
  }

  .about-voice-media img,
  .about-voice-monogram {
    grid-area: 1 / 1;
  }

  .about-voice-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 20%;
    animation: aboutPortraitReveal 0.45s ease both;
  }

  @keyframes aboutPortraitReveal {
    from { opacity: 0; transform: scale(1.025); }
    to { opacity: 1; transform: scale(1); }
  }

  .about-voice-monogram {
    color: rgba(255, 255, 255, 0.62);
    font-size: 1.6rem;
    font-weight: 700;
    letter-spacing: 0.14em;
  }

  /* Fallback tile: shows when a portrait is unavailable or fails to load (offline). */
  .about-voice-media:not(:has(img)) {
    background:
      radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.24) 0 15%, rgba(255, 255, 255, 0.07) 15% 17%, transparent 17%),
      repeating-radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.08) 0 1px, transparent 1px 6px),
      linear-gradient(160deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.02));
  }

  .about-voice-name {
    display: block;
    margin: 0.25rem 1rem 0;
    color: #fff;
    font-size: 0.95rem;
    font-weight: 600;
  }

  .about-voice-role {
    display: block;
    margin: 0 1rem;
    color: rgba(255, 255, 255, 0.62);
    font-size: 0.78rem;
    line-height: 1.45;
  }

  .about-voices-credit {
    margin-top: 1rem;
    color: rgba(255, 255, 255, 0.48);
    font-size: 0.72rem;
    line-height: 1.6;
  }

  .about-signoff {
    margin-top: 3.75rem;
  }

  .about-signoff::before {
    content: "";
    display: block;
    width: 7rem;
    height: 1px;
    margin: 0 auto 1.5rem;
    border-radius: 999px;
    background: linear-gradient(90deg, transparent, rgba(237, 204, 163, 0.82), transparent);
  }

  /* The station name is the one piece of pure display type on the page: a
     tracked serif wordmark with a soft vertical sheen. */
  .about-signoff #aboutTitle {
    margin: 0;
    font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
    font-size: clamp(2.1rem, 4.4vw, 3.5rem);
    font-weight: 400;
    letter-spacing: 0.14em;
    line-height: 1.25;
    text-transform: uppercase;
    background: linear-gradient(180deg, #fff 0%, rgba(255, 255, 255, 0.85) 45%, rgba(255, 255, 255, 0.6) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    filter:
      drop-shadow(
        0 0.35rem 1.1rem
        rgba(0, 0, 0, 0.45)
      );
  }

  .about-signoff-note {
    margin: 0.75rem 0 0;
    color: rgba(255, 255, 255, 0.58);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
  }

  .about-signoff + .site-page-action {
    min-height: 2.9rem;
    margin-top: 1.5rem;
    padding: 0 1.5rem;
    border-color: rgba(237, 204, 163, 0.42);
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0.055));
    box-shadow: 0 0.55rem 1.5rem rgba(0, 0, 0, 0.18), inset 0 1px rgba(255, 255, 255, 0.16);
  }

  .about-signoff + .site-page-action:hover,
  .about-signoff + .site-page-action:focus-visible {
    border-color: rgba(255, 232, 203, 0.72);
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.08));
    box-shadow: 0 0.8rem 1.8rem rgba(0, 0, 0, 0.24), inset 0 1px rgba(255, 255, 255, 0.22);
  }

  body.view-now-playing .now-playing-heading {
    /* Absolute (not fixed) so that in a short window the heading scrolls away
       with the page instead of staying pinned over the player. At scroll 0 it
       sits exactly where the fixed heading did. */
    position: absolute;
    top: calc(4rem + clamp(1.25rem, 3.5vw, 2rem) - 1.25rem);
    left: 50%;
    z-index: 4;
    display: block;
    width: min(58rem + clamp(2rem, 4vw, 4rem) + min(27rem, 29vw), 100vw - 2rem);
    max-width: none;
    box-sizing: border-box;
    padding-left: clamp(1.25rem, 3.5vw, 3.5rem);
    transform: translateX(-50%);
    text-align: left;
    pointer-events: none;
    overflow: visible;
  }

  body.view-now-playing .app { top: 0; }

  .now-playing-heading h1 {
    margin: 0;
    color: #fff;
    font-size: clamp(2.25rem, 4.5vw, 4rem);
    font-weight: 700;
    letter-spacing: -0.045em;
    line-height: 1.05;
    overflow: visible;
  }

  .now-playing-heading p {
    margin: 0.75rem 0 0;
    color: rgba(255, 255, 255, 0.75);
    font-size: clamp(1rem, 1.5vw, 1.2rem);
    line-height: 1.6;
  }

  .home-page .site-page-content {
    width: min(88rem, 100%);
    max-height: calc(100dvh - 4rem);
    padding: clamp(1rem, 2.5vw, 2.25rem);
    overflow-x: hidden;
    overflow-y: auto;
    scroll-behavior: auto;
    overscroll-behavior-y: contain;
    -webkit-overflow-scrolling: touch;
    text-align: left;
    scrollbar-width: none;
  }

  .home-page .site-page-content::-webkit-scrollbar {
    display: none;
  }

  .home-catalog-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 2rem;
  }

.home-catalog-header h1 {
    font-size: clamp(2.25rem, 4.5vw, 4rem);
  }

  .home-catalog-header .site-page-copy {
    margin-top: 0.75rem;
  }

  .catalog-search {
    position: relative;
    display: flex;
    flex: 0 1 22rem;
    align-items: center;
    gap: 0.35rem;
    min-width: 14rem;
    margin: 0 0 0.4rem;
    padding: 0.35rem 0.45rem 0.35rem 0.9rem;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.1);
    box-shadow: inset 0 1px rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(1rem);
    -webkit-backdrop-filter: blur(1rem);
  }

  .catalog-suggestions {
    position: absolute;
    top: calc(100% + 0.65rem);
    right: 0;
    z-index: 12;
    width: min(28rem, 86vw);
    max-height: min(24rem, 55vh);
    overflow-y: auto;
    padding: 0.4rem;
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 1rem;
    background: rgba(25, 22, 25, 0.88);
    box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(1.25rem);
    -webkit-backdrop-filter: blur(1.25rem);
  }

  .catalog-suggestions[hidden] { display: none; }
  .catalog-suggestion { display: flex; width: 100%; flex-direction: column; gap: 0.2rem; padding: 0.7rem 0.8rem; border: 0; border-radius: 0.65rem; background: transparent; color: #fff; text-align: left; cursor: pointer; }
  .catalog-suggestion:hover, .catalog-suggestion:focus-visible { outline: 0; background: rgba(255, 255, 255, 0.13); }
  .catalog-suggestion-title { overflow: hidden; font-size: 0.88rem; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
  .catalog-suggestion-artist, .catalog-suggestion-status { color: rgba(255, 255, 255, 0.62); font-size: 0.78rem; }
  .catalog-suggestion-artist { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .catalog-suggestion-status { padding: 0.8rem; }
  .catalog-search:focus-within { border-color: rgba(255, 255, 255, 0.55); background: rgba(255, 255, 255, 0.15); }
  .catalog-search input { width: 100%; min-width: 0; padding: 0.45rem 0; border: 0; outline: 0; background: transparent; color: #fff; font: inherit; font-size: 0.9rem; }
  .catalog-search input::placeholder { color: rgba(255, 255, 255, 0.62); }
  .catalog-search input::-webkit-search-cancel-button { display: none; }
  .catalog-search > button[hidden] { display: none; }
  .catalog-search .catalog-clear-button { background: transparent; }
  .catalog-search .catalog-clear-button:hover, .catalog-search .catalog-clear-button:focus-visible { background: rgba(255, 255, 255, 0.15); }
  .catalog-search > button { display: grid; flex: 0 0 2.2rem; width: 2.2rem; height: 2.2rem; place-items: center; border: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.15); color: #fff; cursor: pointer; }
  .catalog-search > button:hover, .catalog-search > button:focus-visible { background: rgba(255, 255, 255, 0.25); }
  .catalog-search > button:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.8); outline-offset: 2px; }
  .catalog-search svg { width: 1.05rem; height: 1.05rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }

  .genre-tabs[hidden] { display: none; }

  .genre-tabs {
    display: flex;
    align-items: center;
    gap: clamp(1rem, 2vw, 2rem);
    margin-top: clamp(2rem, 4vh, 3rem);
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  }

  .genre-tab {
    display: inline-flex;
    min-height: 2.75rem;
    padding: 0 0.15rem;
    align-items: center;
    justify-content: center;
    border: 0;
    border-bottom: 2px solid transparent;
    background: transparent;
    color: rgba(255, 255, 255, 0.62);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: color 0.2s ease, border-color 0.2s ease;
  }

  .genre-tab:hover,
  .genre-tab:focus-visible,
  .genre-tab.is-selected {
    color: #fff;
  }

  .genre-tab.is-selected {
    border-bottom-color: #d7ecff;
  }

  .genre-tab:focus-visible {
    outline: 1px solid rgba(255, 255, 255, 0.65);
    outline-offset: 2px;
  }

  .genre-results {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 1rem 0.85rem;
    margin-top: 1rem;
    overflow: visible;
  }

  /* One-shot cascade after an intentional genre-tab switch: script.js spends
     a flag armed only by tab clicks/arrow keys, so first loads, searches, and
     background refreshes never set .is-shuffling. The tiles deal themselves
     into the fresh slots with alternating tilts — deliberately not a slide. */
  .genre-results.is-shuffling .genre-card {
    /* Compositor layer for the cascade only: transform/opacity stay on the
       GPU so 21 tiles never repaint mid-flight. The class is stripped after
       GENRE_SHUFFLE_MS, which releases the layers again. */
    will-change: transform, opacity;
    backface-visibility: hidden;
    animation: genreTileShuffle 0.72s both;
    /* 36ms per tile across all 21 slots (cap 20): even spacing, no clumped
       tail, and the long glide overlaps the tiles into one continuous wave.
       Keep GENRE_SHUFFLE_MS in index.html in sync (20 x 36ms + 720ms + buffer). */
    animation-delay: calc(min(var(--genre-tile-index, 0), 20) * 36ms);
  }

  @keyframes genreTileShuffle {
    from {
      opacity: 0;
      transform: translateY(0.9rem) scale(0.88)
        rotate(var(--genre-tile-tilt, 3deg));
      /* easeOutExpo: quick out of the gate, then a long glide to the landing.
         Each keyframe carries its own timing function on purpose — the
         animation-level easing restarts at every stop, and sharing one ease
         across both segments made velocity jump at the hand-off and visibly
         hitch the settle. */
      animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
    }
    65% {
      opacity: 1;
      /* Barely-there overshoot so the tile breathes into place. */
      transform: translateY(0) scale(1.02) rotate(0deg);
      /* Released with an ease whose starting velocity is zero, matching the
         near-zero arrival above, so the settle glides instead of snapping. */
      animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    }
    to {
      opacity: 1;
      transform: translateY(0) scale(1) rotate(0deg);
    }
  }

  .genre-card,
  .genre-card-link {
    min-width: 0;
    color: #fff;
    text-decoration: none;
  }

  .genre-card-link {
    display: block;
  }

  .genre-card-media {
    position: relative;
    width: 100%;
  }

  .genre-card-art-frame {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 0.85rem;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.08) url("album-placeholder.svg?v=2") center / cover no-repeat;
    box-shadow: 0 0.75rem 1.5rem rgba(0, 0, 0, 0.2);
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
  }

  .genre-card-art {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 0.3s ease;
  }

  .genre-card-art.is-loaded {
    opacity: 1;
  }

  .genre-card-media:hover .genre-card-art-frame {
    border-color: rgba(255, 255, 255, 0.3);
    box-shadow: 0 0.9rem 1.8rem rgba(0, 0, 0, 0.3);
  }

  .genre-play-button,
  .genre-info-button {
    position: absolute;
    z-index: 2;
    display: grid;
    place-items: center;
    padding: 0;
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.72);
    background: rgba(16, 18, 20, 0.68);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    transition: background-color 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
  }

  .genre-play-button {
    top: 50%;
    left: 50%;
    /* Scales with the card (percentage of .genre-card-media) so it stays
       proportionate when the cards shrink on narrower windows. */
    width: clamp(1.9rem, 38%, 3rem);
    aspect-ratio: 1;
    border-radius: 50%;
    transform: translate(-50%, -50%);
  }

  .genre-play-button svg {
    width: 45%;
    height: 45%;
    fill: currentColor;
  }

  .genre-info-button {
    right: clamp(0.35rem, 6%, 0.65rem);
    bottom: clamp(0.35rem, 6%, 0.65rem);
    width: clamp(1.15rem, 16%, 1.55rem);
    height: clamp(1.15rem, 16%, 1.55rem);
    border-radius: 50%;
    font: 700 clamp(0.6rem, 1.1vw, 0.78rem)/1 system-ui, sans-serif;
    text-shadow: none;
  }

  .genre-play-button:hover {
    background: rgba(16, 18, 20, 0.88);
    border-color: #fff;
    transform: translate(-50%, -50%) scale(1.08);
  }

  .genre-info-button:hover {
    background: rgba(16, 18, 20, 0.88);
    border-color: #fff;
    transform: scale(1.08);
  }

  .genre-play-button:focus-visible,
  .genre-info-button:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.8);
    outline-offset: 3px;
  }

  @media (prefers-reduced-motion: reduce) {
    .genre-card-art,
    .genre-card-art-frame {
      transition: none;
    }

    .genre-results.is-shuffling .genre-card {
      animation: none;
    }
  }

  .genre-card-details {
    margin-top: 0.5rem;
  }

  .genre-skeleton-details {
    display: grid;
    gap: 0.35rem;
    margin-top: 0.5rem;
  }

  .genre-card-skeleton .genre-card-art-frame {
    animation: genrePlaceholderPulse 1.2s ease-in-out infinite alternate;
  }

  .genre-skeleton-line {
    display: block;
    height: 0.8rem;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.16);
    animation: genrePlaceholderPulse 1.2s ease-in-out infinite alternate;
  }

  .genre-skeleton-title {
    width: 68%;
  }

  .genre-skeleton-artist {
    width: 48%;
    height: 0.7rem;
    animation-delay: 0.12s;
  }

  @keyframes genrePlaceholderPulse {
    from {
      opacity: 0.55;
    }

    to {
      opacity: 1;
    }
  }

  .genre-card-title,
  .genre-card-artist {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .genre-card-title {
    font-size: 0.82rem;
    font-weight: 650;
  }

  .genre-card-artist {
    margin-top: 0.2rem;
    color: rgba(255, 255, 255, 0.64);
    font-size: 0.74rem;
  }

  .genre-empty {
    grid-column: 1 / -1;
    color: rgba(255, 255, 255, 0.68);
    font-size: 0.95rem;
  }

  @media (prefers-reduced-motion: reduce) {
    .genre-card-skeleton .genre-card-art-frame,
    .genre-skeleton-line {
      animation: none;
    }
  }

}

@media (prefers-reduced-motion: reduce) {
  body.view-now-playing .player-view,
  body.view-now-playing #nowPlayingHeading,
  body.view-rises-from-mini.view-now-playing .player-view,
  body.view-rises-from-mini.view-now-playing #nowPlayingHeading,
  body.view-home .site-page.home-page,
  body.view-about .site-page.about-page {
    animation: none;
  }
}

@media (min-width: 56.25rem) and (pointer: fine) {
  .now-playing-heading {
  display: none;
}

.desktop-nav {
    position: fixed;
    z-index: 5;
    top: 0.45rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    width: max-content;
    max-width: calc(100vw - 1.5rem);
    height: 2.25rem;
    align-items: center;
    justify-content: center;
    padding: 0 0.35rem;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.1);
    box-shadow: 0 0.35rem 1.25rem rgba(0, 0, 0, 0.12), inset 0 1px rgba(255, 255, 255, 0.12);
    color: #fff;
    backdrop-filter: blur(1.25rem) saturate(150%);
    -webkit-backdrop-filter: blur(1.25rem) saturate(150%);
  }

  .desktop-nav-links a,
  .desktop-nav-links button {
    color: inherit;
    text-decoration: none;
  }

  .desktop-nav-links {
    position: relative;
    display: flex;
    height: 100%;
    margin: 0;
    align-items: center;
    gap: 0.1rem;
  }

  /* Sliding segmented-control highlight behind the current tab. JS measures
     the active button and publishes --nav-pill-x / --nav-pill-width here. */
  .desktop-nav-pill {
    position: absolute;
    top: 0.05rem;
    bottom: 0.05rem;
    left: 0;
    width: var(--nav-pill-width, 0px);
    transform: translateX(var(--nav-pill-x, 0px));
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.16);
    box-shadow: inset 0 1px rgba(255, 255, 255, 0.14);
    pointer-events: none;
    /* Own layer up front: the glide runs on the compositor and never waits
       on main-thread style/layout work (only the width tween does). */
    will-change: transform;
    /* Matches viewSlideIn's duration and easing so the pill and the incoming
       page glide together instead of the pill arriving first. */
    transition:
      transform 0.8s cubic-bezier(0.4, 0, 0.2, 1),
      width 0.8s cubic-bezier(0.4, 0, 0.2, 1);
  }

  .desktop-nav-links a,
  .desktop-nav-links button {
    position: relative;
    z-index: 1;
    display: inline-flex;
    height: calc(100% - 0.1rem);
    padding: 0 0.8rem;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 999px;
    background: transparent;
    font: inherit;
    font-size: 0.86rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease;
  }

  .desktop-nav-links button {
    appearance: none;
    -webkit-appearance: none;
  }

  .desktop-nav-links a:hover,
  .desktop-nav-links a:focus-visible,
  .desktop-nav-links button:hover,
  .desktop-nav-links button:focus-visible {
    color: #fff;
  }

  .desktop-nav-links a:hover,
  .desktop-nav-links button:hover {
    background: transparent;
    box-shadow: none;
  }

  .desktop-nav-links a:focus-visible,
  .desktop-nav-links button:focus-visible {
    outline: 1px solid rgba(255, 255, 255, 0.48);
    outline-offset: -0.2rem;
  }

  .desktop-nav-links .is-current {
    border: 0;
    background: transparent;
    color: #d7ecff;
    font-weight: 600;
  }

  @media (prefers-reduced-motion: reduce) {
    .desktop-nav-pill {
      transition: none;
    }
  }

}

[class~="album-art-card"] {
  width: 100%;
  aspect-ratio: 1 / 1;

  border-radius: 1.375rem;

  background:
    rgba(255, 255, 255, 0.08);

  display: flex;
  align-items: center;
  justify-content: center;

  pointer-events: none;

  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;

  box-shadow:
    0 0.75rem 2.1875rem
    rgba(0, 0, 0, 0.42),
    0 0.1875rem 0.625rem
    rgba(0, 0, 0, 0.25);

  margin-bottom: 4.375rem;
  margin-top: -0.9375rem;

  overflow: hidden;

  border:
    0.0625rem solid
    rgba(255, 255, 255, 0.12);

  backdrop-filter: blur(0.5rem);
  -webkit-backdrop-filter: blur(0.5rem);

  position: relative;
}

[class~="album-art-card"] img {
  width: 100%;
  height: 100%;

  position: relative;
  z-index: 5;

  object-fit: cover;

  transition:
    opacity 0.5s ease,
    transform 0.8s ease;

  pointer-events: none;

  user-select: none;
  -webkit-user-select: none;
}

[class~="album-art-card"] img.is-wide-thumbnail {
  transform: scale(1.35);
}

[class~="album-art-card"] img:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: -3px;
}

.song-feedback {
  position: absolute;

  left: 50%;
  top: 50%;

  transform:
    translate(-50%, -50%)
    scale(0.96);

  z-index: 10;

  padding:
    0.65rem 1rem;

  border-radius: 0.75rem;

  background:
    rgba(0, 0, 0, 0.55);

  border:
    0.0625rem solid
    rgba(255, 255, 255, 0.16);

  color: #fff;

  font-size: 0.78rem;

  font-weight: 600;

  letter-spacing: 0.01rem;

  white-space: nowrap;

  opacity: 0;

  pointer-events: none;

  backdrop-filter: blur(0.75rem);
  -webkit-backdrop-filter: blur(0.75rem);

  box-shadow:
    0 0.5rem 1.5rem
    rgba(0, 0, 0, 0.3);

  transition:
    opacity 0.25s ease,
    transform 0.25s ease;
}

.song-feedback.show {
  opacity: 1;

  transform:
    translate(-50%, -50%)
    scale(1);
}

[class~="station"] {
  font-size: 0.875rem;
  font-weight: 600;

  margin-bottom: 0.25rem;

  pointer-events: none;

  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;

  text-shadow:
    0 0.125rem 0.625rem
    rgba(0, 0, 0, 0.45);
}

/* Keeps the station label and the "Back to Live" button on one line, above the
   live/progress bar. */
.station-row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  margin-bottom: 0.25rem;
}

[class~="tagline"] {
  font-size: 0.875rem;

  opacity: 0.72;

  margin-bottom: 1.375rem;

  width: 100%;

  overflow: hidden;

  white-space: nowrap;

  position: relative;

  pointer-events: none;

  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;

  text-shadow:
    0 0.125rem 0.625rem
    rgba(0, 0, 0, 0.45);
}

.on-demand-like-button {
  appearance: none;
  -webkit-appearance: none;
  display: none;
  position: absolute;
  z-index: 2;
  top: 50%;
  right: 0.15rem;
  width: 3rem;
  height: 3rem;
  padding: 0;
  align-items: center;
  justify-content: center;
  transform: translateY(-50%);
  border: 0;
  border-radius: 50%;
  background: none;
  box-shadow: none;
  color: #fff;
  cursor: pointer;
  pointer-events: auto;
  opacity: 0.9;
  transition:
    background 0.2s ease,
    opacity 0.2s ease,
    transform 0.2s ease;
  -webkit-tap-highlight-color: transparent;
}

.on-demand-like-button:hover,
.on-demand-like-button:active {
  background: none;
}

.on-demand-like-button:hover {
  opacity: 1;
}

.on-demand-like-button:active {
  transform: translateY(-50%) scale(0.92);
}

.on-demand-like-button svg {
  width: 1.45rem;
  height: 1.45rem;
  fill: transparent;
  stroke: currentColor;
  stroke-width: 1.8;
}

.on-demand-like-button.rating-selected svg {
  fill: currentColor;
  stroke: none;
  filter: drop-shadow(0 0 0.32rem rgba(255, 255, 255, 0.95));
}

.on-demand-like-button.rating-selected {
  opacity: 1;
}

.on-demand-like-button:focus-visible {
  outline: 0.125rem solid rgba(255, 255, 255, 0.85);
  outline-offset: 0.2rem;
}

body.on-demand-active .on-demand-like-button {
  display: flex;
}

body.on-demand-active .tagline.now-playing-track {
  padding-right: 0;
}

[class~="tagline"] span {
  display: inline-block;

  white-space: nowrap;

  will-change: transform;

  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

/* Narrow layout marquee: marquee.js duplicates the text into two
   `.marquee-item` spans and the head styles run the seamless `marqueeLoop` on
   #nowPlaying (0 -> -50% - half the 3rem gap). A leftover `marqueeScroll` rule
   here used to win that cascade (id outranks the head selector) and pushed the
   whole strip a full width off-screen before it travelled twice its own width,
   so the tagline raced past and stayed blank for seconds. */

.tagline[class~="paused"] > #nowPlaying {
  animation-play-state: paused;
}

[class~="live-row"] {
  display: flex;

  align-items: center;

  gap: 0.875rem;

  margin-bottom: 2.125rem;
}

[class~="live-line"] {
  flex: 1;

  height: 0.25rem;

  border-radius: 0.25rem;

  pointer-events: none;

  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;

  background:
    linear-gradient(
      to right,
      rgba(255, 255, 255, 0.12),
      rgba(255, 255, 255, 0.55),
      rgba(255, 255, 255, 0.12)
    );

  box-shadow:
    0 0 0.75rem
    rgba(255, 255, 255, 0.08);
}

[class~="live-text"] {
  font-size: 0.75rem;

  letter-spacing: 0.125rem;

  opacity: 0.85;

  pointer-events: none;

  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;

  text-shadow:
    0 0.125rem 0.625rem
    rgba(0, 0, 0, 0.45);
}

.progress-bar {
  display: none;

  position: relative;

  flex: 1 1 auto;
  width: 100%;
  height: 0.25rem;

  border-radius: 999px;

  background: rgba(255, 255, 255, 0.16);

  overflow: hidden;

  pointer-events: none;

  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

.progress-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;

  width: 0%;

  border-radius: inherit;

  background:
    linear-gradient(
      to right,
      rgba(255, 255, 255, 0.55),
      rgba(255, 255, 255, 0.95)
    );

  box-shadow:
    0 0 0.75rem
    rgba(255, 255, 255, 0.25);

  transition: width 0.25s linear;
}

/* The live radio bar (lines + "LIVE") is the default. When a song is playing
   on demand, swap it for the real track progress bar. */
body.on-demand-active .live-row .progress-bar {
  display: block;
}

/* Scrubbing is an on-demand affordance only: the live radio bar is a read-only
   indicator of the song currently on air. The visual bar stays 0.25rem tall
   while a transparent hit area widens it to a comfortable touch target. */
body.on-demand-active .live-row .progress-bar {
  pointer-events: auto;
  cursor: pointer;
  /* Let the drag thumb overflow the bar instead of clipping it. */
  overflow: visible;
  touch-action: none;
}

body.on-demand-active .live-row .progress-bar::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 1.5rem;
  transform: translateY(-50%);
}

.progress-thumb {
  position: absolute;
  top: 50%;
  left: 0;

  width: 0.75rem;
  height: 0.75rem;

  border-radius: 50%;

  background: #fff;

  box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.45);

  /* Positioned from JS; never animate or it lags behind the pointer. */
  transform: translate(-50%, -50%);

  opacity: 0;

  transition: opacity 0.2s ease;

  pointer-events: none;
}

body.on-demand-active .progress-thumb {
  opacity: 1;
}

/* While dragging, freeze the 500ms progress tick so the fill cannot fight the
   pointer for control of the bar. */
body.on-demand-active .progress-bar.is-scrubbing .progress-fill {
  transition: none;
}

body.on-demand-active .live-row .live-line,
body.on-demand-active .live-row .live-text {
  display: none;
}

.return-to-live-button {
  padding: 0.35rem 0.65rem;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  font: inherit;
  font-size: 0.7rem;
  cursor: pointer;
  /* The station row can sit inside #nowPlayingHeading, which disables pointer
     events; re-enable them so the button stays clickable. */
  pointer-events: auto;
}

.return-to-live-button:hover,
.return-to-live-button:focus-visible {
  background: rgba(255, 255, 255, 0.2);
}

.track-time .stream-quality,
.now-playing-track .stream-quality {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 3.4rem;
  padding: 0.22rem 0.45rem;
  border: 0.0625rem solid rgba(255, 255, 255, 0.16);
  border-radius: 0.3rem;
  background: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.78);
  font-size: 0.56rem;
  font-weight: 600;
  letter-spacing: 0.06rem;
  line-height: 1;
  text-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.3);
}

.track-time .stream-quality.is-offline,
.now-playing-track .stream-quality.is-offline {
  border-color: rgba(255, 255, 255, 0.3);
  background: rgba(255, 255, 255, 0.2);
  color: #fff;
}
.track-time {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);

  align-items: center;

  width: 100%;

  margin-top: -1.5rem;
  margin-bottom: 1.75rem;

  padding: 0 0.1rem;

  font-size: 0.72rem;
  font-weight: 500;

  letter-spacing: 0.04rem;

  color: rgba(255, 255, 255, 0.62);

  text-shadow:
    0 0.125rem 0.625rem
    rgba(0, 0, 0, 0.35);
}

.track-time span {
  display: block;
}

.track-time > span:first-child {
  justify-self: start;
}

.track-time > span:last-child {
  justify-self: end;
}

.track-time > .stream-quality {
  grid-column: 2;
  justify-self: center;
}

/* The stream quality badge moves into the artist tagline on desktop, which would
   auto-place the duration in the centred column. Pin it to the trailing column so
   the total time always lines up with the right end of the live bar. */
.track-time > #trackDuration {
  grid-column: 3;
  justify-self: end;
}

[class~="play-btn"] {
  position: relative;
  -webkit-tap-highlight-color: transparent;

  width: 4rem;
  height: 4rem;

  margin: 0 auto;

  border-radius: 0.875rem;

  background:
    rgba(255, 255, 255, 0.10);

  border:
    0.0625rem solid
    rgba(255, 255, 255, 0.14);

  display: flex;

  align-items: center;
  justify-content: center;

  cursor: pointer;

  backdrop-filter: blur(0.75rem);
  -webkit-backdrop-filter: blur(0.75rem);

  box-shadow:
    0 0.5rem 1.5625rem
    rgba(0, 0, 0, 0.28);

  transition:
    transform 0.2s ease,
    background 0.2s ease,
    box-shadow 0.2s ease;
}

[class~="play-btn"]:hover {
  background:
    rgba(255, 255, 255, 0.16);

  transform:
    translate3d(0, -0.125rem, 0);

  box-shadow:
    0 0.625rem 1.875rem
    rgba(0, 0, 0, 0.32);
}

[class~="play-btn"]:active {
  transform: scale(0.94);
}

[class~="play-btn"] svg {
  width: 1.75rem;
  height: 1.75rem;

  fill: #fff;

  filter:
    drop-shadow(
      0 0.125rem 0.3125rem
      rgba(0, 0, 0, 0.35)
    );
}

.play-btn.is-loading svg {
  opacity: 0;
}

.play-btn.is-loading::after {
  position: absolute;
  width: 1.35rem;
  height: 1.35rem;
  border: 0.15rem solid rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
  border-radius: 50%;
  content: '';
  animation: streamSpinner 0.8s linear infinite;
}

@keyframes streamSpinner {
  to {
    transform: rotate(360deg);
  }
}

@media (min-width: 37.5rem) {
  [class~="app"] {
    max-width: 22.5rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  [class~="album-background"] {
    animation: none;
  }

  .tagline[class~="marquee"] span {
    animation: none;
  }
}

.player-controls {
  display: grid;

  grid-template-columns:
    3rem
    3rem
    4rem
    3rem
    3rem;

  column-gap: 0.75rem;

  align-items: center;
  justify-content: center;

  width: 100%;

  min-height: 5rem;

  overflow: visible;
}

.app {
  overflow: visible;
}

.player-controls .play-btn {
  grid-column: 3;
  margin: 0;
}

.player-controls #songInfoButton {
  grid-column: 2;
  justify-self: center;
}

.player-controls #sleepTimerButton {
  grid-column: 4;
  justify-self: center;
}

.rating-btn {
  display: flex;

  align-items: center;
  justify-content: center;

  width: 3rem;
  height: 3rem;

  padding: 0;

  border: 0;

  border-radius: 50%;

  background: transparent;

  color: #fff;

  cursor: pointer;

  opacity: 0.9;

  transition:
    background 0.2s ease,
    opacity 0.2s ease,
    transform 0.2s ease;

  -webkit-tap-highlight-color: transparent;
}

#thumbsDownButton {
  grid-column: 1;
  justify-self: center;
}

#thumbsDownButton svg {
  transform: rotate(180deg);
}

#thumbsUpButton {
  grid-column: 5;
  justify-self: center;
}

/* On-demand previous/next reuse the grid slots the thumbs up/down occupy, so
   swapping between them never shifts the rest of the control row. */
#onDemandPrevButton {
  grid-column: 1;
  justify-self: center;
}

#onDemandNextButton {
  grid-column: 5;
  justify-self: center;
}

.transport-btn {
  display: none;

  align-items: center;
  justify-content: center;

  width: 3rem;
  height: 3rem;

  padding: 0;

  border: 0;

  border-radius: 50%;

  background: transparent;

  color: #fff;

  cursor: pointer;

  opacity: 0.9;

  transition:
    background 0.2s ease,
    opacity 0.2s ease,
    transform 0.2s ease;

  -webkit-tap-highlight-color: transparent;
}

/* Ratings are a live-radio concept: hide them while an on-demand song plays and
   reveal the transport buttons in their place. */
body.on-demand-active .rating-btn {
  display: none;
}

body.on-demand-active .transport-btn {
  display: flex;
}

body.on-demand-active .transport-btn:disabled {
  opacity: 0.3;
  cursor: default;
}

.transport-btn svg {
  display: block;

  width: 1.45rem;
  height: 1.45rem;

  fill: #fff;

  color: #fff;
}

.transport-btn:hover:not(:disabled) {
  background: transparent;

  opacity: 1;
}

.transport-btn:active:not(:disabled) {
  transform: scale(0.92);
}

.rating-btn svg {
  display: block;

  width: 1.45rem;
  height: 1.45rem;

  fill: #fff;

  color: #fff;
}

.rating-btn:hover {
  background: transparent;

  opacity: 1;
}

.rating-btn:active {
  transform: scale(0.92);
}

.rating-btn.rating-selected {
  background: transparent;

  opacity: 1;
}

.rating-btn.rating-selected svg {
  filter:
    drop-shadow(
      0 0 0.2rem
      rgba(255, 255, 255, 0.85)
    )
    drop-shadow(
      0 0 0.5rem
      rgba(255, 255, 255, 0.45)
    );
}

.rating-btn:disabled,
.rating-btn.is-disabled,
.side-control:disabled,
.side-control.is-disabled {
  opacity: 0.35;
  cursor: not-allowed;
  transform: none;
}

.rating-btn:disabled:hover,
.rating-btn.is-disabled:hover,
.side-control:disabled:hover,
.side-control.is-disabled:hover,
.rating-btn:disabled:active,
.rating-btn.is-disabled:active,
.side-control:disabled:active,
.side-control.is-disabled:active {
  background: transparent;
  opacity: 0.35;
  transform: none;
}

.rating-btn:focus-visible,
.side-control:focus-visible,
.play-btn:focus-visible,
.timer-options button:focus-visible,
.dialog-close:focus-visible {
  outline: 0.125rem solid #fff;
  outline-offset: 0.2rem;
}

.side-control {
  display: flex;

  align-items: center;
  justify-content: center;

  width: 3rem;
  height: 3rem;

  padding: 0;

  border: 0;

  border-radius: 50%;

  background: transparent;

  color: #fff;

  cursor: pointer;

  opacity: 0.9;

  transition:
    background 0.2s ease,
    opacity 0.2s ease,
    transform 0.2s ease;

  -webkit-tap-highlight-color: transparent;
}

.side-control:hover {
  background: transparent;

  opacity: 1;
}

.side-control:active {
  transform: scale(0.92);
}

.side-control span {
  display: grid;

  place-items: center;

  width: 1.25rem;
  height: 1.25rem;

  border:
    0.1rem solid currentColor;

  border-radius: 50%;

  font-family: Georgia, serif;

  font-size: 0.9rem;

  font-weight: 700;

  line-height: 1;
}

.side-control svg {
  width: 1.45rem;
  height: 1.45rem;

  fill: currentColor;
}

.side-control.timer-active {
  background: transparent;

  opacity: 1;

  filter:
    drop-shadow(
      0 0 0.2rem
      rgba(255, 255, 255, 0.85)
    )
    drop-shadow(
      0 0 0.5rem
      rgba(255, 255, 255, 0.45)
    );
}

.control-dialog {
  width:
    min(24rem, calc(100vw - 2rem));

  max-height:
    min(80vh, 38rem);

  margin: auto;

  padding: 1.5rem;

  border:
    0.0625rem solid
    rgba(255, 255, 255, 0.18);

  border-radius: 1rem;

  background:
    rgba(27, 25, 24, 0.96);

  color: #fff;

  box-shadow:
    0 1rem 3rem
    rgba(0, 0, 0, 0.4);
}

.install-app-dialog {
  width: min(26rem, calc(100vw - 2rem));
  padding: 1.75rem;
  border-color: rgba(255, 255, 255, 0.2);
  border-radius: 1.1rem;
  background: linear-gradient(145deg, rgba(34, 32, 31, 0.98), rgba(15, 17, 20, 0.98));
}

.install-app-mark {
  display: block;
  width: 3.6rem;
  height: 3.6rem;
  margin-bottom: 1.25rem;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 0.8rem;
  box-shadow: 0 0.6rem 1.4rem rgba(0, 0, 0, 0.28);
}

.install-app-dialog .site-page-eyebrow {
  margin: 0 2rem 0.55rem 0;
  font-size: 0.65rem;
}

.install-app-dialog h2 {
  margin: 0 2rem 0.7rem 0;
  font-size: 1.25rem;
}

.install-app-description,
.install-app-steps {
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.9rem;
  line-height: 1.65;
}

.install-app-description {
  margin: 0;
}

.install-app-description.is-unsupported {
  color: #ff8585;
  font-weight: 700;
}

.install-app-steps {
  margin: 0.9rem 0 0;
  padding-left: 1.25rem;
}

.install-app-steps li + li {
  margin-top: 0.3rem;
}

.install-app-actions {
  display: flex;
  gap: 0.65rem;
  margin-top: 1.4rem;
}

.install-app-actions button {
  min-height: 2.7rem;
  padding: 0 1rem;
  border: 1px solid rgba(255, 255, 255, 0.17);
  border-radius: 0.7rem;
  font: inherit;
  font-size: 0.88rem;
  font-weight: 650;
  cursor: pointer;
}

.install-app-primary {
  flex: 1;
  background: #f0e5d8;
  color: #211914;
}

.install-app-primary:disabled {
  border-color: rgba(255, 255, 255, 0.08);
  background: #77797c;
  color: #292a2c;
  cursor: not-allowed;
  opacity: 0.65;
}

.install-app-secondary {
  background: rgba(255, 255, 255, 0.07);
  color: #fff;
}

.install-app-actions button:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.82);
  outline-offset: 3px;
}

.install-app-actions button[hidden] {
  display: none;
}

.song-info-dialog {
  height:
    min(30rem, calc(100dvh - 2rem));

  max-height:
    min(30rem, calc(100dvh - 2rem));

  overflow-y: auto;

  scrollbar-width: none;

  -ms-overflow-style: none;
}

.catalog-song-dialog {
  width: min(52rem, calc(100vw - 2rem));
  height: fit-content;
  max-height: min(44rem, calc(100dvh - 2rem));
  padding: 1.5rem;
}

.catalog-song-notice {
  margin: 0 2.5rem 1.1rem 0;
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.82rem;
}

.catalog-song-layout {
  display: grid;
  grid-template-columns: minmax(12rem, 0.8fr) minmax(0, 1.2fr);
  align-items: start;
  gap: 1.5rem;
}

.catalog-song-dialog .song-info-art {
  width: 100%;
  max-width: 20rem;
  margin: 0;
}

.catalog-song-copy {
  min-width: 0;
}

.catalog-artist-summary {
  min-width: 0;
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}

.catalog-artist-identity {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-top: 1rem;
}

.catalog-artist-art {
  width: 4.5rem;
  flex: 0 0 4.5rem;
  aspect-ratio: 1;
  border-radius: 50%;
  object-fit: cover;
  background: rgba(255, 255, 255, 0.08);
}

.catalog-artist-art[hidden] {
  display: none;
}

.catalog-song-dialog .song-info-background {
  display: block;
  max-height: none;
  overflow: visible;
}

@media (max-width: 40rem) {
  .catalog-song-dialog {
    padding: 1.25rem;
  }

  .catalog-song-layout {
    grid-template-columns: 1fr;
  }

  .catalog-song-dialog .song-info-art {
    width: min(16rem, 100%);
    margin: 0 auto;
  }
}

.song-info-dialog::-webkit-scrollbar {
  display: none;
}

.control-dialog::backdrop {
  background:
    rgba(0, 0, 0, 0.56);

  backdrop-filter:
    blur(0.25rem);
}

.control-dialog h2 {
  margin:
    0 2rem 1.25rem 0;

  font-size: 1.1rem;
}

.control-dialog form {
  position: absolute;

  top: 0.8rem;

  right: 0.8rem;
}

.dialog-close {
  width: 2rem;
  height: 2rem;

  padding: 0;

  border: 0;

  border-radius: 50%;

  background:
    rgba(255, 255, 255, 0.1);

  color: #fff;

  font-size: 1.4rem;

  cursor: pointer;
}

.song-info-art {
  display: block;

  width:
    min(12rem, 70%);

  aspect-ratio: 1;

  margin:
    0 auto 1.25rem;

  border-radius: 0.8rem;

  object-fit: cover;

  background:
    rgba(255, 255, 255, 0.08);
}

.song-info-track {
  margin:
    0 0 0.4rem;

  font-size: 1rem;

  font-weight: 700;

  line-height: 1.5;
}

.song-info-artist {
  margin:
    0 0 0.8rem;

  color:
    rgba(255, 255, 255, 0.72);

  font-size: 0.88rem;
}

.song-info-details {
  margin: 0;

  color:
    rgba(255, 255, 255, 0.66);

  font-size: 0.84rem;

  line-height: 1.7;

  white-space: pre-line;
}

.song-info-link {
  display: inline-block;

  margin-top: 1rem;

  color: #fff;

  font-size: 0.84rem;

  text-decoration: underline;

  text-underline-offset: 0.2rem;
}

.song-info-link[hidden] {
  display: none;
}

.song-info-background {
  max-height: 9rem;

  margin: 1rem 0 0;

  overflow-y: auto;

  color:
    rgba(255, 255, 255, 0.78);

  font-size: 0.84rem;

  line-height: 1.65;

  scrollbar-width: thin;
}

.song-info-wiki-link {
  display: inline-block;

  margin-top: 0.6rem;

  color:
    rgba(255, 255, 255, 0.72);

  font-size: 0.75rem;

  text-decoration: underline;

  text-underline-offset: 0.18rem;
}

.song-info-wiki-link[hidden] {
  display: none;
}

.timer-options {
  display: grid;

  gap: 0.5rem;
}

.timer-options button {
  min-height: 2.75rem;

  padding:
    0.65rem 0.8rem;

  border:
    0.0625rem solid
    rgba(255, 255, 255, 0.12);

  border-radius: 0.55rem;

  background:
    rgba(255, 255, 255, 0.07);

  color: #fff;

  font: inherit;

  text-align: left;

  cursor: pointer;
}

.timer-options button:hover {
  background:
    rgba(255, 255, 255, 0.15);
}

.timer-options button.timer-option-active {
  border-color:
    rgba(255, 255, 255, 0.48);

  background:
    linear-gradient(
      to right,
      rgba(255, 255, 255, 0.38) 0 var(--timer-progress),
      rgba(255, 255, 255, 0.07) var(--timer-progress) 100%
    );

  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}

.timer-options button[hidden] {
  display: none;
}

.timer-options #cancelSleepTimer {
  border-color:
    rgba(255, 255, 255, 0.24);
}

.timer-status {
  min-height: 1.2rem;

  margin:
    0.9rem 0 0;

  color:
    rgba(255, 255, 255, 0.68);

  font-size: 0.82rem;
}

.lyrics-panel {
  display: block;

  visibility: hidden;

  opacity: 0;

  pointer-events: none;

  width:
    min(32rem, 34vw);

  height:
    min(78vh, 52rem);

  padding:
    clamp(3rem, 7vh, 5rem)
    0
    1rem;

  position: absolute;

  z-index: 2;

  text-align: left;

  transform:
    translateX(1rem);

  transition:
    opacity 0.45s ease,
    transform 0.45s ease,
    visibility 0.45s ease;
}

.lyrics-heading {
  margin-bottom: 0.75rem;

  font-size: 0.75rem;

  letter-spacing: 0.14em;

  text-transform: uppercase;

  opacity: 0.65;
}

.lyrics-notice {
  margin:
    -0.25rem 0 1rem;

  color:
    rgba(255, 255, 255, 0.48);

  font-size: 0.82rem;

  font-weight: 500;
}

.lyrics-toggle {
  display: flex;

  width: 100%;

  align-items: center;

  padding: 0;

  border: 0;

  background: transparent;

  color: inherit;

  text-align: left;
}

.lyrics-toggle-icon {
  display: none;
}

.lyrics-song {
  display: none;
}

.lyrics-content {
  max-height: 68vh;

  overflow-y: auto;

  padding-right: 1rem;

  color:
    rgba(255, 255, 255, 0.9);

  font-size: 1.2rem;

  font-weight: 600;

  line-height: 2;

  white-space: pre-line;

  scrollbar-width: none;

  -ms-overflow-style: none;

  -webkit-overflow-scrolling: touch;

  overscroll-behavior: contain;
}

.lyrics-content::-webkit-scrollbar {
  display: none;
}

@media (min-width: 56.25rem) and (pointer: fine) {
  body.has-lyrics:not(.synced-lyrics)
  .lyrics-content {
    max-height: 68vh;

    overflow-y: auto;

    overscroll-behavior: contain;
  }
}

body.has-lyrics.synced-lyrics
.lyrics-content {
  overflow-y: hidden;

  overscroll-behavior: none;
}

.lyrics-line {
  color:
    rgba(255, 255, 255, 0.68);

  font-weight: 500;

  opacity: 0.58;

  transition:
    color 0.25s ease,
    opacity 0.25s ease,
    transform 0.25s ease,
    font-size 0.25s ease;
}

.lyrics-line.is-active {
  color: #fff;

  font-size: 1.12em;

  font-weight: 800;

  opacity: 1;

  transform:
    translateX(0.35rem);
}

body.has-lyrics {
  gap:
    clamp(4rem, 8vw, 8rem);
}

body.has-lyrics .lyrics-panel {
  visibility: visible;

  opacity: 1;

  pointer-events: auto;

  position: relative;

  transform:
    translateX(clamp(0.5rem, 1vw, 1rem));
}

@media (min-width: 56.25rem) and (pointer: fine) {
  body.has-lyrics .app {
    transform: translateX(0);
  }
}

@media (max-width: 56.1875rem), (pointer: coarse) {

  html {
    overflow-x: hidden;

    overflow-y: auto;

    scrollbar-width: none;

    -ms-overflow-style: none;
  }

  html::-webkit-scrollbar {
    display: none;
  }

  body {
    scrollbar-width: none;

    -ms-overflow-style: none;
  }

  body::-webkit-scrollbar {
    display: none;
  }

  body.has-lyrics {
    display: flex;

    flex-direction: column;

    align-items: center;

    justify-content: flex-start;

    gap: 0;

    overflow: visible;

    padding: 0;
  }

  body.has-lyrics .app {
    margin:
      clamp(5rem, 13vh, 8rem)
      auto
      0;

    transform:
      translateY(-2rem);
  }

  body.has-lyrics .lyrics-panel {
    display: block;

    visibility: visible;

    opacity: 1;

    pointer-events: auto;

    position: relative;

    top: auto;

    left: auto;

    width:
      min(26.25rem, calc(100% - 2.75rem));

    height: auto;

    max-height: 50vh;

    margin:
      0 auto 1rem;

    padding: 1rem;

    transform: none;

    border:
      0.0625rem solid
      rgba(255, 255, 255, 0.14);

    border-radius: 0.875rem;

    background:
      rgba(255, 255, 255, 0.11);

    backdrop-filter:
      blur(1rem);

    -webkit-backdrop-filter:
      blur(1rem);
  }

  body.has-lyrics .lyrics-toggle {
    display: flex;

    width: 100%;

    align-items: center;

    justify-content: space-between;

    padding:
      0 0 0.75rem;

    border: 0;

    border-radius: 0;

    background: transparent;

    color: #fff;

    cursor: pointer;

    text-align: left;
  }

  body.has-lyrics
  .lyrics-toggle
  .lyrics-heading {
    margin: 0;
  }

  body.has-lyrics
  .lyrics-toggle-icon {
    font-size: 1rem;

    opacity: 0.7;

    transform:
      rotate(180deg);

    transition:
      transform 0.25s ease;
  }

  body.has-lyrics
  .lyrics-content {
    display: block;
  }

  body.has-lyrics
  .lyrics-content {
    max-height: 8.5rem;

    overflow-y: auto;

    padding-right: 0;
  }

  body.has-lyrics
  .lyrics-content {
    font-size: 0.92rem;

    font-weight: 400;

    line-height: 1.9;
  }

  body.has-lyrics
  .lyrics-line {
    font-weight: 400;
  }

  body.has-lyrics
  .lyrics-line {
    margin-bottom: 0.12rem;
  }

  body.has-lyrics
  .lyrics-line.is-active {
    font-size: 1.04em;

    font-weight: 600;
  }

  body.has-lyrics.synced-lyrics
  .lyrics-content {
    overflow-y: hidden;

    touch-action: none;

    overscroll-behavior: none;
  }

  body.has-lyrics.mobile-lyrics-expanded
  .lyrics-toggle-icon {
    transform:
      rotate(0);
  }

  body.has-lyrics.mobile-lyrics-expanded
  .lyrics-content {
    display: block;

    max-height: 8.5rem;
  }

  /* Adding the "Back to Live" button turned the schedule label into a flex
     item, so it no longer inherits the centred text of .app and sat flush
     against the left edge of the column. Re-centre the row, and let a long
     label wrap instead of stretching the layout past the viewport. */
  .station-row {
    justify-content: center;
    flex-wrap: wrap;
  }

  .station {
    min-width: 0;
    max-width: 100%;
    text-align: center;
    overflow-wrap: anywhere;
  }
}

/* Desktop listening layout: artwork beside the complete player controls. */
@media (min-width: 56.25rem) and (pointer: fine) {
  body.view-now-playing .app {
    top: 0;
    display: grid;
    width: min(68rem, calc(100vw - 6rem));
    max-width: none;
    grid-template-columns: minmax(19rem, 27rem) minmax(24rem, 1fr);
    grid-template-rows: auto auto auto auto;
    grid-template-areas:
      "art title"
      "art live"
      "art time"
      "art controls";
    align-items: center;
    column-gap: clamp(3rem, 6vw, 6rem);
    row-gap: 0.4rem;
    padding: clamp(2rem, 4vw, 3.75rem);
    border: 1px solid rgba(255, 255, 255, 0.16);
    border-radius: 2rem;
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.105), rgba(255, 255, 255, 0.035));
    box-shadow: 0 2rem 5rem rgba(0, 0, 0, 0.2), inset 0 1px rgba(255, 255, 255, 0.12);
    text-align: left;
    backdrop-filter: blur(1.2rem) saturate(135%);
    -webkit-backdrop-filter: blur(1.2rem) saturate(135%);
  }

  body.view-now-playing .album-art-card {
    grid-area: art;
    width: 100%;
    margin: 0;
    transform: translateX(1.5rem);
    border-radius: 0;
    box-shadow: 0 1.5rem 3rem rgba(0, 0, 0, 0.36), 0 0.25rem 0.75rem rgba(0, 0, 0, 0.22);
  }

  body.view-now-playing #radio {
    display: none;
  }

  body.view-now-playing .station-row {
    margin: 3rem 0 0;
  }

  body.view-now-playing .station {
    position: static;
    width: auto;
    max-width: none;
    margin: 0;
    padding: 0;
    transform: none;
    text-align: left;
    color: rgba(255, 255, 255, 0.75);
    font: inherit;
    font-weight: 600;
    letter-spacing: normal;
    line-height: normal;
    text-transform: none;
    text-shadow: none;
  }

  body.view-now-playing .tagline {
    grid-area: title;
    align-self: start;
    width: 100%;
    /* Real spacing instead of a transform: translateY moved the title, album
       line and quality badge visually but not in layout, so they overlapped
       the progress bar below. The dvh terms tighten the block on short
       (stretched) windows so the controls below stay above the fold. */
    margin: clamp(0.9rem, 3dvh, 2.2rem) 0 clamp(0.5rem, 1.5dvh, 1.25rem);
    color: #fff;
    font-size: clamp(1.35rem, 2vw, 1.9rem);
    font-weight: 650;
    line-height: 1.35;
    text-shadow: none;
  }

  body.view-now-playing .now-playing-track {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    align-items: start;
    column-gap: 0.5rem;
    white-space: normal;
  }

  body.view-now-playing .now-playing-track > #nowPlaying {
    grid-column: 1;
    grid-row: 1;
  }

  body.view-now-playing .now-playing-track > .on-demand-like-button {
    grid-column: 2;
    grid-row: 1;
    align-self: start;
    position: relative;
    top: auto;
    right: auto;
    transform: none;
  }

  body.view-now-playing .now-playing-track > .stream-quality {
    grid-column: 1;
    grid-row: 2;
    justify-self: start;
    margin-top: 0.35rem;
    opacity: 1;
    text-shadow: none;
    animation: none !important;
    transform: none !important;
    will-change: auto;
  }

  body.view-now-playing #nowPlaying {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    width: auto;
    min-width: 0;
    overflow: hidden;
    color: transparent;
    font-size: 0;
    white-space: normal;
    overflow-wrap: anywhere;
    animation: none !important;
    transform: none !important;
  }

  body.view-now-playing #nowPlaying::before,
  body.view-now-playing #nowPlaying::after {
    display: block;
    flex: 0 0 auto;
    width: max-content;
    max-width: none;
    white-space: normal;
  }

  body.view-now-playing #nowPlaying::before {
    color: #fff;
    content: attr(data-track-title);
    font-size: clamp(1.35rem, 2vw, 1.9rem);
    font-weight: 700;
    line-height: 1.25;
  }

  body.view-now-playing #nowPlaying::after {
    color: rgba(255, 255, 255, 0.72);
    content: attr(data-track-artist);
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
  }

  body.view-now-playing .tagline.title-marquee #nowPlaying::before {
    white-space: nowrap;
    content: attr(data-track-title) "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0" attr(data-track-title) "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0";
    animation: desktopTitleMarquee var(--title-marquee-duration, 14s) linear infinite;
  }

  body.view-now-playing .tagline.artist-marquee #nowPlaying::after {
    white-space: nowrap;
    content: attr(data-track-artist) "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0" attr(data-track-artist) "\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0";
    animation: desktopArtistMarquee var(--artist-marquee-duration, 12s) linear infinite;
  }

  body.view-now-playing .tagline.paused.title-marquee #nowPlaying::before,
  body.view-now-playing .tagline.paused.artist-marquee #nowPlaying::after {
    animation-play-state: paused;
  }

  body.view-now-playing .tagline.marquee #nowPlaying {
    animation: none !important;
  }

  @keyframes desktopTitleMarquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
  }

  @keyframes desktopArtistMarquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
  }

  body.view-now-playing #nowPlaying > span {
    display: none !important;
  }

  body.view-now-playing .live-row {
    grid-area: live;
    width: min(100%, 27rem);
    margin: 0.25rem 0 0;
  }

  body.view-now-playing .track-time {
    grid-area: time;
    width: min(100%, 27rem);
    margin: 0.35rem 0 0.25rem;
    transform: translateY(-0.5rem);
  }

  /* Flexible side slots around a play slot sized to its button. The row is a
     size container, so when the lyrics panel narrows the column the buttons
     scale down with it (cqw) instead of overflowing their grid cells and
     crowding together. The plain rem widths are the fallback for browsers
     without container query units. */
  body.view-now-playing .player-controls {
    grid-area: controls;
    container-type: inline-size;
    grid-template-columns:
      minmax(0, 1fr)
      minmax(0, 1fr)
      auto
      minmax(0, 1fr)
      minmax(0, 1fr);
    width: min(100%, 27rem);
    min-height: min(5.5rem, 11dvh);
    justify-content: stretch;
    column-gap: 0.25rem;
    margin-top: 0.15rem;
  }

  body.view-now-playing .player-controls .play-btn {
    width: 4.5rem;
    height: 4.5rem;
    width: clamp(3.25rem, min(17cqw, 11.5dvh), 4.5rem);
    height: clamp(3.25rem, min(17cqw, 11.5dvh), 4.5rem);
    border-radius: 1.15rem;
    background: rgba(255, 255, 255, 0.16);
    box-shadow: 0 0.75rem 1.75rem rgba(0, 0, 0, 0.2), inset 0 1px rgba(255, 255, 255, 0.15);
  }

  body.view-now-playing .player-controls .side-control,
  body.view-now-playing .player-controls .rating-btn,
  body.view-now-playing .player-controls .transport-btn {
    width: 2.75rem;
    height: 2.75rem;
    width: clamp(2rem, 11cqw, 2.75rem);
    height: clamp(2rem, 11cqw, 2.75rem);
    border-radius: 50%;
    transition: background 0.2s ease, transform 0.2s ease;
  }

  @container (max-width: 17rem) {
    body.view-now-playing .player-controls .side-control svg,
    body.view-now-playing .player-controls .rating-btn svg,
    body.view-now-playing .player-controls .transport-btn svg {
      width: 1.2rem;
      height: 1.2rem;
    }

    body.view-now-playing .player-controls .side-control span {
      width: 1.1rem;
      height: 1.1rem;
      font-size: 0.8rem;
    }
  }

  body.view-now-playing .player-controls .side-control:hover,
  body.view-now-playing .player-controls .rating-btn:hover {
    background: rgba(255, 255, 255, 0.12);
    transform: translateY(-2px);
  }

  body.view-now-playing .player-controls .rating-btn.is-active {
    background: rgba(255, 255, 255, 0.18);
  }

  body.view-now-playing .now-playing-heading::after {
    position: absolute;
    top: calc(100% + 0.75rem);
    right: clamp(1.25rem, 3.5vw, 3.5rem);
    left: clamp(1.25rem, 3.5vw, 3.5rem);
    border-top: 1px solid rgba(255, 255, 255, 0.16);
    content: "";
  }

  body.has-lyrics.view-now-playing .app {
    width: min(58rem, calc(100vw - min(27rem, 29vw) - clamp(2rem, 4vw, 4rem) - 2rem));
    grid-template-columns: minmax(13rem, var(--now-playing-art-max)) minmax(16rem, 1fr);
    column-gap: clamp(2rem, 4vw, 4rem);
    padding: var(--now-playing-app-pad, 2.5rem);
    transform: none;
  }

  body.has-lyrics.view-now-playing .now-playing-heading::after {
    right: calc(
      clamp(2rem, 4vw, 4rem) +
      min(27rem, 29vw)
    );
  }

  /* Lyrics sit in their own column beside the heading, not centred with the
     player: pin the panel to the top of the heading (the body's top padding
     is cancelled with a negative margin) so the lyrics start level with the
     "Now playing" title and have the full height below it. */
  body.has-lyrics.view-now-playing .lyrics-panel {
    align-self: flex-start;
    width: min(27rem, 29vw);
    height: min(78vh, 52rem, calc(100dvh - var(--now-playing-heading-top) - 2rem));
    max-height: min(78vh, 52rem, calc(100dvh - var(--now-playing-heading-top) - 2rem));
    margin-top: calc(var(--now-playing-heading-top) - var(--now-playing-content-top, 14rem));
    padding-top: 2rem;
    padding-right: 1rem;
  }

  /* Reserve the fixed heading's measured height (published by index.html) and
     centre the player in the space beneath it. "safe" keeps a tall player
     pinned below the heading instead of overflowing upward over it. */
  body.view-now-playing {
    /* Mirrors the heading's `top` (see .now-playing-heading and the inline
       rule in index.html). */
    --now-playing-heading-top: calc(4rem + clamp(1.25rem, 3.5vw, 2rem) - 1.25rem);
    --now-playing-content-top: calc(var(--now-playing-heading-bottom, 12rem) + 1.5rem);
    /* Height left for the player once the heading is reserved. Used only to cap
       the square album art so it can't grow tall enough to overflow. */
    --now-playing-available: calc(
      100dvh - var(--now-playing-content-top, 13.75rem) - 1.5rem
    );
    /* Card padding shrinks with the window height too (stretched-wide
       desktops), so the whole player, controls included, stays inside the
       viewport. Used by the .app padding and the art cap below. */
    --now-playing-app-pad: min(clamp(1.5rem, 3vw, 2.5rem), 5.5dvh);
    /* The square album art grows with window width up to 20rem, which on a
       stretched-but-short window makes the player taller than the viewport and
       forces a scrollbar. Cap its column against the space left below the
       heading so the player shrinks to fit and everything stays visible. */
    --now-playing-art-max: min(
      20rem,
      calc(
        var(--now-playing-available, 35rem) -
          2 * var(--now-playing-app-pad, 2.5rem) - 1rem
      )
    );
    gap: clamp(2rem, 4vw, 4rem);
    align-items: safe center;
    padding-top: var(--now-playing-content-top);
    padding-bottom: 1.5rem;
  }

  /* The global `* { overflow: hidden }` also locks the page itself, so a short
     window clipped the player and its controls with no way to reach them. Let
     the Now Playing page scroll (scrollbar hidden) once the content no longer
     fits. */
  html:has(body.view-now-playing) {
    overflow-x: hidden;
    overflow-y: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  html:has(body.view-now-playing)::-webkit-scrollbar {
    display: none;
  }

  body.view-now-playing .app {
    width: min(58rem, calc(100vw - min(27rem, 29vw) - clamp(2rem, 4vw, 4rem) - 2rem));
    grid-template-columns: minmax(13rem, var(--now-playing-art-max)) minmax(16rem, 1fr);
    column-gap: clamp(2rem, 4vw, 4rem);
    padding: var(--now-playing-app-pad, 2.5rem);
    transform: none;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  body.view-now-playing .lyrics-panel {
    display: block;
    position: relative;
    width: min(27rem, 29vw);
    height: min(78vh, 52rem, max(14rem, calc(100dvh - var(--now-playing-content-top, 14rem) - 2rem)));
    max-height: min(78vh, 52rem, max(14rem, calc(100dvh - var(--now-playing-content-top, 14rem) - 2rem)));
    padding: 2rem 1rem 1rem 0;
    visibility: visible;
    opacity: 1;
    pointer-events: auto;
    transform: none;
  }

  body.view-now-playing:not(.has-lyrics) .lyrics-content::before {
    width: fit-content;
    max-width: 100%;
    color: rgba(255, 255, 255, 0.58);
    content: "No Lyrics Yet";
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 0.02em;
  }

  body.view-now-playing:not(.has-lyrics) .lyrics-content {
    display: grid;
    height: 100%;
    max-height: none;
    padding: 0;
    place-items: center;
    text-align: center;
  }

  body.view-now-playing:not(.has-lyrics) .lyrics-toggle {
    display: none;
  }

  body.view-now-playing:not(.has-lyrics) .lyrics-panel {
    padding-top: 0;
  }

  body.view-now-playing .now-playing-heading::after {
    right: calc(
      clamp(2rem, 4vw, 4rem) +
      min(27rem, 29vw)
    );
  }
}
html,
body {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

img {
  -webkit-user-drag: none;
  user-drag: none;
}

