/* SUB92: radio register. Small on purpose; no framework. Brand tokens: docs/brand.md. */
/* Self-hosted fonts only (docs/brand.md, "Type"): Big Shoulders Display (display), IBM
   Plex Mono (labels), IBM Plex Sans (body), DejaVu Sans for the symbols and scripts Plex
   lacks (fetched only when a page uses them). Licences next to the files. */
@font-face{font-family:"IBM Plex Sans";font-style:normal;font-weight:400;font-display:swap;src:url(/static/fonts/IBMPlexSans-Regular.woff2) format("woff2")}
@font-face{font-family:"IBM Plex Sans";font-style:normal;font-weight:500;font-display:swap;src:url(/static/fonts/IBMPlexSans-Medium.woff2) format("woff2")}
@font-face{font-family:"IBM Plex Sans";font-style:italic;font-weight:400;font-display:swap;src:url(/static/fonts/IBMPlexSans-Italic.woff2) format("woff2")}
@font-face{font-family:"DejaVu Sans";font-style:normal;font-weight:400;font-display:swap;src:url(/static/fonts/DejaVuSans-Fallback.woff2) format("woff2");unicode-range:U+0180-02AF,U+0370-03FF,U+0400-052F,U+1E00-1EFF,U+2190-21FF,U+25A0-25FF,U+2600-26FF,U+2700-27BF}
@font-face{font-family:"Big Shoulders Display";font-style:normal;font-weight:900;font-display:swap;src:url(/static/fonts/BigShouldersDisplay-Black.ttf) format("truetype")}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:500;font-display:swap;src:url(/static/fonts/IBMPlexMono-Medium.ttf) format("truetype")}
@font-face{font-family:"Fraunces";font-style:italic;font-weight:900;font-display:swap;src:url(/static/fonts/Fraunces-BlackItalic.ttf) format("truetype")}
:root{color-scheme:light;--bar-bg:#17160F;--bar-fg:#F4EFE6;--bar-muted:#CFC8B8;--bar-accent:#E8743B;--bar-line:#3a352e;--bar-on-accent:#1d1b18;--logo-bg:#ece6da;--bg:#F4EFE6;--fg:#1B1A17;--muted:#625b51;--card:#fffdf8;--line:#e2d9c8;--accent:#c2410c;--on-accent:#fff;--live:#2b7a3b;--radius:10px;--needle:19.5%;--wm-accent:#F07A3A;--dial-ink:#F4EFE6;--dial-dim:#8C877A;--dial-accent:#F07A3A;--dial-band:rgba(240,122,58,.2);--display:"Big Shoulders Display","IBM Plex Sans","DejaVu Sans",sans-serif;--mono:"IBM Plex Mono","DejaVu Sans",monospace;--body:"IBM Plex Sans","DejaVu Sans",sans-serif;
  /* Type scale (docs/brand.md): rem steps; body text never under 14 px. */
  --t-label:.75rem;--t-sm:.875rem;--t-md:1rem;--t-call:1.375rem;--t-h2:1.5rem;--t-ask:1.75rem;--t-h1:clamp(2rem,7vw,2.75rem)}
