/* ── tokens ─────────────────────────────────────────────────────────── */
:root{
  --paper:#F3F2EF; --surface:#FFF; --surface-2:#EDECE7; --shell:#20302E;
  --ink:#17211F; --ink-2:#4A5754; --ink-3:#7A8683;
  --rule:#D8D8D1; --rule-soft:#E6E5DF;
  --signal:#0B7B72; --signal-dim:#0B7B7214;
  --dial:#B0602F; --dial-dim:#B0602F14;
  --crit:#A63A2E;
  --tile:#F7F7F4;
  --display:"Familjen Grotesk","Helvetica Neue",Arial,sans-serif;
  --body:"Newsreader",Georgia,serif;
  --mono:"IBM Plex Mono",ui-monospace,Menlo,monospace;
  --player-h:150px; /* pre-JS reserve; app.js replaces it with the measured height */
}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
  --paper:#101817; --surface:#16201F; --surface-2:#1D2827; --shell:#0A1211;
  --ink:#E8E9E5; --ink-2:#A6B2AF; --ink-3:#76817E;
  --rule:#2C3735; --rule-soft:#232E2C;
  --signal:#4FBDAF; --signal-dim:#4FBDAF1A;
  --dial:#D98A55; --dial-dim:#D98A5518;
  --crit:#DE7A6B;
  --tile:#E9E9E4;
}}
:root[data-theme="dark"]{
  --paper:#101817; --surface:#16201F; --surface-2:#1D2827; --shell:#0A1211;
  --ink:#E8E9E5; --ink-2:#A6B2AF; --ink-3:#76817E;
  --rule:#2C3735; --rule-soft:#232E2C;
  --signal:#4FBDAF; --signal-dim:#4FBDAF1A;
  --dial:#D98A55; --dial-dim:#D98A5518;
  --crit:#DE7A6B;
  --tile:#E9E9E4;
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--paper);color:var(--ink);font:16px/1.6 var(--body);
  -webkit-font-smoothing:antialiased;padding-bottom:calc(var(--player-h) + env(safe-area-inset-bottom))}
a{color:var(--signal);text-underline-offset:2px}
:focus-visible{outline:2px solid var(--signal);outline-offset:2px}
button{font:inherit;cursor:pointer}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.shell{max-width:78rem;margin:0 auto;padding:0 1rem}
svg{display:block}

/* ── header ─────────────────────────────────────────────────────────── */
.top{position:sticky;top:0;z-index:40;background:color-mix(in srgb,var(--paper) 92%,transparent);
  backdrop-filter:blur(10px);border-bottom:1px solid var(--rule)}
.top-in{display:flex;align-items:center;gap:.7rem;padding-block:.55rem}
.brand{display:flex;align-items:center;gap:.45rem;font-family:var(--display);font-weight:700;
  font-size:1rem;letter-spacing:-.02em;color:var(--ink);text-decoration:none;flex:0 0 auto}
.brand .tick{width:11px;height:20px;border-left:2px solid var(--signal);
  background-image:repeating-linear-gradient(to bottom,var(--rule) 0 1px,transparent 1px 5px)}
.brand span.txt{display:none}
.brand .live{color:var(--signal)}
@media(min-width:30rem){.brand span.txt{display:inline}}
form.search{flex:1 1 auto;min-width:0}
form.search input{width:100%;padding:.42rem .8rem;border:1px solid var(--rule);border-radius:999px;
  background:var(--surface);color:var(--ink);font:inherit;font-size:.92rem}
form.search input::placeholder{color:var(--ink-3)}
.iconctl{flex:0 0 auto;background:var(--surface);border:1px solid var(--rule);border-radius:999px;
  width:32px;height:32px;color:var(--ink-2);display:grid;place-items:center;padding:0}
.iconctl svg{width:15px;height:15px}

/* ── #4 compact filter bar: one row of selects, not two chip rows ──── */
.fbar{border-bottom:1px solid var(--rule);background:var(--surface)}
.fbar-in{display:flex;align-items:center;gap:.4rem;padding-block:.4rem;flex-wrap:wrap}
.fsel{position:relative;display:inline-flex;align-items:center;flex:0 0 auto}
.fsel select{appearance:none;font-family:var(--display);font-weight:500;font-size:.79rem;
  border:1px solid var(--rule);border-radius:5px;padding:.26rem 1.4rem .26rem .5rem;
  background:var(--paper);color:var(--ink-2);max-width:8.5rem;text-overflow:ellipsis}
@media(min-width:34rem){.fsel select{max-width:12rem}}
.fsel::after{content:"";position:absolute;right:.45rem;pointer-events:none;
  width:0;height:0;border:3.5px solid transparent;border-top-color:var(--ink-3);margin-top:3px}
.fsel[data-on="1"] select{border-color:var(--signal);background:var(--signal-dim);color:var(--signal);font-weight:600}
.fsel[data-on="1"]::after{border-top-color:var(--signal)}
/* The right-hand cluster travels as one and never splits. */
.fbar-right{display:flex;align-items:center;gap:.4rem;flex:0 0 auto;margin-left:auto}
.fcount{font-family:var(--mono);font-size:.64rem;letter-spacing:.06em;color:var(--ink-3);
  white-space:nowrap;font-variant-numeric:tabular-nums}
