/* =========================================================
   BOTTOM PLAYER
   ========================================================= */

.player {
  position: fixed;

  left: 0;
  right: 0;
  bottom: 0;

  height: 92px;

  z-index: 1000;

  display: grid;

  /* Outer columns are capped (not 1fr) so cover+title and the volume
     cluster never stretch past what their own content needs; the
     middle column is the one 1fr track, so it — not the whole
     grid — absorbs any extra width. That's what actually pins .left
     to the true left edge and .right-controls to the true right edge
     at any viewport size: no justify-content trick, because the grid
     itself already spans exactly the bar's full width. (An earlier
     version capped every column and centered the whole track set —
     that fixed wide-viewport gaps *between* sections, but left equal
     padding stranded at both outer edges instead. .controls below
     does the actual centering now, inside the flexible middle
     column, so both problems are fixed at once.) */
  grid-template-columns:
    minmax(210px, 420px)
    minmax(0, 1fr)
    minmax(150px, 280px);

  align-items: center;

  gap: 24px;
  padding: 12px 24px;

  background: var(--surface);

  border-top: 1px solid var(--border);
}

/* Player left */

.left {
  min-width: 0;

  display: flex;
  align-items: center;

  gap: 14px;
}

.cover-sm {
  width: 56px;
  height: 56px;

  flex-shrink: 0;

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

  border-radius: 8px;

  background: linear-gradient(135deg, var(--pink-fill), var(--pink-dark));

  color: #fff;

  font-weight: 700;

  overflow: hidden;
}

.cover-sm img {
  width: 100%;
  height: 100%;

  object-fit: cover;
}

/* Empty state: nothing loaded yet — a neutral placeholder, not a
   gradient badge that reads as "there's a real track here." */
.cover-sm.is-empty {
  background: var(--surface-alt);
  color: var(--text-faint);
}

.player-meta {
  min-width: 0;

  display: flex;
  flex-direction: column;

  gap: 4px;
}

