/* tuner.css: the hard dance radio tuner in #tuner (v2-spec F1). Owner: Agent F.
   A hi-fi tuner faceplate in the site's blacklit-warehouse language: an LCD window (the same
   dotted --void panel as the countdown) over a backlit dial glass with a lamp-orange needle, the
   station marks and the genre scale printed on the glass, then 8 station cards.
   Flash safety: nothing here changes brightness faster than a 250 ms ease except the small
   signal meter (about 100 x 10 px) and the small lamp dots; the backlight fades over 800 ms.
   Tokens only (site.css :root); no laser colours. */

.tuner { display: block; margin-bottom: clamp(40px, 5vw, 64px); }

/* ---------- faceplate ---------- */

.tuner-set {
  --pos: .5;
  --prox: 1;
  display: grid;
  gap: clamp(12px, 1.6vw, 18px);
  padding: clamp(14px, 2.2vw, 26px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background:
    linear-gradient(180deg, rgb(var(--ink-rgb) / .035), transparent 42%),
    var(--booth);
  box-shadow:
    inset 0 1px 0 rgb(var(--ink-rgb) / .06),
    0 40px 90px -60px rgb(var(--uv-rgb) / .55);
}

/* ---------- LCD window ---------- */

.tuner-lcd {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px 20px;
  align-items: start;
  min-height: 118px;
  padding: 16px clamp(14px, 2vw, 22px) 14px;
  border: 1px solid rgb(var(--lamp-rgb) / .2);
  border-radius: var(--radius-sm);
  background: var(--lcd-dots), var(--void);
}
.tuner-lcd-main { min-width: 0; }

.tuner-name {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  font: 900 clamp(1.25rem, 3.3vw, 2.25rem)/1.05 var(--f-lcd);
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--lamp);
  text-shadow: 0 0 18px rgb(var(--lamp-rgb) / .45);
  overflow-wrap: anywhere;
}
.tuner-name-text {
  min-width: 0;
  opacity: calc(.28 + .72 * var(--prox));
  transition: opacity .15s linear;
}

/* The tuning lamp: dim when off, a slow opacity pulse while tuning, lit once the stream flows. */
.tuner-lamp {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgb(var(--lamp-rgb) / .22);
  transition: background-color .4s, box-shadow .4s;
}
.tuner-set[data-phase="playing"] .tuner-lamp {
  background: var(--lamp);
  box-shadow: 0 0 10px rgb(var(--lamp-rgb) / .8);
}
.tuner-set[data-phase="tuning"] .tuner-lamp {
  background: var(--lamp);
  animation: tuner-pulse 1.2s ease-in-out infinite;
}
.tuner-set[data-phase="failed"] .tuner-lamp { background: rgb(var(--smoke-rgb) / .5); }

.tuner-status {
  margin: 12px 0 0;
  min-height: 1.4em;
  font: 600 1.0625rem/1.4 var(--f-body);
  color: var(--ink);
  overflow-wrap: anywhere;
  text-wrap: pretty;
}
.tuner-set[data-phase="idle"] .tuner-status,
.tuner-set[data-phase="failed"] .tuner-status { color: var(--smoke); font-weight: 500; }
.tuner-np-artist { font-weight: 700; }
.tuner-np-sep { color: var(--smoke); }
.tuner-status .tuner-np-title { font-weight: 500; }

.tuner-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  min-height: 1.4em;  /* keeps its row when empty (HardBase.FM shows its genre in the status line) */
  margin: 6px 0 0;
  font-size: .875rem;
  line-height: 1.4;
  color: var(--smoke);
}

.tuner-meta-next,
.tuner-meta-genre { min-width: 0; overflow-wrap: anywhere; }

.tuner-live {
  display: inline-block;
  padding: 3px 7px 2px;
  border: 1px solid rgb(var(--lamp-rgb) / .7);
  border-radius: 6px;
  font: 700 .6875rem/1.1 var(--f-body);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--lamp);
  white-space: nowrap;
}

