/* ──────────────────────────────────────────────────────────
 *  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);
}

/* The pressed row, while it loads. songs.js puts .is-loading on the card
   the player calls current and takes it off on any other state, so the
   ring cannot outlive the load it announced. It sits over the artwork,
   which is where the thumb landed and where the eye still is. */
.song.is-loading .song-art { position: relative; }

.song.is-loading .song-art::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2.5px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  animation: song-spin-turn 760ms linear infinite;
}

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

@media (prefers-reduced-motion: reduce) {
  .song.is-loading .song-art::after { animation: none; }
}

.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; }

/* The share button sits next to save and behaves the same way: always drawn,
   one press, no menu. */
.song-share {
  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-share:hover {
  background: var(--tg-surface-3);
  color: var(--tg-purple);
}

.song-share: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;
}

/* ── made for you ─────────────────────────────────────────────────────────
   The rotation batch, ordered by the listener's own listening. The head
   carries the one action the rail offers as a whole (play it as a mix) and
   the one honest line about whose order this is. The cards are the
   rotation cards above, unchanged: one rail does not invent a second card. */
.songs-mfy { margin-bottom: 14px; }
.songs-mfy[hidden] { display: none; }

.songs-mfy-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}

.songs-mfy-h {
  margin: 0;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: .02em;
  color: var(--tg-ink);
}

.songs-mfy-sub {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--tg-ink-mute);
}

.songs-mfy-mix { flex: none; }

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

/* ── 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; }
