/* ──────────────────────────────────────────────────────────
 *  Tunegether — the world's radio, on the front page.
 *
 *  LIGHT, like the rest of this page. It shipped dark, on the argument that a
 *  player is a performance surface and the tokens file reserves the dark ramp
 *  for those. That argument is right inside the app and wrong here: this is the
 *  first section a stranger sees, directly under a white nav and directly above
 *  a white hero, and a black band across the top read as a different site
 *  wedged into this one. The dark ramp still owns /dj, /pads and the app.
 *
 *  It sits at the top because it is the one thing on this page a stranger can
 *  use in one click with no account, no app and no sign-in.
 * ────────────────────────────────────────────────────────── */

.radio-band {
  background: linear-gradient(180deg, var(--tg-surface-2) 0%, var(--tg-bg) 100%);
  color: var(--tg-ink);
  padding: clamp(28px, 4vw, 48px) 0 clamp(28px, 3vw, 44px);
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid var(--tg-rule);
}

/* The gradient as a light source rather than a stripe. Far weaker than the
   dark version was: on white, the same opacity reads as a stain. */
.radio-band::before {
  content: '';
  position: absolute;
  inset: -40% 30% auto -10%;
  height: 380px;
  background: radial-gradient(60% 60% at 30% 50%, rgba(122, 60, 255, 0.13), transparent 70%),
              radial-gradient(50% 50% at 80% 30%, rgba(255, 45, 209, 0.10), transparent 70%);
  filter: blur(24px);
  pointer-events: none;
}

/* ── the pulse ──────────────────────────────────────────────────────────────
 *
 * MagicRings, on a canvas mounted by /scripts/rings.js. It sits above the
 * static ::before wash and below every piece of content, which is the only
 * stacking order in which a moving thing can go behind a search box without
 * ever intercepting a tap: pointer-events are off and z-index is 1.
 *
 * Held well under full strength because the band is white and the rings are saturated. At full
 * strength on white they stop being a light source and start being a picture,
 * and the section's job is to be the clean top of a clean page. The fade at
 * the edges keeps the canvas from ending in a visible rectangle.
 */