/* Indicators, top right: the signal meter and the Tuned lamp (decorative, aria-hidden). */
.tuner-ind {
  display: grid;
  grid-template-columns: auto auto;
  align-items: center;
  justify-items: end;
  gap: 10px 10px;
  padding-top: 4px;
  font: 700 .6875rem/1 var(--f-body);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--smoke);
}
.tuner-ind-label { justify-self: end; }
.tuner-meter {
  --sig: 0;
  position: relative;
  display: block;
  width: 100px;
  height: 10px;
  background: linear-gradient(90deg, var(--uv) 0 50%, var(--ink) 50% 80%, var(--lamp) 80%);
  -webkit-mask: repeating-linear-gradient(90deg, #000 0 7px, transparent 7px 10px);
  mask: repeating-linear-gradient(90deg, #000 0 7px, transparent 7px 10px);
  opacity: 1;
}
.tuner-meter::before {
  /* the unlit segments */
  content: "";
  position: absolute;
  inset: 0;
  background: var(--void);
  opacity: .86;
  clip-path: inset(0 0 0 calc(var(--sig) * 100%));
}
.tuner-meter-fill { display: none; }
.tuner-tuned {
  grid-column: 1 / -1;
  padding: 3px 7px 2px;
  border: 1px solid rgb(var(--smoke-rgb) / .3);
  border-radius: 6px;
  color: rgb(var(--smoke-rgb) / .55);
  transition: color .5s, border-color .5s, text-shadow .5s;
}
.tuner-tuned.is-on {
  border-color: rgb(var(--lamp-rgb) / .65);
  color: var(--lamp);
  text-shadow: 0 0 10px rgb(var(--lamp-rgb) / .6);
}

/* ---------- the dial ---------- */

.tuner-dial {
  position: relative;
  border-radius: var(--radius-sm);
  cursor: grab;
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.tuner-set.is-dragging .tuner-dial { cursor: grabbing; }
.tuner-dial:focus-visible { outline: 2px solid var(--uv); outline-offset: 4px; }

.tuner-glass {
  --ticks: 96;
  --base: 62%;     /* the printed baseline the marks stand on */
  position: relative;
  height: clamp(132px, 13vw, 164px);
  overflow: hidden;
  border: 1px solid rgb(var(--uv-rgb) / .3);
  border-radius: var(--radius-sm);
  background:
    repeating-linear-gradient(180deg, rgb(var(--ink-rgb) / .018) 0 1px, transparent 1px 4px),
    radial-gradient(90% 120% at 50% 130%, rgb(var(--uv-rgb) / .2), transparent 62%),
    linear-gradient(180deg, var(--mt-tuner-glass) 0%, var(--void) 100%);
  box-shadow:
    inset 0 14px 30px rgba(0, 0, 0, .55),
    inset 0 -1px 0 rgb(var(--ink-rgb) / .05);
}
/* Backlight: the glass warms while a station is tuned in (a slow fade, never a flash). */
.tuner-glass::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(70% 140% at 50% 120%, rgb(var(--lamp-rgb) / .13), transparent 70%);
  opacity: 0;
  transition: opacity .8s ease;
  pointer-events: none;
}
.tuner-set:is([data-phase="playing"], [data-phase="tuning"]) .tuner-glass::before { opacity: 1; }
/* Glass sheen over everything, needle included. */
.tuner-glass::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  background: linear-gradient(172deg, rgb(var(--ink-rgb) / .075) 0%, rgb(var(--ink-rgb) / .02) 30%, transparent 46%);
  pointer-events: none;
}

.tuner-ticks {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% - var(--base));
  height: 18px;
  background:
    repeating-linear-gradient(90deg, rgb(var(--smoke-rgb) / .55) 0 1px, transparent 1px calc(100% / var(--ticks))) 0 100% / 100% 7px no-repeat,
    repeating-linear-gradient(90deg, rgb(var(--uv-soft-rgb) / .7) 0 1px, transparent 1px calc(100% / 16)) 0 100% / 100% 13px no-repeat;
}
.tuner-ticks::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: rgb(var(--uv-rgb) / .45);
}

.tuner-names,
.tuner-marks,
.tuner-bands {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.tuner-dial-name {
  position: absolute;
  top: 16px;
  left: calc(var(--x) * 100%);
  transform: translateX(-50%);
  font: 700 .6875rem/1 var(--f-body);
  letter-spacing: .12em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--smoke);
  transition: color .3s;
}
.tuner-dial-name.is-near { color: var(--ink); }
@media (max-width: 899px) {
  .tuner-names { display: none; }
  .tuner-glass { --base: 50%; height: 118px; }
}
/* Wide screens: each station's mark rises from the scale to just under its printed name, and the
   needle stops under the name row with a pointer cap, so the tuned name stays readable. */
