/* RECEPTION LOG — what is specific to the public site.
   The register itself — the near-black, the amber, the content-label colours, the two
   faces, the header, the controls — is ui/tokens.css + ui/base.css, which site/build.py
   copies into dist/static/ui/ and base.html links BEFORE this file. Copied, not linked
   across directories: dist/ is rsynced to a CDN and has to stand on its own.
   Both themes, as ui/tokens.css defines them — nothing here picks one. */
body { padding-bottom: calc(var(--player-h) + 12px + env(safe-area-inset-bottom, 0px)); }
a:hover, a:focus-visible { text-decoration-color: var(--amber-dark); }
img { display: block; max-width: 100%; height: auto; }
.facets label > span, .facets legend {
  font-family: var(--cond); text-transform: uppercase; letter-spacing: .14em; font-weight: 600;
}


/* A reception is read across, not down: the columns that identify one never wrap. */
/* Frequency, tier and length never wrap; UTC wraps only between its date and its time
   (each is an .nb), and the frequency only before its mode — a squeezed table folds the
   least important half of a cell under the other, never a number (W-0013). */
.c-f, .c-tier, .c-d { white-space: nowrap; }
/* ...and where a narrow screen forces a break, it may only fall between the parts. */
.nb { white-space: nowrap; }

/* The gutter is a variable because two other things have to agree with it: the hero,
   which bleeds to the edge by cancelling it, and the safe-area inset, which widens it on a
   phone held sideways so text does not run under the notch. */
main { --gut: 20px; --gut-l: calc(var(--gut) + env(safe-area-inset-left, 0px));
       --gut-r: calc(var(--gut) + env(safe-area-inset-right, 0px));
       max-width: var(--maxw); margin: 0 auto; padding: 22px var(--gut-r) 40px var(--gut-l); }
main:focus { outline: none; }
.foot { max-width: var(--maxw); margin: 0 auto; padding: 20px; border-top: 1px solid var(--line); color: var(--dim); font-size: 0.82rem; display: grid; gap: 4px; }
.foot .social a { white-space: nowrap; }
.foot .social .si { width: 1em; height: 1em; margin-right: .35em; vertical-align: -.15em; fill: currentColor; }

/* home (#19, laid out as the owner's mock B in #22): the release's cover on the left, what
   explains it on the right in reading order — the project, the record, one of its tracks;
   a divider; the log's numbers as figures, then the latest receptions. One column on a
   phone (the 900 px rule). `.solo` is the top without a cover. */
.home-top { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 32px; align-items: start; }
.home-top.solo { grid-template-columns: minmax(0, 1fr); }
.home-cover { display: block; }
.home-cover img { width: 100%; aspect-ratio: 1; object-fit: cover; border: 1px solid var(--line2); }
.home-right { display: grid; gap: 12px; align-content: start; min-width: 0; }
/* The project, as a lead paragraph under a rule in the cover's mint — the one green on the
   page, so it reads as the cover's caption rather than a third card. */
.home-intro { border-left: 3px solid var(--keep); padding: 4px 0 4px 18px; }
.home-intro .kicker { color: var(--keep); margin-bottom: 8px; }
.home-intro .tagline { margin: 0; font-size: 1.12rem; line-height: 1.55; max-width: 62ch; }
.home-divider { border: 0; border-top: 1px solid var(--line2); margin: 36px 0 0; }
.home-figs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin: 26px 0 6px; }
.home-figs > div { border-top: 2px solid var(--line2); padding-top: 8px; display: flex; flex-direction: column; }
.home-figs dd { margin: 0; order: -1; font: 600 1.7rem/1.1 var(--mono); color: var(--fg); }
.home-figs dd.sub { order: 1; font: inherit; font-size: .82rem; margin-top: 2px; }
.home-figs dt { font-family: var(--cond); text-transform: uppercase; letter-spacing: .14em; font-size: .74rem;
  color: var(--dim); font-weight: 400; }
.home-log h2 { margin-top: 22px; }
.kicker { font-family: var(--cond); text-transform: uppercase; letter-spacing: .14em;
  font-size: 0.78rem; color: var(--dim); margin: 0 0 6px; }
