/* ──────────────────────────────────────────────────────────────────────────
 *  tg-player.css — the Tunegether player bar.
 *
 *  One bar, every surface. The website, the desktop app and whatever a phone
 *  wraps all mount the same markup from tg-bar.js and style it from here, so a
 *  control that moves moves everywhere and there is no such thing as "the web
 *  version behaves differently".
 *
 *  It adapts by DEVICE, not by product:
 *
 *    phone    a compact bar above the thumb, which opens into a full-screen
 *             now-playing sheet — artwork, transport, scrubber, the lot.
 *    tablet   the bar keeps its scrubber inline; the sheet is a tall card.
 *    desktop  everything inline, sheet is a large centred panel.
 *
 *  THE STAGE
 *
 *  One element carries whatever is making sound, and it never moves in the DOM
 *  — moving an iframe reloads it, which would stop the music every time
 *  somebody opened the sheet. It is the artwork slot, and it grows.
 *
 *  Surfaces that can be rendered without anybody else's furniture are shown and
 *  become the artwork. Surfaces that cannot are one pixel across behind the
 *  artwork, still playing. `.tgp-veil` sits over the top of both so a hover
 *  never reaches a frame and every click lands on this player's own controls.
 *
 *  Theme comes from tokens.css. The dark surfaces set --tgp-* overrides on a
 *  [data-tg-theme="dark"] ancestor; nothing here hard-codes a colour that a
 *  theme should own.
 *
 *  THE GLASS
 *
 *  The bar and everything that springs from it are glass: translucent fills
 *  over a blur, edges made of an inset highlight and a soft shadow rather
 *  than drawn lines, floating with air around them instead of welded to the
 *  bottom edge of the screen. The expanded sheet goes further: the artwork
 *  is the hero, edge to edge, and the whole sheet is tinted by the colour
 *  tg-player.js measures from that artwork (--tgp-tint). One material on
 *  every surface is the point, the same as one bar is.
 * ────────────────────────────────────────────────────────────────────────── */

