/* web/v2in.css: every page (/, /admin, /live, /ui, /logo, /social), in positron's kit (~/personal/positron
   demo/shell/shell.css) with VÄIN's green. What positron does, kept: its blue-black grounds, two lines and two dims,
   1px edges on every control and surface, the 4px radius, 34px controls (26 small), mono capitals for what names a
   thing (buttons, labels, tags, table heads, the nav), a filled primary with dark text, a 1px focus ring in the accent,
   and glue: cards in a list share one 1px seam, only the outer corners rounded. What differs: the accent is VÄIN's
   green in place of positron's yellow, and the faces are Libre Franklin (text) and Ubuntu Sans Mono (mono). Every
   editable field's text stays 16px, since iOS Safari zooms into a focused field under 16. */
:root {
  color-scheme: dark;
  --bg: #0b0e14;
  --fg: #e6e6e6;
  --muted: #8b93a1;        /* positron --dim: any text that must be read */
  --faint: #6a7280;        /* positron --dim2: labels, outlines, decoration */
  --line: #1f2937;
  --line2: #2b3546;        /* the stronger line: control edges */
  --surface: #131821;      /* the one near black above the ground: cards, buttons, alerts, the slides */
  --card: var(--surface);
  --field: var(--bg);      /* a field is a well on the page ground */
  --accent: #42b649;       /* the logo's green, also the app's Material accent */
  --alert-bg: var(--surface);
  --live-bg: #11241a;
  --error: #e0908a;        /* positron --bad: errors, refusals and destructive buttons */
  --radius: 4px;
  --edge: 1px solid var(--line);
  --ctl: 34px;
  --ctl-sm: 26px;
  --gap: 16px;
  --gap-cards: 24px;       /* between separate cards (the player's channels, /admin's, /media's folders): half again the space inside one */
  /* Libre Franklin (Impallari Type, variable 100 to 900, full Cyrillic, OFL) and Ubuntu Sans Mono (Ubuntu Font
     Licence), both in web/vendor and linked by each page */
  --font: "Libre Franklin", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "Ubuntu Sans Mono", ui-monospace, "SF Mono", Menlo, monospace;
  font: 16px/1.5 var(--font);
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }
body { margin: 0; background: var(--bg); color: var(--fg); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; }   /* light text on dark: grayscale smoothing keeps it from looking bold */
main { max-width: 760px; margin: 0 auto; padding: 32px var(--gap) 64px; }
header.brand { display: flex; gap: 14px; align-items: center; margin-bottom: 8px; }
header.brand h1 { margin: 0; }
/* the player's header: the white wordmark, 48 high, a link home; the h1 named by its alt */
header.brand.logo { margin-bottom: 32px; }
header.brand.logo a { display: block; line-height: 0; }
header.brand.logo img { display: block; width: auto; height: 48px; }
/* type: three sizes for content, nothing between: 20 section headings; 16 body, which is also every item's title (a
   file, a song, a channel, a key) and what plays; 14 the quiet line under it. The page title 24/700 above them */
h1 { font-size: 24px; font-weight: 700; line-height: 1.2; margin: 0 0 8px; }
h2 { font-size: 20px; font-weight: 500; line-height: 1.3; margin: 40px 0 12px; }
h3 { font-size: 16px; font-weight: 600; line-height: 1.5; margin: 24px 0 12px; }   /* a part of a section */
p { margin: 0 0 12px; }
.lead { color: var(--muted); margin-bottom: 24px; }
.muted { color: var(--muted); }
small, .small { font-size: 14px; }
a { color: var(--accent); border-radius: 2px; }
/* focus: a 1px ring in the accent, 2px outside; a field colours its own edge instead */
:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; }
input:focus, select:focus, textarea:focus, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: none; border-color: var(--accent); }