.radio-rings {
  position: absolute;
  inset: -12% -6% auto -6%;
  height: 132%;
  z-index: 1;
  pointer-events: none;
  opacity: .6;
  -webkit-mask-image: radial-gradient(72% 70% at 50% 42%, #000 40%, transparent 100%);
          mask-image: radial-gradient(72% 70% at 50% 42%, #000 40%, transparent 100%);
}

.radio-rings canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* A phone gets less of it: the band is taller relative to the screen, the
   rings are correspondingly bigger behind the text, and the type has to stay
   the loudest thing in the section. */
@media (max-width: 760px) {
  .radio-rings { opacity: .34; }
}

/* Belt and braces. rings.js already refuses to start under reduced motion;
   this also hides a canvas that was mounted before the preference changed. */
@media (prefers-reduced-motion: reduce) {
  .radio-rings { display: none; }
}

.radio-inner { position: relative; z-index: 2; }

.radio-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 16px 32px;
  margin-bottom: 22px;
}

.radio-head .eyebrow { margin-bottom: 0; }

.radio-title {
  font-family: var(--tg-font-display);
  font-size: clamp(30px, 4.4vw, 54px);
  line-height: 1.04;
  letter-spacing: -0.02em;
  margin: 6px 0 0;
  color: var(--tg-ink);
}

.radio-title .grad {
  background: var(--tg-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.radio-sub {
  color: var(--tg-ink-soft);
  font-size: 15px;
  margin: 8px 0 0;
  max-width: 56ch;
}

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

.radio-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-bottom: 16px;
}

.radio-search {
  display: flex;
  align-items: center;
  gap: 8px;
  background: #fff;
  border: 1px solid var(--tg-rule-strong);
  border-radius: 999px;
  padding: 0 6px 0 14px;
  min-width: min(320px, 100%);
  flex: 1 1 280px;
  max-width: 460px;
  color: var(--tg-ink-mute);
  box-shadow: var(--tg-shadow-1);
}

.radio-search:focus-within { border-color: var(--tg-purple); }

.radio-search input {
  flex: 1;
  background: none;
  border: 0;
  outline: none;
  color: var(--tg-ink);
  font: inherit;
  font-size: 15px;
  padding: 12px 0;
  min-width: 0;
}

.radio-search input::placeholder { color: var(--tg-ink-mute); }

.radio-search button {
  border: 0;
  background: var(--tg-grad);
  color: #fff;
  font: inherit;
  font-weight: 700;
  font-size: 14px;
  border-radius: 999px;
  padding: 9px 16px;
  cursor: pointer;
  min-height: 38px;
  text-shadow: 0 1px 2px rgba(11, 11, 18, 0.28);
}

.radio-selects { display: flex; gap: 10px; flex-wrap: wrap; }

.radio-selects select {
  background: #fff;
  color: var(--tg-ink);
  border: 1px solid var(--tg-rule-strong);
  border-radius: 999px;
  padding: 11px 14px;
  font: inherit;
  font-size: 14px;
  min-height: 44px;
  cursor: pointer;
  max-width: 210px;
  box-shadow: var(--tg-shadow-1);
}

.radio-chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 8px;
  margin-bottom: 18px;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

.radio-chip {
  flex: 0 0 auto;
  background: #fff;
  color: var(--tg-ink-soft);
  border: 1px solid var(--tg-rule-strong);
  border-radius: 999px;
  padding: 9px 15px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 550;
  cursor: pointer;
  white-space: nowrap;
  min-height: 38px;
  transition: border-color var(--tg-dur-fast) var(--tg-ease-out),
              color var(--tg-dur-fast) var(--tg-ease-out),
              box-shadow var(--tg-dur-fast) var(--tg-ease-out);
}

.radio-chip:hover { color: var(--tg-ink); border-color: var(--tg-purple); }

/* The door to the other few hundred. Marked so it does not read as one more
   genre sitting at the end of the rail. */
.radio-chip.all-genres {
  color: var(--tg-ink);
  border-color: var(--tg-purple);
  border-style: dashed;
}

.radio-chip.all-genres[aria-expanded='true'] {
  border-style: solid;
  background: var(--tg-surface-2);
}

/* What to press when a phrase found nothing. Wraps rather than scrolls: this
   is six things at the end of a sentence, not a rail to swipe through. */
.radio-suggest {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.radio-chip[aria-pressed='true'] {
  background: var(--tg-grad);
  color: #fff;
  border-color: transparent;
  font-weight: 700;
  text-shadow: 0 1px 2px rgba(11, 11, 18, 0.28);
}

/* ─────────────────────────── every genre ───────────────────────────
 *
 * The chip rail above holds eighteen and scrolls sideways. This is the rest of
 * them — several hundred — so it wraps and scrolls DOWN inside a bounded box,
 * because a list that pushes the grid a full screen off the page is a list
 * nobody opens twice.
 *
 * `[hidden]` is written out against the class for the reason documented at the
 * top of songs.css: the UA rule for `hidden` is the weakest thing in the
 * cascade and any class that sets `display` beats it, which is how a panel
 * ends up visibly open while its `hidden` attribute is perfectly true.
 */
.radio-genres {
  display: block;
  margin: -6px 0 18px;
  padding: 14px;
  background: #fff;
  border: 1px solid var(--tg-rule-strong);
  border-radius: 16px;
}

.radio-genres[hidden] { display: none; }

.radio-genre-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}

.radio-genre-search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 12px;
  border: 1px solid var(--tg-rule-strong);
  border-radius: 999px;
  color: var(--tg-ink-soft);
}

.radio-genre-search:focus-within { border-color: var(--tg-purple); }

.radio-genre-search input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  outline: 0;
  background: none;
  font: inherit;
  font-size: 14.5px;
  color: var(--tg-ink);
  padding: 10px 0;
}

.radio-genre-count {
  flex: 0 0 auto;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--tg-ink-soft);
  white-space: nowrap;
}

.radio-genre-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  max-height: 264px;
  overflow-y: auto;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

/* How many stations are behind the word, so a scene with nine hundred reads
   differently from one with fourteen before it is pressed. */
.radio-genre-list .radio-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.radio-genre-list .radio-chip .n {
  font-size: 11.5px;
  font-weight: 600;
  opacity: 0.6;
  font-variant-numeric: tabular-nums;
}