.tgp {
  --tgp-bg: #ffffff;
  --tgp-ink: var(--tg-ink, #0B0B12);
  --tgp-ink-soft: var(--tg-ink-soft, #3B3B4C);
  --tgp-ink-mute: var(--tg-ink-mute, #5A5A70);
  --tgp-rule: var(--tg-rule, #E8E6F3);
  --tgp-surface-2: var(--tg-surface-2, #F3F1FF);
  --tgp-accent: var(--tg-purple, #7A3CFF);
  --tgp-shadow: 0 -2px 12px rgba(11, 11, 18, .06), 0 -18px 60px rgba(122, 60, 255, .10);

  /* ── the glass ──────────────────────────────────────────────────────────
   * The bar floats, and it is made of light rather than lines. A translucent
   * fill over a blur of whatever is behind it, an inset highlight along the
   * top edge where light would catch a pane, and a deep soft shadow under
   * it. There is deliberately no border anywhere in this treatment: a glass
   * edge is the highlight and the shadow, and a drawn line reads as a box.
   *
   * --tgp-tint is an "r, g, b" triplet measured from the artwork by
   * tg-player.js (the brand ink until a record is playing). It washes the
   * bar faintly and tints the expanded sheet deeply; CSS composes every
   * alpha from the one triplet so no colour is hard-coded twice. */
  --tgp-tint: 11, 11, 18;
  --tgp-glass-bg: rgba(255, 255, 255, .72);
  --tgp-glass-filter: blur(20px) saturate(160%);
  --tgp-glass-hi: inset 0 1px 0 rgba(255, 255, 255, .55);
  --tgp-glass-shadow: 0 18px 50px rgba(11, 11, 18, .18), 0 2px 10px rgba(11, 11, 18, .08);
  --tgp-glass-r: 26px;

  position: fixed;
  left: 10px;
  right: 10px;
  bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  z-index: 90;
  background-color: var(--tgp-glass-bg);
  background-image: linear-gradient(180deg,
      rgba(var(--tgp-tint), .13), rgba(var(--tgp-tint), .04));
  -webkit-backdrop-filter: var(--tgp-glass-filter);
  backdrop-filter: var(--tgp-glass-filter);
  border-radius: var(--tgp-glass-r);
  box-shadow: var(--tgp-glass-hi), var(--tgp-glass-shadow);
  color: var(--tgp-ink);
  font-family: var(--tg-font-body, system-ui, sans-serif);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "art main right";
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  transform: translateY(calc(110% + 20px));
  transition: transform var(--tg-dur, 220ms) var(--tg-ease-out, ease),
              height var(--tg-dur, 220ms) var(--tg-ease-out, ease);
  will-change: transform;
}

/* No blur, no glass. A browser that cannot do backdrop-filter gets the solid
   surface the bar always had, at the same shape: the float and the radius
   are layout, the translucency is the finish. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .tgp {
    background-color: var(--tgp-bg);
    background-image: none;
  }
}

[data-tg-theme="dark"] .tgp,
.tgp[data-theme="dark"] {
  --tgp-bg: var(--tg-dark-surface, #10101C);
  --tgp-ink: var(--tg-dark-ink, #F5F5FA);
  --tgp-ink-soft: var(--tg-dark-ink-soft, #B4B4C8);
  --tgp-ink-mute: var(--tg-dark-ink-mute, #7E7E96);
  --tgp-rule: var(--tg-dark-rule, #242438);
  --tgp-surface-2: var(--tg-dark-surface-2, #181828);
  --tgp-shadow: 0 -2px 12px rgba(0, 0, 0, .5);
  /* Dark glass is a smoked pane: the fill is the dark surface made
     translucent, the highlight is fainter (less light reaches it), and the
     shadow is the black it floats over. */
  --tgp-glass-bg: rgba(16, 16, 28, .62);
  --tgp-glass-hi: inset 0 1px 0 rgba(255, 255, 255, .14);
  --tgp-glass-shadow: 0 18px 50px rgba(0, 0, 0, .5), 0 2px 10px rgba(0, 0, 0, .4);
}

.tgp[data-open="true"] { transform: translateY(0); }

/* ─────────────────────────────── the stage ──────────────────────────────── */

.tgp-art {
  grid-area: art;
  position: relative;
  width: 52px;
  height: 52px;
  flex: none;
  border: 0;
  padding: 0;
  /* Round in the bar, the way a record is round. The sheet overrides this
     when the same slot grows into the hero. */
  border-radius: 50%;
  overflow: hidden;
  background: var(--tgp-surface-2);
  cursor: pointer;
  transition: width var(--tg-dur, 220ms) var(--tg-ease-out, ease),
              height var(--tg-dur, 220ms) var(--tg-ease-out, ease),
              border-radius var(--tg-dur, 220ms) var(--tg-ease-out, ease);
}

.tgp-art-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tgp-art-letter {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--tg-font-display, system-ui, sans-serif);
  font-weight: 700;
  font-size: 20px;
  color: #fff;
  background: var(--tg-grad-diagonal, linear-gradient(135deg, #FF2DD1, #00F5FF));
}

/* The surface that is actually making sound. A clean video fills it and
   becomes the artwork; a frame that carries somebody else's marks sits at one
   pixel and the artwork above covers for it. */
.tgp-stage-host {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.tgp-stage-host[data-visible="false"] {
  width: 1px;
  height: 1px;
  opacity: 0;
  inset: auto auto 0 0;
}

.tgp-stage-host[data-visible="true"] { opacity: 1; z-index: 1; }

/* A 16:9 frame inside a square crop: scaled to cover, centred, so a music
   video reads as artwork rather than as a letterboxed strip. */
.tgp-frame-video {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 178%;
  height: 178%;
  transform: translate(-50%, -50%);
  border: 0;
}

.tgp-frame-headless {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  border: 0;
  pointer-events: none;
}

/* Over everything in the art slot: stops a pointer ever reaching a frame, so
   no hover furniture of anybody else's is ever summoned, and a click on the
   artwork is a click on this player. */
.tgp-veil {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: transparent;
}

/* The loading ring. Shown by the root's data-status alone, so it cannot be
   left behind by a state change nobody painted: loading shows it, anything
   else hides it. Centred on the artwork, above the veil, and never in the
   way of a press (pointer-events none, and the art button still takes it). */
.tgp-spin {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  border: 2.5px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  transition: opacity 140ms ease;
}

.tgp[data-status="loading"] .tgp-spin {
  opacity: 1;
  animation: tgp-spin-turn 760ms linear infinite;
}

@keyframes tgp-spin-turn {
  to { transform: rotate(360deg); }
}

/* ──────────────────────────────── the middle ────────────────────────────── */

.tgp-main {
  grid-area: main;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.tgp-title {
  font-weight: 650;
  font-size: 14px;
  line-height: 1.25;
  color: var(--tgp-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tgp-sub {
  font-size: 12.5px;
  color: var(--tgp-ink-mute);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: center;
  gap: 8px;
}

.tgp-live {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--tgp-accent);
}

.tgp-live .pip {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  animation: tgp-pulse 1.8s var(--tg-ease-out, ease) infinite;
}

@keyframes tgp-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .35; transform: scale(.7); }
}

/* ─────────────────────────────── the scrubber ───────────────────────────── */

.tgp-scrub {
  grid-area: scrub;
  display: none;
  align-items: center;
  gap: 8px;
}

.tgp-scrub[data-on="true"] { display: flex; }

.tgp-time {
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--tgp-ink-mute);
  min-width: 34px;
}

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

.tgp-range {
  -webkit-appearance: none;
  appearance: none;
  flex: 1 1 auto;
  min-width: 60px;
  height: 4px;
  border-radius: 999px;
  background: linear-gradient(to right,
      var(--tgp-accent) 0%, var(--tgp-accent) var(--tgp-pct, 0%),
      var(--tgp-rule) var(--tgp-pct, 0%), var(--tgp-rule) 100%);
  cursor: pointer;
  /* A drag on a slider in a fixed bar is otherwise read as a drag on the page
     behind it, and the seek is lost halfway through on every phone. */
  touch-action: none;
}

.tgp-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--tgp-accent);
  border: 2px solid var(--tgp-bg);
  box-shadow: 0 1px 4px rgba(11, 11, 18, .25);
}

.tgp-range::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border: 2px solid var(--tgp-bg);
  border-radius: 50%;
  background: var(--tgp-accent);
}

.tgp-range:disabled { opacity: .45; cursor: default; }

/* ──────────────────────────────── controls ──────────────────────────────── */

.tgp-right {
  grid-area: right;
  display: flex;
  align-items: center;
  gap: 4px;
}

.tgp-btn {
  -webkit-appearance: none;
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--tgp-ink-soft);
  width: 36px;
  height: 36px;
  border-radius: 10px;
  display: inline-grid;
  place-items: center;
  cursor: pointer;
  flex: none;
  transition: background var(--tg-dur-fast, 150ms), color var(--tg-dur-fast, 150ms);
}

.tgp-btn:hover { background: var(--tgp-surface-2); color: var(--tgp-ink); }
.tgp-btn:focus-visible { outline: 2px solid var(--tgp-accent); outline-offset: 2px; }
.tgp-btn[aria-pressed="true"] { color: var(--tgp-accent); }
.tgp-btn[disabled] { opacity: .3; cursor: default; }

/* `hidden` has to actually hide.
 *
 * Every control in this bar sets `display` in a class rule, and an author
 * `display` beats the user agent's own `[hidden] { display: none }` however
 * specific that looks — so a control the player had hidden stayed on screen.
 * That is how a station's "save to a list" plus ended up standing next to a
 * record's save plus, two identical + buttons on a phone with nothing to tell
 * them apart, and how a recorded track carried a "live" pip. One rule, scoped
 * to the player, closes the whole class of it. */
.tgp [hidden] { display: none !important; }
.tgp-btn svg { width: 20px; height: 20px; display: block; }

.tgp-play {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--tg-grad-diagonal, linear-gradient(135deg, #FF2DD1, #00F5FF));
  color: #fff;
}

.tgp-play:hover { background: var(--tg-grad-diagonal, linear-gradient(135deg, #FF2DD1, #00F5FF)); color: #fff; filter: brightness(1.06); }
.tgp-play svg { width: 22px; height: 22px; }

.tgp-vol {
  width: 78px;
  height: 4px;
  flex: none;
}

.tgp-data {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--tgp-ink-mute);
  padding: 3px 7px;
  border-radius: 999px;
  background: var(--tgp-surface-2);
  white-space: nowrap;
}

/* ───────────────────────────── the note line ────────────────────────────── */

.tgp-note {
  grid-column: 1 / -1;
  font-size: 12.5px;
  color: var(--tgp-ink-soft);
  /* A translucent wash of the playing record's colour, not a solid slab:
     the note sits ON the glass, so it is tinted like the glass is. */
  background: rgba(var(--tgp-tint), .07);
  border-radius: 12px;
  padding: 7px 11px;
  margin-top: 2px;
}

.tgp-note[hidden] { display: none; }

/* ───────────────────────────── the device menu ──────────────────────────── */

.tgp-pop {
  position: absolute;
  bottom: calc(100% + 10px);
  right: 12px;
  min-width: 232px;
  max-width: min(320px, calc(100vw - 24px));
  /* The same glass as the bar it springs from, at a rounder radius. Its
     edge is the highlight and the shadow, like the bar's. */
  background-color: var(--tgp-glass-bg);
  -webkit-backdrop-filter: var(--tgp-glass-filter);
  backdrop-filter: var(--tgp-glass-filter);
  border-radius: 20px;
  box-shadow: var(--tgp-glass-hi), var(--tgp-glass-shadow);
  padding: 8px;
  z-index: 5;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .tgp-pop { background-color: var(--tgp-bg); }
}

.tgp-pop[hidden] { display: none; }

/* In the expanded sheet the player itself is the whole screen (position
   fixed, height 100dvh, and it always carries a transform, which makes it
   the containing block for fixed descendants too). The rule above anchors
   the popover above the player's top edge, which in this mode is above the
   top of the screen: the devices, light and blend popovers opened exactly
   there, fully off-screen, so their buttons looked dead. Here the popover
   is pinned inside the sheet instead, above the bottom safe area, and can
   scroll when it is taller than the space. The collapsed bar keeps the
   rule above, where above the bar is right. */
.tgp[data-expanded="true"] .tgp-pop {
  position: fixed;
  left: 50%;
  right: auto;
  top: auto;
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  width: min(360px, calc(100vw - 24px));
  min-width: 0;
  max-width: none;
  max-height: 62dvh;
  overflow-y: auto;
}

.tgp-pop h4 {
  margin: 4px 8px 8px;
  font-family: var(--tg-font-display, system-ui, sans-serif);
  font-size: 13px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--tgp-ink);
  font-weight: 800;
}

.tgp-dev {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--tgp-ink);
  text-align: left;
  padding: 9px 8px;
  border-radius: 9px;
  cursor: pointer;
  font-size: 13.5px;
}

.tgp-dev:hover { background: var(--tgp-surface-2); }
/* The chosen device is a filled row, translucent so the glass shows
   through it: a fill, never an outline. */
.tgp-dev[aria-pressed="true"] {
  color: var(--tgp-accent);
  font-weight: 600;
  background: rgba(122, 60, 255, .10);
}
.tgp-dev .tgp-dev-sub { display: block; font-size: 11.5px; color: var(--tgp-ink-mute); font-weight: 400; }
.tgp-dev svg { width: 18px; height: 18px; flex: none; }
.tgp-pop-note { margin: 8px; font-size: 11.5px; color: var(--tgp-ink-mute); line-height: 1.45; }

/* ───────────────────────────── expanded sheet ───────────────────────────── */

.tgp[data-expanded="true"] {
  /* The sheet is a room lit by the artwork. Whatever the page's theme, the
     sheet takes the dark token set and a background that starts as the
     artwork's measured colour at the top and melts down into the black
     base, so the picture never sits in a box on a page colour, it becomes
     the room and then fades out of it. */
  --tgp-bg: #10101C;
  --tgp-ink: #F5F5FA;
  --tgp-ink-soft: #C9C9DA;
  --tgp-ink-mute: #9C9CB4;
  --tgp-rule: rgba(255, 255, 255, .16);
  --tgp-surface-2: rgba(255, 255, 255, .08);
  --tgp-glass-bg: rgba(16, 16, 28, .66);
  --tgp-glass-hi: inset 0 1px 0 rgba(255, 255, 255, .14);

  top: 0;
  left: 0;
  right: 0;
  bottom: auto;
  height: 100dvh;
  border-radius: 0;
  background-color: #08080F;
  background-image: linear-gradient(180deg,
      rgba(var(--tgp-tint), .55) 0%,
      rgba(var(--tgp-tint), .32) 30%,
      rgba(var(--tgp-tint), .13) 56%,
      rgba(8, 8, 15, 0) 80%);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: none;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto auto auto auto auto 1fr;
  grid-template-areas: "head" "art" "main" "scrub" "controls" "rail" "rest";
  align-content: start;
  justify-items: center;
  gap: 16px;
  padding: 0 20px calc(24px + env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
}

/* The head rides over the melt as its own glass strip, so the way out of the
   sheet is readable against any artwork, loud or quiet. */
.tgp[data-expanded="true"] .tgp-head {
  margin: 0 -20px;
  padding: max(14px, env(safe-area-inset-top, 0px)) 20px 10px;
  background: rgba(8, 8, 15, .22);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  backdrop-filter: blur(14px) saturate(150%);
  align-self: stretch;
}

/* The artwork is the hero: edge to edge, no frame, its bottom corners
   rounded where it lets go of the top of the sheet. The ::after is the melt
   itself: the picture's last stretch fades into the tint below it, so there
   is no line where artwork ends and background begins. */
.tgp[data-expanded="true"] .tgp-art {
  width: calc(100% + 40px);
  height: min(88vw, 400px);
  margin: 0 -20px;
  border-radius: 0 0 28px 28px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
}

.tgp[data-expanded="true"] .tgp-art::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(180deg,
      rgba(8, 8, 15, 0) 52%,
      rgba(var(--tgp-tint), .28) 84%,
      rgba(8, 8, 15, .38) 100%);
}

/* While the record plays, the hero artwork breathes: a slow drift in and
   across, the pace of a camera that is in no hurry. It runs only in the
   sheet and only while the root says playing (data-status is painted by
   the bar from the player's own state), it stops the moment the music
   pauses, and the reduced-motion block below turns it off entirely. The
   art slot clips (overflow hidden), so the scaled picture never paints
   outside its own frame. */
.tgp[data-expanded="true"][data-status="playing"] .tgp-art-img {
  animation: tgp-art-drift 24s ease-in-out infinite alternate;
  will-change: transform;
}

@keyframes tgp-art-drift {
  from { transform: scale(1) translate(0, 0); }
  to { transform: scale(1.08) translate(-1.6%, 1.6%); }
}

.tgp[data-expanded="true"] .tgp-main { align-items: center; text-align: center; width: 100%; max-width: 520px; gap: 6px; }
.tgp[data-expanded="true"] .tgp-title {
  font-family: var(--tg-font-display, system-ui, sans-serif);
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -.01em;
  white-space: normal;
}
.tgp[data-expanded="true"] .tgp-sub { font-size: 15px; white-space: normal; justify-content: center; }
.tgp[data-expanded="true"] .tgp-scrub { display: flex; width: 100%; max-width: 520px; }

/* The transport is one glass pill, and the blur seam lives here: what is
   behind the controls (the melt, the end of the artwork) is frosted where
   the listener's thumb works, so the buttons never fight the picture for
   contrast. The play button keeps its gradient, inside a glass pill it is
   the one solid thing, which is what the eye is meant to find first. */
.tgp[data-expanded="true"] .tgp-controls {
  justify-content: center;
  gap: 14px;
  background: rgba(255, 255, 255, .10);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  backdrop-filter: blur(18px) saturate(160%);
  border-radius: 999px;
  padding: 8px 18px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), 0 14px 34px rgba(0, 0, 0, .35);
}

/* The rail under it: every secondary control is a small glass chip of the
   same material, filled rather than outlined. */
.tgp[data-expanded="true"] .tgp-right .tgp-btn {
  background: rgba(255, 255, 255, .08);
  border-radius: 999px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12);
}
.tgp[data-expanded="true"] .tgp-right .tgp-btn:hover { background: rgba(255, 255, 255, .16); }
.tgp[data-expanded="true"] .tgp-data { background: rgba(255, 255, 255, .10); }
.tgp[data-expanded="true"] .tgp-play { width: 62px; height: 62px; }
.tgp[data-expanded="true"] .tgp-play svg { width: 30px; height: 30px; }
.tgp[data-expanded="true"] .tgp-head { display: flex; }

/* The secondary rail rides under the transport rather than beside it (save,
   data, volume, devices) because in a full-screen sheet the transport is the
   thing the thumb is aiming at and everything else is one row further away.
   It WRAPS. Counted across, the rail is eleven controls and a volume slider:
   over 600px of furniture in a row. Centred on a 360 or 390px phone with no
   wrap, it overflowed both edges at once and the two outermost controls were
   drawn half off the screen, unreachable. Two rows that fit beat one row
   that does not, and every control stays where a thumb can land on it. */
.tgp[data-expanded="true"] .tgp-right {
  grid-area: rail;
  width: 100%;
  max-width: 520px;
  justify-content: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* The volume slider gives first: it is a control with a range of useful
   widths, where a button below 36px stops being a target. It keeps its
   desktop width when there is room and shrinks rather than pushing the
   row's ends off the phone. */
.tgp[data-expanded="true"] .tgp-vol {
  flex: 0 1 78px;
  min-width: 44px;
}

.tgp[data-expanded="true"] .tgp-note { max-width: 520px; }

.tgp-head {
  grid-area: head;
  display: none;
  width: 100%;
  align-items: center;
  justify-content: space-between;
}

.tgp-head-label {
  font-family: var(--tg-font-display, system-ui, sans-serif);
  font-size: 14px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--tgp-ink);
  font-weight: 800;
}

/* ─────────────────────────────── responsive ─────────────────────────────── */

.tgp-controls { display: flex; align-items: center; gap: 6px; }

/* Phone and tablet: the bar is thumb-sized and everything else lives in the
   sheet one tap away. Fewer, bigger targets beats more, smaller ones on a
   device held in one hand — and the whole bar is 60 pixels of a screen
   somebody is reading.
   900 and not 700. The inline layout's own controls measure 879px before a
   title or a time is drawn, so between 701 and 900 — an iPad upright, a phone
   turned sideways — the bar ran off its own right-hand edge and took the
   volume, the mute and the device picker with it. Nothing was hidden and
   nothing errored; those controls were simply past the side of the screen. */
@media (max-width: 900px) {
  .tgp:not([data-expanded="true"]) {
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    /* Two rows, and the second one is the scrubber.
       It used to be a 2px line painted on the bar's ::after — a picture of
       progress that nothing could grab. "How far in am I" and "take me to
       2:14" are not the same question, and a music player has to answer both
       on the device most people are holding. The row costs 22px. */
    grid-template-areas:
      "art main controls right"
      "scrub scrub scrub scrub";
    gap: 8px;
    /* The safe area is in the bar's bottom offset now that it floats; the
       padding inside the pill stays even on all four sides. */
    padding: 8px 10px;
  }
  .tgp:not([data-expanded="true"]) .tgp-art { width: 44px; height: 44px; }

  /* Full width, and a thumb-sized thing to drag. The visible track stays 4px:
     the element is 22px tall and paints its gradient as a 4px band down the
     middle, so the hit area is a thumb and the bar still looks like a hairline. */
  .tgp:not([data-expanded="true"]) .tgp-scrub[data-on="true"] {
    grid-area: scrub;
    display: flex;
    margin-top: 2px;
  }
  .tgp:not([data-expanded="true"]) .tgp-range {
    height: 22px;
    background-size: 100% 4px;
    background-position: center;
    background-repeat: no-repeat;
  }
  .tgp:not([data-expanded="true"]) .tgp-vol,
  .tgp:not([data-expanded="true"]) .tgp-data,
  .tgp:not([data-expanded="true"]) [data-tgp="prev"],
  .tgp:not([data-expanded="true"]) [data-tgp="mute"],
  /* Shuffle and repeat are real controls and they are not thumb-sized ones.
     On the compact bar they live one tap away in the sheet, where the artwork
     is and where somebody is actually choosing how the queue behaves. */
  .tgp:not([data-expanded="true"]) [data-tgp="shuffle"],
  .tgp:not([data-expanded="true"]) [data-tgp="repeat"],
  .tgp:not([data-expanded="true"]) [data-tgp="devices"],
  /* The glass bar floats with air at both sides, which costs the rail 20px
     it never had. Measured on a 390px phone with a station playing (the
     fattest rail: save AND save-to-a-list), the row ran 24px past the bar
     and took the close button with it, and the title column was squeezed to
     nothing: a mini player that cannot say what is playing. The list,
     share, light and blend controls join shuffle and repeat in the sheet,
     one tap away on the artwork, and the compact rail keeps what a thumb
     needs while something plays: save, play, next, stop. */
  .tgp:not([data-expanded="true"]) [data-tgp="list"],
  .tgp:not([data-expanded="true"]) [data-tgp="share"],
  .tgp:not([data-expanded="true"]) [data-tgp="light"],
  .tgp:not([data-expanded="true"]) [data-tgp="blend"] { display: none; }
  .tgp:not([data-expanded="true"]) .tgp-title { font-size: 13.5px; }
  .tgp:not([data-expanded="true"]) .tgp-play { width: 38px; height: 38px; }
  .tgp:not([data-expanded="true"]) .tgp-play svg { width: 19px; height: 19px; }

}

/* Tablet and up: the scrubber is inline, which is what a wider bar is for.
 *
 * `minmax(0, …)` and not `minmax(160px, …)`. A grid track whose minimum is a
 * real number cannot go below it, so between 701px and about 1000px — every
 * tablet, and every phone turned sideways — the five tracks added up to more
 * than the screen and the bar overflowed its own page. It does not clip: it
 * widens the document, so the header and the list and the console all stretch
 * with it and the whole surface reads as slightly too big for the device, with
 * nothing visibly sticking out of anything. Letting the title and the scrubber
 * give is what the wide layout was for. */
@media (min-width: 901px) {
  .tgp:not([data-expanded="true"]) {
    grid-template-columns: auto minmax(0, 1fr) auto minmax(min-content, 2fr) auto;
    grid-template-areas: "art main controls scrub right";
    gap: 16px;
    padding: 12px 18px;
  }
  .tgp:not([data-expanded="true"]) .tgp-controls { grid-area: controls; }
  .tgp:not([data-expanded="true"]) .tgp-scrub { grid-area: scrub; display: flex; min-width: 0; }
  /* The TITLE gives first and the scrubber never goes below its own contents:
     a scrub track squeezed past `min-content` clips the two times, and "2:14
     of 4:02" is the reading somebody is actually taking off this bar. A title
     that ellipses is a title you can still read most of. */
  .tgp:not([data-expanded="true"]) .tgp-range { min-width: 40px; }
}

/* The page needs to know the bar is there, or the last row of any list on any
   surface sits underneath it forever. The bar floats now, so its clearance
   is its height plus the air underneath it. */
body[data-tgp-open="true"] { padding-bottom: 98px; }

@media (max-width: 900px) {
  /* The compact bar now carries a scrub row, so the last item in any list on
     the page has to clear a taller bar than it used to. */
  body[data-tgp-open="true"] { padding-bottom: 108px; }
}

@media (prefers-reduced-motion: reduce) {
  .tgp, .tgp-art { transition: none; }
  .tgp-live .pip { animation: none; }
  .tgp[data-status="loading"] .tgp-spin { animation: none; }
  .tgp[data-expanded="true"][data-status="playing"] .tgp-art-img { animation: none; }
}

/* ─────────────────────────── the light on the beat ───────────────────────────
 *
 * Two surfaces, one control. The veil is the strobe on a phone whose browser
 * will not hand over the camera lamp — every iPhone, today — and it has to be
 * the topmost thing on the page and take no clicks, because it is a light and
 * not a layer of the interface.
 *
 * It is appended to <body> rather than to the player bar on purpose: the bar is
 * a frosted, backdrop-filtered element, and a backdrop filter makes an element
 * the containing block for its own fixed children. A veil parented there would
 * be a rectangle the size of the bar.
 */

.tgl-veil {
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  background: #FFFFFF;
  opacity: 0;
  pointer-events: none;
  transition: opacity 90ms linear;
  will-change: opacity;
}

.tgp-light {
  padding: 8px;
  min-width: 250px;
}

.tgp-light-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 8px;
}