.player-title {
  color: var(--text);

  font-size: var(--fs-body);
  font-weight: 700;

  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.player-artist {
  color: var(--text-muted);

  font-size: var(--fs-body-sm);

  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.player-artist-link {
  color: inherit;

  text-decoration: none;
}

.player-artist-link:hover {
  color: var(--text);

  text-decoration: underline;
}

/* =========================================================
   PLAYER CONTROLS
   ========================================================= */

.controls {
  width: 100%;
  max-width: 720px;
  min-width: 0;

  /* Centers within the flexible middle column instead of stretching
     to fill it — the middle column can be very wide on a large
     screen, but the transport row + seek bar shouldn't be. */
  justify-self: center;

  display: flex;
  flex-direction: column;

  align-items: center;

  gap: 8px;
}

.control-buttons {
  display: flex;
  align-items: center;
  justify-content: center;

  gap: 6px;
}

.control-buttons .btn {
  /* Was a hardcoded 36px, silently overriding the shared --tap-md
     token via higher selector specificity (.control-buttons .btn
     beats .btn alone) — every other icon-only button in the app
     actually got the token; these four (shuffle/prev/next/repeat)
     didn't, until this was caught during a size-system audit. */
  width: var(--tap-md);
  height: var(--tap-md);

  background: transparent;

  border: none;

  color: var(--text-muted);
}

.control-buttons .btn:hover {
  background: var(--surface-alt);
  color: var(--text);
}

.control-buttons .btn.is-active {
  color: var(--pink);
}

.control-buttons .btn.is-active:hover {
  background: var(--pink-soft);
  color: var(--pink);
}

/* Disabled transport buttons (no active queue) shouldn't light up on
   hover — .control-buttons .btn:hover above would otherwise win the
   cascade (same specificity, later file) over the generic
   .btn:disabled:hover in components.css. */
.control-buttons .btn:disabled,
.control-buttons .btn:disabled:hover {
  background: transparent;
  color: var(--text-muted);
  opacity: 0.3;
  cursor: not-allowed;
  transform: none;
}

/* Main play button — the single most-used control in the whole app,
   and the one place in the icon-size system where both the touch
   target AND the glyph itself step up a full tier (--tap-lg/--icon-lg
   in css/base.css) rather than just the target. See base.css's
   comment for the research behind this: every mainstream player
   (Spotify, Apple Music, YouTube Music) renders Play/Pause visibly
   larger than its neighbors, not just in a bigger circle. */

.btn.play {
  width: var(--tap-lg);
  height: var(--tap-lg);

  margin: 0 6px;

  background: var(--pink-fill);

  color: #fff;
}

.btn.play svg {
  width: var(--icon-lg);
  height: var(--icon-lg);
}

/* Prev/Next — frequent, but secondary to Play/Pause: one tier down
   from it, one tier up from the utility controls (shuffle, repeat,
   etc.) around them. */
#prevBtn svg,
#nextBtn svg {
  width: var(--icon-md);
  height: var(--icon-md);
}

.btn.play:hover {
  background: var(--pink-fill-hover);
  color: #fff;

  transform: scale(1.06);
}

/* Disabled only when there's truly nothing anywhere to play — looks
   deliberately "off," not just a faded version of the pink button. */
.btn.play:disabled,
.btn.play:disabled:hover {
  background: var(--surface-alt);
  color: var(--text-faint);
  opacity: 1;
  cursor: not-allowed;
  transform: none;
}

/* =========================================================
   PROGRESS
   ========================================================= */

.progress {
  width: min(100%, 560px);

  display: flex;
  align-items: center;

  gap: 10px;
}

.time-current,
.time-duration {
  flex-shrink: 0;

  min-width: 32px;

  color: var(--text-faint);

  font-size: var(--fs-label);
  /* A real, well-established rule for any number that changes in
     place (timers, stopwatches, live counters): proportional digits
     have different widths, so "0:05" -> "0:06" visibly twitches
     without this. min-width above is a coarser backstop; this is the
     actual fix. */
  font-variant-numeric: tabular-nums;
}

.time-current {
  text-align: right;
}

.time-duration {
  text-align: left;
}

/* .bar is the real click/touch target (24px tall) — kept invisible
   itself so the seek bar still LOOKS thin; the visible unfilled
   track is a centered ::before, and .fill is a matching absolutely-
   positioned overlay. This fixes a real "seek doesn't register"
   problem: at 5-6px tall, most clicks near the visible line missed
   the actual hit box entirely. */
.bar {
  flex: 1;
  height: 24px;

  position: relative;

  display: flex;
  align-items: center;

  cursor: pointer;
}

.bar.disabled {
  cursor: default;
  pointer-events: none;
}

.bar::before {
  content: "";

  position: absolute;
  left: 0;
  right: 0;
  top: 50%;

  height: 4px;

  transform: translateY(-50%);

  border-radius: var(--radius-pill);

  background: var(--surface-alt);

  pointer-events: none;

  transition: height 0.15s ease;
}

.bar.disabled::before {
  opacity: 0.6;
}

/* Pink, the same as the volume slider beside it: one accent for every
   "how far along" control. Hover shows the drag handle. */
.fill {
  position: absolute;
  left: 0;
  top: 50%;

  height: 4px;
  width: 0%;

  transform: translateY(-50%);

  border-radius: var(--radius-pill);

  background: var(--pink);

  pointer-events: none;

  transition:
    width 0.08s linear,
    height 0.15s ease,
    background 0.1s ease;
}

/* Drag handle: hidden until hover, white so it reads on the pink fill */
.fill::after {
  content: "";

  position: absolute;
  right: 0;
  top: 50%;

  width: 12px;
  height: 12px;

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

  border-radius: 50%;

  background: var(--text);

  opacity: 0;

  transition: opacity 0.15s ease;
}

.bar:not(.disabled):hover::before,
.bar:not(.disabled):hover .fill {
  height: 6px;
}

.bar:not(.disabled):hover .fill::after {
  opacity: 1;
}

/* Hover time preview — follows the cursor, not playback position */
.seek-tooltip {
  position: absolute;
  left: 0;
  bottom: 100%;

  margin-bottom: 10px;
  padding: 4px 8px;

  transform: translateX(-50%);

  border-radius: var(--radius-sm);

  background: var(--surface-alt);
  color: var(--text);

  font-size: var(--fs-label);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;

  box-shadow: var(--shadow-sm);

  pointer-events: none;
  opacity: 0;

  transition: opacity 0.15s ease;
}

.bar:not(.disabled):hover .seek-tooltip {
  opacity: 1;
}

/* =========================================================
   VOLUME / LYRICS
   ========================================================= */

.right-controls {
  display: flex;
  align-items: center;

  justify-content: flex-end;

  gap: 6px;
}

.volume-icon {
  display: flex;
  align-items: center;
  justify-content: center;

  color: var(--text-muted);
}

/* muteBtn/lyricsBtn/queueBtn keep a 38px hit area (touch target) but
   stay visually flat */
button.volume-icon,
#lyricsBtn,
#queueBtn {
  width: 38px;
  height: 38px;

  background: transparent;
  border: none;

  color: var(--text-muted);
}

