/* ──────────────────────────────────────────────────────────
 *  Tunegether — songs, in the same band as the radio.
 *
 *  Light, like radio.css and like the rest of this page. The band is the first
 *  thing a stranger sees and it has to read as one surface whichever half of it
 *  they are using, so everything here borrows radio.css's card geometry rather
 *  than inventing a second one.
 *
 *  The player panel at the bottom is the exception and is deliberately its own
 *  object: it holds somebody else's iframe, at whatever size that source draws
 *  itself, and it has to be visible while it plays.
 * ────────────────────────────────────────────────────────── */

/* ── the mode switch ─────────────────────────────────────────────────────── */

.tg-modes {
  position: relative;
  z-index: 2;
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  margin: 0 0 14px;
  background: rgba(255, 255, 255, .72);
  border: 1px solid var(--tg-rule);
  border-radius: 999px;
  box-shadow: var(--tg-shadow-1);
  backdrop-filter: blur(8px);
}

.tg-mode {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 16px;
  min-height: 40px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--tg-ink-mute);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--tg-dur-fast) var(--tg-ease-out),
              color var(--tg-dur-fast) var(--tg-ease-out);
}

.tg-mode:hover { color: var(--tg-ink); }

.tg-mode[aria-selected="true"] {
  background: var(--tg-ink);
  color: #fff;
}

.tg-mode:focus-visible {
  outline: 2px solid var(--tg-purple);
  outline-offset: 2px;
}

/* ── which half of the band is on ─────────────────────────────────────────
 *
 * Driven from one attribute on the section rather than from the `hidden`
 * attribute on each piece, and that is not a style preference — it is a bug
 * fix. `hidden` is a UA rule (`display: none`) with the lowest possible
 * specificity, so ANY class that sets `display` beats it. `.radio-grid` is
 * `display: grid`; setting `hidden` on it changed the property and left the
 * stations on screen underneath the songs.
 *
 * It passed a smoke test, too, because the test asserted `el.hidden` — the
 * attribute, which was perfectly true — rather than whether the thing could be
 * seen. A visibility check now reads the computed style.
 *
 * The `hidden` attribute is still set, because it is what a screen reader and
 * the accessibility tree read. This is what makes it mean something visually.
 */
#radio[data-mode="songs"] .radio-selects,
#radio[data-mode="songs"] .radio-chips,
#radio[data-mode="songs"] .radio-genres,
#radio[data-mode="songs"] .radio-more,
#radio[data-mode="songs"] .radio-grid,
#radio[data-mode="stations"] .songs-wrap { display: none; }

/* The songs' own "more" row. It borrows the stations' button, and cannot
   borrow the stations' row: that one is switched off in this mode, above. */
.songs-more {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 14px;
}

.songs-more[hidden] { display: none; }

.songs-shown {
  color: var(--tg-ink-mute);
  font-size: 13.5px;
}

/* ── the results ─────────────────────────────────────────────────────────── */

.songs-wrap { position: relative; z-index: 2; }

.songs-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
  gap: 10px;
  margin: 4px 0 14px;
}

.song {
  position: relative;
  display: flex;
  align-items: center;
  background: rgba(255, 255, 255, .78);
  border: 1px solid var(--tg-rule);
  border-radius: var(--tg-r);
  transition: border-color var(--tg-dur-fast) var(--tg-ease-out),
              box-shadow var(--tg-dur-fast) var(--tg-ease-out),
              transform var(--tg-dur-fast) var(--tg-ease-out);
}

.song:hover {
  border-color: var(--tg-rule-strong);
  box-shadow: var(--tg-shadow-1);
  transform: translateY(-1px);
}

.song[aria-current="true"] {
  border-color: var(--tg-purple);
  box-shadow: 0 0 0 1px var(--tg-purple) inset, var(--tg-shadow-1);
}

.song-hit {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 11px;
  min-width: 0;
  padding: 10px 4px 10px 10px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  border-radius: var(--tg-r);
}

.song-hit:focus-visible { outline: 2px solid var(--tg-purple); outline-offset: -2px; }