.tgp-light-row label {
  font-size: 12px;
  color: var(--tgp-ink-mute);
  flex: none;
  min-width: 68px;
}

.tgp-light-row input[type="range"] { flex: 1; }

.tgp-light-row select {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 12.5px;
  color: var(--tgp-ink);
  background: var(--tgp-surface-2);
  border: 0;
  border-radius: 8px;
  padding: 6px 8px;
}

.tgp-light-bpm {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 4px 8px 2px;
}

.tgp-light-bpm b {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--tgp-ink);
  font-variant-numeric: tabular-nums;
}

.tgp-light-bpm span { font-size: 11.5px; color: var(--tgp-ink-mute); }

.tgp-light-acts {
  display: flex;
  gap: 8px;
  padding: 6px 8px 2px;
}

.tgp-light-acts button {
  flex: 1;
  border: 0;
  background: var(--tgp-surface-2);
  color: var(--tgp-ink);
  border-radius: 9px;
  padding: 8px 6px;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}

.tgp-light-acts button:hover { background: var(--tgp-surface-2); }
.tgp-light-acts button[disabled] { opacity: .45; cursor: default; }

/* A record that stops when the screen goes dark says so here, once, in the
   place the listener is already looking. */
.tgp-lock {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  color: var(--tgp-ink-mute);
}