button.volume-icon:hover,
#lyricsBtn:hover,
#queueBtn:hover {
  background: var(--surface-alt);
  color: var(--text);
}

/* Active = the queue panel is open — same "lit up pink" language as
   shuffle/repeat's is-active state elsewhere in the bar. */
#queueBtn.is-active {
  color: var(--pink);
}

#queueBtn.is-active:hover {
  background: var(--pink-soft);
  color: var(--pink);
}

input[type="range"] {
  width: 88px;
  height: 4px;

  appearance: none;
  -webkit-appearance: none;

  background: transparent;

  cursor: pointer;

  margin-left: 4px;
}

#volumeRange::-webkit-slider-runnable-track {
  height: 4px;

  border-radius: var(--radius-pill);

  background: linear-gradient(
    to right,
    var(--pink) 0%,
    var(--pink) var(--volume-percent, 100%),
    var(--surface-alt) var(--volume-percent, 100%),
    var(--surface-alt) 100%
  );
}

input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;

  width: 12px;
  height: 12px;

  margin-top: -4px;

  border-radius: 50%;

  background: var(--text);
  border: none;

  box-shadow: var(--shadow-sm);
}

#volumeRange::-moz-range-track {
  height: 4px;

  border-radius: var(--radius-pill);

  background: var(--surface-alt);
}

#volumeRange::-moz-range-progress {
  height: 4px;

  border-radius: var(--radius-pill);

  background: var(--pink-fill);
}

input[type="range"]::-moz-range-thumb {
  width: 12px;
  height: 12px;

  border: none;

  border-radius: 50%;

  background: var(--text);
}

/* =========================================================
   QUEUE PANEL
   ========================================================= */

.queue-panel {
  /* display:none/flex, not opacity+transform — a real, verified bug
     in one test environment (not something browser-specific we can
     assume is rare enough to ignore) left this rendering as an inert,
     see-through layer with page content bleeding through it despite
     every computed style and the CSSOM confirming the "open" rule
     was correctly matched: className, matched rule, and its cssText
     all checked out, but the painted/reported opacity still read as
     the closed-state 0. display toggling doesn't depend on a
     transition/compositor step at all, so there's no equivalent
     failure mode — it's just present or not. */
  display: none;

  position: fixed;
  right: 24px;
  bottom: 108px; /* player height (92px) + a gap, so it floats clear of the bar */

  width: min(380px, calc(100vw - 48px));
  max-height: min(60vh, 560px);

  flex-direction: column;

  border-radius: 12px;

  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md);

  z-index: 1100;
}

.queue-panel.open {
  display: flex;
}

/* Entrance animation — a real @keyframes animation, not a transition.
   Transitions can't reliably animate a display:none -> visible change
   in the same style recalc (there's no "previous frame" to interpolate
   from, which is exactly the failure mode documented above for the
   display toggle itself) — @keyframes sidesteps that since it defines
   explicit from/to states instead of depending on the element's prior
   computed style. Respects prefers-reduced-motion: motion-sensitive
   users get the plain instant show above, nothing more.

   translateY/opacity only — never a layout property (height, margin,
   top) — is deliberate beyond just perf: both are compositor-only, so
   the animation runs entirely off the main thread and can't trigger a
   reflow of anything else. That's what makes it safe to touch here
   without risking the main screen: .left-col's width is already
   settled by flexbox the instant .open is toggled (transform doesn't
   participate in layout at all), so the track list next to it never
   moves, jumps, or waits on this animation — only the panel's own
   paint is animated.

   60px / 480ms / MD3's "emphasized decelerate" curve — tuned up from
   an earlier, too-subtle 28px/320ms pass that read more like a fade
   than a slide: at that distance and an aggressively front-loaded
   ease-out curve, ~90% of the motion resolved in the first third of
   the animation, so it registered as a snap-into-place rather than a
   visible slide. Emphasized-decelerate (cubic-bezier(0.05,0.7,0.1,1),
   Material Design 3's motion spec — specifically the curve MD3
   recommends for elements entering the screen) spends most of its
   duration still visibly in motion rather than resolving early, which
   is what actually reads as "sliding" rather than "fading with a
   slight offset." */