/* Theme (settings popover, theme.js): no data-theme = follow the OS; data-theme="light"|"dark"
   forces one. The bar/dial tokens (--bar-*, --dial-*, --wm-accent) stay dark in both. */
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){color-scheme:dark;--bg:#141210;--fg:#efe9df;--muted:#aaa296;--card:#1e1b18;--line:#3a352e;--accent:#ff8a4c;--on-accent:#1d1b18;--live:#63d47a}}
:root[data-theme="dark"]{color-scheme:dark;--bg:#141210;--fg:#efe9df;--muted:#aaa296;--card:#1e1b18;--line:#3a352e;--accent:#ff8a4c;--on-accent:#1d1b18;--live:#63d47a}
*{box-sizing:border-box}
/* Only real faces: Plex Sans comes in 400/500/italic, so bold asks get Medium, never a smeared fake. */
html{-webkit-text-size-adjust:100%;font-synthesis:none}
body{margin:0;background:var(--bg);color:var(--fg);font:400 16px/1.5 var(--body);overflow-wrap:anywhere}
b,strong{font-weight:500}
/* Form controls take the body font (browsers default them to their own, notably on phones). */
input,button,select,textarea{font:inherit}
::placeholder{color:var(--muted);opacity:1}
a{color:inherit}
img{max-width:100%}
main{display:block;outline:0;max-width:60rem;margin:0 auto;padding:0 max(1rem,env(safe-area-inset-right)) 0 max(1rem,env(safe-area-inset-left))}
h1,h2,h3{margin:1.4rem 0 .6rem}
h1{font:900 var(--t-h1)/1.05 var(--display);letter-spacing:.01em}
h2{font:900 var(--t-h2)/1.1 var(--display);letter-spacing:.02em;text-transform:uppercase}
/* Kickers (state headings): Plex Mono caps. */
h3{font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin:1rem 0 .4rem}
/* Top bar: sticky, holds the nav, the settings gear and (once something plays) the player. */
.topbar{position:sticky;top:0;z-index:20;background:var(--bar-bg);color:var(--bar-fg);box-shadow:0 2px 8px rgba(0,0,0,.25);
  padding:env(safe-area-inset-top) max(1rem,env(safe-area-inset-right)) 0 max(1rem,env(safe-area-inset-left))}
.bar{display:flex;align-items:center;gap:1rem;max-width:60rem;margin:0 auto;min-height:2.9rem}
/* The wordmark: SUB + accent 92 in Big Shoulders Display Black (no icon in the masthead). */
.wordmark{display:inline-flex;align-items:center;font:900 1.9rem/1 var(--display);letter-spacing:.01em;text-decoration:none;color:var(--bar-fg)}
.wordmark .n92{color:var(--wm-accent)}
.bar nav{display:flex;gap:1rem;margin-left:auto}
/* Nav: Plex Mono caps, spaced like the tagline. */
.bar nav a{text-decoration:none;font:500 .8rem/1.2 var(--mono);letter-spacing:.1em;text-transform:uppercase}
.bar nav a:hover,.bar nav a:focus-visible{text-decoration:underline}
.settings{position:relative}
.settings summary{list-style:none;cursor:pointer;display:grid;place-items:center;width:2.2rem;height:2.2rem;border-radius:50%;color:var(--bar-muted)}
.settings summary::-webkit-details-marker{display:none}
.settings[open] summary,.settings summary:hover{color:var(--bar-fg);background:var(--bar-line)}
.settings .pop{position:absolute;z-index:5;right:0;top:2.6rem;width:15rem;background:var(--card);color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);padding:.8rem;box-shadow:0 6px 20px rgba(0,0,0,.25);display:grid;gap:.4rem}
.settings .pop label{font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.08em;text-transform:uppercase}
.settings select{font:inherit;padding:.35rem;border-radius:6px;border:1px solid var(--line);background:var(--bg);color:var(--fg)}
.settings .pop p{margin:0;font:400 var(--t-sm)/1.4 var(--body);letter-spacing:0;text-transform:none;color:var(--muted)}
/* The dial (docs/brand.md, "Dial"): 88-108 MHz, the last row of the sticky top bar.
   A tick every 0.5 MHz, taller every 2; ink over the reserved 88-92 band (shaded in
   the accent), muted beyond; the accent needle slides to the playing station. */
.dial{--maj:.45rem;--min:.22rem;position:relative;max-width:60rem;margin:0 auto;height:1.25rem;display:flex;justify-content:space-between;align-items:flex-start;font:500 .62rem/1 var(--mono);color:var(--dial-dim);border-bottom:1px solid var(--dial-dim);
  background:
    linear-gradient(90deg,var(--dial-ink) 0 1px,transparent 1px calc(50% - .5px),var(--dial-ink) 0 calc(50% + .5px),transparent 0 calc(100% - 1px),var(--dial-ink) 0) 0 100%/calc(20% + 1px) var(--maj) no-repeat,
    repeating-linear-gradient(90deg,var(--dial-ink) 0 1px,transparent 1px 12.5%) 0 100%/20% var(--min) no-repeat,
    linear-gradient(var(--dial-dim),var(--dial-dim)) 100% 100%/1px var(--maj) no-repeat,
    repeating-linear-gradient(90deg,var(--dial-dim) 0 1px,transparent 1px 12.5%) 100% 100%/80% var(--maj) no-repeat,
    repeating-linear-gradient(90deg,var(--dial-dim) 0 1px,transparent 1px 3.125%) 100% 100%/80% var(--min) no-repeat}
.dial span{position:absolute;top:.1rem;width:auto;white-space:nowrap;transform:translateX(-50%)}.dial span:nth-of-type(1){left:0;transform:none}.dial span:nth-of-type(2){left:20%}.dial span:nth-of-type(3){left:40%}.dial span:nth-of-type(4){left:60%}.dial span:nth-of-type(5){left:80%}.dial span:nth-of-type(6){left:auto;right:0;transform:none;text-align:right}
.dial span:nth-of-type(-n+2){color:var(--dial-ink)}
.dial b{position:absolute;left:0;bottom:0;width:20%;height:100%;background:var(--dial-band)}
.dial i{position:absolute;left:var(--needle);bottom:-1px;width:3px;margin-left:-1px;height:calc(100% + 3px);background:var(--dial-accent);transition:left .4s ease}
@media (prefers-reduced-motion:reduce){.dial i{transition:none}}
@media (max-width:440px){.dial{--maj:5px;--min:3px;height:14px;font-size:8px}.dial span{top:1px}}
/* Install hints in the settings popover (player.js shows the one that applies). */
.settings .install{border-top:1px solid var(--line);padding-top:.5rem;margin-top:.2rem}
.settings .install p{margin:.35rem 0 0}
.settings .install button{padding:.35rem .9rem}
/* Theme switch in the settings popover: three radios as one segmented control (theme.js). */
.theme{border:0;margin:0;padding:0;min-width:0}
.theme[hidden]{display:none}
.theme legend{padding:0;margin-bottom:.25rem;font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.08em;text-transform:uppercase}
.seg{display:flex;border:1px solid var(--line);border-radius:6px;overflow:hidden}
.seg label{flex:1;text-align:center;cursor:pointer;padding:.3rem 0;font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
.seg label+label{border-left:1px solid var(--line)}
.seg input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
.seg label:has(:checked){background:var(--fg);color:var(--bg)}
.seg label:has(:focus-visible){outline:2px solid var(--accent);outline-offset:-2px}
.vibe{display:grid;gap:.6rem;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:1rem;margin-top:1rem}
/* "Play me something like…": the display face; the box, its placeholder and the variety radios in Plex Mono. */
.vibe>label{font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.vibe>label.ask{font:900 var(--t-ask)/1.05 var(--display);letter-spacing:.01em;color:var(--fg)}
.vibe input[type=search]{font:500 var(--t-md)/1.4 var(--mono);padding:.6rem .7rem;border:1px solid var(--line);border-radius:6px;background:var(--bg);color:var(--fg);width:100%}
/* Phones: the placeholder a step smaller so it fits (the typed text stays 16 px: no iOS zoom). */
@media (max-width:30rem){.vibe input[type=search]::placeholder{font-size:var(--t-sm)}}
/* Autosuggest listbox under the search box (htmx fills it; player.js drives the keys). */
.combo{position:relative}
.suggest{position:absolute;left:0;right:0;top:calc(100% + 2px);z-index:10;margin:0;padding:.25rem 0;list-style:none;background:var(--card);border:1px solid var(--line);border-radius:6px;box-shadow:0 6px 20px rgba(0,0,0,.18);max-height:22rem;overflow-y:auto}
.suggest a{display:flex;gap:.5rem;align-items:baseline;padding:.45rem .7rem;text-decoration:none}
.sg-label{font-weight:500;flex:none;white-space:nowrap}
.sg-detail{color:var(--muted);font-size:var(--t-sm);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:1 1 auto}
.sg-kind{margin-left:auto;flex:none;white-space:nowrap;color:var(--muted);font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.08em;text-transform:uppercase}
.sg-tag{color:var(--muted);font-size:.7rem;border:1px solid var(--line);border-radius:3px;padding:0 .3rem;line-height:1.4}
.sg[aria-selected=true] a,.sg a:hover{background:var(--line)}
.variety{border:0;padding:0;margin:0;display:flex;gap:.3rem .9rem;font:500 var(--t-sm)/1.4 var(--mono);flex-wrap:wrap;align-items:center}
.variety legend{float:left;margin-right:.2rem;color:var(--muted);font-size:var(--t-label);letter-spacing:.12em;text-transform:uppercase}
button,.button{font:500 var(--t-md)/1.4 var(--body);cursor:pointer;border:0;border-radius:999px;padding:.45rem 1rem;background:var(--fg);color:var(--bg);text-decoration:none;display:inline-block}
.cards{list-style:none;padding:0;margin:0;display:grid;gap:.7rem;grid-template-columns:repeat(auto-fill,minmax(min(100%,17rem),1fr))}
/* On now: states flow in columns so the page fills on desktop. Phones get a plain single
   column, not a one-column multicol: Safari re-balances a multicol container whenever a card
   changes (Play <-> Stop) and throws the scroll position to the end of the list. */
@media (min-width:36rem){.now{columns:17rem 3;column-gap:.7rem}}
.now .states{list-style:none;margin:0;padding:0}
.now .state{break-inside:avoid;padding:.3rem 0 .8rem;margin-bottom:.5rem;border-bottom:1px solid var(--dial-dim)}
.now .state:last-child{border-bottom:0;margin-bottom:0}
.now .state[hidden]{display:none}
.now .cards{grid-template-columns:1fr}
.now h3{margin:0 0 .4rem}
.now>.empty{column-span:all}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:.75rem;display:grid;gap:.3rem .6rem;grid-template-columns:1fr auto;align-content:start}
.card>*{grid-column:1/-1}
.card>.ident{grid-column:1}
.card>.actions{grid-column:2;grid-row:1;margin:0;align-self:start;flex-direction:column;align-items:flex-end;gap:.2rem}
.card.top{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
.ident{display:flex;align-items:center;gap:.6rem;min-width:0}
.station{display:inline-flex;align-items:baseline;flex-wrap:wrap;gap:.1rem .45rem}
.logo{--s:40px;position:relative;flex:none;width:var(--s);height:var(--s);border-radius:6px;overflow:hidden;display:inline-grid;place-items:center;background:var(--fg);color:var(--bg);font:500 calc(var(--s)*.25)/1 var(--mono);letter-spacing:0}
.logo img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:var(--logo-bg);padding:2px}
/* Call signs: the display face on cards and headings; frequencies, bitrates and times in Plex Mono. */
.call{font:900 var(--t-call)/1.1 var(--display);letter-spacing:.03em;text-decoration:none}
h1 .call{font-size:inherit;line-height:inherit}
.freq{font:500 .8125rem/1.4 var(--mono);color:var(--muted)}
.donate{font:500 var(--t-label)/1.5 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--accent);border:1px solid currentColor;border-radius:999px;padding:0 .5rem;text-decoration:none;white-space:nowrap}
.city,.dj,.when,.score,.sub,.empty{color:var(--muted)}
.city{font-size:var(--t-sm)}
.when{font:500 .8125rem/1.5 var(--mono);margin-right:.4rem;white-space:nowrap}
.song{font-weight:500}
.song .mystery,.np-song.mystery{color:var(--muted);font-weight:400;letter-spacing:.2em}
.when a{text-decoration:none}
.np-song.mystery{color:var(--bar-muted)}
.artist{text-decoration:none}
.meta{font-size:.9375rem}
.actions{display:flex;flex-wrap:wrap;align-items:center;gap:.7rem;margin:.3rem 0}
.play{background:var(--accent);color:var(--on-accent);font-weight:500;white-space:nowrap}
.play.on{background:var(--live)}
/* Same width playing or not: both labels sit in zero-height, hidden sizers. */
.play{text-align:center}.play::before,.play::after{display:block;height:0;overflow:hidden;visibility:hidden}
.play::before{content:attr(data-text)}.play::after{content:"\25A0  Stop"}
.score{font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.06em;text-transform:uppercase;white-space:nowrap}
.nostream{font-size:var(--t-sm);color:var(--muted);font-style:italic;max-width:7rem;text-align:right}
.badge{font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.08em;color:var(--live);text-transform:uppercase}
.empty{font-style:italic}
.hero{display:flex;gap:1rem;align-items:center;margin-top:1rem}
.hero h1{margin:0 0 .3rem}
.hero .sub{margin:.2rem 0}
.hero-art{border-radius:var(--radius);object-fit:cover;flex:none;width:96px;height:96px}
.hero>.logo{border-radius:var(--radius)}
.schedule,.spins{padding-left:0;list-style:none}
.schedule li,.spins li{padding:.35rem 0;border-bottom:1px solid var(--line)}
.schedule li.live{font-weight:500}
.tags{list-style:none;padding:0;display:flex;flex-wrap:wrap;gap:.4rem}
.tags a{display:inline-block;border:1px solid var(--line);border-radius:999px;padding:.15rem .6rem;text-decoration:none}
.about{max-width:38rem}
.lede{font-size:1.25rem;font-weight:500;line-height:1.4}
.player{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.7rem;max-width:60rem;margin:0 auto;padding:.45rem 0 .6rem;border-top:1px solid var(--bar-line)}
.player[hidden]{display:none}
.np-toggle{width:2.6rem;height:2.6rem;padding:0;border-radius:50%;background:var(--bar-accent);color:var(--bar-on-accent);font-size:1.05rem;line-height:1;display:grid;place-items:center}
/* the glyphs' side bearings are uneven: a touch right for the play triangle, none for the square */
.np-toggle.off{padding-left:.15em}
.np-toggle.off{background:var(--bar-fg)}
.np{min-width:0;display:grid;gap:.05rem}
.np-line{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.4}
.np-station{font-size:var(--t-sm);color:var(--bar-muted);display:flex;align-items:baseline;min-width:0}
.np-station b{color:var(--bar-fg);font:500 .8125rem/1.4 var(--mono);letter-spacing:.06em}
.np-station .freq{color:var(--bar-muted)}
.np-song{font-weight:500}
.np-album{font-size:var(--t-sm);color:var(--bar-muted)}
.np-album:empty{display:none}
.br{font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.04em;color:var(--muted);white-space:nowrap;text-transform:none}
.np-station .np-line{min-width:0}
.np-station .br{color:var(--bar-muted);margin-left:.5rem;flex:none}
.np-side{display:flex;flex-direction:column;align-items:flex-end;gap:.35rem}
.player .donate{color:var(--bar-accent)}
.vol{width:6rem;height:1rem;accent-color:var(--bar-accent);margin:0}
.vol[hidden]{display:none}
.bitrate-note{font-size:.9375rem;color:var(--muted);margin:.2rem 0 .6rem}
.bitrate-note button{background:none;color:var(--fg);padding:0;border-radius:0;text-decoration:underline;font-size:inherit}
.card.lowbr,li.lowbr{display:none}
.sep{color:var(--muted)}
.call,.freq{white-space:nowrap}
/* Call-sign chips (one Play per simulcast member): Plex Mono. */
.play.sm{font:500 .8125rem/1.4 var(--mono);letter-spacing:.06em;padding:.3rem .75rem}
.also{font-size:.9375rem}
@media (max-width:30rem){.vol{width:4.5rem}.bar{gap:.6rem}.bar nav{gap:.8rem}}
@media (max-width:25rem){.bar nav{gap:.6rem}.wordmark{font-size:1.7rem}}
/* Bookmarks (v0.2): a form per button so it works without JS. */
.bm{display:inline;margin:0}
.heart{background:none;color:var(--fg);border:1px solid var(--line);padding:.35rem .8rem;font-size:var(--t-sm);white-space:nowrap}
.heart.on{color:var(--accent);border-color:currentColor}
.heart span{font-size:1.05em}
.np-acts{display:flex;align-items:center;gap:.5rem}
.np-heart{background:none;color:var(--bar-fg);padding:0;width:1.9rem;height:1.9rem;border-radius:50%;font-size:1.15rem;line-height:1}
.np-heart.on{color:var(--bar-accent)}
.np-heart[hidden]{display:none}
/* Chromecast (Chrome/Edge only; cast.js shows the button). */
.np-cast{background:none;color:var(--bar-muted);padding:0;width:1.9rem;height:1.9rem;border-radius:50%;display:grid;place-items:center}
.np-cast:hover,.np-cast.on{color:var(--bar-accent)}
.np-cast.wait{opacity:.5}
.np-cast[hidden],.np-casting[hidden]{display:none}
.np-casting{font-size:var(--t-sm);color:var(--bar-accent);display:flex;gap:.5rem;align-items:center;min-width:0}
.np-casting span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.np-casting button{padding:.05rem .6rem;font-size:var(--t-sm);background:var(--bar-fg);color:var(--bar-bg);flex:none}
.account{max-width:38rem}
.account .vibe input[type=email]{font:400 var(--t-md)/1.4 var(--body);padding:.6rem .7rem;border:1px solid var(--line);border-radius:6px;background:var(--bg);color:var(--fg);width:100%}
.lede-sm{font-size:1.2rem;font-weight:500;line-height:1.4}
.error{color:var(--accent);font-weight:500}
.saved{list-style:none;padding:0;margin:0}
.saved li{display:flex;gap:.7rem;align-items:baseline;justify-content:space-between;padding:.45rem 0;border-bottom:1px solid var(--line)}
.saved-main{min-width:0}
.note{font-size:.9375rem;color:var(--muted)}
button.link{background:none;color:var(--muted);padding:0;border-radius:0;text-decoration:underline;font:400 .9375rem/1.4 var(--body);white-space:nowrap}
.export{color:var(--muted);font-size:.9375rem;margin:1.5rem 0 0}
.logout{margin:1.5rem 0}
.delete{margin:1.5rem 0;display:flex;flex-wrap:wrap;gap:.5rem 1rem;align-items:center}.delete p{flex-basis:100%;margin:0}
.foot{max-width:60rem;margin:2rem auto;padding:0 1rem;color:var(--muted);font-size:var(--t-sm)}
/* QA pass 2: hidden-but-read text, skip link, primary button, two-line song on phones. */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.skip{position:absolute;left:.5rem;top:-3rem;z-index:30;background:var(--bar-fg);color:var(--bar-bg);padding:.4rem .8rem;border-radius:6px;font-weight:500}
.skip:focus{top:max(.5rem,env(safe-area-inset-top))}
.primary{background:var(--accent);color:var(--on-accent);font-weight:500;padding:.6rem 1.6rem}
@media (max-width:30rem){.np-song{white-space:normal;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2}}
/* Station map (/map): states from us-states.svg via <use>, dots are links. */
:root{--land:#e4dac8;--land-line:#fffdf8;--dot-music:#c2410c;--dot-mixed:#2f6690}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--land:#2c2823;--land-line:#141210;--dot-music:#ff8a4c;--dot-mixed:#6cb6e0}}
:root[data-theme="dark"]{--land:#2c2823;--land-line:#141210;--dot-music:#ff8a4c;--dot-mixed:#6cb6e0}
.maph{font-size:var(--t-h2);margin:1rem 0 .4rem}
.mapbar{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}
.formats{border:0;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:.3rem .9rem;font:500 var(--t-sm)/1.4 var(--mono)}
.formats legend{float:left;margin-right:.6rem;color:var(--muted);font-size:var(--t-label);letter-spacing:.12em;text-transform:uppercase}
.formats label{white-space:nowrap}
.mapwrap{position:relative;margin:.6rem 0;border:1px solid var(--line);border-radius:var(--radius);background:var(--card)}
.map{display:block;width:100%;height:auto;border-radius:var(--radius);touch-action:pan-y;user-select:none;-webkit-user-select:none}
.map.zoomed{touch-action:none;cursor:grab}
.map .states{fill:var(--land);stroke:var(--land-line);stroke-width:.6}
.map .dot circle{fill:var(--dot-music);stroke:var(--card);stroke-width:1.2px;vector-effect:non-scaling-stroke;transition:cx .2s,cy .2s}
.map .dot.mixed circle{fill:var(--dot-mixed)}
.map .dot.quiet circle{fill-opacity:.4}
/* music+talk: blue ring with a music-coloured centre while a song is reported; plain blue in a talk block */
.map .dot.mixed circle.in{fill:var(--dot-music);stroke:none;pointer-events:none}
.map .dot.mixed.quiet circle{fill-opacity:1}
.map .dot.mixed.quiet circle.in{display:none}
.map .dot:hover circle,.map .dot:focus-visible circle,.map .dot.sel circle{stroke:var(--fg);stroke-width:2.5px}
.map .dot:focus{outline:0}
.map .dot.out,.map .dot.lowbr{display:none}
.zoom{position:absolute;right:.5rem;bottom:.5rem;display:flex;flex-direction:column;gap:.25rem}
.zoom button{width:2.2rem;height:2.2rem;padding:0;border-radius:6px;background:var(--card);color:var(--fg);border:1px solid var(--line);font:500 1.1rem/1 var(--body)}
.mapcard{position:absolute;z-index:5;width:min(18rem,calc(100% - 1rem));box-shadow:0 6px 20px rgba(0,0,0,.25);grid-template-columns:1fr auto}
.mapcard[hidden]{display:none}
.mapcard .x{position:absolute;right:.2rem;top:.1rem;background:none;color:var(--muted);padding:.2rem .45rem;font-size:1.1rem;line-height:1}
.mapcard>.ident{padding-right:1.4rem}
.mapcard>.actions{grid-column:1/-1;grid-row:auto;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;gap:.4rem .8rem}
.legend{display:flex;flex-wrap:wrap;align-items:center;gap:.3rem 1rem;font:500 var(--t-label)/1.4 var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--muted);margin:.2rem 0}
.legend .key{display:inline-block;width:.75rem;height:.75rem;border-radius:50%;background:var(--dot-music)}
.legend>span{display:inline-flex;align-items:center;gap:.35rem}
.legend .key{flex:none}
.legend .key.mixed{background:var(--dot-mixed)}
.legend .key.mixed.on{background:radial-gradient(circle,var(--dot-music) 0 36%,var(--dot-mixed) 38%)}
.legend .key.quiet{opacity:.4}
.map-note{font-size:.9375rem;color:var(--muted);margin:.4rem 0 0}
.map-note button{background:none;color:var(--fg);padding:0;border-radius:0;text-decoration:underline;font-size:inherit}
.offmap{padding-left:1.1rem}
.zoom[hidden],.nojs-only[hidden]{display:none}
.wordmark,.bar nav a{white-space:nowrap}
@media (max-width:440px){.bar{gap:.6rem}.bar nav{gap:.75rem}.bar nav a{font-size:.74rem;letter-spacing:.08em}.wordmark{font-size:1.65rem}
  .zoom button{width:1.9rem;height:1.9rem;font-size:1rem}}
/* QA pass 3: the top bar fits 320-400 px phones (no sideways scroll); "On now" goes
   below 350 px (the wordmark links home too). */
@media (max-width:400px){.bar{gap:.5rem}.bar nav{gap:.55rem}.bar nav a{font-size:.7rem;letter-spacing:.06em}.wordmark{font-size:1.4rem}
  .settings summary{width:1.9rem;height:1.9rem}}
@media (max-width:350px){.bar nav a.nav-home{display:none}}
/* Song age: a now-playing line that has run past its length goes muted (colour only; the
   tooltip says when it was heard). The lag note sits small at the foot of station pages. */
.song{transition:color .6s ease}
.song.aged{color:var(--muted)}
@media (prefers-reduced-motion:reduce){.song{transition:none}}
.lagnote{margin:2rem 0 0;font-size:.78rem;color:var(--muted)}
/* ==== On-air glyph (.meter) — BEGIN ====
   A mast with a dot and two arcs each side (inline SVG in _macros.html meter() and the
   player). Shown only in the player (accent, 16 px, left of the call sign) and on the
   playing station's card/heading, right of its frequency (.meter.on, live green, 12 px); never
   without JS. While audio flows (player.js: .live) the inner then the outer arcs fade in
   and out, 1.4 s, opacity only; buffering/paused and reduced motion: still, full opacity. */
.meter{display:none;flex:none;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;overflow:visible}
.meter circle{fill:currentColor;stroke:none}
.meter.on{display:inline-block;width:12px;height:12px;margin-left:.35em;vertical-align:-1px;color:var(--live)}
.player .meter{display:inline-block;width:16px;height:16px;margin-left:.4rem;vertical-align:-2px;color:var(--bar-accent)}
/* Room for the glyph on phones: the bitrate drops to a clipped second row when the call
   sign and frequency would otherwise be cut. */
.np-station{flex-wrap:wrap;max-height:1.1rem;overflow:hidden}
.meter.live .m1,.meter.live .m2{animation:meter-arc 1.4s ease-in-out infinite}
.meter.live .m2{animation-delay:-1.05s} /* the outer arc trails the inner by .35 s */
@keyframes meter-arc{0%,100%{opacity:.2}45%{opacity:1}}
@media (prefers-reduced-motion:reduce){.meter.live .m1,.meter.live .m2{animation:none}}
/* ==== On-air glyph (.meter) — END ==== */
/* ==== Logo tiles (station.logo_bg, weekly logo-bg job) ==== */
/* Theme-independent on purpose: a white-ink logo needs the dark tile in the light theme
   and a black-ink logo the light tile in the dark theme. */
:root{--logo-tile-light:var(--logo-bg);--logo-tile-dark:#2a2724}
.logo img{padding:max(2px,calc(var(--s)*.08))}
.logo.logo-bg-light img{background:var(--logo-tile-light)}
.logo.logo-bg-dark img{background:var(--logo-tile-dark)}

/* On now format filter (player.js): the map's format radios; hidden without JS. */
.formats[hidden],.card.fmtout,li.fmtout{display:none}
.fmt{margin:0 0 .6rem}