.tgp-lock[hidden] { display: none; }

/* The lock line's action. A row of its own, because the sub line is one
   ellipsised line on a phone and a button inside it would be cut in half. */
.tgp-keep {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-start;
  margin-top: -2px;
}

.tgp-keep[hidden] { display: none; }

.tgp-keep-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 5px 12px;
  border: 0;
  border-radius: 999px;
  background: var(--tgp-surface-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18);
  color: var(--tgp-ink-soft);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}

.tgp-keep-btn svg { width: 14px; height: 14px; flex: none; }
.tgp-keep-btn:hover { background: var(--tgp-surface-2); color: var(--tgp-ink); }
.tgp-keep-btn:focus-visible { outline: 2px solid var(--tgp-accent); outline-offset: 2px; }
.tgp[data-expanded="true"] .tgp-keep { justify-content: center; max-width: 520px; }

/* The reverse of the swap: back to the video take. Same row pattern as the
   lock line's action, because it is the same kind of decision. */
.tgp-watch {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-start;
  margin-top: -2px;
}

.tgp-watch[hidden] { display: none; }

.tgp-watch-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 5px 12px;
  border: 0;
  border-radius: 999px;
  background: var(--tgp-surface-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18);
  color: var(--tgp-ink-soft);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}

.tgp-watch-btn svg { width: 14px; height: 14px; flex: none; }
.tgp-watch-btn:hover { background: var(--tgp-surface-2); color: var(--tgp-ink); }
.tgp-watch-btn:focus-visible { outline: 2px solid var(--tgp-accent); outline-offset: 2px; }
.tgp[data-expanded="true"] .tgp-watch { justify-content: center; max-width: 520px; }