@media (min-width: 900px) {
  .tuner-glass .tuner-mark { top: 38px; height: calc(var(--base) - 38px); }
  .tuner-mark::before { background: linear-gradient(180deg, rgb(var(--ink-rgb) / 0), rgb(var(--ink-rgb) / .5) 70%); }
  .tuner-mark.is-near::before { background: linear-gradient(180deg, rgb(var(--ink-rgb) / .08), var(--ink) 70%); }
  .tuner-glass .tuner-needle-line { top: 38px; }
}

/* A station: a tall mark on the baseline with a dot beneath. */
.tuner-mark {
  position: absolute;
  top: calc(var(--base) - 26px);
  left: calc(var(--x) * 100%);
  width: 0;
  height: 26px;
}
.tuner-mark::before {
  content: "";
  position: absolute;
  left: -1px;
  bottom: 0;
  width: 2px;
  height: 100%;
  border-radius: 1px;
  background: rgb(var(--ink-rgb) / .6);
}
.tuner-mark::after {
  content: "";
  position: absolute;
  left: -4px;
  top: calc(100% + 7px);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgb(var(--smoke-rgb) / .55);
  transition: background-color .35s, box-shadow .35s;
}
.tuner-mark.is-hot::after { background: var(--uv-soft); box-shadow: 0 0 10px rgb(var(--uv-rgb) / .8); }
.tuner-mark.is-near::after { background: var(--ink); }
.tuner-set:is([data-phase="playing"], [data-phase="tuning"]) .tuner-mark.is-near::after {
  background: var(--lamp);
  box-shadow: 0 0 10px rgb(var(--lamp-rgb) / .75);
}

/* The genre scale, printed along the bottom of the glass. */
.tuner-band {
  position: absolute;
  left: calc(var(--x0) * 100%);
  width: calc((var(--x1) - var(--x0)) * 100%);
  bottom: 10px;
  padding: 7px 8px 0;
  text-align: center;
}
.tuner-band::before {
  /* the bracket: a hairline with two end ticks */
  content: "";
  position: absolute;
  top: 0;
  left: 8px;
  right: 8px;
  height: 5px;
  border: 1px solid rgb(var(--uv-rgb) / .5);
  border-bottom: 0;
  border-radius: 3px 3px 0 0;
  transition: border-color .35s;
}
.tuner-band-label {
  /* Centred on its bracket even when wider than it (it then overflows both ends equally). */
  position: relative;
  left: 50%;
  display: block;
  width: max-content;
  transform: translateX(-50%);
  font: 700 .625rem/1.2 var(--f-body);
  letter-spacing: .14em;
  text-transform: uppercase;
  white-space: nowrap;  /* one line, so every bracket stands on the same baseline */
  color: var(--uv-soft);
  transition: color .35s;
}
.tuner-band-short { display: none; }
/* Phones: the two long genre names use their short forms (tuner.js SHORT_BAND); the glass is
   aria-hidden and the dial's aria-valuetext names the genre in full. */
@media (max-width: 599.98px) {
  .tuner-band-full.has-short { display: none; }
  .tuner-band-short { display: inline; }
}
.tuner-band.is-near::before { border-color: rgb(var(--ink-rgb) / .7); }
.tuner-band.is-near .tuner-band-label { color: var(--ink); }
@media (max-width: 520px) {
  .tuner-band { padding-inline: 2px; }
  .tuner-band::before { left: 3px; right: 3px; }
  .tuner-band-label { letter-spacing: .06em; }
}
/* 360 px phones: the Hard trance band is narrower than "TRANCE" with any tracking, which then ran
   into "HARDSTYLE". No tracking keeps the same gap 390 px has. */
@media (max-width: 380px) {
  .tuner-band-label { letter-spacing: 0; }
}