@media (prefers-reduced-motion: no-preference) {
  @keyframes queue-panel-in {
    from {
      transform: translateY(60px);
      opacity: 0;
    }

    to {
      transform: translateY(0);
      opacity: 1;
    }
  }

  .queue-panel.open {
    animation: queue-panel-in 0.48s cubic-bezier(0.05, 0.7, 0.1, 1);
  }
}

/* Above ~1100px wide, dock the panel as a real flex sibling of
   .left-col inside .content (a "fixed block of space" alongside the
   track list, matching Spotify's own queue) instead of floating over
   the content. It's a plain flex item now, not a grid placement — see
   index.html's comment on why it lives inside .content and not
   alongside <header>: docking it at the <main>/grid level made it
   span the header's row too, squeezing the search bar even though
   only the track list below the header was meant to share space with
   it. .left-col's existing `flex:1; min-width:0` does the actual
   shrinking automatically once this sibling takes its own width —
   no parent-level class or grid-column reservation needed. Below
   this breakpoint there's no room to dock it, so the floating-
   overlay rules above stay in effect unchanged. */
@media (min-width: 1101px) {
  .queue-panel {
    position: static;
    right: auto;
    bottom: auto;

    flex-shrink: 0;

    width: 340px;
    height: 100%;
    max-height: none;
    min-height: 0;

    border-radius: 14px;
    border: none;
    box-shadow: none;

    z-index: auto;
  }
}

.queue-panel-header {
  flex-shrink: 0;

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

  padding: 16px 12px 8px 18px;
}

.queue-tabs {
  display: flex;

  gap: 18px;
}

.queue-tab {
  position: relative;

  padding: 4px 0 10px;

  background: transparent;
  border: none;

  color: var(--text-muted);

  font-size: var(--fs-body-lg);
  font-weight: var(--fw-body-lg);

  cursor: pointer;

  transition: color 0.15s ease;
}

.queue-tab:hover {
  color: var(--text);
}

.queue-tab.active {
  color: var(--text);
  font-weight: 700; /* same reasoning as .hero-play: a bit more weight than body text earns the "this is the active one" read */
}

/* The active indicator, not a border — a border would shift layout
   by its own width; this sits on top of the already-reserved bottom
   padding instead. */
.queue-tab.active::after {
  content: "";

  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;

  height: 2px;

  border-radius: var(--radius-pill);

  background: var(--pink-fill);
}

/* Sizing comes from .btn.small (components.css: width/height:
   var(--tap-sm), svg: var(--icon-xs)) — was hardcoded to 30px here,
   which (via #id beating .two.classes on specificity) silently
   overrode that token, leaving this the one button in the whole app
   not actually on the shared icon-size scale. Only the
   transparent/hover treatment is genuinely specific to this button. */
#queuePanelClose {
  background: transparent;
  border: none;

  color: var(--text-muted);
}

#queuePanelClose:hover {
  background: var(--surface-alt);
  color: var(--text);
}

.queue-panel-body {
  flex: 1;
  min-height: 0;

  overflow-y: auto;

  padding: 0 10px 14px;
}

.queue-section-label {
  padding: 12px 8px 6px;

  color: var(--text-faint);

  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
}

/* Reuses .queue-item/.meta/.title/.sub from components.css (the
   sidebar "Up Next" card) so both queue views look consistent. */
.queue-item-current .title {
  color: var(--pink);
}

/* Not clickable — no handler is attached, unlike the "next" rows */
.queue-item-current {
  cursor: default;
}

.queue-item-current:hover {
  background: transparent;
}

.queue-empty {
  padding: 8px;

  color: var(--text-muted);

  font-size: var(--fs-body-sm);
}

/* Heart beside the current song's title (js/player-like.js) */
.player-like-btn {
  width: var(--tap-sm);
  height: var(--tap-sm);

  flex-shrink: 0;

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

  border: none;
  border-radius: 50%;

  background: transparent;
  color: var(--text-muted);

  cursor: pointer;

  transition:
    color 0.15s ease,
    transform 0.15s ease;
}

.player-like-btn svg {
  width: 18px;
  height: 18px;
}

.player-like-btn:hover {
  color: var(--text);
  transform: scale(1.08);
}

.player-like-btn.is-liked {
  color: var(--pink);
}

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

/* Sleep timer running: the moon is pink, like Shuffle and Repeat on. */
#sleepTimerBtn.is-active {
  color: var(--pink);
}