@media (prefers-reduced-motion: reduce) {
  /* A full-screen strobe is the single most likely thing on this platform to
     hurt somebody. If the operating system has been told to reduce motion, the
     screen never flashes — the lamp, which is not the screen and not in anyone's
     visual field, still can. */
  .tgl-veil { display: none; }
}

/* ── the blend ────────────────────────────────────────────────────────────
 * How long one song takes to become the next one. A slider and two lines:
 * what it is set to, and what it can and cannot reach. The second line is not
 * decoration — without it a blend that was a clean cut on a framed record
 * reads as the setting not working. */
.tgp-blend { min-width: 280px; }
.tgp-pop-title {
  margin: 4px 8px 8px;
  font-family: var(--tg-font-display, system-ui, sans-serif);
  font-size: 13px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--tgp-ink);
  font-weight: 800;
}
.tgp-blend .tgp-range { width: calc(100% - 16px); margin: 0 8px; }
.tgp-blend .tgp-pop-note:first-of-type { color: var(--tgp-ink); }

/* ─────────────────── the sheet's lower half ─────────────────────────────
 * Playback (sleep, speed, offline save) and Lyrics. Sheet only: the
 * compact bar has no room for rows of choices, and a control that is not
 * visible cannot be pressed by accident. Everything here is a filled
 * surface from the same tokens as the rest of the sheet (the sheet has
 * already swapped in its dark set), edges from fill and shadow, never a
 * drawn line. Chosen options are filled with the accent, the same answer
 * the device list gives. */