/* The needle rides a full-width track translated by --pos (compositor-only movement). */
.tuner-needle-track {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  transform: translateX(calc(var(--pos) * 100%));
  transition: transform .3s cubic-bezier(.3, 1.35, .6, 1);
  will-change: transform;
}
.tuner-set.is-dragging .tuner-needle-track { transition: none; }
.tuner-needle { position: absolute; top: 0; bottom: 0; left: 0; width: 0; }
.tuner-needle-glow {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -90px;
  width: 180px;
  background: radial-gradient(closest-side, rgb(var(--lamp-rgb) / .17), transparent);
  opacity: .6;
  transition: opacity .8s ease;
}
.tuner-set:is([data-phase="playing"], [data-phase="tuning"]) .tuner-needle-glow { opacity: 1; }
.tuner-needle-line {
  position: absolute;
  top: 8px;
  bottom: 8px;
  left: -1px;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(180deg, rgb(var(--lamp-rgb) / .15), var(--lamp) 16%, var(--lamp) 84%, rgb(var(--lamp-rgb) / .15));
  box-shadow: 0 0 10px 1px rgb(var(--lamp-rgb) / .6);
}
.tuner-needle-line::before {
  content: "";
  position: absolute;
  top: -7px;
  left: 50%;
  width: 10px;
  height: 6px;
  transform: translateX(-50%);
  background: var(--lamp);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
.tuner-needle-grip {
  position: absolute;
  bottom: 0;
  left: -10px;
  width: 20px;
  height: 7px;
  border-radius: 5px 5px 0 0;
  background: var(--lamp);
  box-shadow: 0 0 12px rgb(var(--lamp-rgb) / .55);
}
/* Hunting for the signal while the stream connects. Movement only, a few pixels. */
.tuner-set[data-phase="tuning"] .tuner-needle { animation: tuner-hunt 1.1s ease-in-out infinite; }

@keyframes tuner-hunt {
  0%, 100% { transform: translateX(-2.5px); }
  50% { transform: translateX(2.5px); }
}
@keyframes tuner-pulse {
  0%, 100% { opacity: .35; }
  50% { opacity: 1; }
}

.tuner-note {
  max-width: 72ch;
  margin: 14px 0 0;
  font-size: .875rem;
  line-height: 1.5;
  color: var(--smoke);
  text-wrap: pretty;
}

/* ---------- station cards ---------- */

.tuner-cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 1000px) {
  .tuner-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
}

.tuner-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding: 16px 16px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--booth);
  transition: border-color .35s, box-shadow .35s, background-color .35s;
}
.tuner-card.is-current {
  border-color: rgb(var(--lamp-rgb) / .55);
  background: linear-gradient(180deg, rgb(var(--lamp-rgb) / .07), transparent 55%), var(--booth);
  box-shadow: 0 16px 40px -24px rgb(var(--lamp-rgb) / .55);
}
.tuner-card-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  min-height: 22px;
}
.tuner-card-band {
  font: 700 .625rem/1.2 var(--f-body);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--uv-soft);
}
.tuner-card-name {
  margin: 2px 0 0;
  font: 700 1.0625rem/1.2 var(--f-body);
  letter-spacing: -.005em;
  overflow-wrap: anywhere;
}
.tuner-card-genre { margin: 0; font-size: .875rem; line-height: 1.4; color: var(--smoke); }
.tuner-card-state {
  margin: 2px 0 0;
  font-size: .875rem;
  line-height: 1.4;
  font-weight: 600;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.tuner-card[data-state="failed"] .tuner-card-state,
.tuner-card[data-state="paused"] .tuner-card-state { color: var(--smoke); }
.tuner-card[data-state="tuning"] .tuner-card-state { color: var(--lamp); }
.tuner-card-state .tuner-np-title { font-weight: 500; }
.tuner-card-next { margin: 0; font-size: .8125rem; line-height: 1.4; color: var(--smoke); overflow-wrap: anywhere; }
/* HardBase.FM's track list link, under the genre (tuner.js), so its Tune in lines up with the rest. */
.tuner-card-tl { margin: 0; line-height: 1.4; }

.tuner-card-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  margin-top: auto;
  padding-top: 10px;
}
.tuner-tune {
  gap: 8px;
  border-color: rgb(var(--ink-rgb) / .6);
  background: transparent;
  color: var(--ink);
}
@media (hover: hover) {
  .tuner-tune:hover { background: rgb(var(--ink-rgb) / .08); border-color: var(--ink); }
  .tuner-tune[aria-pressed="true"]:hover { background: color-mix(in oklab, var(--lamp) 84%, var(--ink)); }
}
.tuner-tune[aria-pressed="true"] { background: var(--lamp); border-color: var(--lamp); color: var(--void); }
.tuner-tune-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgb(var(--smoke-rgb) / .8);
  transition: background-color .3s;
}
.tuner-tune[aria-pressed="true"] .tuner-tune-dot { background: var(--void); }
.tuner-card[data-state="tuning"] .tuner-tune-dot { animation: tuner-pulse 1.2s ease-in-out infinite; }
.tuner-visit { font-size: .875rem; }
/* Touch: Visit site is a 22 px text link and Tune in a 38 px button. Transparent boxes take both to
   44 px (the actions row is 6 px apart vertically and at least 12 px across, so nothing overlaps). */