.clearf{font-family:var(--mono);font-size:.64rem;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-3);background:none;border:0;padding:.2rem .3rem;text-decoration:underline}

/* #11 favourites lifted out of the filter row, in copper */
.favlink{display:inline-flex;align-items:center;gap:.4rem;flex:0 0 auto;text-decoration:none;
  font-family:var(--display);font-weight:600;font-size:.78rem;border:1px solid var(--dial);
  color:var(--dial);background:var(--dial-dim);border-radius:999px;padding:.3rem .7rem .3rem .6rem;
  transition:background .15s ease,color .15s ease,transform .1s ease}
.favlink:hover{background:var(--dial);color:var(--paper)}
.favlink:active{transform:scale(.95)}
.favlink svg{width:12px;height:12px}
/* The count as a small solid badge rather than plain digits — inverted from
   the pill's own colours so it reads as a chip sitting on top of it, and
   invisible via :empty rather than a JS-side show/hide check. */
.favlink b{font-family:var(--mono);font-size:.66em;font-weight:700;
  background:var(--dial);color:var(--paper);border-radius:999px;line-height:1;
  min-width:1.4em;height:1.4em;display:inline-flex;align-items:center;justify-content:center;
  padding:0 .3em}
.favlink b:empty{display:none}
.favlink:hover b{background:var(--paper);color:var(--dial)}
.favlink[aria-current="page"]{background:var(--dial);color:var(--paper)}
.favlink[aria-current="page"] b{background:var(--paper);color:var(--dial)}

/* ── active filters ─────────────────────────────────────────────────── */
.active{display:flex;align-items:center;flex-wrap:wrap;gap:.35rem;margin:1rem 0 0}
.active-lbl{font-family:var(--mono);font-size:.6rem;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-3);margin-right:.1rem}
.active-or{font-family:var(--mono);font-size:.6rem;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-3)}
.achip{display:inline-flex;align-items:center;gap:.4rem;text-decoration:none;
  font-family:var(--display);font-weight:600;font-size:.76rem;
  border:1px solid var(--signal);background:var(--signal-dim);color:var(--signal);
  border-radius:999px;padding:.2rem .5rem .2rem .6rem}
.achip span{font-size:1rem;line-height:1;opacity:.65;margin-top:-1px}
.achip:hover{background:var(--signal);color:#fff}
.achip:hover span{opacity:1}
.active-clear{font-family:var(--mono);font-size:.62rem;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-3);margin-left:.2rem}

/* ── feature hint ────────────────────────────────────────────────────────
   The sleep timer, favourites and the dial are all easy to miss. One rotating
   line, dismissible, remembered — cheap discovery without a tour. */
.hint{display:flex;align-items:flex-start;gap:.55rem;background:var(--surface);
  border:1px solid var(--rule);border-left:3px solid var(--dial);border-radius:9px;
  padding:.55rem .7rem;margin:1rem 0 0;font-size:.88rem;color:var(--ink-2);max-width:46rem}
.hint[hidden]{display:none}
.hint svg{width:14px;height:14px;flex:0 0 auto;color:var(--dial);margin-top:.15rem}
.hint-lbl{flex:0 0 auto;font-family:var(--mono);font-size:.6rem;letter-spacing:.11em;
  text-transform:uppercase;color:var(--dial);font-weight:600;margin-top:.2rem}
.hint b{font-family:var(--display);font-weight:600;color:var(--ink)}
.hint-x{margin-left:auto;background:none;border:0;color:var(--ink-3);font-size:1rem;
  line-height:1;padding:0 .1rem;flex:0 0 auto}
.hint-x:hover{color:var(--ink)}