.tgp-xsec { display: none; }

.tgp[data-expanded="true"] .tgp-xsec {
  display: block;
  grid-column: 1 / -1;
  justify-self: center;
  width: 100%;
  max-width: 520px;
  margin-top: 10px;
  text-align: left;
}

.tgp-xsec-title {
  margin: 0 0 4px;
  font-family: var(--tg-font-display, system-ui, sans-serif);
  font-size: 14px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--tgp-ink);
  font-weight: 800;
}

.tgp-xrow {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.tgp-xrow-label {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--tgp-ink);
  min-width: 52px;
}

.tgp-xopts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.tgp-xchip {
  -webkit-appearance: none;
  appearance: none;
  border: 0;
  border-radius: 999px;
  background: var(--tgp-surface-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12);
  color: var(--tgp-ink-soft);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 7px 12px;
  min-height: 34px;
  cursor: pointer;
}

.tgp-xchip:hover { color: var(--tgp-ink); }
.tgp-xchip:focus-visible { outline: 2px solid var(--tgp-accent); outline-offset: 2px; }
.tgp-xchip[aria-pressed="true"] { background: var(--tgp-accent); color: #fff; }
.tgp-xchip[disabled] { opacity: .45; cursor: default; }

.tgp-xsave { flex: none; }
.tgp-xsave[aria-pressed="true"] { background: var(--tgp-surface-2); color: var(--tgp-ink-soft); }

.tgp-xnote {
  margin: 8px 0 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--tgp-ink-mute);
}