.home-rel { border: 1px solid var(--line); border-radius: 6px; padding: 16px; background: var(--panel); }
.home-rel h2 { margin: 0 0 6px; padding: 0; border: 0; font-size: 1.15rem; color: var(--fg); }
.home-rel p { margin: 0 0 8px; }
/* The way to the record: the one filled control on the page apart from a playing ▶. */
.btn.go { margin-top: 4px; padding: 7px 16px; font-size: 0.82rem; letter-spacing: .14em;
  background: var(--amber); color: var(--on-accent); border-color: var(--accent); }
.btn.go:hover { background: var(--panel2); color: var(--accent); text-decoration: none; }
/* Empty until home.js puts the picked track in, and then a card of its own. */
.trk-pick:has(.trk) { border: 1px solid var(--line); border-radius: 6px; padding: 12px 16px; margin-top: 12px; }
.trk-head { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.trk-note { margin: 8px 0 2px; font-size: 0.85rem; }

/* /random/ (#19): the full-width spectrogram and player that used to open the home page,
   bleeding to the edge by cancelling the gutter; the station and the receiver below. */
.hero { margin: -22px calc(-1 * var(--gut-r)) 14px calc(-1 * var(--gut-l));
        border-bottom: 1px solid var(--line); background: var(--stage); }
.hero-img img { width: 100%; aspect-ratio: 1600/300; object-fit: cover; image-rendering: auto; }
/* The frame the pick lands in, at the picture's own height, so nothing below moves. */
.hero-wait { aspect-ratio: 1600/300; }
.hero-line { display: flex; gap: 18px; align-items: center; padding: 10px 20px; flex-wrap: wrap; font-size: 0.9rem; }
.hero-line .mono { color: var(--dim); }
.hero .empty { margin: 16px 20px; }
.random-ctl { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.more { font-family: var(--cond); text-transform: uppercase; letter-spacing: .14em; font-size: 0.8rem; }

/* tables */
table.captures { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
table.captures th { text-align: left; color: var(--dim); font-size: 0.8rem; padding: 6px 8px; border-bottom: 1px solid var(--line); }
table.captures td { padding: 7px 8px; border-bottom: 1px solid var(--line); vertical-align: middle; }
table.captures tr.row:hover { background: var(--hover); }
table.captures tr.row.now { background: var(--sel); box-shadow: inset 3px 0 var(--amber); }
table.captures tr.row[hidden] { display: none; }
.c-play { width: 34px; }
/* The picture is the point of the column, so it is never squeezed: a fixed 160 px image
   with the global img { max-width: 100% } lifted is the column's minimum (W-0013). What
   gives instead: station names break inside a token ("Monday/Wednesday") when the table is
   tight, and below that div.captures scrolls sideways rather than the page. */
.c-th { width: 160px; }
.c-th img { width: 160px; max-width: none; height: 30px; border: 1px solid var(--line); }
.c-st { overflow-wrap: anywhere; }

/* archived: logged, audio not published. Dimmed and marked, never made to look like a
   row that failed to load — there is nothing to play and the row says so. */
table.captures tr.row.arc { color: var(--dim); }
table.captures tr.row.arc .c-st a { color: var(--dim); }
table.captures tr.row.arc:hover .c-st a { color: var(--fg); }
table.captures tr.row.arc .c-th img { opacity: .55; filter: saturate(.5); }
table.captures tr.row.arc:hover .c-th img { opacity: .9; filter: none; }
/* An empty slot the play button's size, drawn in CSS: no glyph, so no font can lack it
   (W-0016: "▣" is in none of the site's fonts and fell back to a speck). */
.noaudio { display: inline-block; box-sizing: border-box; width: 2.1em; height: 2em; vertical-align: middle;
  border: 1px dashed var(--line2); font-size: 0.85rem; cursor: help; }
.tag.noaudio-tag { border-style: dashed; }
.nothumb { display: inline-block; width: 160px; height: 30px; border: 1px dashed var(--line); }
.nothumb.wide { width: 320px; height: 60px; }
.gallery li.arc img { opacity: .5; filter: saturate(.4); }
.gallery li.arc:hover img { opacity: .9; filter: none; }
.thumbs a.arc img { opacity: .5; }
.related .nothumb { grid-row: 1 / 3; width: 96px; height: 18px; }
.arcline { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; margin: 10px 0 0;
  padding: 8px 10px; border: 1px dashed var(--line2); color: var(--dim); font-size: 0.82rem; }
.arcline.on { border-style: solid; }
.arcline button { font: inherit; font-family: var(--mono); color: var(--accent); background: none;
  border: 1px solid var(--line2); padding: 2px 9px; cursor: pointer; }
.arcline button:hover { border-color: var(--accent); }
.facets .f-arc { display: block; border-top: 1px solid var(--line); padding-top: 8px; }
.facets .tiny { font-size: 0.8rem; line-height: 1.5; margin: 4px 0 0; }
.p-err { color: var(--warn); font-size: 0.8rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.notice { border: 1px solid var(--line2); border-left: 3px solid var(--amber-dark); background: var(--panel);
  padding: 10px 14px; margin: 0 0 14px; font-size: 0.9rem; }
.notice p { margin: 0 0 .4rem; }
.notice p:last-child { margin: 0; }
.spec.arc .spec-wrap img { opacity: .8; }
.c-rx { max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.82rem; }
.mix { display: flex; height: 4px; width: 90px; margin-top: 4px; background: var(--silence); overflow: hidden; }
.mix i { display: block; height: 100%; }
.lbl { line-height: 1.6; }      /* the rest is ui/base.css — one definition of a label */
.lbl-voice { background: var(--voice); } .lbl-keyed-tone { background: var(--keyed); } .lbl-morse { background: var(--morse); }
.lbl-carrier { background: var(--carrier); } .lbl-data { background: var(--data); } .lbl-noise { background: var(--noise); color: var(--fg); }
.lbl-mixed { background: var(--mixed); } .lbl-silence { background: var(--silence); color: var(--dim); } .lbl-tonal { background: var(--carrier); }
.tag { display: inline-block; padding: 0 5px; border: 1px solid var(--line2); color: var(--dim); font-size: 0.8rem; font-family: var(--mono); vertical-align: middle; }
.tag.star, .star { color: var(--accent); border-color: var(--amber-dark); }
button.play, .btn, .p-btn { font: inherit; color: var(--accent); background: none; border: 1px solid var(--line2); padding: 2px 9px; cursor: pointer; font-family: var(--mono); font-size: 0.85rem; }
button.play:hover, .btn:hover, .p-btn:hover { border-color: var(--accent); text-decoration: none; }
button.play.on { background: var(--amber); color: var(--on-accent); border-color: var(--accent); }
button.play.big { font-size: 1rem; padding: 6px 16px; }
.empty { color: var(--dim); padding: 30px; text-align: center; }

/* archive */
/* minmax(0, 1fr): a bare 1fr cannot shrink below the table's minimum width, and the
   table then runs off the page (W-0013); with it the column shrinks and div.captures scrolls.
   A page with the list beside its facets is nine columns wide and gets more room than a
   page of prose; a narrower window folds cells and breaks names before anything scrolls. */
main:has(.archive) { --maxw: 1400px; }
.archive { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 24px; align-items: start; }
div.captures { overflow-x: auto; }
.facets { position: sticky; top: 66px; display: grid; gap: 10px; font-size: 0.85rem; }
.facets label { display: grid; gap: 3px; }
.facets label > span, .facets legend { font-size: 0.8rem; color: var(--dim); }
.facets fieldset { border: 0; padding: 0; margin: 0; display: flex; gap: 12px; align-items: baseline; }
.facets fieldset label { display: inline; font-family: var(--mono); }
.facets .f-check { display: block; }
input, select { font: inherit; color: var(--fg); background: var(--field); border: 1px solid var(--line2); padding: 5px 7px; width: 100%; font-family: var(--mono); font-size: 0.85rem; }
input[type=checkbox] { width: auto; accent-color: var(--accent); }
.facets .keys { font-size: 0.84rem; line-height: 1.8; }
/* The fold (#1): on a wide screen it is not a box at all, its children are the grid's own. */
.f-fold { display: contents; }
.f-toggle { display: none; }

/* capture */
.cap-head .cap-tags { color: var(--dim); font-size: 0.85rem; }
.cap-meta { color: var(--dim); margin: .2rem 0 1rem; font-size: 0.9rem; }
.spec { margin: 0 0 12px; }
.spec-wrap { position: relative; background: var(--stage); border: 1px solid var(--line); }
.spec-wrap img { width: 100%; aspect-ratio: 1600/300; }
.spec-wrap #spec-img { cursor: pointer; }          /* the picture is a seek surface (W-0014) */
.playhead { position: absolute; top: 0; bottom: 0; left: 0; width: 2px; background: var(--amber-pale); opacity: 0; pointer-events: none; }
.playhead.live { opacity: .9; }
.loopshade { position: absolute; top: 0; bottom: 0; background: rgba(224,169,74,.18); border-left: 1px solid var(--amber); border-right: 1px solid var(--amber); pointer-events: none; }
.mark { position: absolute; top: 0; width: 0; height: 100%; border: 0; border-left: 2px solid var(--amber); background: none; padding: 0; margin-left: -1px; cursor: pointer; }
.mark::before { content: ""; position: absolute; top: 0; left: -6px; border: 6px solid transparent; border-top-color: var(--accent); }
.axis { display: flex; justify-content: space-between; color: var(--dim); font-size: 0.8rem; padding: 3px 0 0; }
.blocks { display: flex; height: 14px; margin-top: 4px; }
.blocks .blk { flex: 1; border: 0; border-right: 1px solid var(--bg); padding: 0; cursor: pointer; }
.blocks .blk:hover, .blocks .blk:focus-visible { outline: 1px solid var(--fg); outline-offset: -1px; z-index: 1; }
.spec figcaption { font-size: 0.84rem; margin-top: 6px; }
.cap-ctl { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; margin: 14px 0 22px; font-size: 0.85rem; }
.cap-ctl .dl { margin-left: auto; display: flex; gap: 8px; }
.facts { display: grid; grid-template-columns: 170px 1fr; gap: 12px 20px; margin: 0; font-size: 0.95rem; }
.facts dt { color: var(--dim); font-size: 0.8rem; padding-top: .25rem; }
.facts dd { margin: 0; overflow-wrap: anywhere; }
.facts.small { font-size: 0.88rem; grid-template-columns: 150px 1fr; }
.facts dd p { margin: 0 0 .4rem; }
.rignote { color: var(--dim); font-size: 0.88rem; }
.marks { list-style: none; padding: 0; margin: .3rem 0 0; }
.marks li { padding: 2px 0; border-bottom: 1px dashed var(--line); }
.related { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px; }
.related a { display: grid; grid-template-columns: 96px 1fr; gap: 2px 10px; align-items: center; color: var(--fg); font-size: 0.85rem; }
.related a img { grid-row: 1 / 3; width: 96px; height: 18px; border: 1px solid var(--line); }
.related a:hover { text-decoration: none; color: var(--accent); }
.prose { max-width: 72ch; }

/* stations / gallery / receivers / log */
.stationlist, .rxlist, .sessionlist, .gallery { list-style: none; padding: 0; margin: 0; }
.stationlist li { display: grid; grid-template-columns: 320px 1fr; gap: 18px; padding: 12px 0; border-bottom: 1px solid var(--line); align-items: center; font-size: 0.9rem; }
.stationlist img { width: 320px; height: 60px; border: 1px solid var(--line); }
.stationlist .name { font-family: var(--cond); text-transform: uppercase; letter-spacing: .1em; font-weight: 700; font-size: 1rem; color: var(--fg); }
/* min(): a 320 px floor is wider than a 320 px phone's column, and the page scrolled sideways (#1). */
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); gap: 12px; }
.gallery a { display: grid; gap: 4px; color: var(--dim); font-size: 0.84rem; }
.gallery img { width: 100%; aspect-ratio: 320/60; border: 1px solid var(--line); }
.rxlist li { padding: 10px 0; border-bottom: 1px solid var(--line); font-size: 0.9rem; }
.rxlist .name { font-weight: 600; }
.etiquette li { margin: .3rem 0; max-width: 72ch; }
/* A receiver on the rim of the map carries its label past the edge of the viewBox, and
   an <svg> does not clip by default — one station in New Zealand was widening the whole
   page. The map is a picture; it may be cropped, it may not push the page sideways. */
.mapwrap { position: relative; border: 1px solid var(--line); background: var(--sunken); margin: 14px 0 6px; overflow: hidden;
           --land: var(--line2); --land-back: var(--line); }
/* The land's two greys, mixed from tokens so both themes get theirs (W-0012). A browser
   without color-mix keeps the plain tokens above instead of losing the dots altogether. */
@supports (color: color-mix(in srgb, red 50%, blue)) {
  .mapwrap { --land: color-mix(in srgb, var(--faint) 55%, var(--line2));
             --land-back: color-mix(in srgb, var(--line2) 60%, var(--sunken)); }
}
.worldmap { width: 100%; height: auto; display: block; }
.worldmap .grat { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.worldmap .grat.eq { stroke: var(--line2); stroke-dasharray: 2 4; }
.worldmap .dots { fill: none; stroke: var(--land); stroke-width: .62; stroke-linecap: round; }
.worldmap .rx { fill: var(--amber); stroke: var(--sunken); stroke-width: 1; vector-effect: non-scaling-stroke; }
.worldmap .halo { fill: none; stroke: var(--amber); stroke-opacity: .35; stroke-width: 1; vector-effect: non-scaling-stroke;
                  animation: rx-pulse 3.2s ease-in-out infinite; }
.worldmap a:hover .halo, .worldmap a:focus .halo { stroke-opacity: 1; animation: none; }
@keyframes rx-pulse { 0%, 100% { stroke-opacity: .15; } 50% { stroke-opacity: .6; } }
/* The globe (static/js/globe.js) replaces the flat map on screen when it can run; the flat
   map stays the picture without JS and on paper. */
.mapwrap canvas.globe { display: none; width: 100%; aspect-ratio: 16 / 10; cursor: grab; touch-action: pan-y; }
.mapwrap.is-globe canvas.globe { display: block; }
.mapwrap.is-globe .worldmap { display: none; }
.mapwrap canvas.globe.drag { cursor: grabbing; }
.mapwrap .tip { position: absolute; pointer-events: none; display: none; transform: translate(12px, -50%);
                background: var(--panel); border: 1px solid var(--line2); color: var(--fg);
                font-family: var(--mono); font-size: 0.75rem; padding: 3px 7px; white-space: nowrap; }
.mapbar { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 14px; }
.mapbar button { font: inherit; font-family: var(--mono); font-size: 0.8rem; color: var(--accent); background: none;
                 border: 1px solid var(--line2); padding: 2px 10px; min-height: 32px; min-width: 36px; cursor: pointer; }
.mapbar button:hover { border-color: var(--accent); }
.sessionlist li { display: flex; gap: 14px; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--line); font-size: 0.88rem; flex-wrap: wrap; }
.sessionlist .thumbs { margin-left: auto; display: flex; gap: 3px; }
.sessionlist .thumbs img { width: 96px; height: 18px; border: 1px solid var(--line); }
.log h2 .dim, section h2 .dim { font-weight: normal; letter-spacing: 0; text-transform: none; }

/* player bar */
/* The bar is the last thing above the home indicator, so its height grows by that
   inset and the extra goes into the bottom padding — the controls stay where they were
   and nothing lands in the swipe strip. `--pl-*` are variables for the same reason the
   gutter is: the narrow layout below changes the numbers, not the insets. */
.player { --pl-x: 16px; --pl-y: 0px;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30; background: var(--panel); border-top: 1px solid var(--line2);
  height: calc(var(--player-h) + env(safe-area-inset-bottom, 0px));
  display: grid; grid-template-columns: auto auto minmax(120px, 240px) auto 1fr auto auto; gap: 12px; align-items: center;
  padding: var(--pl-y) calc(var(--pl-x) + env(safe-area-inset-right, 0px))
           calc(var(--pl-y) + env(safe-area-inset-bottom, 0px))
           calc(var(--pl-x) + env(safe-area-inset-left, 0px));
  font-size: 0.85rem; }
.player[hidden] { display: none; }
.p-title { color: var(--fg); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.p-time { color: var(--dim); white-space: nowrap; }
.p-seekwrap { position: relative; height: 22px; display: flex; align-items: center; }
.p-seek { -webkit-appearance: none; appearance: none; width: 100%; height: 4px; background: var(--line2); border: 0; padding: 0; margin: 0; cursor: pointer; }
.p-seek::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 16px; background: var(--amber); border: 0; border-radius: 1px; }
.p-seek::-moz-range-thumb { width: 12px; height: 16px; background: var(--amber); border: 0; border-radius: 1px; }
.p-loopmark { position: absolute; top: 3px; height: 16px; background: rgba(224,169,74,.25); border-left: 1px solid var(--amber); border-right: 1px solid var(--amber); pointer-events: none; }
.p-ctl { display: flex; gap: 6px; align-items: center; }
/* The disclosure exists only where the bar cannot hold everything at once. */
.p-more { display: none; }
.p-btn.small { padding: 1px 7px; font-size: 0.8rem; }
.p-btn.on { background: var(--amber); color: var(--on-accent); }
.p-rate { width: auto; padding: 2px 4px; font-size: 0.8rem; }

/* volume — prominent, and it does not start at 100 % */
.p-vol { display: flex; align-items: center; gap: 8px; padding: 0 10px; border-left: 1px solid var(--line2); border-right: 1px solid var(--line2); }
.p-volume { -webkit-appearance: none; appearance: none; width: 110px; height: 6px; border: 0; padding: 0; margin: 0; cursor: pointer;
  --vol: 35%; background: linear-gradient(to right, var(--amber) 0 var(--vol), var(--line2) var(--vol) 100%); }
.p-volume::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 18px; background: var(--amber); border: 1px solid var(--line2); border-radius: 1px; }
.p-volume::-moz-range-thumb { width: 14px; height: 18px; background: var(--amber); border: 1px solid var(--line2); border-radius: 1px; }
.p-volume:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
#p-volpct { min-width: 3.2em; text-align: right; font-size: 0.84rem; }

/* a laptop window: the list folds the mode under the frequency before anything scrolls (W-0013) */
@media (max-width: 1240px) { .c-f.c-fold { white-space: normal; } }

/* mobile */
@media (max-width: 900px) {
  .top { gap: 12px; padding: 10px 14px; flex-wrap: wrap; }
  .top nav { margin-left: 0; width: 100%; gap: 14px; }
  main { --gut: 14px; padding: 16px var(--gut-r) 40px var(--gut-l); }
  .home-top { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .hero { margin: -16px calc(-1 * var(--gut-r)) 14px calc(-1 * var(--gut-l)); }
  .archive { grid-template-columns: 1fr; }
  .facets { position: static; grid-template-columns: 1fr 1fr; }
  .facets .f-search, .facets .f-check, .facets p { grid-column: 1 / -1; }
  /* Folded by default on anything this narrow: the search, the button and the count, then
     the list. The button carries how many filters are set (table.js). */
  .f-toggle { display: inline-flex; align-items: center; justify-self: start; grid-column: 1 / -1; }
  .facets:not(.open) .f-fold { display: none; }
  .facets.open .f-toggle { border-color: var(--accent); color: var(--accent); }
  table.captures .c-rx, table.captures .c-th, table.captures th:nth-child(8), table.captures th:nth-child(9) { display: none; }
  table.captures .c-tier, table.captures th:nth-child(5) { display: none; }
  .facts { grid-template-columns: 1fr; gap: 4px; }
  .facts dt { margin-top: 10px; }
  .stationlist li { grid-template-columns: 1fr; }
  .stationlist img { width: 100%; height: auto; }
  /* THE BAR ON A PHONE. Nine controls do not fit across 390px, and the answer is not to
     make them smaller — it is to decide which four you reach for while listening and put
     the rest one tap away. What stays out: play, what it is, where you are in it, and
     stop. What moves into the drawer: the level, the loop, the speed and the download —
     the listening-room controls, which you set once and then leave alone.
       row 1  ▶ · title · ⋯ · stop
       row 2  seek · time
       row 3  the level          } only when the drawer is open
       row 4  loop, speed, download */
  .player { --pl-x: 12px; --pl-y: 6px; --player-h: 84px;
    grid-template-columns: auto 1fr auto auto; grid-template-rows: auto auto; height: auto; row-gap: 4px; }
  #p-play { grid-column: 1; grid-row: 1; }
  .p-title { grid-column: 2; grid-row: 1; min-width: 0; }
  .p-more { display: inline-flex; grid-column: 3; grid-row: 1; }
  #p-close { grid-column: 4; grid-row: 1; }
  .p-seekwrap { grid-column: 1 / 4; grid-row: 2; }
  .p-time { grid-column: 4; grid-row: 2; display: block; font-size: 0.8rem; text-align: right; }
  /* The drawer. Closed is the default, so a phone that never opens it sees four controls
     and nothing else; `hidden` on #p-clear still wins, because a loop you have not set
     has no clear button whether the drawer is open or not. */
  .p-vol, .p-ctl { display: none; }
  .player.open .p-vol { display: flex; grid-column: 1 / -1; grid-row: 3;
    padding: 4px 0 0; border-left: 0; border-right: 0; }
  .player.open .p-ctl { display: flex; grid-column: 1 / -1; grid-row: 4;
    flex-wrap: wrap; gap: 8px; padding-bottom: 2px; }
  .player.open .p-more { background: var(--amber); color: var(--on-accent); }
  .p-volume { flex: 1; width: auto; min-width: 0; } #p-volpct { display: inline; min-width: 3.4em; }
  body { padding-bottom: calc(100px + env(safe-area-inset-bottom, 0px)); }
}

/* A phone, held upright. The 900 px rule above already drops the receiver, the thumbnail
   and the tier; on a 390 px screen the four columns that are left still do not fit,
   because UTC, frequency and length are all `white-space: nowrap` by design — a reception
   is read across, not down — and together they claim the whole width before the station
   name gets any. Two more columns go: the content label and the length, both of which the
   capture page carries in full. What stays is the four things that identify a reception
   you are scrolling past: play it, when, what, where on the dial. */
@media (max-width: 640px) {
  table.captures { font-size: 0.85rem; }
  table.captures th, table.captures td { padding: 7px 5px; }
  table.captures .c-c, table.captures th:nth-child(6) { display: none; }
  table.captures .c-d, table.captures th:nth-child(7) { display: none; }
  /* The four that are left are still 27 px too wide for a 390 px screen, because a
     table cannot be narrower than its own nowrap content. So the rule at the top of this
     file — a reception is read ACROSS, not down — is suspended for the two columns that
     read the same broken over two lines: the date above the minute, the frequency above
     the mode. It is suspended only here; on anything wider the columns still line up. */
  table.captures .c-utc, table.captures .c-f { white-space: normal; }
  /* the log's four figures, two by two (#22) */
  .home-figs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .home-figs dd { font-size: 1.35rem; }
  .hero-line { gap: 12px; padding: 10px 14px; }
  /* The transport bar used to drop the level and the speed here, because nine controls
     do not fit across 390 px. Neither is dropped any more: the 900 px rule above puts
     both in a drawer behind ⋯, so the bar carries four controls and everything is still
     one tap away. The level in particular had to come back — it was hidden partly
     because iOS ignores `volume`, and ui/volume.js now drives the gain node there
     instead, so the slider does something on every phone that can read the media. */
  /* A fieldset of content labels is a single unwrapped flex row, which is the widest
     thing on the archive page and the reason it scrolls sideways. */
  .facets fieldset { flex-wrap: wrap; row-gap: 4px; }
  /* THE HEADER ON A PHONE (#1). Wrapped, it was four rows — the brand, five links, `about`
     alone, the theme switch — 190 px of an 844 px screen, and sticky, so it stayed there
     while the list scrolled under it, above the player bar's 90. Two rows now: the brand
     with the theme switch beside it, and the six links on one line that scrolls sideways
     rather than wrapping. And it scrolls away with the page: on a phone the way back to
     the nav is the flick up that every browser answers by showing its own bar too. */
  .top { position: static; align-items: center; row-gap: 8px; }
  .top .brand { gap: 10px; }
  .top .artist { letter-spacing: .16em; font-size: .92rem; }
  .top .logname { letter-spacing: .18em; font-size: .74rem; }
  .top .theme { order: 2; margin-left: auto; }
  .top nav { order: 3; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
             margin: 0 calc(-1 * var(--pad-x, 14px)); padding: 0 var(--pad-x, 14px); width: auto;
             flex: 1 0 100%; gap: 14px; letter-spacing: .1em; }
  .top nav::-webkit-scrollbar { display: none; }
  .top .theme button { padding: 4px 7px; }
}

/* A phone on its side: wide enough for every rule above to stand down, 390 px tall. A
   sticky header there is a third of the screen. */
@media (max-height: 500px) { .top { position: static; } }

/* An iPhone SE: the brand and the theme switch are 35 px too wide for one row, and the
   switch fell onto a third. The brand's two words stack instead. */
@media (max-width: 380px) { .top .brand { flex-direction: column; gap: 2px; } }

/* Touched, not clicked. The shared rules in ui/base.css give every <button> a 44 px box;
   these are the site's own controls, which are not buttons or need more than height. */
@media (pointer: coarse) {
  .p-btn, a.p-btn { display: inline-flex; align-items: center; justify-content: center;
                    min-height: 44px; min-width: 40px; }
  .p-btn.small { padding: 1px 10px; }
  /* A 4 px seek bar cannot be grabbed with a thumb: the track stays thin, the reachable
     area does not. */
  .p-seekwrap { height: 40px; }
  .p-seek { height: 22px; background: transparent;
            background-image: linear-gradient(var(--line2), var(--line2));
            background-size: 100% 4px; background-position: center; background-repeat: no-repeat; }
  .p-seek::-webkit-slider-thumb { width: 16px; height: 22px; }
  .p-seek::-moz-range-thumb { width: 16px; height: 22px; }
  .p-loopmark { top: 9px; }
  /* An analysis block is a tap target on the capture page, not only a hover surface. */
  .blocks .blk { min-height: 34px; }
  /* Keyboard shortcuts, told to a thumb. */
  .facets .keys { display: none; }
}

@media (prefers-reduced-motion: no-preference) { .playhead { transition: left .25s linear; } }

/* peaks strip under the spectrogram (drawn from index.json) */
.peaks { display: block; width: 100%; height: 26px; background: var(--stage); border: 1px solid var(--line); border-top: 0; }
.btn.mono { letter-spacing: -.05em; }

/* ---- the record on the site ----------------------------------------------------------
   The log explains the record and the record explains the log: every track says which
   receptions it is made of, and each one links into the archive. */
.rel-head { display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 8px; }
.rel-cover { width: 260px; height: 260px; object-fit: cover; border: 1px solid var(--line2); }
.rel-goal { max-width: 62ch; }
.rel-tracks { list-style: none; padding: 0; margin: 0; counter-reset: trk; }
/* Only the tracks themselves, never the credit lines nested under each (#21). */
.rel-tracks > li { border-bottom: 1px solid var(--line); padding: 10px 0 10px 34px; position: relative; }
.rel-tracks > li::before { counter-increment: trk; content: counter(trk, decimal-leading-zero);
  position: absolute; left: 0; top: 12px; font-family: var(--mono); font-size: .8em; color: var(--faint); }
.rel-tracks p { margin: 4px 0 0; max-width: 70ch; font-size: 0.9rem; }
.rel-caps { font-size: 0.82rem; }
.rel-video { display: block; width: 100%; max-width: 960px; aspect-ratio: 16 / 9; margin: 8px 0 4px; background: var(--sunken); }
.rel-caps a { word-break: break-all; }
ul.rel-caps { list-style: none; padding: 0; margin: 4px 0 0; }
/* the release downloads (#14): three rows, the format first, the facts quiet; wraps on a phone */
.rel-dl ul { list-style: none; padding: 0; margin: 0; }
.rel-dl li { display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline; border-bottom: 1px solid var(--line); padding: 8px 0; }
.rel-dl li a { min-width: 6.5em; }
.rel-dl .sha { font-size: 0.75rem; }
.rel-dl p { max-width: 70ch; font-size: 0.9rem; }
.rel-credits { white-space: pre-wrap; font-size: 0.82rem; color: var(--dim); background: var(--panel);
  border: 1px solid var(--line); border-radius: 5px; padding: 12px 14px; max-height: 420px; overflow: auto; }
.rel-card { display: flex; gap: 18px; align-items: flex-start; border-bottom: 1px solid var(--line);
  padding: 14px 0; }
.rel-card img { width: 120px; height: 120px; object-fit: cover; border: 1px solid var(--line2); }
@media (max-width: 640px) { .rel-head, .rel-card { flex-direction: column; }
  .rel-cover { width: 100%; height: auto; } }

/* A partial build (--limit / --no-media) is not the site, and may not be mistaken for it. */
.partialwarn { background: var(--alarm); color: var(--on-alarm); padding: 8px 14px; font-size: 13px;
  line-height: 1.4; position: sticky; top: 0; z-index: 30; }
.partialwarn code { color: var(--on-alarm); }

/* The receivers map (W-0012): a squarer globe on a phone, no pulse under reduced motion,
   and on paper the flat map, never a frozen frame of the globe. */
@media (max-width: 600px) { .mapwrap canvas.globe { aspect-ratio: 1 / 1; } }
@media (prefers-reduced-motion: reduce) { .worldmap .halo { animation: none; } }
@media print {
  .mapwrap.is-globe .worldmap { display: block; }
  .mapwrap canvas.globe, .mapwrap .tip, .mapbar { display: none !important; }
}