/* ───────────────────────────── the grid ───────────────────────────── */

.radio-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px;
}

/* ─────────────────────── how many, and the rest ─────────────────────── */

.radio-more {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 14px;
}

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

.radio-more-btn {
  background: #fff;
  color: var(--tg-ink);
  border: 1px solid var(--tg-rule-strong);
  border-radius: 999px;
  padding: 11px 20px;
  font: inherit;
  font-size: 14px;
  font-weight: 650;
  cursor: pointer;
  min-height: 42px;
  transition: border-color var(--tg-dur-fast) var(--tg-ease-out),
              box-shadow var(--tg-dur-fast) var(--tg-ease-out);
}

.radio-more-btn:hover:not(:disabled) { border-color: var(--tg-purple); }
.radio-more-btn:disabled { opacity: 0.6; cursor: default; }
.radio-more-btn[hidden] { display: none; }

.radio-shown {
  font-size: 13px;
  font-weight: 600;
  color: var(--tg-ink-soft);
  font-variant-numeric: tabular-nums;
}

.station {
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  background: #fff;
  border: 1px solid var(--tg-rule);
  border-radius: var(--tg-r);
  padding: 10px;
  color: inherit;
  font: inherit;
  cursor: pointer;
  min-height: 72px;
  width: 100%;
  box-shadow: var(--tg-shadow-1);
  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);
}

.station:hover {
  border-color: var(--tg-purple);
  box-shadow: var(--tg-shadow-2);
  transform: translateY(-1px);
}
.station:focus-visible { outline: 3px solid var(--tg-purple); outline-offset: 2px; }

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

.station-art {
  flex: 0 0 auto;
  width: 46px;
  height: 46px;
  border-radius: 10px;
  object-fit: cover;
  background: var(--tg-surface-3);
  display: grid;
  place-items: center;
  font-family: var(--tg-font-display);
  font-weight: 700;
  font-size: 18px;
  color: #fff;
}

.station-art.letter {
  background: var(--tg-grad-diagonal);
  text-shadow: 0 1px 2px rgba(11, 11, 18, 0.3);
}

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

.station-name {
  font-weight: 650;
  font-size: 14.5px;
  line-height: 1.25;
  color: var(--tg-ink);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.station-where {
  font-size: 12px;
  color: var(--tg-ink-mute);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* What a station costs to listen to, on every card, because a listener on a
   metered connection is entitled to know before they press it rather than
   after the bill. */
.station-cost {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--tg-ink-mute);
  margin-top: 4px;
  display: flex;
  align-items: center;
  gap: 5px;
}

.station-cost .pill {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--tg-rule-strong);
  background: var(--tg-surface);
}

.station-cost .pill.low {
  border-color: rgba(0, 167, 255, 0.45);
  background: rgba(0, 167, 255, 0.08);
  color: #0069A8;
}