.tgp-xsave-state { margin: 0; }

/* The sleep chip on the compact bar: a filled pill riding with the title,
   naming the time left. In the sheet it hides, because the Sleep row
   itself is right there. */
.tgp-sleep-chip {
  -webkit-appearance: none;
  appearance: none;
  border: 0;
  border-radius: 999px;
  background: var(--tgp-accent);
  color: #fff;
  font: inherit;
  font-size: 11.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: 3px 9px;
  cursor: pointer;
  flex: none;
  white-space: nowrap;
}

.tgp[data-expanded="true"] .tgp-sleep-chip { display: none; }

/* ── the words ──────────────────────────────────────────────────────────
 * A soft filled card of glass, its own scroll, lines in the muted ink
 * until their moment comes. The line being sung is full strength and
 * bold; inside it, each word takes the accent as it is sung, when the
 * record carries word timings. A plain set of words (no timings) is
 * simply readable, in the softer ink, with nothing pretending to move. */
.tgp-lyrics-msg {
  margin: 10px 0 0;
  font-size: 13.5px;
  color: var(--tgp-ink-mute);
}

.tgp-lyrics {
  margin-top: 10px;
  max-height: 34dvh;
  overflow-y: auto;
  border-radius: 18px;
  background: rgba(255, 255, 255, .06);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .10);
  padding: 10px 6px;
  overscroll-behavior: contain;
}