button, input, select, textarea { font: inherit; color: inherit; border-radius: var(--radius); }
/* buttons: the surface, a 1px --line2 edge, 34px, mono 13/500 in capitals; the primary filled green with dark text */
button {
  display: inline-flex; gap: 8px; align-items: center; justify-content: center;
  height: var(--ctl); padding: 0 16px; border: 1px solid var(--line2); background: var(--surface);
  color: var(--fg); font: 500 13px/1 var(--mono); text-transform: uppercase; letter-spacing: 0.02em; cursor: pointer; white-space: nowrap;
}
button.primary {
  color: var(--bg); background: var(--accent); border-color: var(--accent); font-weight: 700;
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--accent) 40%, transparent), 0 4px 18px -4px color-mix(in oklab, var(--accent) 55%, transparent);
}
button.quiet { background: transparent; }
button.danger { color: var(--error); border-color: color-mix(in oklab, var(--error) 45%, var(--line2)); }   /* destructive: kick, remove, clear, a raid on */
button.sm { height: var(--ctl-sm); padding: 0 10px; font-size: 12px; }   /* an action inside a row or a card */
button.icon { width: var(--ctl); padding: 0; }
button.icon.sm { width: var(--ctl-sm); }   /* an icon button is square at either height */
/* hover a little lighter with a brighter edge, pressed darker; the green primary lighter and darker */
button:not(:disabled):hover { background: color-mix(in oklab, var(--surface), var(--fg) 9%); border-color: var(--muted); }
button:not(:disabled):active { background: var(--bg); border-color: var(--faint); }
button.primary:not(:disabled):hover { background: color-mix(in oklab, var(--accent), #fff 14%); border-color: color-mix(in oklab, var(--accent), #fff 14%); }
button.primary:not(:disabled):active { background: color-mix(in oklab, var(--accent), #000 18%); border-color: color-mix(in oklab, var(--accent), #000 18%); }
button.danger:not(:disabled):hover { background: color-mix(in oklab, var(--surface), var(--error) 12%); border-color: var(--error); }
button:disabled { opacity: 0.4; box-shadow: none; cursor: default; }
/* working: positron's sweep, a band of light across the button's ground, set straight (90deg); the band is the ground
   itself lightened (--ground: the surface, the green on the primary), never the ink or the icon's colour, so a green
   play icon does not tint it; after the hover so a hover's background does not drop it */
button { --ground: var(--surface); }
button.primary { --ground: var(--accent); }
button[data-busy="1"], button[data-busy="1"]:hover {
  cursor: progress;
  background-color: var(--ground);
  background-image: linear-gradient(90deg, transparent 0%, transparent 35%, color-mix(in oklab, var(--ground), #fff 16%) 50%, transparent 65%, transparent 100%);
  background-size: 220% 100%; background-repeat: no-repeat;
  animation: busy-sweep 1.1s linear infinite;
}
@keyframes busy-sweep { from { background-position: 160% 0; } to { background-position: -60% 0; } }
@media (prefers-reduced-motion: reduce) { button[data-busy="1"] { animation: none; opacity: 0.62; } }

/* icons: Feather (web/vendor/icons/feather/icons.svg, tools/icons.mjs), round ends; two sizes, 16 in buttons and
   text, 24 in the play button and the icon sheet */
.ico { display: inline-block; flex: none; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
button > .ico { margin-inline-start: -2px; }
button.icon > .ico { margin: 0; }

/* fields: a well on the page ground, a 1px --line2 edge, 34px, --faint under the pointer, green on focus (above);
   checkboxes, radios, sliders and the drop zone's file input are left out */
input, select { accent-color: var(--accent); width: 100%; }
input:where(:not([type=checkbox], [type=radio], [type=range], [type=file])), select, textarea {
  background-color: var(--field); border: 1px solid var(--line2); min-height: var(--ctl); padding: 0 10px;
}
input:where(:not([type=checkbox], [type=radio], [type=range], [type=file])) { height: var(--ctl); }
input:where(:not([type=checkbox], [type=radio], [type=range], [type=file])):hover:not(:focus, :disabled), select:hover:not(:focus, :disabled), textarea:hover:not(:focus, :disabled) { border-color: var(--faint); }
/* every editable field's text 16 px whatever its label's size (iOS Safari zooms into a focused field under 16);
   :where keeps it as weak as a bare `input`, so a field with a size of its own keeps it */
input:where(:not([type=checkbox], [type=radio], [type=range])), select, textarea { font-size: 16px; font-weight: 400; line-height: 1.3; }
/* numbers, times and dates in digits of one width, so a value does not shift as it changes */
input[type=number], input[type=time], input[type=datetime-local], .log td { font-variant-numeric: tabular-nums; }
::placeholder { color: var(--muted); opacity: 1; }
input[type=checkbox], input[type=radio] { width: auto; min-height: 0; border: 0; padding: 0; }
input:disabled, select:disabled, textarea:disabled { color: var(--faint); border-color: var(--line); cursor: default; }
select {
  /* the caret: Feather's chevron-down, 16px, 10px in from the right */
  appearance: none; -webkit-appearance: none; padding-right: 32px; cursor: pointer;
  background: var(--field) no-repeat right 10px center / 16px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23e6e6e6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
select:disabled { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236a7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); }
select option { background: var(--bg); }
/* sliders: a 34 px lane with the controls' 1px edge and radius, a solid green handle that stops against it at both
   ends; label.sld puts the name left and the value right above it */
input[type=range] {
  -webkit-appearance: none; appearance: none; height: var(--ctl); min-height: var(--ctl); margin: 0; padding: 0;
  background: var(--field); border: 1px solid var(--line2); cursor: pointer;
}
input[type=range]::-webkit-slider-runnable-track { height: 32px; background: transparent; }
input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 32px; margin-top: 0; border-radius: 3px; background: var(--accent); }
input[type=range]::-moz-range-track { height: 32px; background: transparent; }
input[type=range]::-moz-range-thumb { width: 16px; height: 32px; border: 0; border-radius: 3px; background: var(--accent); }
label.sld { display: grid; grid-template-columns: 1fr auto; gap: 7px 12px; align-items: baseline; }
label.sld > output { color: var(--fg); font-variant-numeric: tabular-nums; text-align: right; }   /* the value at the lane's right edge */
label.sld > input { grid-column: 1 / -1; margin-top: 0; }
/* labels as positron's field labels: mono capitals, small, the faint ink, 7 above the field */
label { display: block; font: 500 11px/1.4 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); }
label > input, label > select { margin-top: 7px; color: var(--fg); font-family: var(--font); letter-spacing: 0; text-transform: none; }
.acts { display: flex; gap: 8px; flex-wrap: wrap; }

.banner { background: var(--surface); border: 1px solid var(--line2); color: var(--fg); border-radius: var(--radius); padding: 12px 16px; margin-bottom: 24px; }
/* tags: positron's pill, mono capitals in a 1px edge, no fill */
.tag {
  display: inline-block; font: 500 10.5px/1.5 var(--mono); letter-spacing: 0.07em; text-transform: uppercase;
  padding: 1px 7px; border: 1px solid var(--line2); border-radius: 999px; vertical-align: 1px;
}
.tag.live { color: var(--accent); border-color: color-mix(in oklab, var(--accent) 50%, var(--line)); }
.tag.silent { color: var(--fg); border-color: var(--faint); }
.tag.plain { color: var(--faint); }
.tag.err { color: var(--error); border-color: color-mix(in oklab, var(--error) 45%, var(--line)); }

/* surfaces: the card ground, a 1px edge, 4px corners */
.ch, .item, .secret, details.streams .def { background: var(--card); border: var(--edge); border-radius: var(--radius); }
/* glue (positron's .pos-glue): a list's cards are one surface, the seam a 1px gap over the --line ground, one edge
   round the lot, only its outer corners rounded; each card gives up its own edge and radius */
.list { display: grid; gap: 1px; background: var(--line); border: var(--edge); border-radius: var(--radius); overflow: clip; }
.list > * { border: 0; border-radius: 0; margin: 0; }
/* the player's channels are not glued: each its own card, edge and corners, 16 apart */
.channels { display: grid; gap: var(--gap-cards); }
.list:empty { display: none; }

/* the player */
/* 16 on every side of a closed card: the name and the line under it are 22 each, together the 44 of the play, with no
   gap between, so the play sits 16 from the top, the left and the bottom alike */
.ch { padding: var(--gap); display: grid; grid-template-columns: auto 1fr; gap: 0 var(--gap); align-items: center; }
/* the play: a square 44px control, the icon 24; it carries both icons in one cell, the one not in use faded out, so
   play and stop crossfade. While tuning in it takes the working sweep (data-busy, above) and a press still stops */
.ch .play { grid-row: span 2; width: 44px; height: 44px; padding: 0; color: var(--accent); display: grid; place-items: center; }
.ch .play .ico, button.play .ico { width: 24px; height: 24px; margin: 0; }
.ch .play .ico { grid-area: 1 / 1; transition: opacity 0.15s ease; }
.ch .play:not(.stop) .ico-stop, .ch .play.stop .ico-play { opacity: 0; }
/* the triangle's weight sits left of its box: nudged right so it looks centred in an icon-only button */
button.play .ico-play, button.icon .ico-play { transform: translateX(8%); }
.ch .name { font-weight: 500; font-size: 16px; line-height: 22px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.ch .name > span:first-child { margin-right: auto; }   /* the name left, the badges at the card's right edge */
.ch .now { color: var(--muted); min-width: 0; overflow-wrap: anywhere; font-size: 16px; line-height: 22px; }
/* a date taken out of a file's name: after it, stepping back by size and face (mono 14), not by a fainter grey */
.ch .now .date { font: 400 14px/22px var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; margin-left: 4px; }
/* the waveform's row: closed (0fr) while stopped or tuning in, open once sound plays, 250 ms either way; a closed row
   adds nothing, the 16 inside it puts the wave as far below the text as the card's edge; the
   negative inline margin runs the wave edge to edge across the card */
.ch .wave { grid-column: 1 / -1; display: grid; grid-template-rows: 0fr; margin: 0 calc(-1 * var(--gap)); transition: grid-template-rows 0.25s ease; }
.ch.open .wave { grid-template-rows: 1fr; }
.ch .wave > div { min-height: 0; overflow: hidden; }
.ch canvas { width: 100%; height: 56px; display: block; margin-top: var(--gap); }
@media (prefers-reduced-motion: reduce) { .ch .wave, .ch .play .ico { transition: none; } }

/* the admin page */
/* the layout, two patterns only: a section the full width, or two sections side by side, half each, one under the
   other under 768. At most 1200 wide, 24 gutters (16 on a phone). Paired sections keep their own heights */
main.admin { max-width: 1200px; padding: 32px 24px 64px; }
main.admin > header.brand { margin-bottom: 24px; }   /* the title a step clear of the station bar under it */
.admin-full { width: 100%; min-width: 0; }
.admin-halves { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; align-items: start; }
@media (max-width: 767px) {
  main.admin { padding-inline: 16px; }
  .admin-halves { grid-template-columns: minmax(0, 1fr); row-gap: 40px; }
}
.admin-sec { min-width: 0; container-type: inline-size; }   /* its tables and pairs fit the width it is given */
#app > .admin-full, #app > .admin-halves { margin-top: 40px; }
.admin-sec > h2:first-child { margin-top: 0; }
.admin-sec > h2 + h3 { margin-top: 0; }
/* a row of controls: 8 between neighbours, 12 between lines when it wraps */
.row { display: flex; gap: 12px 8px; flex-wrap: wrap; align-items: end; }
.row.mid { align-items: center; }
.row > .grow { flex: 1 1 220px; }
/* a row of a field and its buttons: 16 between the two groups, 8 between the buttons (in .acts) */
.row.split { column-gap: 16px; }
.pair + .row { margin-top: 16px; }
.form > .row:last-child { margin-top: 4px; }
/* two fields that belong together, half each of the width they are given; .fold puts them one under the other when
   a half would be under 240, as a datetime or a long choice needs that much */
.pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: end; }
.pair.fold { grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); }
.form { display: grid; gap: 12px; }
/* a number with its unit after it and the form's button at the end, under a label of its own */
.field-line { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-top: 7px; }
.field-line > input { width: 96px; flex: none; }
.field-line > .unit { color: var(--muted); }
.field-line > button { margin-left: 4px; }
.key { flex: 1 1 auto; overflow-wrap: anywhere; }
.mt { margin-top: 12px; }
.mt-lg { margin-top: 40px; }
.grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
/* a card: rows breathe, lines inside a row stay close, actions stand off */
.item { padding: 16px; display: grid; gap: 6px; }
.item .head { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.item .head b, .file .head b, .switch b { font-size: 16px; font-weight: 500; line-height: 1.5; }
.item .head .muted, .file .head .muted { font-size: 16px; font-weight: 400; }
.item .now { font-size: 16px; line-height: 1.5; }
.item .meta, .file .meta, .item .small, .file .small { font-size: 14px; line-height: 1.5; color: var(--muted); overflow-wrap: anywhere; }
.item .meta code, .item .meta .mono { font-size: 14px; }
.item .actions, .file .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.item .actions:empty { display: none; }
/* the on air and health cards: the name left and the tag at the right edge, so every tag sits in one place */
.item.air { gap: 4px; }
.item.air .head .tag, .item.keycard .head .tag { margin-left: auto; align-self: center; vertical-align: 0; }
@container (min-width: 600px) { .item.keycard .actions { justify-content: flex-end; } }
.secret { padding: 20px; display: grid; gap: 12px; }
.secret > * { min-width: 0; }
code, pre, .mono { font-family: var(--mono); font-size: 14px; }
/* the OBS and BUTT settings wrap on a phone rather than scroll sideways */
pre { margin: 0; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
/* tables: body 16, the head in mono capitals over a --line2 rule, rows parted by 1px --line; ids, channels and
   addresses in mono (td.mono) */
.log { width: 100%; border-collapse: collapse; font-size: 16px; }
.log td, .log th { text-align: left; padding: 8px 12px 8px 0; border-bottom: var(--edge); vertical-align: top; white-space: nowrap; }
.log th { font: 500 11px/1.4 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); border-bottom-color: var(--line2); }
.log td.mono { font-size: 14px; }
.log .no { color: var(--error); }
.scroll { overflow-x: auto; }
/* a table in a section under 480 wide: each row a stack of label and value, the labels from data-label */
@container (max-width: 479px) {
  .log.stack thead { display: none; }
  .log.stack tr { display: grid; padding: 8px 0; border-bottom: var(--edge); }
  .log.stack td { display: grid; grid-template-columns: minmax(0, 8em) minmax(0, 1fr); gap: 12px; padding: 2px 0; border: 0; white-space: normal; overflow-wrap: anywhere; }
  .log.stack td[data-label]::before { content: attr(data-label); color: var(--faint); font: 500 11px/2 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; }
  .log.stack td:not([data-label]) { display: block; }
}
/* the listeners: the last 30 days' peaks as bars, today on the right, scaled to the channel's own highest */
.spark { display: block; width: 120px; height: 24px; }
.spark rect { fill: var(--accent); }
.spark rect.none { fill: var(--line2); }
.err { color: var(--error); }
.err.slot { min-height: 1.5em; margin: -16px 0 8px; }   /* an error line that keeps its place empty, so its message moves nothing */
.ok { color: var(--accent); font-weight: 500; }
.switch { display: flex; gap: 12px; align-items: center; justify-content: space-between; }

/* the go live page */
.meter { height: 12px; background: var(--surface); border: var(--edge); border-radius: var(--radius); overflow: hidden; margin-bottom: 8px; }
.meter > div { height: 100%; width: 0; background: var(--accent); transition: width 60ms linear; }
.meter > div.hot { background: var(--fg); }

/* the in-page nav: grey links in mono capitals, the one in use white with the green line under it */
.jump { display: flex; flex-wrap: wrap; gap: 6px 22px; margin: -8px 0 8px; font: 500 12px/1.4 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; }
.jump a { color: var(--muted); text-decoration: none; padding: 2px 6px; margin: -2px -6px; }   /* room inside the ring, the same place on the page */
.jump a:hover { color: var(--fg); }
.jump a[aria-current="true"], .jump a[aria-pressed="true"] { color: var(--fg); text-decoration: underline; text-decoration-color: var(--accent); text-decoration-thickness: 2px; text-underline-offset: 7px; }

/* the channels as text: the textarea alone, a field like any other, mono 16 (iOS zooms under 16) */
.editor { margin-bottom: 8px; }
.editor textarea {
  display: block; width: 100%; min-width: 0; height: 440px; margin: 0; padding: 10px 12px; resize: vertical;
  font-family: var(--mono); font-size: 16px; line-height: 22px; color: var(--fg); white-space: pre; overflow: auto; tab-size: 2;
}
/* the problem lines, glued: one box, a seam between lines */
.checks { display: grid; gap: 4px; margin-bottom: 12px; min-height: 20px; }
.checks:has(> .line) { gap: 1px; background: var(--line); border: var(--edge); border-radius: var(--radius); overflow: clip; }
.checks .line { display: block; text-align: left; line-height: 1.5; text-decoration: none; background: var(--surface); color: var(--fg); padding: 8px 12px; }
a.line:hover { color: var(--fg); background: var(--line); }
.checks .line b { margin-right: 6px; }
details.streams { margin-top: 16px; }
details.streams summary { cursor: pointer; color: var(--muted); }
details.streams .def { padding: 12px; margin-bottom: 12px; }

/* music folders: separate cards, not glued, a chevron at the right turning when the folder opens; the files inside
   run edge to edge of the card, glued by a 1px seam above each; list entries a step down (14, the line under 14) */
.list:has(> details.folder) { gap: var(--gap); background: none; border: 0; border-radius: 0; overflow: visible; }
.list > details.folder { border: var(--edge); border-radius: var(--radius); background: var(--card); }
details.folder > summary { cursor: pointer; display: grid; gap: 2px; list-style: none; position: relative; padding-right: 32px; }
details.folder > summary::-webkit-details-marker { display: none; }
details.folder > summary::after {
  content: ""; position: absolute; right: 0; top: 4px; width: 16px; height: 16px; background: var(--muted); transition: transform 0.15s;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") center / contain no-repeat;
}
details.folder[open] > summary::after { transform: rotate(180deg); }
details.folder > summary:hover::after { background: var(--fg); }
details.folder[open] > summary { padding-bottom: 16px; }
details.folder > summary .head .muted, details.folder > summary .meta { font-size: 14px; }
.files { display: grid; }
.file { display: grid; gap: 2px; padding: 16px 0; }
.file + .file { border-top: var(--edge); }
.file .head b { overflow-wrap: anywhere; }
.file .tags { margin-top: 4px; }
details.folder > .files { margin: 0 -16px; }
details.folder .file, details.folder > .files > p { padding: 14px 16px 16px; border-top: var(--edge); margin: 0; }
details.folder .file .head b, details.folder .file .head .muted { font-size: 14px; line-height: 1.5; }
details.folder .file .meta, details.folder .file .meta code, details.folder .file .meta .mono { font-size: 13px; }
details.folder > .upload { margin: 0 -16px; padding: 16px 16px 0; border-top: var(--edge); }
.upload { padding-top: 12px; display: grid; gap: 12px; }
/* a music folder's drop zone, after eccm's: the file input stretched invisible over the whole box, so a drop or a
   click lands on it with no drag code; the dashed edge turns green on hover, on focus and while files are over it.
   It is a label too: it keeps the text face */
.drop-zone {
  position: relative; display: grid; place-content: center; gap: 4px; min-height: 120px; padding: 16px; text-align: center;
  border: 1px dashed var(--line2); border-radius: var(--radius); cursor: pointer;
}
label.drop-zone { font: 400 16px/1.5 var(--font); letter-spacing: 0; text-transform: none; color: var(--fg); }
.drop-zone:hover, .drop-zone.over, .drop-zone:has(input:focus-visible) { border-color: var(--accent); border-style: solid; }
.drop-zone b { font-weight: 500; }
.drop-zone .hint { font-size: 14px; color: var(--muted); }
.drop-zone .picked { font-size: 14px; overflow-wrap: anywhere; }
.drop-zone .picked:empty { display: none; }
.drop-zone input { position: absolute; inset: 0; width: 100%; height: 100%; min-height: 0; margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer; }
.health .meta { overflow-wrap: anywhere; }
/* the admin page's station bar: one line on the surface, health's tag first, the check at the right edge; it wraps on
   a phone, and a failed check's line goes under it the full width */
header.brand > button { margin-left: auto; }   /* Sign out, at the header's right edge */
/* the station on /admin: a glued list, one status a line: a mono label, the status cut to one line, its tag or button
   at the right; the same three columns on a phone, the label narrower */
.stats { margin-bottom: var(--gap); }
.stat { display: grid; grid-template-columns: 9rem minmax(0, 1fr) auto; gap: 12px; align-items: center; min-height: 44px; padding: 6px var(--gap); background: var(--card); }
.stat .k { font: 500 12px/1.4 var(--mono); text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stat .v { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 520px) { .stat { grid-template-columns: 6.5rem minmax(0, 1fr) auto; } }
/* the admin page's channel cards: side by side, as many 280 wide as fit, one column on a phone */
.channels.tiles { grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); align-items: start; }

/* /media (web/media/index.html): every audio file on the server, a card per folder, 16 apart as the player's; inside,
   the files glued by a 1px seam, one line each: the small play (26, the icon 16), the title in 14, then duration, size
   and date in mono 13, each in a column of its own width so the rows line up; under 520 the figures go under the title */
.mf-list { display: grid; gap: var(--gap-cards); }
.mf { background: var(--card); border: var(--edge); border-radius: var(--radius); overflow: clip; container-type: inline-size; }
.mf-head { display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline; padding: 12px var(--gap); }
.mf-head b { font-size: 16px; font-weight: 500; line-height: 1.5; }
.mf-head .sum { font: 400 13px/1.5 var(--mono); color: var(--muted); font-variant-numeric: tabular-nums; }
.mf-head .mf-add { margin-left: auto; align-self: center; }   /* + Add at the head's right end */
.mf-drop { padding: 0 var(--gap) var(--gap); }
.mf-head .tags { display: flex; flex-wrap: wrap; gap: 6px; margin-left: auto; align-self: center; }
.mf-empty { margin: 0; padding: 12px var(--gap); border-top: var(--edge); font-size: 14px; color: var(--muted); }
.mf-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; gap: 0 12px; align-items: center; padding: 6px var(--gap); border-top: var(--edge); }
button.mf-play { width: var(--ctl-sm); color: var(--accent); display: grid; place-items: center; }
button.mf-play .ico { grid-area: 1 / 1; transition: opacity 0.15s ease; }
button.mf-play:not(.stop) .ico-stop, button.mf-play.stop .ico-play { opacity: 0; }
.mf-title { font-size: 14px; line-height: 1.5; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mf-title .tag { vertical-align: 1px; margin-left: 6px; }
/* a file that cannot be read: the row in the second ink, the reason in red text, no badge */
.mf-row.broken .mf-title, .mf-row.broken .mf-meta { color: var(--faint); }
.mf-title .bad { color: var(--error); margin-left: 8px; }
.mf-row.on .mf-title { color: var(--accent); }
.mf-meta { display: flex; gap: 12px; font: 400 13px/1.5 var(--mono); color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.mf-meta .d { min-width: 7ch; text-align: right; }
.mf-meta .s { min-width: 8ch; text-align: right; }
.mf-meta .t { min-width: 17ch; }   /* "3 Nov 2026 19:06" fits; kept when a file has no date, so every card lines up */
.mf-acts { display: flex; gap: 6px; }   /* download and delete, the row's own actions at its right end */
.mf-acts .ico { width: 16px; height: 16px; margin: 0; }
@container (max-width: 519px) {
  .mf-row { grid-template-columns: auto minmax(0, 1fr) auto; }
  .mf-play, .mf-acts { grid-row: span 2; }
  .mf-meta { grid-column: 2; grid-row: 2; }
  .mf-meta .d, .mf-meta .s, .mf-meta .t { min-width: 0; text-align: left; }
}
@media (prefers-reduced-motion: reduce) { button.mf-play .ico { transition: none; } }
/* /media's sidecar: who made it muted after the title on the same line; Edit opens a form under the row */
.mf-title .by { color: var(--muted); }
.mf-form { display: grid; gap: 12px; padding: 12px var(--gap) 16px; border-top: var(--edge); }
.mf-form textarea { display: block; width: 100%; margin-top: 7px; padding: 8px 10px; resize: vertical; color: var(--fg); font-family: var(--font); letter-spacing: 0; text-transform: none; }