@media (pointer: coarse) {
  .tuner-visit, .tuner-tune { position: relative; }
  .tuner-visit::after { content: ""; position: absolute; inset: -11px -4px; }
  .tuner-tune::after { content: ""; position: absolute; inset: -3px 0; }
}

/* Phones and tablets: every state (hint, Let go to tune in, Tuning in, a now-playing line, Up next)
   takes the same height, so the dial and the cards below never jump under the visitor's finger. The
   station name keeps one line, the status two, the meta line two rows (genre and Live DJ, then Up
   next), each clamped to that. The station's card below shows the same text in full, and #radio-now
   reads it to screen readers. */
@media (max-width: 899.98px) {
  .tuner-lcd { min-height: 0; }
  .tuner-status {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    height: 2.8em;
  }
  .tuner-meta { height: calc(2.8em + 6px); align-content: flex-start; overflow: hidden; }
  .tuner-meta-genre { flex: 0 1 auto; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  .tuner-meta-next {
    flex: 1 0 100%;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 1;
    overflow: hidden;
  }
}
@media (max-width: 380px) {
  /* "Hard Trance (laut.fm)" and "Old Skool Rave Tapes" stay on one line at 360 px. */
  .tuner-name { font-size: 1.125rem; }
}
@media (max-width: 520px) {
  .tuner-lcd { grid-template-columns: 1fr; }
  .tuner-ind { order: -1; grid-template-columns: auto auto auto; justify-content: start; justify-items: start; }
  .tuner-tuned { grid-column: auto; }
  .tuner-card { padding: 12px 12px 12px; }
  .tuner-card-name { font-size: 1rem; }
  .tuner-card-actions { gap: 6px 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .tuner-needle-track,
  .tuner-name-text { transition: none; }
  .tuner-set[data-phase="tuning"] .tuner-needle,
  .tuner-set[data-phase="tuning"] .tuner-lamp,
  .tuner-card[data-state="tuning"] .tuner-tune-dot { animation: none; }
}

/* ==== on air card and Listen again (Mark, workflow 2) ==== */

/* The show's station card lights (steady) while the show is on air. */
html[data-onair] .tuner-card[data-show] {
  border-color: color-mix(in srgb, var(--onair) 70%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--onair) 35%, transparent);
}

html[data-onair] .tuner-card[data-show] .tuner-card-band::before {
  content: '';
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 7px;
  border-radius: 50%;
  vertical-align: 1px;
  background: var(--onair);
  box-shadow: 0 0 6px color-mix(in srgb, var(--onair) 60%, transparent);
}

/* Listen again, under the station cards. */
.tuner-la { margin-top: 28px; }

.tuner-la-head {
  margin: 0 0 10px;
  font: 800 clamp(18px, 2vw, 22px)/1.1 var(--f-display);
  letter-spacing: .02em;
  text-transform: uppercase;
}

.tuner-la-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.tuner-la-list .la-play {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--booth);
  color: var(--ink);
  font: 600 14px/1.3 var(--f-body);
  text-align: left;
  cursor: pointer;
}

.tuner-la-list .la-play:hover { border-color: color-mix(in srgb, var(--uv) 60%, transparent); }
.tuner-la-list .la-play:focus-visible { outline: 2px solid var(--uv); outline-offset: 2px; }
.tuner-la-list .la-play[aria-pressed="true"] { box-shadow: inset 3px 0 0 var(--lamp); }
.tuner-la-list .la-dur { flex: none; color: var(--smoke); font: 500 13px/1 var(--f-lcd); }