.tgp-lyric {
  padding: 6px 12px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--tgp-ink-mute);
  border-radius: 10px;
}

.tgp-lyric[role="button"] { cursor: pointer; }
.tgp-lyric[role="button"]:hover { background: rgba(255, 255, 255, .07); }

.tgp-lyric.cur {
  color: var(--tgp-ink);
  font-weight: 700;
}

.tgp-lyric .tgp-word { transition: color 160ms linear; }
.tgp-lyric.cur .tgp-word.on { color: var(--tgp-accent); }

.tgp-kara-btn { margin-top: 10px; }

/* ── the karaoke stage ────────────────────────────────────────────────────
 * A separate root on the body (see tg-bar.js for why it cannot live inside
 * the sheet), so it carries its own defaults for the two tokens it borrows:
 * the artwork tint, which the bar's root measures and this root is handed
 * at open time, and the accent. Everything else is stated outright: white
 * words on a stage that is the artwork's colour deepened toward black.
 * Filled glass controls, the transport's own shapes, one size up. The line
 * being sung wraps, always: a stage that scrolls sideways is a stage nobody
 * can sing from. */

.tgp-kara {
  --tgp-tint: 11, 11, 18;
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  padding: max(18px, env(safe-area-inset-top)) 20px max(16px, env(safe-area-inset-bottom));
  background:
    linear-gradient(180deg, rgba(var(--tgp-tint), .5), rgba(5, 5, 10, .97) 58%),
    #05050A;
  color: #F5F5FA;
  overflow: hidden;
}

.tgp-kara[hidden] { display: none; }

.tgp-kclose {
  position: absolute;
  top: max(14px, env(safe-area-inset-top));
  right: 16px;
  width: 44px;
  height: 44px;
}

.tgp-khead {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  padding-right: 56px;
}

.tgp-kart-wrap {
  position: relative;
  width: 52px;
  height: 52px;
  flex: none;
  border-radius: 12px;
  overflow: hidden;
  background: rgba(255, 255, 255, .12);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18);
}

.tgp-kart-letter {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 22px;
  font-weight: 800;
  color: #F5F5FA;
}

.tgp-kart {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.tgp-khead-text { min-width: 0; }

.tgp-ktitle {
  font-size: 17px;
  font-weight: 800;
  color: #FFFFFF;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tgp-kartist {
  font-size: 14px;
  color: rgba(245, 245, 250, .78);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tgp-kstage {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 12px 0;
}

.tgp-klines {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  width: 100%;
}

.tgp-kline.side {
  font-size: clamp(17px, 3.4vw, 24px);
  font-weight: 600;
  line-height: 1.35;
  color: rgba(245, 245, 250, .45);
  max-width: 32em;
  overflow-wrap: break-word;
  min-height: 1.35em;
}

.tgp-kline.now {
  font-size: clamp(34px, 9vw, 64px);
  font-weight: 800;
  line-height: 1.15;
  color: #FFFFFF;
  max-width: 100%;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

.tgp-kword { transition: color 160ms linear; }
.tgp-kword.on { color: var(--tg-purple, #7A3CFF); }

.tgp-kmsg {
  margin: 0;
  font-size: 21px;
  font-weight: 700;
  color: #FFFFFF;
}

.tgp-kctl {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
}

.tgp-kbtn {
  -webkit-appearance: none;
  appearance: none;
  border: 0;
  border-radius: 50%;
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, .14);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 8px 24px rgba(0, 0, 0, .35);
  color: #FFFFFF;
  cursor: pointer;
  flex: none;
}

.tgp-kbtn svg { width: 24px; height: 24px; }

.tgp-kbtn.big {
  width: 68px;
  height: 68px;
  background: var(--tg-purple, #7A3CFF);
}

.tgp-kbtn.big svg { width: 30px; height: 30px; }

.tgp-kbtn:focus-visible,
.tgp-kchip:focus-visible { outline: 2px solid #FFFFFF; outline-offset: 2px; }

.tgp-ksing {
  display: flex;
  align-items: center;
  gap: 10px;
}

.tgp-kchip {
  -webkit-appearance: none;
  appearance: none;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, .14);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22);
  color: #FFFFFF;
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  padding: 10px 20px;
  min-height: 44px;
  cursor: pointer;
}

.tgp-kchip[aria-pressed="true"] { background: var(--tg-purple, #7A3CFF); }

.tgp-knote {
  font-size: 13px;
  color: rgba(245, 245, 250, .78);
}

.tgp-kmic-msg {
  margin: 12px 0 0;
  font-size: 13.5px;
  text-align: center;
  color: rgba(245, 245, 250, .88);
}

.tgp-kprog {
  position: relative;
  height: 4px;
  margin-top: 16px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .18);
  overflow: hidden;
  flex: none;
}

.tgp-kprog-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 0;
  display: block;
  background: var(--tg-purple, #7A3CFF);
}