.song-art {
  flex: 0 0 auto;
  width: 56px; height: 56px;
  object-fit: cover;
  border-radius: 9px;
  background: var(--tg-surface-3);
  display: grid;
  place-items: center;
}

.song-art.letter {
  font-family: 'Space Grotesk', system-ui, sans-serif;
  font-weight: 700;
  font-size: 20px;
  color: var(--tg-purple);
}

.song-text { min-width: 0; display: grid; gap: 2px; }

.song-title {
  font-weight: 600;
  font-size: 14.5px;
  line-height: 1.25;
  color: var(--tg-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.song-artist {
  font-size: 13px;
  color: var(--tg-ink-mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.song-meta {
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 11.5px;
  color: var(--tg-ink-mute);
}

/* The catalogue a row came from, said out loud on every card. It is not a
   decoration: it is who is about to serve the audio, and a listener who taps a
   SoundCloud row should not be surprised by SoundCloud's player. */
.song-src {
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--tg-surface-3);
  color: var(--tg-ink-soft);
  font-weight: 600;
  letter-spacing: .01em;
}

.song-add {
  flex: 0 0 auto;
  width: 38px; height: 38px;
  margin-right: 8px;
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: 9px;
  background: transparent;
  color: var(--tg-ink-mute);
  cursor: pointer;
  transition: background var(--tg-dur-fast) var(--tg-ease-out),
              color var(--tg-dur-fast) var(--tg-ease-out);
}

.song-add:hover {
  background: var(--tg-surface-3);
  color: var(--tg-purple);
}

.song-add:focus-visible { outline: 2px solid var(--tg-purple); outline-offset: 1px; }

/* Skeletons match the real card's box exactly, so the grid does not jump when
   the answer lands. */
.song.skeleton { pointer-events: none; padding: 10px; gap: 11px; }
.song.skeleton .song-art { animation: tg-pulse 1.4s ease-in-out infinite; }
.song.skeleton .song-text { flex: 1 1 auto; }
.song.skeleton .bar {
  height: 10px;
  border-radius: 5px;
  background: var(--tg-surface-3);
  animation: tg-pulse 1.4s ease-in-out infinite;
}
.song.skeleton .bar:last-child { width: 58%; }

@keyframes tg-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

@media (prefers-reduced-motion: reduce) {
  .song.skeleton .song-art,
  .song.skeleton .bar { animation: none; }
  .song { transition: none; }
}

/* ── the empty state ──────────────────────────────────────────────────────
 *
 * A search box with nothing in it teaches nobody what it can do. These five
 * are real queries that return good answers, and pressing one runs it. */

.songs-empty {
  grid-column: 1 / -1;
  padding: 18px 4px 6px;
  display: grid;
  gap: 12px;
}

.songs-empty p {
  margin: 0;
  color: var(--tg-ink-mute);
  font-size: 14.5px;
  max-width: 62ch;
}

.songs-try { display: flex; flex-wrap: wrap; gap: 8px; }

/* ── on rotation ──────────────────────────────────────────────────────────
   A scrolling row rather than a grid: it is three or four records, it sits
   above a results grid that is already the densest thing on the page, and on a
   phone a row that scrolls sideways reads as "there is more" without taking
   another screenful to say so. */
.songs-rotation { margin-bottom: 14px; }
.songs-rotation[hidden] { display: none; }

.songs-rotation-h {
  margin: 0 0 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--tg-ink-mute);
}

.songs-rotation-row {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 4px;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

.songs-rot {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
  max-width: 260px;
  padding: 8px 14px 8px 8px;
  background: var(--tg-surface);
  border: 1px solid var(--tg-rule);
  border-radius: 999px;
  cursor: pointer;
  text-align: left;
  transition: border-color 140ms ease, transform 140ms ease;
}
.songs-rot:hover { border-color: var(--tg-rule-strong); transform: translateY(-1px); }
.songs-rot:focus-visible { outline: 2px solid var(--tg-purple); outline-offset: 2px; }
.songs-rot .song-art { width: 36px; height: 36px; border-radius: 50%; flex: none; }
.songs-rot-text { min-width: 0; }
.songs-rot-title {
  font-size: 13.5px; font-weight: 600; color: var(--tg-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.songs-rot-artist {
  font-size: 12px; color: var(--tg-ink-mute);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── playlists ───────────────────────────────────────────────────────────── */

.songs-lists[hidden] { display: none; }
.songs-lists { margin: 2px 0 14px; display: grid; gap: 8px; }

.songs-lists-h {
  margin: 0;
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--tg-ink-mute);
  font-weight: 700;
}

.songs-lists-row { display: flex; flex-wrap: wrap; gap: 8px; }

/* `display` on a element carrying the hidden attribute defeats it — the box
   stays on screen as an empty rounded rectangle. Stated explicitly rather than
   relying on the UA sheet, which this rule outranks. */
.songs-picker[hidden] { display: none; }

.songs-picker {
  display: grid;
  gap: 8px;
  padding: 12px;
  margin-top: 10px;
  background: var(--tg-surface);
  border: 1px solid var(--tg-rule-strong);
  border-radius: var(--tg-r);
  box-shadow: var(--tg-shadow-1);
}

/* The save that was interrupted by signing in has no button to hang off any
   more, so it stands at the top of the results it came from. */
.songs-picker.is-float { margin: 0 0 12px; }

.songs-picker-h { font-size: 13.5px; font-weight: 600; color: var(--tg-ink); }
.songs-picker-list { display: flex; flex-wrap: wrap; gap: 8px; }
.songs-picker-msg { margin: 0; font-size: 13px; color: var(--tg-ink-mute); }

.radio-chip.primary {
  background: var(--tg-ink);
  color: #fff;
  border-color: var(--tg-ink);
}

.songs-note { margin-top: 2px; }

/* ── the standing account line ────────────────────────────────────────────
 *
 * One row, in the band, on every load — signed in or out. Not a banner and not
 * a dismissible thing: a banner implies it will go away, and this is a fact
 * about the page that is always true. Quiet enough to ignore, present enough
 * that nobody has to go looking for it. */

.tg-account-line[hidden] { display: none; }

.tg-account-line {
  position: relative;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  margin: 14px 0 6px;
  padding: 11px 14px;
  background: rgba(255, 255, 255, .7);
  border: 1px solid var(--tg-rule);
  border-radius: var(--tg-r);
}

.tg-account-line[data-signed-in="true"] {
  background: rgba(122, 60, 255, .05);
  border-color: rgba(122, 60, 255, .22);
}

.tg-account-line-text {
  flex: 1 1 320px;
  min-width: 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--tg-ink-mute);
}

.tg-account-line-btn {
  flex: 0 0 auto;
  min-height: 38px;
  padding: 9px 16px;
  border: 0;
  border-radius: 10px;
  background: var(--tg-ink);
  color: #fff;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
}

.tg-account-line-btn:hover { background: var(--tg-purple); }
.tg-account-line-btn:focus-visible { outline: 2px solid var(--tg-purple); outline-offset: 2px; }

.tg-account-line[data-signed-in="true"] .tg-account-line-btn {
  background: transparent;
  color: var(--tg-purple);
  border: 1px solid rgba(122, 60, 255, .35);
}

/* ── the player panel ─────────────────────────────────────────────────────
 *
 * Sits above the radio's own bar when both exist, and neither is ever playing
 * at the same time as the other — the two files trade the floor through one
 * event. The stage is sized so YouTube's player clears its own minimum and
 * SoundCloud's widget gets the height it wants; nothing here is 1x1 or
 * off-screen, which is both a licence term and the honest thing to do. */

.song-player {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 70;
  background: rgba(255, 255, 255, .94);
  border-top: 1px solid var(--tg-rule-strong);
  box-shadow: 0 -8px 32px rgba(11, 11, 18, .1);
  backdrop-filter: blur(14px);
  transform: translateY(102%);
  transition: transform var(--tg-dur) var(--tg-ease-out);
}

.song-player[data-open="true"] { transform: none; }

@media (prefers-reduced-motion: reduce) { .song-player { transition: none; } }

.sp-inner {
  max-width: var(--tg-maxw);
  margin: 0 auto;
  padding: 10px clamp(12px, 3vw, 24px) max(10px, env(safe-area-inset-bottom));
  display: grid;
  gap: 8px;
}

.sp-head {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.sp-text { min-width: 0; flex: 1 1 auto; }

.sp-title {
  font-weight: 700;
  font-size: 14.5px;
  color: var(--tg-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.sp-artist {
  font-size: 13px;
  color: var(--tg-ink-mute);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.sp-badge {
  flex: 0 0 auto;
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--tg-surface-3);
  color: var(--tg-ink-soft);
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
}

.sp-btn {
  flex: 0 0 auto;
  min-height: 38px;
  padding: 8px 14px;
  border: 1px solid var(--tg-rule-strong);
  border-radius: 10px;
  background: #fff;
  color: var(--tg-ink);
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
}

.sp-btn:hover { border-color: var(--tg-purple); color: var(--tg-purple); }
.sp-btn:focus-visible { outline: 2px solid var(--tg-purple); outline-offset: 2px; }

.sp-btn.ghost {
  width: 38px;
  padding: 0;
  display: grid;
  place-items: center;
  border-color: transparent;
  color: var(--tg-ink-mute);
}

.sp-out {
  flex: 0 0 auto;
  font-size: 13px;
  font-weight: 600;
  color: var(--tg-purple);
  text-decoration: none;
  white-space: nowrap;
}

.sp-out:hover { text-decoration: underline; }

.sp-stage { display: grid; }

.sp-stage iframe {
  width: 100%;
  border: 0;
  border-radius: var(--tg-r);
  background: var(--tg-surface-3);
  display: block;
}

/* YouTube's player has a documented minimum it will honour; below it the
   controls stop rendering. 200px tall on a phone, 240 on anything wider. */
.sp-frame-youtube { height: 200px; }
@media (min-width: 700px) { .sp-frame-youtube { height: 260px; } }

.sp-frame-soundcloud { height: 120px; }
.sp-frame-mixcloud { height: 120px; }

.sp-audio { width: 100%; height: 40px; }

.sp-note {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--tg-ink-mute);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.sp-note-action { display: inline-flex; }

/* When a song is playing the radio's own bar is closed, so nothing stacks —
   but a page scrolled to the bottom still needs room under the panel. */
body[data-tg-song="true"] { padding-bottom: 300px; }

@media (max-width: 640px) {
  .sp-head { flex-wrap: wrap; }
  .sp-text { flex: 1 1 100%; order: -1; }
  .sp-out { font-size: 12.5px; }
  body[data-tg-song="true"] { padding-bottom: 320px; }
}

/* ─────────────────── playing it without touching the screen ────────────────
 *
 * Collapsed by default. This is setup instructions, and instructions that are
 * open by default are noise on a page whose job is to start playing music —
 * but instructions nobody can find are the same as not having built it.
 */

.tg-voice {
  margin: 14px 0 0;
  border: 1px solid var(--tg-rule, rgba(0, 0, 0, .1));
  border-radius: var(--tg-r, 12px);
  padding: 0;
  background: var(--tg-surface-2, rgba(0, 0, 0, .02));
}

.tg-voice > summary {
  cursor: pointer;
  padding: 11px 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--tg-ink, inherit);
  list-style: none;
}

.tg-voice > summary::-webkit-details-marker { display: none; }

.tg-voice > summary::before {
  content: "▸";
  display: inline-block;
  width: 1em;
  color: var(--tg-ink-mute);
}

.tg-voice[open] > summary::before { content: "▾"; }

.tg-voice-body {
  padding: 0 14px 12px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--tg-ink-mute);
}

.tg-voice-body p { margin: 0 0 10px; }
.tg-voice-body p:last-child { margin-bottom: 0; }
.tg-voice-body strong { color: var(--tg-ink, inherit); }

.tg-voice-body code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  background: var(--tg-surface, rgba(0, 0, 0, .05));
  border-radius: 5px;
  padding: 1px 5px;
  word-break: break-all;
}

.tg-voice-fine { font-size: 12px; opacity: .85; }