/* ── recent genres: quick repeats in the filter bar's open space ────── */
.recents{display:none;align-items:center;gap:.3rem;
  /* The only flexible thing in the row, so it is what gives way. A fade on the
     trailing edge makes the clip read as deliberate rather than broken. */
  flex:1 1 0;min-width:0;overflow:hidden;flex-wrap:nowrap;
  mask-image:linear-gradient(to right,#000 calc(100% - 22px),transparent 100%)}
.recents[hidden]{display:none}
@media (min-width:64rem){
  .recents:not([hidden]){display:flex}
  /* A wrapping row moves an item to the next line before it will shrink one,
     which is what put Favourites underneath. With nowrap, the recents clip
     instead and everything else keeps its place. */
  .fbar-in{flex-wrap:nowrap}
}
.recents .rlbl{font-family:var(--mono);font-size:.58rem;letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink-3);margin-right:.05rem}
.rchip{font-family:var(--display);font-weight:600;font-size:.72rem;text-decoration:none;
  border:1px solid var(--rule-soft);border-radius:999px;padding:.16rem .5rem;
  color:var(--ink-2);background:var(--paper);white-space:nowrap;flex:0 0 auto;
  max-width:11rem;overflow:hidden;text-overflow:ellipsis}
.rchip:hover{border-color:var(--signal);color:var(--signal);background:var(--signal-dim)}
.rchip[aria-current="true"]{background:var(--signal);border-color:var(--signal);color:#fff}

/* ── layout: optional desktop rail beside the content ───────────────── */
.layout{display:block}
/* The content column wraps the hint and <main> so they stay in one grid cell;
   as separate children the hint took the column and pushed main to a new row. */
.col{min-width:0}
.rail{display:none}
@media (min-width:68rem){
  .layout{display:grid;grid-template-columns:13.5rem minmax(0,1fr);gap:1.6rem;align-items:start}
  .rail{display:block;position:sticky;top:4.6rem;padding:1.2rem 0 1rem;
    max-height:calc(100vh - 6rem);overflow-y:auto;scrollbar-width:thin}
}
.railgrp{display:flex;flex-direction:column;gap:1px;margin:0 0 1.1rem}
.railhd{font-family:var(--mono);font-size:.58rem;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-3);margin:0 0 .35rem;padding-left:.5rem}
.raili{display:flex;align-items:center;gap:.45rem;text-decoration:none;color:var(--ink-2);
  font-family:var(--display);font-weight:500;font-size:.82rem;border-radius:7px;
  padding:.34rem .5rem;transition:background .15s ease,color .15s ease}
.raili svg{width:13px;height:13px;flex:0 0 auto;opacity:.65}
.raili .railtxt{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.raili .railn{font-family:var(--mono);font-size:.62rem;font-weight:600;color:var(--ink-3);
  font-variant-numeric:tabular-nums;flex:0 0 auto;margin-left:auto}
.raili:hover{background:var(--surface);color:var(--ink)}
.raili.on{background:var(--signal-dim);color:var(--signal)}
.raili.on svg{opacity:1}
.raili.on .railn{color:var(--signal)}
.railclear{color:var(--ink-3);font-weight:400;font-size:.76rem}

/* ── first-run / misconfiguration notices ───────────────────────────── */
.setup{background:var(--surface);border:1px solid var(--rule);border-left:3px solid var(--dial);
  border-radius:10px;padding:1.2rem 1.3rem;margin:0 0 1.6rem;max-width:44rem}
.setup h2{font-family:var(--display);font-weight:600;font-size:1.1rem;margin:0 0 .5rem}
.setup p{margin:.6rem 0 0;color:var(--ink-2);font-size:.95rem;max-width:none}
.setup pre{font-family:var(--mono);font-size:.82rem;background:var(--surface-2);
  border:1px solid var(--rule-soft);border-radius:6px;padding:.6rem .8rem;margin:.7rem 0;overflow-x:auto}
.setup code,.setup-warn code{font-family:var(--mono);font-size:.85em;background:var(--surface-2);
  border:1px solid var(--rule-soft);border-radius:3px;padding:.06em .3em}
.setup-warn{color:var(--crit)}
.setup-banner{background:var(--surface);border:1px solid var(--crit);border-radius:8px;
  padding:.7rem .9rem;margin:1rem 0 0;font-size:.9rem;max-width:none}

/* ── page head ──────────────────────────────────────────────────────── */
/* An explicit line-height matters more than the size here: without it the
   heading inherited the body's 1.6, which on two lines was costing more space
   than the font size ever did. */
h1.page{font-family:var(--display);font-weight:700;font-size:clamp(1.3rem,3.4vw,1.7rem);
  line-height:1.18;letter-spacing:-.025em;margin:1rem 0 .15rem;text-wrap:balance}
/* Only shown on phones, where the filter bar's own count is hidden. */
.h1count{display:none;margin-left:.4rem}
.lede{color:var(--ink-2);margin:0 0 1rem;max-width:62ch;font-size:.95rem}

/* ── listing ────────────────────────────────────────────────────────
   Two views off one markup. `grid` stacks a square logo tile, the name, then
   a footer of bitrate + actions. `list` turns both wrappers into
   display:contents so logo / name / meta / actions become one row on the box.

   Logo tiles keep a light ground in both themes and use object-fit:contain,
   because station artwork is overwhelmingly a dark wordmark drawn for white —
   `cover` crops it and a dark tile erases it. */
.panel{display:grid;gap:.6rem;margin:0 0 1.6rem;padding:0;list-style:none}
.panel[data-view="grid"]{grid-template-columns:repeat(auto-fill,minmax(min(100%,8.5rem),1fr))}
@media(min-width:34rem){.panel[data-view="grid"]{grid-template-columns:repeat(auto-fill,minmax(min(100%,10.5rem),1fr))}}
.panel[data-view="list"]{grid-template-columns:repeat(auto-fill,minmax(min(100%,20.5rem),1fr));gap:.55rem}

.box{container-type:inline-size;position:relative;width:100%;text-align:left;
  background:var(--surface);border:1px solid var(--rule-soft);border-radius:12px;
  color:var(--ink);box-shadow:0 1px 2px rgba(23,33,31,.05);
  transition:box-shadow .18s ease,border-color .18s ease,transform .18s ease}
.box:hover{border-color:var(--rule);box-shadow:0 5px 18px rgba(23,33,31,.1);transform:translateY(-2px)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .box{box-shadow:0 1px 2px rgba(0,0,0,.3)}
  :root:not([data-theme="light"]) .box:hover{box-shadow:0 5px 20px rgba(0,0,0,.45)}}
:root[data-theme="dark"] .box{box-shadow:0 1px 2px rgba(0,0,0,.3)}
:root[data-theme="dark"] .box:hover{box-shadow:0 5px 20px rgba(0,0,0,.45)}
.box[data-playing="true"]{border-color:var(--signal);
  box-shadow:0 0 0 1px var(--signal),0 5px 18px color-mix(in srgb,var(--signal) 24%,transparent)}
.box[data-playing="true"] .nm{color:var(--signal)}

.play-hit{font:inherit;color:inherit;text-align:left;background:none;border:0;padding:0}

.logo{position:relative;background:var(--tile);display:grid;place-items:center;
  overflow:hidden;flex:0 0 auto;isolation:isolate}
.logo img{width:100%;height:100%;object-fit:contain;display:block;grid-area:1/1}
/* Treated as a thumbnail, not a backdrop: contained and inset like any real
   logo, so the tile ground still shows around it. */
.logo img.artfallback{object-fit:contain}

/* Missing artwork gets a shared illustration rather than a glyph. It fills the
   frame like a real logo would, so a station without one still looks like a
   station. cover, not contain: the drawing is designed to be cropped. */
.artfallback{grid-area:1/1;width:100%;height:100%;object-fit:contain;display:block}

/* Play badge, after the radioindia.in pattern: a large arrow in a translucent
   ring, no scrim over the artwork. The ring carries its own shadow so it holds
   on light logos as well as dark ones. */
.playbadge{position:absolute;inset:0;display:grid;place-items:center;z-index:3;
  opacity:0;transition:opacity .16s ease;pointer-events:none}
/* Black circle with a white arrow, after the reference. Sized against its
   64px: 51px here (80%) and 32px on phones (50%). The min() keeps it inside
   the 42px thumbnail the list view uses, where a fixed 51px would spill over.
   Lighter than the reference's b3 so more of the artwork reads through. */
.pbring{width:min(51px,78%);aspect-ratio:1;border-radius:50%;
  display:grid;place-items:center;background:#00000080}
.pbring svg{width:50%;height:50%;color:#fff;transform:translateX(6%)}
@media (hover:hover){
  .box:hover .playbadge,
  .detail-art:hover .playbadge,
  .play-hit:focus-visible .playbadge{opacity:1}
}
/* Nothing reveals it on touch, so keep it visible but a touch softer. */
@media (hover:none){ .playbadge{opacity:.92} }
.box[data-playing="true"] .playbadge,
.detail-art[data-playing="true"] .playbadge{opacity:0!important}

/* Equaliser in the thumbnail — sized down to ~55% of the first pass, which
   was loud enough to distract from the artwork. */
.eq{position:absolute;inset:0;z-index:4;display:none;align-items:flex-end;justify-content:center;
  gap:6%;padding:38% 39% 34%;background:color-mix(in srgb, #0C1917 34%, transparent)}
.box[data-playing="true"] .eq,
.detail-art[data-playing="true"] .eq{display:flex}
.eq i{flex:1 1 0;min-width:2px;max-width:4px;border-radius:2px;background:#fff;
  transform-origin:bottom;box-shadow:0 0 6px color-mix(in srgb,var(--signal) 60%,transparent);
  animation:eqbar .85s ease-in-out infinite}
.eq i:nth-child(1){height:42%;animation-delay:-.62s}
.eq i:nth-child(2){height:88%;animation-delay:-.31s}
.eq i:nth-child(3){height:62%;animation-delay:-.47s}
.eq i:nth-child(4){height:100%}
@keyframes eqbar{0%,100%{transform:scaleY(.28)}50%{transform:scaleY(1)}}
@media (prefers-reduced-motion:reduce){.eq i{animation:none;transform:scaleY(.75)}}

.nm{font-family:var(--display);font-weight:600;color:inherit;display:block}
.mt{font-family:var(--mono);letter-spacing:.04em;text-transform:uppercase;color:var(--ink-3);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;display:block}
.acts{display:flex;gap:.25rem;align-items:center;flex:0 0 auto}
.abtn{display:inline-flex;align-items:center;gap:.3rem;border:1px solid var(--rule-soft);
  border-radius:7px;background:var(--paper);color:var(--ink-3);text-decoration:none;
  font-family:var(--display);font-weight:600;font-size:.68rem;line-height:1;
  transition:color .15s ease,border-color .15s ease,background .15s ease}
.abtn svg{width:12px;height:12px;flex:0 0 auto}
.abtn .lbl{display:none}
.abtn:hover{border-color:var(--signal);color:var(--signal);background:var(--signal-dim)}
.abtn.fav[aria-pressed="true"]{color:var(--dial);border-color:color-mix(in srgb,var(--dial) 40%,transparent);
  background:var(--dial-dim)}
.abtn.fav[aria-pressed="true"]:hover{border-color:var(--dial);color:var(--dial);background:var(--dial-dim)}

/* ── grid view: big tile, name under, info footer ── */
.panel[data-view="grid"] .box{display:flex;flex-direction:column;overflow:hidden}
.panel[data-view="grid"] .play-hit{display:flex;flex-direction:column;width:100%}
.panel[data-view="grid"] .logo{width:100%;aspect-ratio:1;border-bottom:1px solid var(--rule-soft)}
.panel[data-view="grid"] .logo img{padding:9%}
.panel[data-view="grid"] .nm{font-size:.82rem;line-height:1.25;padding:.5rem .55rem .1rem;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:2.6em}
.panel[data-view="grid"] .foot{display:flex;align-items:center;justify-content:space-between;
  gap:.3rem;margin-top:auto;padding:.1rem .45rem .45rem}
.panel[data-view="grid"] .mt-full{display:none}
.panel[data-view="grid"] .mt-short{font-size:.58rem}
.panel[data-view="grid"] .abtn{padding:.24rem .3rem}

/* ── list view: the horizontal row ── */
.panel[data-view="list"] .box{display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  grid-template-areas:"logo name acts" "logo meta acts";column-gap:.65rem;
  align-items:center;padding:.6rem .65rem}
.panel[data-view="list"] .play-hit,
.panel[data-view="list"] .foot{display:contents}
.panel[data-view="list"] .logo{grid-area:logo;width:42px;height:42px;border-radius:9px}
.panel[data-view="list"] .logo img{padding:8%}
.panel[data-view="list"] .eq{padding:22% 24%;gap:8%}
.panel[data-view="list"] .eq i{max-width:3px;box-shadow:none}
.panel[data-view="list"] .nm{grid-area:name;font-size:.9rem;line-height:1.25;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:1.2rem}
.panel[data-view="list"] .mt-short{display:none}
.panel[data-view="list"] .mt-full{grid-area:meta;font-size:.6rem;margin-top:.15rem}
.panel[data-view="list"] .acts{grid-area:acts}
.panel[data-view="list"] .abtn{padding:.28rem .45rem}
/* The label is only worth its width when the card has room — ask the card,
   not the viewport. */
@container (min-width:19rem){.panel[data-view="list"] .abtn .lbl{display:inline}}

/* ── view switch ── */
.viewsw{display:inline-flex;gap:1px;border:1px solid var(--rule);border-radius:6px;
  overflow:hidden;flex:0 0 auto;background:var(--rule)}
.viewsw button{background:var(--paper);border:0;color:var(--ink-3);padding:.22rem .34rem;
  display:grid;place-items:center;line-height:1}
.viewsw button svg{width:13px;height:13px}
.viewsw button[aria-pressed="true"]{background:var(--signal);color:#fff}

.empty{border:1px dashed var(--rule);border-radius:12px;padding:2.2rem 1rem;text-align:center;color:var(--ink-2)}
.morebtn{display:block;margin:0 auto 1.6rem;font-family:var(--display);font-weight:600;font-size:.85rem;
  border:1px solid var(--rule);background:var(--surface);color:var(--ink-2);border-radius:9px;
  padding:.5rem 1.2rem;box-shadow:0 1px 2px rgba(23,33,31,.05)}
.morebtn:hover{border-color:var(--signal);color:var(--signal)}

/* ── #7 detail page ─────────────────────────────────────────────────── */
.detail{display:grid;grid-template-columns:auto minmax(0,1fr);gap:1rem;align-items:start;
  background:var(--surface);border:1px solid var(--rule);border-radius:6px;padding:1rem;margin:1.2rem 0 1.6rem}
.detail-art{width:104px;flex:0 0 auto;border-radius:11px;overflow:hidden;padding:0}
.detail-art .play-hit{display:block;width:100%}
.detail-art .logo{width:100%;aspect-ratio:1;border-radius:0}
.detail-art .logo img{padding:8%}
.detail-np{font-size:.92rem;color:var(--signal);margin:.35rem 0 0;font-weight:500;
  display:flex;align-items:center;gap:.4rem}
.detail-np[hidden]{display:none}
.detail-np::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--signal);flex:0 0 auto}
.detail h1{font-family:var(--display);font-weight:700;font-size:clamp(1.3rem,3.6vw,1.8rem);
  letter-spacing:-.02em;margin:0 0 .3rem;line-height:1.15;text-wrap:balance}
.dbtns{display:flex;gap:.4rem;flex-wrap:wrap;margin:.6rem 0 0}
.dbtn{display:inline-flex;align-items:center;gap:.4rem;font-family:var(--display);font-weight:600;
  font-size:.82rem;border:1px solid var(--rule);background:var(--paper);color:var(--ink);
  border-radius:5px;padding:.35rem .75rem;text-decoration:none}
.dbtn svg{width:12px;height:12px}
.dbtn.primary{background:var(--signal);border-color:var(--signal);color:#fff}
.dbtn.copper[aria-pressed="true"]{background:var(--dial);border-color:var(--dial);color:#fff}
.facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(8.5rem,1fr));gap:1px;
  background:var(--rule);border:1px solid var(--rule);border-radius:5px;overflow:hidden;margin:0 0 1.6rem}
.fact{background:var(--surface);padding:.5rem .7rem}
.fact dt{font-family:var(--mono);font-size:.58rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3)}
.fact dd{font-family:var(--display);font-weight:600;font-size:.9rem;margin:.15rem 0 0;
  overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
.tagrow{display:flex;gap:.3rem;flex-wrap:wrap;margin:0 0 1.6rem}
.tagrow a{font-family:var(--mono);font-size:.66rem;letter-spacing:.04em;text-decoration:none;
  border:1px solid var(--rule);border-radius:999px;padding:.15rem .5rem;color:var(--ink-2);background:var(--surface)}
.tagrow a:hover{border-color:var(--signal);color:var(--signal)}
h2.sec{font-family:var(--display);font-weight:600;font-size:1.02rem;letter-spacing:-.01em;
  margin:0 0 .5rem;padding-bottom:.3rem;border-bottom:1px solid var(--rule)}

/* ── player ─────────────────────────────────────────────────────────── */
.player{position:fixed;inset:auto 0 0 0;z-index:50;background:var(--shell);color:#EDEBE4;
  border-top:1px solid var(--rule);padding-bottom:env(safe-area-inset-bottom)}
.player-in{max-width:78rem;margin:0 auto;padding:.45rem 1rem .55rem}

/* #10 station logo in the bottom bar */
.np{display:grid;grid-template-columns:auto minmax(0,1fr);gap:.7rem;align-items:center}
.np-art{position:relative;flex:0 0 auto;display:block}
.np-logo{width:38px;height:38px;border-radius:4px;
  background:rgba(237,235,228,.08);border:1px solid rgba(237,235,228,.14);
  display:grid;place-items:center;overflow:visible;
  font-family:var(--display);font-weight:700;font-size:.7rem;color:rgba(237,235,228,.65)}
.np-logo img{border-radius:3px}

/* Status light on the thumbnail — always visible, no words needed. */
.np-dot{position:absolute;right:-3px;bottom:-3px;width:10px;height:10px;border-radius:50%;
  border:2px solid var(--shell);background:rgba(237,235,228,.45);z-index:2}
.player[data-state="playing"] .np-dot{background:var(--signal);
  box-shadow:0 0 0 0 color-mix(in srgb,var(--signal) 60%,transparent);animation:dotpulse 2.4s ease-out infinite}
.player[data-state="loading"] .np-dot{background:#C6A24E;animation:pulse 1s ease-in-out infinite}
.player[data-state="error"] .np-dot{background:var(--crit)}
@keyframes dotpulse{
  0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--signal) 55%,transparent)}
  70%,100%{box-shadow:0 0 0 6px transparent}
}
@media (prefers-reduced-motion:reduce){.np-dot{animation:none!important}}
.np-logo img{width:100%;height:100%;object-fit:cover;display:block}
.np-txt{min-width:0}
.np-live{font-family:var(--mono);font-size:.55rem;letter-spacing:.13em;text-transform:uppercase;
  color:var(--signal);display:inline-flex;align-items:center;gap:.4em;flex:0 0 auto}
.np-live-txt{white-space:nowrap}

/* Live bars rather than a static dot: the movement is the point, it is the
   one thing on the bar that says audio is actually flowing. */
.np-eq{display:inline-flex;align-items:flex-end;gap:1.5px;height:9px;width:11px}
.np-eq i{flex:1 1 0;border-radius:1px;background:currentColor;transform-origin:bottom;height:100%}
.player[data-state="playing"] .np-eq i{animation:eqbar .8s ease-in-out infinite}
.np-eq i:nth-child(1){animation-delay:-.55s}
.np-eq i:nth-child(2){animation-delay:-.27s}
.player:not([data-state="playing"]) .np-eq i:nth-child(1){height:40%}
.player:not([data-state="playing"]) .np-eq i:nth-child(2){height:70%}
.player:not([data-state="playing"]) .np-eq i:nth-child(3){height:50%}
.player[data-state="loading"] .np-eq{animation:pulse 1s ease-in-out infinite}
.player[data-state="error"] .np-live{color:var(--crit)}
.player[data-state="idle"] .np-live{color:rgba(237,235,228,.5)}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.25}}
@media (prefers-reduced-motion:reduce){.np-eq i{animation:none!important}}
/* Second line holds the status until a track title arrives, then the title
   takes it over — the wording is only useful while there is nothing better. */
.np-second{display:flex;align-items:baseline;gap:.5rem;min-width:0}
.player[data-has-track="true"] .np-live{display:none}
.np-name{display:block;font-family:var(--display);font-weight:700;font-size:.98rem;
  letter-spacing:-.015em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.np-name a{color:inherit;text-decoration:none}
.np-name a:hover{text-decoration:underline}
/* The track title gets a fixed slice of the bar and scrolls inside it when it
   is too long. Both this and .np-name are spans, so they need display:block —
   an inline element ignores overflow entirely, which is what let a long title
   bleed 300px across the dial. */
.np-track{display:block;font-size:.8rem;opacity:.72;min-height:1.15em;margin:.03rem 0 0;
  overflow:hidden;white-space:nowrap;position:relative;
  mask-image:linear-gradient(to right,transparent 0,#000 6px,#000 calc(100% - 10px),transparent 100%)}
.np-track:not(.scrolls){text-overflow:ellipsis;mask-image:none}
.np-track .roll{display:inline-block;white-space:nowrap}
.np-track.scrolls .roll{animation:roll var(--roll-dur,14s) linear infinite alternate}
@keyframes roll{
  0%, 10%   {transform:translateX(0)}
  90%, 100% {transform:translateX(var(--roll-shift,0px))}
}
@media (prefers-reduced-motion:reduce){
  .np-track.scrolls{mask-image:none;text-overflow:ellipsis}
  .np-track.scrolls .roll{animation:none}
}

/* the tuning dial — #9 same order as the listing */
.dial{position:relative;margin:.3rem 0 .05rem}
.dial-ticks{height:10px;background-image:repeating-linear-gradient(to right,
  rgba(237,235,228,.22) 0 1px,transparent 1px 9px)}
.needle{position:absolute;left:50%;top:-3px;bottom:-5px;width:2px;background:var(--dial);
  transform:translateX(-50%);z-index:2;pointer-events:none}
.needle::after{content:"";position:absolute;top:-3px;left:-3px;width:8px;height:8px;border-radius:50%;background:var(--dial)}
.strip{display:flex;gap:.45rem;overflow-x:auto;scroll-snap-type:x mandatory;
  padding:.45rem calc(50% - 2.6rem);scrollbar-width:none}
.strip::-webkit-scrollbar{display:none}
.dot{flex:0 0 5.2rem;scroll-snap-align:center;border-radius:6px;padding:.3rem .3rem;
  background:rgba(237,235,228,.08);border:1px solid rgba(237,235,228,.12);color:#EDEBE4;
  display:grid;grid-template-columns:auto minmax(0,1fr);gap:.3rem;align-items:center;
  transition:transform .18s ease,background .18s ease,border-color .18s ease}
.dot-lg{position:relative;width:20px;height:20px;border-radius:3px;
  background:rgba(237,235,228,.1);overflow:hidden;display:grid;place-items:center;
  font-family:var(--display);font-weight:700;font-size:.5rem;opacity:.8}
.dot-lg img{width:100%;height:100%;object-fit:cover;display:block}
.dot b{font-family:var(--display);font-weight:600;font-size:.63rem;line-height:1.15;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left}
.dot[aria-current="true"]{background:var(--signal);border-color:var(--signal);color:#fff;transform:scale(1.06)}
.dot[aria-current="true"] .dot-lg{background:rgba(255,255,255,.18);opacity:1}

.transport{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  gap:.75rem;padding:.1rem 0 0}
.tzone{display:flex;align-items:center;gap:.75rem;min-width:0}
.tzone--left{justify-content:flex-end}
.tzone--right{justify-content:flex-start}
.tbtn{background:none;border:0;color:#EDEBE4;opacity:.78;padding:.3rem;display:grid;place-items:center}
.tbtn svg{width:16px;height:16px}
.tbtn:hover{opacity:1}
.tbtn[disabled]{opacity:.28;cursor:not-allowed}
.tbtn--main{background:var(--signal);color:#fff;width:2.5rem;height:2.5rem;border-radius:50%;opacity:1}
.tbtn--main svg{width:14px;height:14px}
.tbtn--fav[aria-pressed="true"]{color:var(--dial);opacity:1}
.tbtn--sleep[aria-pressed="true"]{color:var(--dial);opacity:1}
/* A depleting ring around the minutes left, rather than a bare number — it
   reads as a countdown at a glance. */
.sleepring{position:relative;display:grid;place-items:center;width:20px;height:20px}
.sleepring svg{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg)}
.sleepring circle{fill:none;stroke-width:2.6}
.sleepring .ring-bg{stroke:rgba(237,235,228,.22)}
.sleepring .ring-fg{stroke:var(--dial);stroke-linecap:round;transition:stroke-dashoffset .6s linear}
.sleepnum{position:relative;font-family:var(--mono);font-size:.52rem;font-weight:600;
  letter-spacing:0;line-height:1;color:var(--dial)}

/* custom duration */
.sleepcustom{display:flex;align-items:center;gap:.3rem;padding:.35rem 0 0;justify-content:center;
  border-top:1px solid rgba(237,235,228,.14);margin-top:.35rem}
.sleepcustom input{width:3.4rem;background:rgba(237,235,228,.08);color:#EDEBE4;
  border:1px solid rgba(237,235,228,.2);border-radius:6px;padding:.18rem .35rem;
  font-family:var(--mono);font-size:.66rem;text-align:center}
.sleepcustom input::-webkit-outer-spin-button,
.sleepcustom input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.sleepcustom input[type=number]{-moz-appearance:textfield}
.sleepcustom label{font-family:var(--mono);font-size:.58rem;letter-spacing:.08em;
  text-transform:uppercase;color:rgba(237,235,228,.6)}
.sleepcustom button{background:var(--dial);border:1px solid var(--dial);color:#fff;
  border-radius:6px;font-family:var(--display);font-weight:600;font-size:.66rem;
  padding:.2rem .55rem;opacity:1}

/* #6 volume popover */
.volwrap{position:relative;display:inline-grid}
.volpop{position:absolute;bottom:calc(100% + .45rem);left:50%;transform:translateX(-50%);
  background:var(--shell);border:1px solid rgba(237,235,228,.2);border-radius:8px;
  padding:.55rem .6rem;display:flex;align-items:center;gap:.45rem;box-shadow:0 6px 20px rgba(0,0,0,.3)}
.volpop[hidden]{display:none}
.volpop input[type=range]{appearance:none;width:7.5rem;height:4px;border-radius:4px;background:rgba(237,235,228,.25)}
.volpop input[type=range]::-webkit-slider-thumb{appearance:none;width:13px;height:13px;border-radius:50%;
  background:var(--signal);border:2px solid var(--shell);cursor:pointer}
.volpop input[type=range]::-moz-range-thumb{width:13px;height:13px;border:2px solid var(--shell);
  border-radius:50%;background:var(--signal);cursor:pointer}
.volpct{font-family:var(--mono);font-size:.6rem;color:rgba(237,235,228,.7);
  min-width:2.2rem;text-align:right;font-variant-numeric:tabular-nums}

.sleepmenu{display:flex;gap:.3rem;justify-content:center;padding:.25rem 0 0;flex-wrap:wrap}
.sleepmenu[hidden]{display:none}
.sleepmenu button{background:none;border:1px solid rgba(237,235,228,.18);color:#EDEBE4;
  border-radius:999px;font-family:var(--mono);font-size:.6rem;letter-spacing:.06em;padding:.18rem .5rem;opacity:.8}
.sleepmenu button:hover{opacity:1;border-color:var(--dial)}

@media (min-width:60rem){
  .player-in{display:grid;grid-template-columns:minmax(0,19rem) minmax(0,1fr) auto;
    grid-template-areas:"np dial transport";align-items:center;gap:1.1rem}
  .np{grid-area:np}
  .dial{grid-area:dial;margin:0}
  .transport{grid-area:transport;padding:0}
  /* On desktop the transport sits in an auto-width column, so the 1fr zones
     size to their content and the heavier right-hand side pushes play off
     centre. A shared minimum evens them out. */
  .tzone{min-width:7.5rem}
  .sleepmenu{position:absolute;right:1rem;bottom:calc(100% + .4rem);background:var(--shell);
    border:1px solid rgba(237,235,228,.2);border-radius:10px;padding:.5rem}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important;scroll-behavior:auto!important}}


/* ── phones: buy back vertical space ─────────────────────────────────────
   Everything below is scoped to small screens; the desktop layout is
   untouched. The filter bar goes from four rows to two, the page header from
   three lines to one, and the tiles shrink so more of the list is visible
   above the fold. */
@media (max-width:47.99rem){

  /* All three selects share one row rather than wrapping to two. They stay —
     the browse rail is already hidden at this width, so removing them would
     leave no way to filter by language or genre at all. */
  .fbar-in{gap:.35rem}
  .fsel{flex:1 1 0;min-width:0;order:1}
  .fsel select{width:100%;max-width:none;padding-right:1.2rem}
  .fsel::after{right:.35rem}

  /* A zero-height flex item forces a line break, so the three selects get the
     whole first row instead of being squeezed to "Indi…" by the controls.
     Two rows, as intended — one row only fits by truncating the labels. */
  .fbar-in::after{content:"";flex-basis:100%;height:0;order:2}
  .clearf{order:3}
  .fbar-right{order:4}

  /* Row two: clear, layout switch, favourites. The count moves into the
     heading and the active-filter bar goes: the selects already show what is
     applied, and Clear undoes it. */
  .fcount{display:none}
  .active{display:none}
  .fbar-right{gap:.3rem}

  /* One line instead of three. */
  /* Block, not flex: as a flex container the count became its own item and
     was pushed to the far right, away from the words it belongs to. Inline it
     simply follows the text, wrapping with it. */
  h1.page{font-size:1.05rem;line-height:1.22;margin:.7rem 0 .55rem;display:block}
  .h1count{display:inline;font-family:var(--mono);font-size:.68rem;font-weight:400;
    color:var(--ink-3);letter-spacing:.04em;white-space:nowrap}
  .lede{display:none}
  .hint{margin-top:.7rem;padding:.5rem .6rem;font-size:.82rem}

  /* Four columns instead of three, and the tile loses the height it was
     spending on a two-line name it can no longer fill anyway. The logo is
     ~two thirds of a tile, so the column count is the main lever. */
  .panel[data-view="grid"]{grid-template-columns:repeat(auto-fill,minmax(min(100%,4.9rem),1fr));gap:.35rem}
  .panel[data-view="grid"] .nm{font-size:.63rem;line-height:1.2;padding:.28rem .3rem .02rem;
    min-height:0;-webkit-line-clamp:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}
  .panel[data-view="grid"] .foot{padding:.02rem .28rem .28rem;gap:.15rem}
  .panel[data-view="grid"] .mt-short{font-size:.46rem;letter-spacing:.02em}
  .panel[data-view="grid"] .abtn{padding:.14rem .16rem;border-radius:5px}
  .panel[data-view="grid"] .abtn svg{width:9px;height:9px}
  /* Lighter still on phones: the badge is always visible there (no hover to
     reveal it), so it sits over the artwork the whole time. */
  .pbring{background:#00000066}
  .panel[data-view="grid"] .pbring{width:min(32px,78%)}
  .box{border-radius:8px}

  /* No clamp at all: every tip here is a short, fixed string this file
     controls, not open-ended content that needs a truncation safety net —
     so it can just wrap to whatever it actually needs (2-3 lines) instead of
     losing words to an ellipsis either way. */
  .hint #hint-text{min-width:0}
}