.radio-note {
  color: var(--tg-ink-mute);
  font-size: 13.5px;
  margin: 16px 0 0;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.radio-note a { color: var(--tg-purple); text-decoration: underline; }

.radio-note button {
  background: none;
  border: 0;
  color: var(--tg-ink-mute);
  font: inherit;
  font-size: 13.5px;
  text-decoration: underline;
  cursor: pointer;
  padding: 4px 0;
}

.radio-status {
  color: var(--tg-ink-soft);
  font-size: 14px;
  padding: 22px 0;
}

.radio-status .retry {
  margin-left: 10px;
  background: #fff;
  border: 1px solid var(--tg-rule-strong);
  color: var(--tg-ink);
  border-radius: 999px;
  padding: 7px 14px;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

/* Skeletons: the grid keeps its shape while the directory answers, so the
   page does not jump when 24 cards arrive at once. */
.station.skeleton {
  cursor: default;
  pointer-events: none;
  background: #fff;
  box-shadow: none;
  animation: radio-pulse 1.4s var(--tg-ease-out) infinite;
}
.station.skeleton .station-art { background: var(--tg-surface-3); }
.station.skeleton .bar {
  height: 10px;
  border-radius: 5px;
  background: var(--tg-surface-3);
}
.station.skeleton .bar + .bar { margin-top: 8px; width: 60%; }

@keyframes radio-pulse { 50% { opacity: 0.55; } }
@media (prefers-reduced-motion: reduce) {
  .station.skeleton { animation: none; }
  .station:hover { transform: none; }
}

/* ─────────────────────── the data-usage explainer ─────────────────────── */

.radio-data {
  /* The nav is sticky, so a details that scrolls itself into view lands with
     its own summary underneath the bar. */
  scroll-margin-top: calc(var(--tg-nav-h) + 16px);
  margin-top: 22px;
  background: #fff;
  border: 1px solid var(--tg-rule);
  border-radius: var(--tg-r-lg);
  padding: 18px 20px;
  box-shadow: var(--tg-shadow-1);
}

.radio-data > summary {
  cursor: pointer;
  list-style: none;
  font-family: var(--tg-font-display);
  font-weight: 650;
  font-size: 16px;
  color: var(--tg-ink);
  display: flex;
  align-items: center;
  gap: 10px;
}
.radio-data > summary::-webkit-details-marker { display: none; }
.radio-data > summary::after {
  content: '+';
  margin-left: auto;
  font-weight: 500;
  color: var(--tg-ink-mute);
  font-size: 20px;
  line-height: 1;
}
.radio-data[open] > summary::after { content: '\2212'; }

.radio-data p {
  font-size: 14.5px;
  color: var(--tg-ink-soft);
  margin-top: 12px;
  max-width: 72ch;
}

.radio-data table {
  margin-top: 14px;
  border-collapse: collapse;
  font-size: 14px;
  width: 100%;
  max-width: 560px;
}
.radio-data th, .radio-data td {
  text-align: left;
  padding: 7px 14px 7px 0;
  border-bottom: 1px solid var(--tg-rule);
  color: var(--tg-ink-soft);
}
.radio-data th { color: var(--tg-ink); font-weight: 650; }
.radio-data td:last-child { font-variant-numeric: tabular-nums; }

/* ─────────────────────────── the player bar ─────────────────────────── */

.radio-player {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-top: 1px solid var(--tg-rule-strong);
  box-shadow: 0 -8px 32px rgba(11, 11, 18, 0.08);
  color: var(--tg-ink);
  transform: translateY(110%);
  transition: transform var(--tg-dur) var(--tg-ease-out);
  padding-bottom: env(safe-area-inset-bottom);
}

.radio-player[data-open='true'] { transform: translateY(0); }

.radio-player-inner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 24px;
  max-width: var(--tg-maxw);
  margin: 0 auto;
}

.rp-btn {
  flex: 0 0 auto;
  width: 44px; height: 44px;
  border-radius: 50%;
  border: 0;
  display: grid;
  place-items: center;
  cursor: pointer;
  background: var(--tg-grad);
  color: #fff;
}

.rp-btn.ghost {
  background: #fff;
  color: var(--tg-ink-mute);
  border: 1px solid var(--tg-rule-strong);
}

.rp-btn.ghost:hover { color: var(--tg-ink); border-color: var(--tg-purple); }
.rp-btn.ghost[aria-pressed='true'] { color: var(--tg-magenta); border-color: var(--tg-magenta); }

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

/* The track, when the broadcaster sends one, is the line a listener reads
   first — so it takes the top slot and the station drops underneath it. When
   nothing is sent, the station moves back up and there is no empty row. */
.rp-name {
  font-weight: 650;
  font-size: 14.5px;
  color: var(--tg-ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rp-where {
  font-size: 12px;
  color: var(--tg-ink-mute);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rp-where .sep { padding: 0 5px; color: var(--tg-rule-strong); }

.rp-name .track-note {
  font-weight: 500;
  font-size: 12px;
  color: var(--tg-ink-mute);
  margin-left: 8px;
}

.rp-live {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tg-purple);
  display: flex;
  align-items: center;
  gap: 6px;
}

.rp-live .pip {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--tg-magenta);
  animation: rp-blink 1.6s var(--tg-ease-out) infinite;
}
@keyframes rp-blink { 50% { opacity: 0.25; } }
@media (prefers-reduced-motion: reduce) { .rp-live .pip { animation: none; } }

/* How much of a data plan this session has actually spent. Measured from the
   stream's own bitrate and the seconds it has been playing, on the device — we
   never see it, and it is the honest answer to "is this going to cost me". */
.rp-data {
  flex: 0 0 auto;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--tg-ink-mute);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  border: 1px solid var(--tg-rule);
  border-radius: 999px;
  padding: 5px 10px;
  background: var(--tg-surface);
}

.rp-vol { flex: 0 0 auto; width: 96px; accent-color: var(--tg-purple); }

.rp-open {
  flex: 0 0 auto;
  border-radius: 999px;
  border: 1px solid var(--tg-rule-strong);
  background: #fff;
  color: var(--tg-ink);
  text-decoration: none;
  font-size: 13px;
  font-weight: 600;
  padding: 10px 16px;
  white-space: nowrap;
}

.rp-open:hover { border-color: var(--tg-purple); }

@media (max-width: 980px) {
  .rp-data { display: none; }
}

@media (max-width: 720px) {
  .rp-vol, .rp-open { display: none; }
  .radio-player-inner { padding: 10px 16px; }
  .radio-selects select { flex: 1 1 45%; max-width: none; }
}

/* The bar covers the last rows of whatever is at the bottom of the page. */
body[data-radio-playing='true'] footer { padding-bottom: 88px; }

/* ── the catalogue's reach ──────────────────────────────────────────────────
   One line under the station count. The number is canon
   (product_canon_facts · tunegether · catalogue.reach_tracks), a floor rounded
   down from the catalogues the search box queries, and it never names one of
   them. */
.radio-reach {
  margin: 10px 0 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--tg-ink-soft, #3B3B4C);
}
.radio-reach strong {
  font-family: var(--tg-font-display, system-ui, sans-serif);
  font-size: 19px;
  font-weight: 700;
  background: var(--tg-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* A gradient clipped to text disappears entirely where the property is not
     supported; the fallback colour under it is the thing that stops that. */
  -webkit-text-fill-color: transparent;
}
.radio-reach span { color: var(--tg-ink-mute, #5A5A70); }

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .radio-reach strong { color: var(--tg-purple, #7A3CFF); -webkit-text-fill-color: currentColor; }
}

@media (max-width: 640px) {
  .radio-reach { font-size: 14px; }
  .radio-reach strong { font-size: 17px; }
}

/* ─────────────────────────── the lists sheet ───────────────────────────
 *
 * One sheet, bottom-centre, for both jobs: putting a station into a named list
 * and opening one. It sits above the player bar rather than inside it, because
 * the bar is 56px tall on a phone and a menu that grows inside it pushes the
 * transport off screen — and the transport is the one control that must never
 * move while music is playing. See scripts/lists.js.
 */
.tg-lists {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(var(--tgp-h, 76px) + 14px);
  z-index: 60;
  width: min(460px, calc(100vw - 24px));
  max-height: min(60vh, 460px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 14px 14px 16px;
  border-radius: 16px;
  border: 1px solid var(--tg-rule, #E4E4EE);
  background: var(--tg-card, #fff);
  box-shadow: 0 18px 48px rgba(11, 11, 18, 0.22);
}
.tg-lists-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.tg-lists-title { margin: 0; flex: 1; font-size: 14.5px; font-weight: 700; color: var(--tg-ink, #14141F); }
.tg-lists-x {
  flex: none;
  width: 32px; height: 32px;
  border-radius: 10px;
  border: 1px solid var(--tg-rule, #E4E4EE);
  background: transparent;
  color: var(--tg-ink-mute, #5A5A70);
  cursor: pointer;
  font-size: 13px;
}
.tg-lists-x:hover { color: var(--tg-ink, #14141F); border-color: var(--tg-purple, #7A3CFF); }
.tg-lists-row { display: flex; flex-wrap: wrap; gap: 8px; }
.tg-lists-msg { margin: 6px 0 0; font-size: 13px; color: var(--tg-ink-mute, #5A5A70); }
.tg-lists-msg.is-bad { color: var(--tg-bad, #C0392B); }
.radio-chip.is-on { background: var(--tg-grad); color: #fff; border-color: transparent; font-weight: 700; }
.radio-chip.primary { border-color: var(--tg-purple, #7A3CFF); color: var(--tg-purple, #7A3CFF); font-weight: 600; }

@media (max-width: 520px) {
  .tg-lists { left: 12px; right: 12px; width: auto; transform: none; }
}
