/* VNLGRV — "Vinyl Groove"
   Colours, motifs and type rules come from the VNLGRV Brand Spec.
   Black is always the ground; red and teal are accents, never fills. */

:root {
  --ink:        #09090A;  /* near-black, true ground */
  --ink-raised: #0E0E10;  /* panels, gradient top */
  --ink-line:   #1C1C20;
  --on-air:     #22C55E;  /* the radio is a live signal, not a warning */
  --red:        #D62228;  /* signal red — the default accent */
  --teal:       #20C4BE;  /* secondary accent, never with red */
  --off-white:  #F5F5F7;
  --muted:      #8A8A93;

  /* Body copy sits a step below --off-white. Both are mixed from the two
     palette ends rather than written out, so they land in the right place on
     whichever ground is under them instead of needing a light twin. */
  --body:       color-mix(in srgb, var(--off-white) 57%, var(--muted));
  --body-soft:  color-mix(in srgb, var(--off-white) 49%, var(--muted));

  /* Artwork and video are shot on black, so their frames stay dark on a light
     page — the way a photograph keeps its mat. */
  --media-bg:   #0E0E10;

  --grain:      0.16;   /* film grain, heavier on the dark ground */
  --scrim:      0.16;   /* how hard the wallpaper is held back */
  --mark-invert: 0;     /* the logo ships white; 1 flips it for a light ground */

  --accent: var(--red);

  --shell: min(1180px, 100% - 3rem);
  --radius: 4px;
  --track-wide: 0.35em;   /* MUSIC LABEL tracking, per spec */

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  /* Geometric sans, resolved from the system so the page ships no webfont. */
  --font: 'Avenir Next', Avenir, Futura, 'Century Gothic', 'URW Gothic',
          ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

/* ---------- appearance, set from the dashboard ----------
   The tokens above are the dark ground. `--ink` means "the ground" and
   `--off-white` means "text on it", so a light site is those two values
   swapped rather than a second stylesheet. The names keep the dark reading;
   the meanings are what the rest of the file uses.

   `data-mode` is set on <html> by theme.js before the body paints. */

:root[data-mode='light'] {
  --ink:        #F6F6F8;
  --ink-raised: #FFFFFF;
  --ink-line:   #DEDEE4;
  --off-white:  #131316;
  --muted:      #5E5E68;
  --red:        #C21C22;   /* darkened so it still passes on white */
  --teal:       #0E8F8A;
  --grain:      0.05;      /* the same grain reads as dirt on white */
  --scrim:      0.72;      /* a photograph under black is texture; under
                              white it is noise the body copy has to fight */
  --mark-invert: 1;
}

/* "System" follows the visitor's own setting, so the same choice can look
   different to two people — which is the point of it. */
@media (prefers-color-scheme: light) {
  :root[data-mode='system'] {
    --ink:        #F6F6F8;
    --ink-raised: #FFFFFF;
    --ink-line:   #DEDEE4;
    --off-white:  #131316;
    --muted:      #5E5E68;
    --red:        #C21C22;
    --teal:       #0E8F8A;
    --grain:      0.05;
    --scrim:      0.72;
    --mark-invert: 1;
  }
}

/* The accent. Teal is never used alongside red, per the brand spec. */
[data-theme='teal'], :root[data-accent='teal'] { --accent: var(--teal); }
:root[data-accent='amber'] { --accent: #D98714; }
:root[data-accent='violet'] { --accent: #7C5CFF; }

/* Calm turns off the motion the site adds — the page transition, the
   carousel's crossfade, the on-air blink — for everyone, the way
   prefers-reduced-motion does for one person. */
:root[data-motion='calm'] *,
:root[data-motion='calm'] *::before,
:root[data-motion='calm'] *::after {
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
  scroll-behavior: auto !important;
}

/* Compact tightens the vertical rhythm without touching type size, for
   someone who would rather scroll less. */
:root[data-density='compact'] { --section-y: 3rem; }

*, *::before, *::after { box-sizing: border-box; }

/* The hidden attribute must win. Any class that sets `display` — .form is
   `display: grid` — otherwise outranks the browser's own [hidden] rule, and
   an element told to hide simply stays on screen. */
[hidden] { display: none !important; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h, 78px) + 1.25rem);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ink);
  color: var(--off-white);
  font-family: var(--font);
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Fine film grain over everything — kills banding on the dark gradients and
   is part of the look, not decoration. */
html::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: var(--grain);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)' opacity='0.5'/%3E%3C/svg%3E");
}

h1, h2, h3 { line-height: 1.1; margin: 0 0 0.5em; font-weight: 600; letter-spacing: -0.01em; }
h1 { font-size: clamp(2.4rem, 1.6rem + 3.6vw, 4.4rem); }
h2 { font-size: clamp(1.7rem, 1.3rem + 1.7vw, 2.5rem); }
h3 { font-size: 1.15rem; }
p  { margin: 0 0 1.1em; color: var(--body); }
a  { color: var(--off-white); text-decoration-color: color-mix(in srgb, var(--accent) 60%, transparent); text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--accent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

.shell { width: var(--shell); margin-inline: auto; }
.eyebrow {
  font-size: 0.7rem;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 1rem;
}
.eyebrow--accent { color: var(--accent); }
.rule { width: 56px; height: 2px; background: var(--accent); border: 0; margin: 0 0 1.75rem; }
.center { text-align: center; }
.center .rule { margin-inline: auto; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ---------- wordmark ---------- */

.wordmark { display: inline-flex; flex-direction: column; gap: 0.35em; text-decoration: none; }
.wordmark__name {
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--off-white);   /* never tinted red or teal, per spec */
  line-height: 1;
}
.wordmark__sub {
  font-size: 0.55em;
  letter-spacing: var(--track-wide);
  color: var(--muted);
  line-height: 1;
  text-indent: var(--track-wide);
}

/* ---------- header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--ink) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--ink-line);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 0.9rem 0;
}
.site-header .wordmark__name { font-size: 1.15rem; }

.nav { display: flex; align-items: center; gap: 1.75rem; }
.nav a {
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--muted);
  transition: color 0.2s var(--ease);
}
.nav a:hover, .nav a[aria-current='page'] { color: var(--off-white); }
.nav a[aria-current='page'] { position: relative; }
.nav a[aria-current='page']::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -0.45rem;
  height: 1px; background: var(--accent);
}

.nav-toggle {
  display: none; background: none; border: 1px solid var(--ink-line);
  color: var(--off-white); border-radius: var(--radius);
  padding: 0.45rem 0.7rem; font: inherit; font-size: 0.75rem;
  letter-spacing: 0.12em; text-transform: uppercase; cursor: pointer;
}

/* The nav is nine links wide — 762px — and with the mark and the socials
   beside it the row needs 1159px. Below that it folds behind the Menu
   button, which is why this breakpoint is where it is rather than at a round
   phone width: it is measured, not guessed. Adding or renaming a link moves
   the number, so re-measure rather than nudging it — tests/e2e.mjs checks
   the header never overflows on either side of the fold. */
@media (max-width: 1158px) {
  .nav-toggle { display: block; }
  /* Once the nav folds away the row is mark + Menu + accounts, which at 360px
     was 22px wider than the screen with a 1.5rem gap each. Everything
     tightens rather than anything being dropped. */
  .site-header__inner { gap: 0.75rem; }
  .socials { gap: 0.1rem; }
  .socials a { width: 1.75rem; height: 1.75rem; }
  .nav {
    position: absolute; inset: 100% 0 auto; flex-direction: column;
    align-items: flex-start; gap: 0; padding: 0.5rem 1.5rem 1.25rem;
    background: var(--ink-raised); border-bottom: 1px solid var(--ink-line);
  }
  .nav:not([data-open]) { display: none; }
  .nav a { padding: 0.7rem 0; width: 100%; }
  .nav a[aria-current='page']::after { display: none; }
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.6rem;
  /* A label that wraps inside a narrow tier card still centres: justify-content
     only places the text block, text-align centres the lines inside it. */
  text-align: center;
  padding: 0.85rem 1.6rem;
  font: inherit; font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; text-decoration: none;
  border-radius: var(--radius); border: 1px solid var(--accent);
  background: var(--accent); color: #fff; cursor: pointer;
  transition: transform 0.2s var(--ease), filter 0.2s var(--ease);
}
.btn:hover { filter: brightness(1.12); transform: translateY(-1px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
.btn--ghost { background: transparent; color: var(--off-white); border-color: var(--ink-line); }
.btn--ghost:hover { border-color: var(--accent); }

/* ---------- hero (Bloom motif) ---------- */

.hero {
  position: relative;
  display: grid;
  place-items: center;
  text-align: center;
  padding: clamp(5rem, 14vh, 9rem) 0 clamp(4rem, 10vh, 7rem);
  isolation: isolate;
  overflow: hidden;
}
.hero::before { /* soft red glow rising from below */
  content: '';
  position: absolute; inset: 0; z-index: -2;
  background:
    radial-gradient(120% 75% at 50% 118%,
      color-mix(in srgb, var(--accent) 55%, transparent) 0%,
      color-mix(in srgb, var(--accent) 12%, transparent) 38%,
      transparent 68%),
    linear-gradient(180deg, var(--ink-raised) 0%, var(--ink) 55%);
}
.hero::after { /* thin horizon rule */
  content: '';
  position: absolute; left: 50%; bottom: clamp(2.5rem, 7vh, 4.5rem);
  z-index: -1; width: min(760px, 78%); height: 1px;
  transform: translateX(-50%);
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 75%, transparent), transparent);
}
.hero .wordmark { align-items: center; }
.hero .wordmark__name { font-size: clamp(3rem, 1.6rem + 7vw, 7rem); letter-spacing: 0.1em; }
.hero__logo { width: min(520px, 76vw); height: auto; display: block; margin-inline: auto; }
.hero__lede {
  max-width: 46ch; margin: 1.75rem auto 2.25rem;
  font-size: clamp(1.02rem, 0.98rem + 0.3vw, 1.2rem); color: var(--body-soft);
}
.hero__actions { display: flex; flex-wrap: wrap; gap: 0.9rem; justify-content: center; }

/* ---------- grooves motif ---------- */

.grooves { position: relative; isolation: isolate; overflow: hidden; }
.grooves::before {
  content: '';
  position: absolute; inset: -30% -10%; z-index: -2;
  background:
    repeating-radial-gradient(circle at 50% 50%,
      transparent 0 3px, color-mix(in srgb, var(--off-white) 3%, transparent) 3px 4px),
    repeating-radial-gradient(circle at 50% 50%,
      transparent 0 66px, color-mix(in srgb, var(--accent) 11%, transparent) 66px 68px);
  /* every 17th ring carries the accent (17 x 4px pitch = 68px) */
  mask-image: radial-gradient(circle at 50% 50%, #000 4%, transparent 72%);
}

/* ---------- sections ---------- */

.section { padding: var(--section-y, clamp(3.5rem, 9vh, 6.5rem)) 0; }
.section--raised { background: color-mix(in srgb, var(--ink-raised) 86%, transparent); border-block: 1px solid var(--ink-line); }
.section__head { max-width: 62ch; margin-bottom: 2.75rem; }
.section__head.center { margin-inline: auto; }

.grid { display: grid; gap: 1.25rem; }
/* minmax(N, 1fr) will not go below N, so on a screen narrower than N the
   column runs off the side. min(N, 100%) lets it give way instead — this is
   why the playlists page overflowed at 320px and the others did not. */
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(215px, 100%), 1fr)); }

.card {
  background: var(--ink-raised);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  padding: 1.6rem;
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.card:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--ink-line)); transform: translateY(-2px); }
.card h3 { margin-bottom: 0.4rem; }
.card p:last-child { margin-bottom: 0; }
.card__tag {
  font-size: 0.68rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--accent); display: block; margin-bottom: 0.9rem;
}

/* ---------- media gallery (fed live from Google Drive) ---------- */

.gallery { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
@media (max-width: 620px) {
  .gallery { gap: 0.6rem; grid-template-columns: repeat(2, 1fr); }
  .gallery__item figcaption { font-size: 0.68rem; padding: 1.6rem 0.6rem 0.5rem; }
}
.gallery__item {
  position: relative; margin: 0;
  border: 1px solid var(--ink-line); border-radius: var(--radius);
  background: var(--ink-raised); overflow: hidden;
  aspect-ratio: 16 / 9;
}
.gallery__item img, .gallery__item video {
  width: 100%; height: 100%; object-fit: cover; display: block;
  background: var(--media-bg);
}
.gallery__item figcaption {
  position: absolute; inset: auto 0 0; padding: 2.2rem 0.9rem 0.7rem;
  font-size: 0.78rem; letter-spacing: 0.04em;
  color: #F5F5F7;
  background: linear-gradient(180deg, transparent, rgba(9,9,10,0.92));
}
.gallery__item--video::before {
  content: '▶'; position: absolute; z-index: 2; inset: 0;
  display: grid; place-items: center; font-size: 1.6rem;
  /* Over a thumbnail, not over the page — so it stays light either way. */
  color: #F5F5F7; text-shadow: 0 2px 12px rgba(0,0,0,0.6);
  pointer-events: none; transition: opacity 0.2s var(--ease);
}
.gallery__item--video.is-playing::before { opacity: 0; }

.gallery__skeleton {
  aspect-ratio: 16 / 9; border-radius: var(--radius);
  background: linear-gradient(100deg, var(--ink-raised) 30%,
    color-mix(in srgb, var(--ink-raised) 88%, var(--off-white)) 50%, var(--ink-raised) 70%);
  background-size: 220% 100%;
  animation: shimmer 1.4s linear infinite;
}
@keyframes shimmer { to { background-position: -220% 0; } }
@media (prefers-reduced-motion: reduce) { .gallery__skeleton { animation: none; } }

.gallery__note {
  grid-column: 1 / -1; padding: 1.5rem; text-align: center;
  border: 1px dashed var(--ink-line); border-radius: var(--radius); color: var(--muted);
}

/* ---------- tiers ---------- */

.tier { display: flex; flex-direction: column; }
.tier__price {
  font-size: 1.9rem; font-weight: 600; letter-spacing: -0.02em;
  margin: 0.2rem 0 1rem; color: var(--off-white);
}
.tier__price small { font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); display: block; margin-top: 0.35rem; }
.tier ul { list-style: none; padding: 0; margin: 0 0 1.6rem; display: grid; gap: 0.7rem; }
.tier li { position: relative; padding-left: 1.3rem; font-size: 0.93rem; color: var(--body); }
.tier li::before { content: ''; position: absolute; left: 0; top: 0.62em; width: 7px; height: 1px; background: var(--accent); }
.tier .btn { margin-top: auto; justify-content: center; }
.tier { position: relative; }
.tier--featured { border-color: color-mix(in srgb, var(--accent) 55%, var(--ink-line)); }
.tier--featured::before {
  content: 'Most popular';
  position: absolute; top: 0; right: 1.6rem; transform: translateY(-50%);
  font-size: 0.6rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent); background: var(--ink-raised);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 99px; padding: 0.25rem 0.7rem;
}

/* ---------- roster ---------- */

.artist { display: grid; gap: 0.35rem; }
.artist__name { font-size: 1.05rem; letter-spacing: 0.06em; }
.artist__genre { font-size: 0.72rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--accent); }

/* ---------- form ---------- */

.form { display: grid; gap: 1.2rem; max-width: 620px; }
.field { display: grid; gap: 0.45rem; }
.field label { font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); }
.field input, .field select, .field textarea {
  font: inherit; font-size: 0.95rem; color: var(--off-white);
  background: var(--ink-raised); border: 1px solid var(--ink-line);
  border-radius: var(--radius); padding: 0.8rem 0.9rem; width: 100%;
  transition: border-color 0.2s var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--accent); outline: none; }
.field textarea { min-height: 130px; resize: vertical; }
.field__hint { font-size: 0.78rem; color: var(--muted); }
.field--honey { position: absolute; left: -9999px; }

.form__status { min-height: 1.5rem; font-size: 0.92rem; }
.form__status[data-state='error'] { color: color-mix(in srgb, #E5484D 70%, var(--off-white)); }
.form__status[data-state='ok'] { color: var(--teal); }

/* ---------- footer ---------- */

.site-footer {
  border-top: 1px solid var(--ink-line);
  padding: 3rem 0 2.5rem;
  background: var(--ink-raised);
}
.site-footer__inner { display: flex; flex-wrap: wrap; gap: 2rem; justify-content: space-between; align-items: flex-start; }
.site-footer p { font-size: 0.85rem; color: var(--muted); margin: 0; }
.site-footer nav { display: flex; flex-wrap: wrap; gap: 1.25rem; }
.site-footer nav a { font-size: 0.78rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); text-decoration: none; }
.site-footer nav a:hover { color: var(--off-white); }

/* ---------- playlists ---------- */

.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); }

.playlist { display: flex; flex-direction: column; }
.playlist p { margin-bottom: 1.2rem; }
.playlist--featured { border-color: color-mix(in srgb, var(--accent) 45%, var(--ink-line)); }

.playlist__embed {
  width: 100%;
  border: 0;
  border-radius: var(--radius);
  margin-top: auto;
  background: var(--media-bg);
  color-scheme: normal; /* keep Spotify's own dark chrome */
}
.playlist__embed--video { aspect-ratio: 16 / 9; height: auto; }

.playlist__pending {
  margin-top: auto;
  padding: 2.5rem 1rem;
  text-align: center;
  color: var(--muted);
  font-size: 0.88rem;
  border: 1px dashed var(--ink-line);
  border-radius: var(--radius);
}

/* ---------- newsletter ---------- */

.form--newsletter { max-width: 640px; }
.form__row { display: grid; gap: 1.2rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 560px) { .form__row { grid-template-columns: 1fr; } }

.field__optional {
  font-size: 0.9em;
  letter-spacing: 0.08em;
  color: var(--muted);
}

/* Cards carrying links want the accent, not the default underline colour. */
.card a:not(.btn) { color: var(--off-white); text-decoration-color: var(--accent); }

/* ---------- full sets ---------- */

.set { display: flex; flex-direction: column; }
.set__name { margin-bottom: 0.35rem; font-size: 1.25rem; letter-spacing: -0.01em; }
.set p { margin-bottom: 1.1rem; }
.set__embed {
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--radius);
  margin-top: auto;
  background: var(--media-bg);
}
.section__more { margin-top: 2rem; }

/* ---------- admin ---------- */

.admin__bar {
  display: flex; flex-wrap: wrap; gap: 1rem;
  align-items: center; justify-content: space-between;
  padding-bottom: 1.5rem; margin-bottom: 2rem;
  border-bottom: 1px solid var(--ink-line);
}
.admin__who { margin: 0; font-size: 0.9rem; color: var(--muted); }
.admin__who strong { color: var(--off-white); font-weight: 600; }
.admin__section { margin-top: 3rem; }

.stat__value {
  margin: 0.2rem 0 0.3rem;
  font-size: 1.9rem; font-weight: 600; letter-spacing: -0.02em;
  color: var(--off-white); line-height: 1.1;
}
.stat__note { margin: 0; font-size: 0.78rem; color: var(--muted); }

/* ---------- admin: tabs ---------- */

.tabs {
  display: flex; flex-wrap: wrap; gap: 0.35rem;
  border-bottom: 1px solid var(--ink-line);
  margin-bottom: 2.25rem;
}
.tabs__tab {
  appearance: none; background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--muted); font: inherit; font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  padding: 0.7rem 1rem; cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.tabs__tab:hover { color: var(--off-white); }
.tabs__tab[aria-selected='true'] { color: var(--off-white); border-bottom-color: var(--accent); }
.tabs__panel { display: grid; gap: 1.25rem; }
.tabs__panel[hidden] { display: none; }

.admin__h { margin: 2rem 0 0; font-size: 1.15rem; }
.admin__loading { color: var(--muted); font-size: 0.9rem; padding: 2rem 0; }

.admin__notice {
  margin: 0; padding: 1rem 1.15rem; border-radius: var(--radius);
  font-size: 0.9rem; border: 1px solid var(--ink-line); color: var(--body);
}
.admin__notice--warn { border-color: color-mix(in srgb, var(--accent) 45%, var(--ink-line)); }
.admin__notice--info { border-style: dashed; color: var(--muted); }

/* metrics inside a card */
.metrics { display: flex; gap: 1.6rem; margin: 0.4rem 0 1rem; }
.metric { display: grid; }
.metric__value { font-size: 1.35rem; font-weight: 600; color: var(--off-white); line-height: 1.2; }
.metric__label { font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); }

/* simple label/count lists */
.tally { list-style: none; margin: 0.6rem 0 0; padding: 0; display: grid; gap: 0.5rem; }
.tally li {
  display: flex; justify-content: space-between; gap: 1rem;
  font-size: 0.88rem; color: var(--body);
  padding-bottom: 0.5rem; border-bottom: 1px solid var(--ink-line);
}
.tally li:last-child { border-bottom: 0; padding-bottom: 0; }
.tally strong { color: var(--off-white); }

/* submissions table */
.table { display: grid; gap: 1px; background: var(--ink-line); border: 1px solid var(--ink-line); border-radius: var(--radius); overflow: hidden; }
.table__row {
  display: grid; grid-template-columns: 6.5rem 1.2fr 1.2fr 0.9fr 1fr 4rem;
  gap: 1rem; padding: 0.7rem 1rem; background: var(--ink-raised);
  font-size: 0.85rem; color: var(--body);
}
.table__row > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.table__row--head { font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
@media (max-width: 860px) {
  .table__row { grid-template-columns: 1fr 1fr; }
  .table__row--head { display: none; }
}

.idea__bar { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin-top: auto; }
.idea__bar .stat__note { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.rules { margin: 0.5rem 0 0; padding-left: 1.2rem; display: grid; gap: 0.7rem; color: var(--body); font-size: 0.9rem; }

/* upload */
.upload { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin-top: 0.5rem; }
.upload__input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.upload__input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 3px; }
.upload__list { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: 0.5rem; font-size: 0.88rem; }

/* ---------- limelight episode visual ---------- */

.episode {
  margin: 0;
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--ink-raised);
  max-width: 900px;
}
.episode__video,
.episode__still {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: var(--media-bg);
}
/* The episode still, once the feed has answered. It replaces the loop in the
   same box, so it shares .episode__video's framing and only adds the
   link behaviour and the play mark on top. */
.episode__cover { display: block; position: relative; }
.episode__cover[hidden] { display: none; }
.episode__play {
  position: absolute; inset: 0; margin: auto;
  width: 4.4rem; height: 4.4rem; border-radius: 50%;
  background: color-mix(in srgb, var(--ink) 62%, transparent);
  border: 1px solid color-mix(in srgb, var(--off-white) 55%, transparent);
  backdrop-filter: blur(2px);
  transition: background 0.2s var(--ease), transform 0.2s var(--ease);
}
.episode__play::after {
  content: ''; position: absolute; inset: 0; margin: auto;
  width: 0; height: 0;
  border-left: 1.15rem solid var(--off-white);
  border-block: 0.72rem solid transparent;
  transform: translateX(0.17rem);
}
.episode__cover:hover .episode__play,
.episode__cover:focus-visible .episode__play {
  background: color-mix(in srgb, var(--accent) 82%, transparent);
  transform: scale(1.06);
}
.episode__cover:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .episode__play { transition: none; }
  .episode__cover:hover .episode__play { transform: none; }
}

.episode figcaption {
  padding: 0.85rem 1.1rem;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  color: var(--muted);
  border-top: 1px solid var(--ink-line);
}

/* ---------- broadcast genre cards ---------- */

.genres { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
@media (max-width: 620px) { .genres { gap: 0.6rem; grid-template-columns: repeat(2, 1fr); } }

.genre {
  margin: 0;
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--ink-raised);
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.genre:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--ink-line)); transform: translateY(-2px); }
.genre__image { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; background: var(--media-bg); }
.genre figcaption {
  padding: 0.7rem 0.9rem;
  font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); border-top: 1px solid var(--ink-line);
}
@media (max-width: 620px) { .genre figcaption { font-size: 0.62rem; padding: 0.5rem 0.6rem; } }

/* ---------- landing intro ---------- */

/* Stop the page behind it scrolling while the intro is up. */
.intro-open body { overflow: hidden; }

.intro {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  background: var(--ink);
  opacity: 1;
  transition: opacity 0.35s var(--ease);
}
.intro--closing { opacity: 0; pointer-events: none; }

.intro__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.intro__overlay {
  position: relative;
  text-align: center;
  padding: 1.5rem;
  /* Keeps the wordmark legible over whatever frame is behind it. */
  background: radial-gradient(60% 60% at 50% 50%, rgba(9, 9, 10, 0.72), transparent 75%);
}
.intro__wordmark {
  margin: 0;
  font-size: clamp(2.6rem, 1.4rem + 6vw, 6rem);
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--off-white);
  line-height: 1;
}
/* With a logo file the gate shows the mark instead of the name set in type.
   The paragraph keeps its box but stops reserving a line of leading for text
   that is no longer there. */
.intro__wordmark--mark { line-height: 0; }
.intro__logo {
  display: block;
  width: min(520px, 72vw);
  height: auto;
  margin-inline: auto;
}
.intro__sub {
  margin: 0.9rem 0 0;
  font-size: 0.72rem;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--muted);
  /* Offsets the space after the last letter, which would otherwise pull the
     word left of the mark's centre. */
  text-indent: var(--track-wide);
}

.intro__enter {
  position: absolute;
  right: clamp(1rem, 4vw, 2.5rem);
  bottom: clamp(1rem, 4vw, 2.5rem);
  z-index: 2;
  padding: 0.7rem 1.3rem;
  font: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--off-white);
  background: color-mix(in srgb, var(--ink) 70%, transparent);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  backdrop-filter: blur(8px);
  cursor: pointer;
  transition: border-color 0.2s var(--ease);
}
.intro__enter:hover { border-color: var(--accent); color: var(--accent); }
.intro__enter:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* The gate applies to everyone; under reduced motion the video simply does
   not play. It cannot block a scripted-off browser because the markup ships
   `hidden` and only the script reveals it. */
@media (prefers-reduced-motion: reduce) { .intro__video { display: none; } }

/* The mark replaces the typeset name, but MUSIC stays visible beneath it, so
   the lockup still reads VNLGRV Music. The name itself stays in the DOM,
   hidden, so screen readers and copy-paste still get it. */
/* Both marks ship as a white image, which a light ground would swallow. They
   are single-fill, so inverting them gives the dark mark exactly. */
.wordmark__img, .soon__mark { filter: invert(var(--mark-invert)); }
.wordmark__img { display: block; height: 2.1rem; width: auto; }
.wordmark--mark .wordmark__name {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
/* MUSIC sits centred under the mark. The column is as wide as the image, so
   text-align does the centring; the text-indent that is already there offsets
   the trailing letter-space, which would otherwise pull the word visibly left
   of the mark's centre. */
.wordmark--mark .wordmark__sub { margin-top: 0.15rem; text-align: center; }

/* The hero mark is the page's largest element, so it is sized against the
   viewport rather than the 2.1rem the header uses. */
.wordmark--hero .wordmark__img { width: min(520px, 76vw); height: auto; }
.wordmark--hero .wordmark__sub { letter-spacing: 0.5em; font-size: 0.8rem; }
.site-footer .wordmark__img { height: 1.8rem; }

/* ---------- merch ---------- */

.product { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.product__image { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: contain; background: var(--media-bg); }
.product__body { padding: 1.6rem; display: flex; flex-direction: column; gap: 0.2rem; }
.product__form { margin-top: 1rem; gap: 1rem; }
.product__form .field label { font-size: 0.68rem; }

/* ---------- install bar ---------- */

.pwa-bar {
  position: fixed;
  left: 50%;
  bottom: clamp(1rem, 4vw, 2rem);
  z-index: 150;
  transform: translateX(-50%);
  width: min(560px, calc(100% - 2rem));
  display: flex;
  align-items: center;
  gap: 0.8rem;
  flex-wrap: wrap;
  padding: 1rem 1.2rem;
  background: color-mix(in srgb, var(--ink-raised) 94%, transparent);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  backdrop-filter: blur(12px);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}
.pwa-bar__text { margin: 0; flex: 1 1 14rem; font-size: 0.88rem; color: var(--body); }
.pwa-bar .btn { padding: 0.6rem 1.1rem; font-size: 0.7rem; }

.site-footer__alerts { display: grid; gap: 0.6rem; max-width: 22rem; }
.site-footer__alerts .btn { justify-self: start; }

/* ---------- roster cards ---------- */

.artist--link { text-decoration: none; display: flex; flex-direction: column; gap: 0.35rem; }
.artist--link:hover { text-decoration: none; }
.artist__flag {
  align-self: flex-start;
  margin-bottom: 0.4rem;
  padding: 0.2rem 0.6rem;
  font-size: 0.6rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 99px;
}
.artist__meta {
  margin-top: auto; padding-top: 0.8rem;
  font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted);
}
.artist--link:hover .artist__meta { color: var(--off-white); }

/* ---------- release: player + tracklist ---------- */

.release { display: grid; gap: 1.5rem; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); align-items: start; }
@media (max-width: 860px) { .release { grid-template-columns: 1fr; } }

.release__player iframe {
  width: 100%; aspect-ratio: 16 / 9; border: 0;
  border-radius: var(--radius); background: var(--media-bg);
}

.tracklist { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; counter-reset: none; }
.tracklist li { display: flex; align-items: center; gap: 0.75rem; }

.track {
  flex: 1; display: flex; align-items: center; gap: 0.9rem;
  padding: 0.8rem 1rem; text-align: left;
  font: inherit; font-size: 0.92rem; color: var(--body);
  background: var(--ink-raised); border: 1px solid var(--ink-line);
  border-radius: var(--radius); cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.track:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--ink-line)); color: var(--off-white); }
.track[aria-current='true'] { border-color: var(--accent); color: var(--off-white); }
.track__n { font-size: 0.72rem; letter-spacing: 0.1em; color: var(--muted); }
.track__title { flex: 1; }
.track__play { font-size: 0.66rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); }
.track__out { font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
.track__out:hover { color: var(--accent); }

/* A track that cannot be embedded is a span, not a button: no pointer, and the
   row's link does the work. `release--list` drops the player column so the
   tracklist is not squeezed into a third of the width for nothing. */
.track--out { cursor: default; }
.track--out:hover { border-color: var(--ink-line); color: var(--body); }
.release--list { grid-template-columns: minmax(0, 34rem); }

/* A second release follows the first in the same shell, so it needs its own
   breathing room and a rule to separate it. */
.release + .section__head { margin-top: 3.5rem; }
.release__partial { font-size: 0.82rem; color: var(--muted); }

/* A Spotify embed is a short audio bar, so the 16:9 box that suits a YouTube
   video would leave most of it empty. */
.release__frame--audio { aspect-ratio: auto; height: 152px; }

/* The per-track source menu. <details> so it works before the script loads. */
.srcs { position: relative; font-size: 0.68rem; }
.srcs > summary {
  list-style: none; cursor: pointer; padding: 0.35rem 0.5rem;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--ink-line); border-radius: var(--radius); white-space: nowrap;
}
.srcs > summary::-webkit-details-marker { display: none; }
.srcs > summary:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--ink-line)); }
.srcs[open] > summary { color: var(--accent); border-color: var(--accent); }
.srcs > ul {
  position: absolute; right: 0; top: calc(100% + 0.3rem); z-index: 5;
  min-width: 13rem; margin: 0; padding: 0.3rem; list-style: none;
  background: var(--ink-raised); border: 1px solid var(--ink-line);
  border-radius: var(--radius); box-shadow: 0 10px 30px rgb(0 0 0 / 45%);
}
.srcs > ul > li { margin: 0; }
.srcs a, .srcs button {
  display: block; width: 100%; text-align: left;
  padding: 0.5rem 0.6rem; font: inherit; color: var(--body);
  background: none; border: 0; border-radius: calc(var(--radius) - 2px);
  cursor: pointer; text-decoration: none; white-space: nowrap;
}
.srcs a:hover, .srcs button:hover { background: var(--ink-line); color: var(--off-white); }
@media (max-width: 520px) { .srcs > ul { right: auto; left: 0; } }


/* ---------- release artwork carousel ---------- */

.art { display: grid; gap: 1rem; justify-items: center; }

/* The slides stack in one grid cell rather than being absolutely positioned,
   so the track is exactly as tall as a slide — caption included. Absolute
   slides let the caption spill out and swallow clicks on the controls below. */
.art__track {
  display: grid; list-style: none; margin: 0; padding: 0;
  width: min(100%, 30rem);
}
.art__slide {
  grid-area: 1 / 1; margin: 0;
  opacity: 0; visibility: hidden;
  transition: opacity 0.6s var(--ease), visibility 0.6s var(--ease);
}
.art__slide[data-current] { opacity: 1; visibility: visible; }
.art__slide img {
  display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
  border-radius: var(--radius); background: var(--media-bg);
  border: 1px solid var(--ink-line);
}
.art__caption {
  margin: 0.7rem 0 0; text-align: center;
  font-size: 0.78rem; letter-spacing: 0.06em; color: var(--muted);
}

.art__controls { display: flex; align-items: center; gap: 1rem; }
.art__nav {
  padding: 0.45rem 0.9rem; font: inherit; font-size: 0.95rem; line-height: 1;
  color: var(--off-white); background: var(--ink-raised);
  border: 1px solid var(--ink-line); border-radius: var(--radius); cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.art__nav:hover { border-color: var(--accent); color: var(--accent); }
.art__count {
  margin: 0; font-size: 0.7rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted); min-width: 4rem; text-align: center;
}

/* The caption sits under a fixed-ratio box, so the box needs room for it. */
@media (max-width: 520px) { .art__track { width: 100%; } }


/* ---------- on-air badge ---------- */

/* Pinned, not placed: the radio stays reachable however far the page scrolls.
   It sits below the header so it never covers the logo, and collapses to a
   pill until someone actually wants it. */
.live {
  position: fixed;
  top: calc(var(--header-h, 78px) + 0.85rem);
  left: clamp(0.75rem, 2vw, 1.25rem);
  z-index: 40;
  width: max-content;
  max-width: min(22rem, calc(100vw - 1.5rem));
}

.live__toggle {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.45rem 0.8rem;
  font: inherit; font-size: 0.66rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--off-white);
  background: color-mix(in srgb, var(--ink) 82%, transparent);
  border: 1px solid color-mix(in srgb, var(--on-air) 55%, var(--ink-line));
  border-radius: 999px;
  backdrop-filter: blur(10px);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.live__toggle:hover { border-color: var(--on-air); color: var(--on-air); }
.live__toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.live__dot {
  width: 0.45rem; height: 0.45rem; border-radius: 50%;
  background: var(--on-air); flex: none;
  box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--on-air) 18%, transparent);
}
/* On air blinks — a live signal. Off air glows steadily red and does not,
   because a blinking light that means "nothing is happening" reads as a
   fault. */
.live--on .live__dot { animation: live-pulse 1.6s var(--ease) infinite; }
@keyframes live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.2; } }
@media (prefers-reduced-motion: reduce) { .live--on .live__dot { animation: none; } }

.live--off .live__toggle {
  border-color: color-mix(in srgb, var(--accent) 60%, var(--ink-line));
  color: color-mix(in srgb, var(--accent) 85%, var(--off-white));
  cursor: default;
}
.live--off .live__toggle:hover { border-color: color-mix(in srgb, var(--accent) 60%, var(--ink-line)); }
.live--off .live__dot {
  background: var(--accent);
  box-shadow: 0 0 0.5rem 0.1rem color-mix(in srgb, var(--accent) 55%, transparent),
              0 0 0 0.2rem color-mix(in srgb, var(--accent) 16%, transparent);
}

/* What is streaming, under the pill rather than inside it — a show title is
   any length and a pill is not. */
.live__now {
  margin: 0.4rem 0 0;
  max-width: 16rem;
  font-size: 0.68rem; line-height: 1.45; letter-spacing: 0.02em;
  color: var(--muted);
  text-shadow: 0 1px 6px var(--ink);
}
.live__now[hidden] { display: none; }

.live__panel {
  margin-top: 0.5rem;
  padding: 0.85rem;
  background: color-mix(in srgb, var(--ink-raised) 94%, transparent);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  backdrop-filter: blur(10px);
  box-shadow: 0 16px 40px rgb(0 0 0 / 55%);
}
.live__title { margin: 0 0 0.6rem; font-size: 0.86rem; color: var(--off-white); }
.live__audio { width: 100%; height: 2.2rem; }
.live__frame { width: min(19rem, 60vw); aspect-ratio: 16 / 9; border: 0; border-radius: var(--radius); background: var(--media-bg); }
.live__note { margin: 0.6rem 0 0; font-size: 0.68rem; color: var(--muted); }
.live__out {
  display: inline-block; margin-top: 0.6rem;
  font-size: 0.66rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); text-decoration: none;
}
.live__out:hover { color: var(--accent); }

/* Once it is playing the pill stays lit, so the state is readable at a glance
   even when the panel is collapsed again. */
.live--playing .live__toggle { border-color: var(--on-air); color: var(--on-air); }

/* ---------- listen / watch tabs ---------- */

.tabs__bar {
  display: flex; gap: 0.5rem; margin-bottom: 2rem;
  border-bottom: 1px solid var(--ink-line);
}
.tabs__tab {
  padding: 0.7rem 1.2rem; margin-bottom: -1px;
  font: inherit; font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); background: none;
  border: 0; border-bottom: 2px solid transparent;
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.tabs__tab:hover { color: var(--off-white); }
.tabs__tab[aria-selected='true'] { color: var(--accent); border-bottom-color: var(--accent); }
.tabs__tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tabs__panel:focus { outline: none; }
.tabs__panel > .section__head:first-child { margin-top: 0; }


/* ---------- wallpaper ---------- */

/* The EQ cover sits behind everything, fixed, under a scrim heavy enough that
   body text keeps its contrast. It is a decorative layer with no content, so
   it lives on a pseudo-element rather than in the markup. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -10;
  background: url('/assets/media/bg-eq.af9c74d5.jpg') center / cover no-repeat;
  opacity: 0.28;
  pointer-events: none;
}

/* The scrim: darkest at the top and bottom, where the nav and footer sit. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -9;
  pointer-events: none;
  opacity: var(--scrim);
  background:
    linear-gradient(to bottom,
      color-mix(in srgb, var(--ink) 94%, transparent) 0%,
      color-mix(in srgb, var(--ink) 82%, transparent) 40%,
      color-mix(in srgb, var(--ink) 88%, transparent) 72%,
      color-mix(in srgb, var(--ink) 96%, transparent) 100%);
}

/* The header and footer need to stay legible over whatever is behind them. */
.site-header, .site-footer {
  background: color-mix(in srgb, var(--ink) 88%, transparent);
  backdrop-filter: blur(10px);
}

/* ---------- page transitions ---------- */

/* Cross-document view transitions where the browser supports them; every
   other browser simply navigates as before. */
@view-transition { navigation: auto; }

::view-transition-old(root) { animation: page-out 0.22s var(--ease) both; }
::view-transition-new(root) { animation: page-in 0.34s var(--ease) both; }

@keyframes page-out { to { opacity: 0; } }
@keyframes page-in { from { opacity: 0; transform: translateY(10px); } }

/* For browsers without view transitions, the page still arrives with a little
   life rather than snapping in.
   Opacity only, deliberately: a transform on <main> would make it the
   containing block for every `position: fixed` descendant, which quietly
   unpins the on-air badge and turns it into an absolutely positioned one. */
@media (prefers-reduced-motion: no-preference) {
  main { animation: page-fade 0.45s var(--ease) both; }
}

@keyframes page-fade { from { opacity: 0; } }

/* Motion is a preference, not a default. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
  main { animation: none; }
}


/* A "coming soon" tag should not read as a live one. */
.card__tag--soon { color: var(--muted); }


/* ---------- management suite ---------- */

/* Drive is folded away rather than deleted: the galleries still use it. */
.admin__fold { margin-top: 2rem; border-top: 1px solid var(--ink-line); padding-top: 1rem; }
.admin__fold > summary {
  cursor: pointer; font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted);
}
.admin__fold > summary:hover { color: var(--off-white); }
.admin__fold > * { margin-top: 1rem; }

.admin__filters { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 1.25rem 0; }
.admin__filters .btn[aria-pressed='true'] { border-color: var(--accent); color: var(--accent); }

.admin__inline { display: flex; flex-wrap: wrap; gap: 0.9rem; align-items: end; margin: 1.25rem 0 0.5rem; }
.admin__inline .field { margin: 0; min-width: 12rem; flex: 1 1 12rem; }

.admin__row-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem; align-items: center; }
.admin__row-actions .btn { padding: 0.5rem 0.9rem; font-size: 0.68rem; }

.admin__slot {
  flex: 1 1 8rem; min-width: 7rem; padding: 0.5rem 0.7rem;
  font: inherit; font-size: 0.82rem; color: var(--off-white);
  background: var(--ink); border: 1px solid var(--ink-line); border-radius: var(--radius);
}


/* ---------- the landing image ----------

   The home page used to carry the porch scene as its wallpaper, so closing
   the gate was a cross-fade between two views of one image. The hero clip
   took that job: it fills the top of the page opaquely, which is exactly
   where the still was meant to show. All the wallpaper did after that was
   sit behind the body copy — and that scene has the VNLGRV wall art and a
   LIMELIGHT SHOW sign painted into it, so the page ended up with two logos
   and two Limelight headings on one screen.

   So the home page takes the same abstract wallpaper every other page has.
   The porch scene still opens the site, on the gate, and comes back on the
   ambient screen behind the wordmark. */

/* A longer fade than a UI transition wants, because this one is the hand-off
   from the gate to the page. */
.intro--closing { transition: opacity 0.75s var(--ease); }
@media (prefers-reduced-motion: reduce) { .intro--closing { transition: none; } }


/* ---------- roster cells and artist profiles ---------- */

/* The flags label the artist, so they sit above the cell rather than inside
   it, where a pill in the corner of a card reads as part of the copy. */
/* The flags live inside the card now, above the name. Outside it they pushed
   that one card down and left it shorter than the rest of the row. */
.artist__flags { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.9rem 0 0.2rem; }

.artist__flag {
  display: inline-block;
  padding: 0.3rem 0.65rem;
  font-size: 0.6rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  border-radius: 999px;
}
.artist__flag--feature {
  color: var(--ink); background: var(--accent); border-color: var(--accent);
}

.artist__photo {
  /* height: auto matters — see .profile__photo below. */
  display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover;
  border-radius: var(--radius); margin-bottom: 1rem; background: var(--media-bg);
}

.profile { display: grid; gap: clamp(1.5rem, 4vw, 2.5rem); grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); align-items: start; }
@media (max-width: 760px) { .profile { grid-template-columns: 1fr; } }

.profile__photo {
  /* An <img>'s height attribute is a presentational hint that survives
     `width: 100%`, and aspect-ratio only supplies a dimension that is auto —
     so without this the frame takes the attribute's height and crops the
     artwork to a tall slice. */
  width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover;
  border-radius: var(--radius); border: 1px solid var(--ink-line); background: var(--media-bg);
}
.profile__photo--empty {
  display: grid; place-items: center;
  font-size: clamp(3rem, 10vw, 6rem); font-weight: 700; color: var(--ink-line);
}
.profile__body .artist__flag { margin: 0 0.4rem 0.9rem 0; }
.profile__bio { max-width: 60ch; }

.artist__socials { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 0.75rem; }
.artist__socials a {
  font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); text-decoration: none;
}
.artist__socials a:hover { color: var(--accent); }

/* ---------- paid tiers ---------- */

.pay-note {
  margin: 0.8rem 0 0; padding: 0.9rem 1rem;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  font-size: 0.86rem; color: var(--body);
}
.pay-note[hidden] { display: none; }
.pay-note__price {
  display: block; margin-bottom: 0.3rem;
  font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  font-size: 0.74rem; color: var(--off-white);
}
.pay-note__price em {
  font-style: normal; color: var(--accent); letter-spacing: 0.14em;
}
.tier__foot {
  margin: 0.8rem 0 0; font-size: 0.74rem; line-height: 1.5;
  letter-spacing: 0.02em; color: var(--muted); text-align: center;
}

.pay-cta { display: grid; justify-items: center; gap: 1rem; }
.pay-cta__price {
  margin: 0; font-size: 2.6rem; font-weight: 600; letter-spacing: -0.02em;
  color: var(--off-white);
}
.pay-cta__price span {
  display: block; margin-top: 0.4rem;
  font-size: 0.72rem; font-weight: 400; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--muted);
}
.pay-cta__test, .pay-cta__closed {
  margin: 0; font-size: 0.74rem; letter-spacing: 0.02em; color: var(--muted);
}

/* ---------- the shop ---------- */

.shell--narrow { max-width: 46rem; }

.shop-bar { margin: 1rem 0 0; font-size: 0.86rem; }
.shop-bar a { color: var(--accent); text-decoration: none; }
.shop-bar a:hover { text-decoration: underline; }

.field__label {
  display: block; margin-bottom: 0.4rem;
  font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted);
}
.field__note { margin: 0.4rem 0 0; font-size: 0.72rem; color: var(--muted); }

.swatches { display: flex; gap: 0.5rem; }
.swatch {
  width: 1.9rem; height: 1.9rem; border-radius: 50%;
  border: 1px solid var(--ink-line); cursor: pointer; padding: 0;
  transition: box-shadow 0.2s var(--ease), border-color 0.2s var(--ease);
}
/* One rule per colourway in site.config.mjs. A colour with no rule here
   renders as an empty circle, so tests/shop.test.mjs checks the two lists
   against each other. */
.swatch--black { background: #111114; }
.swatch--white { background: #F1F1F3; }
.swatch--purple { background: #533350; }
.swatch[aria-checked='true'] {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent);
}
.swatch:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---------- account: the artist's own dashboard ---------- */
.dash { margin: 0 0 2.5rem; }
.dash__head { display: flex; align-items: center; gap: 0.75rem; margin: 0 0 1rem; }
.dash__badge {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  padding: 0.2rem 0.55rem; border-radius: 999px;
  background: var(--accent); color: var(--off-white);
}
.dash__msg, .dash__sub {
  border: 1px solid var(--ink-line); border-radius: 12px;
  padding: 1rem 1.1rem; margin-bottom: 0.75rem; background: var(--ink-raised);
}
/* An unread one is marked down its edge rather than by colouring the whole
   card: the card still has to be readable, and a tinted block of body text
   is harder to read than a plain one. */
.dash__msg--unread { border-left: 3px solid var(--accent); }
.dash__msg-head, .dash__sub-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-bottom: 0.5rem;
}
.dash__when { font-size: 0.78rem; color: var(--muted); white-space: nowrap; }
.dash__msg-body { margin: 0 0 0.75rem; white-space: pre-wrap; line-height: 1.55; }
.dash__state { margin: 0; font-size: 0.88rem; color: var(--muted); }
.dash__pill {
  display: inline-block; font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  padding: 0.2rem 0.55rem; border-radius: 999px; margin-right: 0.5rem;
  border: 1px solid currentColor;
}
.dash__state--good .dash__pill { color: var(--teal); }
.dash__state--waiting .dash__pill { color: var(--muted); }
.dash__state--closed .dash__pill { color: var(--muted); opacity: 0.8; }
.dash__owing { margin: 0.5rem 0 0; font-size: 0.82rem; color: var(--accent); }

/* ---------- account: signing in with Google ---------- */
.google-auth { margin: 1.4rem 0 0; }
/* Google renders its own button into this, at its own size. Centring it
   here rather than stretching it: the button has a fixed width Google
   controls, and a stretched one is a Google button that looks wrong. */
.google-auth #google-button { display: flex; justify-content: center; min-height: 44px; }
.auth-or {
  display: flex; align-items: center; gap: 0.75rem;
  margin: 1.4rem 0 0; color: var(--muted);
  font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase;
}
.auth-or::before, .auth-or::after { content: ''; flex: 1; height: 1px; background: var(--ink-line); }

.check { display: flex; align-items: center; gap: 0.6rem; margin: 1.2rem 0; font-size: 0.9rem; }
.check input { width: 1rem; height: 1rem; accent-color: var(--accent); }

.account__who { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; font-size: 0.9rem; }
.btn--small { padding: 0.5rem 1rem; font-size: 0.68rem; }

.cart__line {
  display: grid; grid-template-columns: 4rem 1fr auto; gap: 1rem;
  align-items: center; margin-bottom: 1rem; padding: 1rem;
}
.cart__image { width: 4rem; height: 4rem; object-fit: contain; }
.cart__detail h3 { margin: 0 0 0.2rem; }
.cart__detail p { margin: 0; }
.cart__qty { display: grid; gap: 0.4rem; justify-items: end; }
.cart__qty select { min-width: 6rem; }
.cart__line-total { margin: 0; font-weight: 600; color: var(--off-white); }
.cart__foot { margin-top: 2rem; display: grid; gap: 0.8rem; }
.cart__total { margin: 0; font-size: 1.4rem; }
.cart__total strong { color: var(--off-white); }
.cart__note { margin: 0; font-size: 0.8rem; color: var(--muted); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.admin__run { margin: 1.5rem 0; }
.admin__run-list { list-style: none; padding: 0; margin: 0.6rem 0 0; display: grid; gap: 0.35rem; }
.admin__run-list li {
  font-size: 0.9rem; color: var(--body);
  padding: 0.5rem 0.8rem; border: 1px solid var(--ink-line); border-radius: var(--radius);
  background: var(--ink-raised);
}

/* The cart's quantity control sits outside a .field, so it needs the same
   treatment the form controls get rather than the browser's default. */
.cart__qty select {
  font: inherit; font-size: 0.86rem;
  padding: 0.5rem 0.7rem;
  color: var(--off-white); background: var(--ink);
  border: 1px solid var(--ink-line); border-radius: var(--radius);
}
.cart__qty select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- not yet ---------- */

/* Anything the label has not shipped wears the V, so "coming soon" reads as a
   deliberate placeholder rather than an unfinished page. */
.soon { display: inline-flex; align-items: center; gap: 0.45em; }
.soon__mark {
  width: 1em; height: 1em; flex: none;
  opacity: 0.75;
  /* The mark ships in the brand off-white; on a red tag it should take the
     tag's colour instead of sitting on it as a second one. */
}
.card__tag--soon .soon__mark { width: 0.9em; height: 0.9em; margin-right: 0.4em; vertical-align: -0.08em; }
.gallery__note .soon__mark { margin-right: 0.45em; vertical-align: -0.15em; opacity: 0.6; }
.playlist__pending .soon { color: var(--muted); }

/* ---------- folding sections ---------- */

.fold > summary { list-style: none; cursor: pointer; }
.fold > summary::-webkit-details-marker { display: none; }
.fold__summary { display: block; position: relative; }
.fold__summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; border-radius: var(--radius); }
.section__head--fold { margin-bottom: 0; padding-right: 3rem; }

/* The chevron is the only thing that moves, so the heading does not shift
   when the section folds. */
.fold__hint {
  position: absolute; top: 0.2rem; right: 0;
  width: 2rem; height: 2rem; border-radius: 50%;
  border: 1px solid var(--ink-line);
  display: grid; place-items: center;
  transition: transform 0.25s var(--ease), border-color 0.2s var(--ease);
}
.fold__hint::before {
  content: ''; width: 0.5rem; height: 0.5rem;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: translateY(-2px) rotate(45deg);
}
.fold[open] .fold__hint { transform: rotate(180deg); }
.fold__summary:hover .fold__hint { border-color: var(--accent); }
.fold__body { margin-top: 2rem; }

@media (prefers-reduced-motion: reduce) { .fold__hint { transition: none; } }

/* ---------- the sets carousel ---------- */

.art--sets .art__track { width: 100%; max-width: 48rem; }
.art--sets .art__slide { width: 100%; }
.set__slide .card { height: 100%; }

/* ---------- release covers ---------- */

.release__head {
  display: grid; gap: clamp(1.2rem, 3vw, 2rem);
  grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
  align-items: start;
}
@media (max-width: 640px) { .release__head { grid-template-columns: 1fr; } }
.release__head .section__head { margin-bottom: 0; }

.release__cover {
  /* height: auto, or the height attribute wins over aspect-ratio and the
     artwork is cropped to a tall strip. */
  display: block; width: 100%; height: auto; aspect-ratio: 1 / 1;
  object-fit: cover; border-radius: var(--radius);
  border: 1px solid var(--ink-line); background: var(--ink-raised);
}

/* ---------- the label's own accounts, top right ---------- */

.socials { display: flex; align-items: center; gap: 0.9rem; }
.socials a {
  display: grid; place-items: center;
  width: 2rem; height: 2rem; border-radius: 50%;
  color: var(--muted);
  border: 1px solid transparent;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease), transform 0.2s var(--ease);
}
.socials a:hover { color: var(--off-white); border-color: var(--ink-line); transform: translateY(-1px); }
.socials a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.socials svg { display: block; }

/* The nav collapses behind the Menu button on small screens; the accounts
   stay put, because they are two icons rather than a list. */
@media (max-width: 900px) { .socials { gap: 0.4rem; } }

/* Full screen, top right. It hands the whole site the screen — which is the
   point next to a radio that keeps playing. The icon shows what the button
   will do, so the arrows point out while windowed and in while full. */
.fullscreen {
  display: grid; place-items: center;
  width: 2rem; height: 2rem; padding: 0; margin-left: 0.35rem;
  color: var(--muted);
  background: none;
  border: 1px solid transparent; border-radius: 50%;
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease), transform 0.2s var(--ease);
}
.fullscreen:hover { color: var(--off-white); border-color: var(--ink-line); transform: translateY(-1px); }
.fullscreen:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fullscreen svg { display: block; }
.fullscreen .fullscreen__out { display: none; }
.fullscreen[aria-pressed='true'] { color: var(--accent); }
.fullscreen[aria-pressed='true'] .fullscreen__in { display: none; }
.fullscreen[aria-pressed='true'] .fullscreen__out { display: block; }
/* No Fullscreen API, no button: a control that cannot do anything is worse
   than no control. The script adds this. */
.fullscreen[hidden] { display: none; }

/* Below this the header is already full — wordmark, Menu and the two
   accounts leave no room for a third control at 320px, whatever the gaps
   are. The button goes rather than the header overflowing, and a phone loses
   least by it: iOS has no Fullscreen API at all, and the installed app
   already runs without browser chrome. */
@media (max-width: 480px) { .fullscreen { display: none; } }

/* The hero clip and the landing gate are dark footage with a white mark on
   top, so they keep the dark palette even on a light site. Scoping the tokens
   rather than the rules means the scrim, the ghost button, the lede shadow and
   the mark all follow without a second set of rules. */
.hero:has(.hero__media),
.intro,
.ambient {
  --ink:        #09090A;
  --ink-raised: #0E0E10;
  --ink-line:   #1C1C20;
  --off-white:  #F5F5F7;
  --muted:      #8A8A93;
  --red:        #D62228;
  --teal:       #20C4BE;
  --mark-invert: 0;
  --body:       color-mix(in srgb, var(--off-white) 57%, var(--muted));
  --body-soft:  color-mix(in srgb, var(--off-white) 49%, var(--muted));
}

/* ---------- the hero clip ---------- */

/* Wallpaper behind the hero. The section already isolates and clips, so the
   video fills it and the content sits above on its own layer. */
.hero__media {
  position: absolute; inset: 0; z-index: 0;
  overflow: hidden;
  background: var(--ink);
}
.hero__video {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* Text over video is only readable with something between them. A flat tint
   would grey the footage out, so this darkens top and bottom — where the
   wordmark and the buttons are — and leaves the middle alone. */
.hero__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(to bottom,
      color-mix(in srgb, var(--ink) 84%, transparent) 0%,
      color-mix(in srgb, var(--ink) 58%, transparent) 38%,
      color-mix(in srgb, var(--ink) 72%, transparent) 72%,
      var(--ink) 100%);
}

/* The lede sits over the brightest part of the shot, so it carries its own
   shadow rather than making the whole scrim darker and flattening the
   footage. */
.hero .hero__lede { text-shadow: 0 1px 12px var(--ink), 0 0 30px var(--ink); }
/* A ghost button on video needs a ground of its own; on flat ink it does not,
   so this is scoped to the hero. */
.hero .btn--ghost {
  background: color-mix(in srgb, var(--ink) 55%, transparent);
  backdrop-filter: blur(6px);
}

.hero > .shell { position: relative; z-index: 1; }

/* A visitor who asked for less motion gets the still frame instead of the
   loop — from CSS, so nothing has to autoplay first and then be stopped. */
@media (prefers-reduced-motion: reduce) {
  .hero__video { display: none; }
  .hero__media {
    /* Named here rather than inline: the CSP refuses style attributes.
       Keep in step with hero.poster in site.config.mjs. */
    background-image: url('/assets/media/hero-poster.1882dedf.webp');
    background-size: cover;
    background-position: center;
  }
}

/* Narrower still: the mark gives up height before anything gives up its
   place. */
@media (max-width: 430px) {
  .wordmark__img { height: 1.7rem; }
  .nav-toggle { padding: 0.4rem 0.55rem; letter-spacing: 0.08em; }
}

/* ---------- dashboard settings ---------- */

.admin__as { color: var(--muted); font-size: 0.8rem; margin-left: 0.6rem; }

.admin__settings { max-width: 40rem; }

.opts { border: 0; padding: 0; margin: 1.75rem 0 0; }
.opts legend {
  padding: 0; font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted);
}
.opts__note { margin: 0.5rem 0 0.9rem; font-size: 0.8rem; color: var(--muted); max-width: 52ch; }
.opts__row { display: flex; flex-wrap: wrap; gap: 0.6rem; }

.opt {
  display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--ink-line); border-radius: var(--radius);
  background: var(--ink-raised); cursor: pointer;
  font-size: 0.85rem;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.opt:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--ink-line)); }
/* The radio is the control; the label is the target. Hiding it outright
   would take the keyboard with it, so it is only made invisible. */
.opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.opt:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--ink-raised));
}
.opt:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

.opt__swatch {
  width: 0.95rem; height: 0.95rem; border-radius: 50%;
  border: 1px solid var(--ink-line); flex: none;
}
.opt__swatch--mode-dark { background: #09090A; }
.opt__swatch--mode-light { background: #F6F6F8; }
.opt__swatch--mode-system { background: linear-gradient(135deg, #09090A 50%, #F6F6F8 50%); }
.opt__swatch--accent-red { background: #D62228; }
.opt__swatch--accent-teal { background: #20C4BE; }
.opt__swatch--accent-amber { background: #D98714; }
.opt__swatch--accent-violet { background: #7C5CFF; }
/* Motion and density are not colours, so they get no dot to misread. */
.opt__swatch--motion-full, .opt__swatch--motion-calm,
.opt__swatch--density-roomy, .opt__swatch--density-compact { display: none; }


/* ---------- the ambient screen ---------- */

/* The wordmark's easter egg: the landing clip, full bleed, with the mark over
   it. It sits above everything the page has, including the gate, and it never
   contains the radio player — it only covers it, so the stream carries on. */
.ambient {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--ink);
}
.ambient__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The clip is a lit porch, not a backdrop, so the mark needs a ground of its
   own: an even wash over the whole frame, and a deeper pool under the middle
   where the type sits. Both together, because either alone leaves one of the
   two unreadable. */
.ambient::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    radial-gradient(62% 62% at 50% 46%, rgba(9, 9, 10, 0.82), transparent 72%),
    linear-gradient(180deg, rgba(9, 9, 10, 0.5), rgba(9, 9, 10, 0.38) 45%, rgba(9, 9, 10, 0.62));
  pointer-events: none;
}
.ambient__stage {
  position: relative;
  z-index: 2;
  text-align: center;
  padding: 1.5rem;
}
.ambient__logo {
  display: block;
  width: min(560px, 70vw);
  height: auto;
  margin-inline: auto;
  filter: drop-shadow(0 2px 20px rgba(9, 9, 10, 0.85));
}
.ambient__name {
  margin: 0;
  font-size: clamp(2.6rem, 1.4rem + 6vw, 6rem);
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--off-white);
  line-height: 1;
}
.ambient__sub {
  margin: 0.9rem 0 0;
  font-size: 0.72rem;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--body-soft);
  text-indent: var(--track-wide);
  text-shadow: 0 1px 10px var(--ink);
}
.ambient__now {
  margin: 1.4rem 0 0;
  font-size: 0.82rem;
  color: var(--off-white);
  text-shadow: 0 1px 12px var(--ink);
}

.ambient__bar {
  position: absolute;
  z-index: 2;
  right: clamp(1rem, 4vw, 2.5rem);
  bottom: clamp(1rem, 4vw, 2.5rem);
  display: flex;
  gap: 0.6rem;
}
.ambient__btn {
  padding: 0.7rem 1.3rem;
  font: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--off-white);
  background: color-mix(in srgb, var(--ink) 70%, transparent);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  backdrop-filter: blur(8px);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.ambient__btn:hover { border-color: var(--accent); color: var(--accent); }

/* The widget: the same screen, pinned to a corner, so the scene stays up
   while the rest of the site is used. Only the box takes pointer events —
   everything around it is the page again. */
.ambient--widget {
  inset: auto clamp(1rem, 3vw, 2rem) clamp(1rem, 3vw, 2rem) auto;
  width: min(340px, 80vw);
  aspect-ratio: 16 / 10;
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.5);
}
.ambient--widget::before {
  background: radial-gradient(75% 75% at 50% 42%, rgba(9, 9, 10, 0.82), rgba(9, 9, 10, 0.5) 78%);
}
/* The stage sits above the button row rather than centred, so the two never
   share the same pixels in a box this small. */
.ambient--widget .ambient__stage { padding: 0.6rem 0.6rem 2.4rem; }
.ambient--widget .ambient__logo { width: 52%; }
.ambient--widget .ambient__sub { margin-top: 0.45rem; font-size: 0.5rem; }
.ambient--widget .ambient__now { margin-top: 0.5rem; font-size: 0.66rem; }
.ambient--widget .ambient__bar { right: 0.55rem; bottom: 0.55rem; gap: 0.35rem; }
.ambient--widget .ambient__btn {
  padding: 0.35rem 0.6rem; font-size: 0.55rem; letter-spacing: 0.1em; backdrop-filter: none;
}

/* ---------- the mailing-list popup ---------- */

/* Shown once, on the way in from the gate. It is an offer, not a wall: the
   backdrop, Escape and two buttons all close it, which is the difference
   between a dialog and a trap. */
.promo-open body { overflow: hidden; }

.promo {
  position: fixed;
  inset: 0;
  z-index: 250;
  display: grid;
  place-items: center;
  padding: 1.25rem;
  background: color-mix(in srgb, var(--ink) 78%, transparent);
  backdrop-filter: blur(6px);
  animation: promo-in 0.35s var(--ease) both;
}
@keyframes promo-in { from { opacity: 0; } to { opacity: 1; } }

.promo__card {
  position: relative;
  width: min(30rem, 100%);
  max-height: calc(100dvh - 2.5rem);
  overflow-y: auto;
  padding: clamp(1.75rem, 5vw, 2.5rem);
  text-align: center;
  background: var(--ink-raised);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  animation: promo-rise 0.4s var(--ease) both;
}
@keyframes promo-rise { from { transform: translateY(12px); } to { transform: none; } }

.promo__mark {
  display: block;
  width: 2.75rem; height: auto;
  margin: 0 auto 1.1rem;
  filter: invert(var(--mark-invert));
}
.promo .eyebrow { margin-bottom: 0.6rem; }
.promo__title { margin-bottom: 0.6rem; font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2rem); }
.promo__body { max-width: 34ch; margin-inline: auto; color: var(--body); }

.promo__form { display: grid; gap: 0.7rem; margin-top: 1.4rem; }
.promo__input {
  width: 100%;
  padding: 0.85rem 1rem;
  font: inherit;
  color: var(--off-white);
  background: var(--ink);
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  text-align: center;
}
.promo__input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.promo__status { min-height: 1.2em; margin: 0; font-size: 0.85rem; }

/* The honeypot. Off-screen rather than display:none, because a bot that
   reads styles skips what is hidden and fills what is not. */
.promo__trap {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.promo__close {
  position: absolute;
  top: 0.6rem; right: 0.6rem;
  display: grid; place-items: center;
  width: 2rem; height: 2rem;
  color: var(--muted);
  background: none;
  border: 1px solid transparent; border-radius: 50%;
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.promo__close:hover { color: var(--off-white); border-color: var(--ink-line); }
.promo__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Saying no is a real control, not fine print — it is as easy to find as
   the button that says yes. */
.promo__skip {
  margin-top: 1rem;
  padding: 0.35rem 0.5rem;
  font: inherit; font-size: 0.8rem;
  color: var(--muted);
  background: none; border: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.promo__skip:hover { color: var(--off-white); }
.promo__skip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

@media (prefers-reduced-motion: reduce) {
  .promo, .promo__card { animation: none; }
}

/* The export controls under a dashboard list. */
.admin__export {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--ink-line);
}
.admin__export .stat__note { margin: 0; }
.admin__export .stat__note[data-state='ok'] { color: var(--on-air); }
.admin__export .stat__note[data-state='error'] { color: color-mix(in srgb, #E5484D 70%, var(--off-white)); }

/* A note in the label's own voice, set apart from the page's copy without
   shouting. Used for the founder note on the roster. */
.note {
  display: block;
  max-width: 62ch;
  margin: clamp(2rem, 5vw, 3rem) 0 0;
  padding: clamp(1.25rem, 3vw, 1.75rem);
  background: var(--ink-raised);
  border: 1px solid var(--ink-line);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius);
}
.note p { margin: 0 0 0.9em; }
.note p:last-child { margin-bottom: 0; }
.note__sign { font-size: 0.85rem; color: var(--muted); }

/* The set's own title on YouTube, under the player. Quiet, because the short
   name above it is what the eye should land on — this is for the person who
   wants to go and find it on the channel. */
.set__title {
  margin: 0.75rem 0 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--muted);
}
.set__title a { color: inherit; text-decoration-color: color-mix(in srgb, var(--accent) 50%, transparent); }
.set__title a:hover { color: var(--off-white); }

/* A tick-box with its reason next to it, for opt-ins that ride along with a
   form rather than standing on their own. */
.check {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.6rem;
  align-items: start;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--body);
  cursor: pointer;
}
.check input { margin: 0.2rem 0 0; accent-color: var(--accent); width: 1rem; height: 1rem; }
.check:hover span { color: var(--off-white); }

/* Checking out without an account: the details asked for once, on the cart
   page, instead of standing between somebody and the basket. */
.guest { margin: 2rem 0; display: grid; gap: 1rem; }
.guest__set {
  display: grid;
  gap: 0.9rem;
  margin: 0;
  padding: 1.25rem;
  border: 1px solid var(--ink-line);
  border-radius: var(--radius);
  background: var(--ink-raised);
}
.guest__set legend {
  padding: 0 0.5rem;
  font-size: 0.7rem;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--muted);
}
@media (min-width: 640px) {
  .guest__set { grid-template-columns: 1fr 1fr; }
  .guest__set legend { grid-column: 1 / -1; }
}

/* ---------- the station's transport ---------- */

/* Shown only once YouTube has answered the page — see player.js. A slider
   that might do nothing is worse than no slider. */
.live__transport {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}
.live__btn {
  display: grid; place-items: center;
  width: 2rem; height: 2rem; padding: 0;
  color: var(--off-white);
  background: color-mix(in srgb, var(--ink) 70%, transparent);
  border: 1px solid var(--ink-line); border-radius: 50%;
  cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.live__btn:hover { border-color: var(--accent); color: var(--accent); }
.live__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.live__vol { display: flex; align-items: center; gap: 0.4rem; flex: 1 1 auto; color: var(--muted); }
.live__vol input[type='range'] {
  flex: 1 1 auto; min-width: 4rem; max-width: 9rem;
  accent-color: var(--accent);
  background: none;
}
.live__vol input[type='range']:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* What is waiting. Only there when something is. */
.live__queue { margin-top: 0.8rem; padding-top: 0.7rem; border-top: 1px solid var(--ink-line); }
.live__queue-head {
  margin: 0 0 0.4rem;
  font-size: 0.65rem; letter-spacing: var(--track-wide); text-transform: uppercase;
  color: var(--muted);
}
.live__queue ol { margin: 0; padding-left: 1.1rem; display: grid; gap: 0.25rem; font-size: 0.8rem; color: var(--body); }
.live__clear {
  margin-top: 0.5rem; padding: 0;
  font: inherit; font-size: 0.75rem;
  color: var(--muted); background: none; border: 0;
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
.live__clear:hover { color: var(--off-white); }

/* "Play here" sits under a playlist's own embed. */
.playlist__queue { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin: 0.9rem 0 0; }
.playlist__queue .form__status { margin: 0; font-size: 0.78rem; }

/* The same controls on the full-screen view. */
.ambient__air {
  display: inline-flex; align-items: center; gap: 0.45rem;
  margin: 1.1rem 0 0;
  font-size: 0.7rem; letter-spacing: var(--track-wide); text-transform: uppercase;
  color: var(--body);
}
.ambient__dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--muted); }

/* The dot carries the colour and the label stays readable. Signal red on
   near-black is about 3.9:1 — fine for a 8px dot, which only has to be
   distinguishable, and not enough for 11px type, which has to be read. The
   badge solves the same problem by mixing the accent towards white; there is
   no mix that clears AA at this size, so the words keep the body colour and
   say which state it is in themselves. */
.ambient__air[data-state='on'] .ambient__dot {
  background: var(--on-air);
  animation: live-pulse 1.6s var(--ease) infinite;
}
.ambient__air[data-state='list'] .ambient__dot {
  background: var(--accent);
  animation: live-pulse 1.6s var(--ease) infinite;
}
@media (prefers-reduced-motion: reduce) { .ambient__air .ambient__dot { animation: none; } }

.ambient__transport {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 0.6rem;
  margin: 1rem auto 0;
  max-width: 22rem;
}
.ambient__queued {
  display: block;
  margin: 0.55rem auto 0;
  font-size: 0.72rem; color: var(--muted);
}

/* Queueing from the full-screen view. The same buttons as the playlists
   page, in the one shape that fits over a moving picture. */
.ambient__picks {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 0.4rem;
  margin: 0.9rem auto 0;
  max-width: 30rem;
}
.ambient__picks-head {
  flex: 1 0 100%;
  font-size: 0.65rem; letter-spacing: var(--track-wide); text-transform: uppercase;
  color: var(--muted);
}
.ambient__pick {
  padding: 0.35rem 0.7rem;
  font: inherit; font-size: 0.72rem; letter-spacing: var(--track);
  color: var(--off-white);
  background: color-mix(in srgb, var(--ink) 60%, transparent);
  border: 1px solid var(--ink-line); border-radius: 999px;
  cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.ambient__pick:hover { border-color: var(--accent); }
.ambient__pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ambient__picks .form__status { flex: 1 0 100%; margin: 0; font-size: 0.72rem; }

/* In the corner it is a scene, not a console. */
.ambient--widget .ambient__air,
.ambient--widget .ambient__queued,
.ambient--widget .ambient__picks,
.ambient--widget .ambient__transport { display: none; }

/* ---------- KoalaQueue ---------- */

/* One wide card rather than a tier card, because it is deliberately not a
   fourth tier — see the comment in tiers.html. */
.koala { padding: clamp(1.4rem, 3vw, 2.2rem); }
.koala__name {
  margin: 0.7rem 0 0;
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2rem);
  font-weight: 600; letter-spacing: -0.015em;
  color: var(--off-white);
}
.koala__lede {
  margin: 0.7rem 0 0;
  max-width: 56ch;
  font-size: 1rem; color: var(--body);
}

/* The three steps, numbered because they happen in order. */
.koala__steps {
  list-style: none;
  counter-reset: koala;
  margin: 1.8rem 0 0; padding: 0;
  display: grid; gap: 1.1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}
.koala__steps > li { counter-increment: koala; }
.koala__steps h4 {
  display: flex; align-items: baseline; gap: 0.55rem;
  margin: 0 0 0.4rem;
  font-size: 0.95rem; font-weight: 600; color: var(--off-white);
}
.koala__steps h4::before {
  content: counter(koala);
  font-size: 0.68rem; letter-spacing: var(--track-wide);
  color: var(--accent);
}
.koala__steps p { margin: 0; font-size: 0.88rem; color: var(--body-soft); }

/* The promises. Set apart because a rule you have to hunt for is not a rule
   anyone is holding you to. */
.koala__rules {
  margin-top: 1.8rem; padding-top: 1.4rem;
  border-top: 1px solid var(--ink-line);
}
.koala__rules h4 {
  margin: 0 0 0.8rem;
  font-size: 0.68rem; letter-spacing: var(--track-wide); text-transform: uppercase;
  color: var(--muted);
}
.koala__rules ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem; }
.koala__rules li {
  position: relative; padding-left: 1.3rem;
  font-size: 0.9rem; color: var(--body);
}
.koala__rules li::before {
  content: ''; position: absolute; left: 0; top: 0.62em;
  width: 7px; height: 1px; background: var(--accent);
}
.koala__note { margin: 1rem 0 0; font-size: 0.78rem; line-height: 1.6; color: var(--muted); }

.koala__form {
  margin-top: 1.8rem; padding-top: 1.4rem;
  border-top: 1px solid var(--ink-line);
  display: grid; gap: 0.6rem;
  max-width: 26rem;
}
.koala__form h4 { margin: 0; font-size: 0.95rem; font-weight: 600; color: var(--off-white); }
.koala__form .koala__note { margin: 0; }
.koala__form .btn { justify-self: start; }
.koala__form .form__status:empty { display: none; }

/* The sign-up reuses .field, so the input matches every other on the site.
   .form's own gap is wider than this stack wants. */
.koala__form { gap: 0.7rem; }
.koala__form .field { gap: 0; }
/* .tier__foot is centred under a tier card. Here it closes a left-aligned
   block, and centring it reads as a stray caption. */
.koala .tier__foot { text-align: left; margin-top: 1.4rem; }

/* The link to the newest Limelight episode, under the looping clip. Hidden
   until the feed has actually answered, so the row never reserves space for
   something that may not arrive. */
.episode__latest {
  display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap;
  margin: 1.1rem 0 0;
}
.episode__meta { font-size: 0.82rem; opacity: 0.7; }

/* ===========================================================
   Discography and sets
   -------------------------------------------------------------------------
   The artist pages' releases (disco.js) and the playlists page's sets
   (mixes.js) share one vocabulary: a release or set on stage, a wall of
   covers to choose from, chips for type and date, and one player. Kept in
   this block so it can be read, and replaced, as one piece.
   ========================================================================= */

/* ---------- shared: tags and chips ---------- */

.tag {
  display: inline-flex; align-items: center;
  padding: 0.24rem 0.6rem;
  font-size: 0.62rem; font-weight: 600; line-height: 1.2;
  letter-spacing: 0.16em; text-transform: uppercase; white-space: nowrap;
  color: var(--off-white);
  background: color-mix(in srgb, var(--ink) 70%, transparent);
  border: 1px solid var(--ink-line); border-radius: 999px;
}
/* Red as a fill is otherwise off-brand; a small "New" is the one place it
   earns it, because it is the thing the eye should find first. */
.tag--new { color: #fff; background: var(--accent); border-color: var(--accent); }
.tag--soon { color: var(--accent); border-color: var(--accent); }

.chip {
  font: inherit; font-size: 0.68rem; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase;
  padding: 0.5rem 0.9rem;
  color: var(--body); background: none;
  border: 1px solid var(--ink-line); border-radius: 999px;
  cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease), background 0.2s var(--ease);
}
.chip span { margin-left: 0.3rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.chip:hover { border-color: var(--accent); color: var(--off-white); }
.chip[aria-pressed='true'] { color: var(--ink); background: var(--off-white); border-color: var(--off-white); }
.chip[aria-pressed='true'] span { color: inherit; opacity: 0.6; }

/* ---------- the discography ---------- */

.disco { position: relative; }
.disco__head { margin-bottom: clamp(1.75rem, 4vw, 2.75rem); }
.disco__heading { margin-bottom: 0.3rem; }
.disco__summary { margin: 0; font-size: 0.85rem; letter-spacing: 0.04em; color: var(--muted); }

/* A release on stage: the cover large, its tracks beside it. With the script
   off every release is listed like this, one after another; with it on, one
   is shown at a time and the wall below chooses which. */
.rel {
  display: grid; align-items: start;
  grid-template-columns: minmax(0, 26rem) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
}
.rel + .rel { margin-top: 3.5rem; padding-top: 3.5rem; border-top: 1px solid var(--ink-line); }
.disco--live .rel { animation: rel-in 0.35s var(--ease); }
@keyframes rel-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

.rel__art { position: sticky; top: calc(var(--header-h, 78px) + 1.5rem); }
.rel__cover {
  display: block; width: 100%; height: auto; aspect-ratio: 1; object-fit: cover;
  background: var(--media-bg); border-radius: calc(var(--radius) * 1.5);
  box-shadow: 0 34px 70px -34px rgb(0 0 0 / 75%), 0 0 0 1px var(--ink-line);
}
.rel__cover--empty { display: grid; place-items: center; font-size: 4rem; color: var(--muted); }

.rel__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.45rem 0.8rem;
  margin: 0 0 1rem; font-size: 0.78rem; letter-spacing: 0.04em; color: var(--muted);
}
.rel__meta time { color: var(--body); }
.rel__title {
  margin: 0 0 0.3rem; overflow-wrap: anywhere;
  font-size: clamp(1.8rem, 1.25rem + 2.2vw, 3rem); letter-spacing: 0.02em;
}
.rel__title:focus { outline: none; }
.rel__title:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.rel__by { margin: 0 0 1rem; font-size: 0.72rem; letter-spacing: var(--track-wide); text-transform: uppercase; color: var(--muted); }
.rel__note { max-width: 60ch; }
.rel__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.55rem; margin: 1.4rem 0 1.6rem; }
.rel__play { padding: 0.8rem 1.5rem 0.8rem 1.25rem; }
.rel__play svg { width: 1.15em; height: 1.15em; }
.rel__link {
  padding: 0.55rem 0.85rem;
  font-size: 0.66rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  text-decoration: none; color: var(--body);
  border: 1px solid var(--ink-line); border-radius: 999px;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.rel__link:hover { color: var(--off-white); border-color: var(--accent); }

/* The tracklist reads as a list here, not a stack of buttons: rows divided by
   hairlines, the number, the title, the time, and a play control that fills
   in when the row is hovered or playing. */
.disco .tracklist { gap: 0; border-top: 1px solid var(--ink-line); }
.disco .trk { display: flex; align-items: center; gap: 0.5rem; border-bottom: 1px solid var(--ink-line); }
.disco .track {
  min-width: 0; min-height: 3.4rem; gap: 1rem;
  padding: 0.7rem 0.6rem; text-decoration: none;
  background: none; border: 0; border-radius: var(--radius);
}
.disco .track:hover { background: color-mix(in srgb, var(--off-white) 5%, transparent); color: var(--off-white); }
.disco .track[aria-current='true'] { color: var(--off-white); background: color-mix(in srgb, var(--accent) 13%, transparent); }
.disco .track[aria-current='true'] .track__n { color: var(--accent); }
.disco .track__n { flex: none; width: 1.5rem; font-variant-numeric: tabular-nums; }
.disco .track__title { min-width: 0; overflow-wrap: anywhere; }
.track__time { flex: none; font-size: 0.8rem; font-variant-numeric: tabular-nums; color: var(--muted); }
.disco .track__play {
  flex: none; display: grid; place-items: center;
  width: 2.1rem; height: 2.1rem; border-radius: 50%;
  color: var(--off-white); border: 1px solid var(--ink-line);
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease);
}
.disco .track__play svg { width: 0.95rem; height: 0.95rem; margin-left: 2px; }
.disco .track:hover .track__play,
.disco .track:focus-visible .track__play,
.disco .track[aria-current='true'] .track__play { color: #fff; background: var(--accent); border-color: var(--accent); }
.disco .track--out { cursor: pointer; }
.disco span.track--out { cursor: default; }
.disco .track--out:hover { background: color-mix(in srgb, var(--off-white) 4%, transparent); }
.disco .track__out { flex: none; }
.disco .srcs > summary { display: flex; align-items: center; padding: 0.3rem 0.5rem; font-size: 0.62rem; }
.srcs__icon { display: none; width: 1rem; height: 1rem; }

/* The wall of covers. */
.disco__browse { margin-top: clamp(3rem, 7vw, 5rem); padding-top: 2rem; border-top: 1px solid var(--ink-line); }
.disco__browsehead {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 1rem; margin-bottom: 1.6rem;
}
.disco__sub { margin: 0; font-size: 0.74rem; font-weight: 600; letter-spacing: var(--track-wide); text-transform: uppercase; color: var(--muted); }
.disco__filters { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.disco__grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 1.6rem 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(min(10.5rem, 42%), 1fr));
}
.disco__cell { min-width: 0; }
.disco__tile { display: grid; gap: 0.3rem; color: inherit; text-decoration: none; border-radius: var(--radius); }
.disco__tile:focus-visible { outline-offset: 5px; }
.disco__art {
  position: relative; display: block; aspect-ratio: 1; overflow: hidden;
  background: var(--media-bg); border-radius: var(--radius);
  box-shadow: 0 0 0 1px var(--ink-line);
  transition: box-shadow 0.2s var(--ease);
}
.disco__art img { display: block; width: 100%; height: 100%; object-fit: cover; transition: transform 0.45s var(--ease); }
.disco__tile:hover .disco__art img { transform: scale(1.04); }
.disco__art .tag { position: absolute; top: 0.5rem; left: 0.5rem; }
.disco__blank { display: grid; place-items: center; height: 100%; font-size: 2.2rem; color: var(--muted); }
.disco__tile[aria-current='true'] .disco__art { box-shadow: 0 0 0 2px var(--accent); }
.disco__name { margin-top: 0.45rem; font-size: 0.9rem; font-weight: 600; line-height: 1.3; color: var(--off-white); overflow-wrap: anywhere; }
.disco__meta { font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); }
.disco__tile[aria-current='true'] .disco__meta { color: var(--accent); }
.disco__more { margin: 2.25rem 0 0; text-align: center; }

/* The one player: a bar that stays at the bottom of the screen while the
   discography is on it, and is put away with the rest of the section. */
.dock {
  position: sticky; bottom: max(0.75rem, env(safe-area-inset-bottom)); z-index: 30;
  display: grid; align-items: center; gap: 0.65rem 0.9rem;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: 'thumb text close' 'frame frame frame';
  margin-top: 2rem; padding: 0.75rem;
  background: color-mix(in srgb, var(--ink-raised) 92%, transparent);
  backdrop-filter: blur(14px);
  border: 1px solid var(--ink-line); border-radius: calc(var(--radius) * 2.5);
  box-shadow: 0 24px 60px -24px rgb(0 0 0 / 70%);
}
.dock__thumb { grid-area: thumb; width: 48px; height: 48px; object-fit: cover; border-radius: var(--radius); }
.dock__text { grid-area: text; min-width: 0; }
.dock__label { margin: 0; font-size: 0.6rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); }
.dock__title { margin: 0; font-size: 0.92rem; font-weight: 600; color: var(--off-white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dock__frame { grid-area: frame; min-width: 0; }
.dock__embed { display: block; width: 100%; border: 0; border-radius: var(--radius); background: var(--media-bg); }
.dock__embed--spotify { height: 80px; }
.dock__embed--apple { height: 175px; }
.dock__embed--video { aspect-ratio: 16 / 9; max-height: 52vh; }
.dock__close {
  grid-area: close; display: grid; place-items: center;
  width: 2.3rem; height: 2.3rem; padding: 0;
  color: var(--off-white); background: none;
  border: 1px solid var(--ink-line); border-radius: 50%; cursor: pointer;
}
.dock__close svg { width: 1rem; height: 1rem; }
.dock__close:hover { border-color: var(--accent); color: var(--accent); }
@media (min-width: 900px) {
  .dock { grid-template-columns: auto minmax(0, 15rem) minmax(0, 1fr) auto; grid-template-areas: 'thumb text frame close'; }
  .dock[data-kind='video'] { grid-template-columns: auto minmax(0, 1fr) minmax(0, 26rem) auto; }
}

@media (max-width: 760px) {
  .rel { grid-template-columns: 1fr; }
  .rel__art { position: static; width: min(100%, 24rem); }
}
@media (max-width: 520px) {
  .disco__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.3rem 0.9rem; }
  .disco .track { gap: 0.7rem; padding-inline: 0.35rem; }
  /* A phone keeps the width for the title: the menu folds to a ⋯ button. */
  .disco .srcs > summary { display: grid; place-items: center; width: 2.1rem; height: 2.1rem; padding: 0; border-radius: 50%; }
  .disco .srcs__label { display: none; }
  .srcs__icon { display: block; }
}

/* ---------- the sets ---------- */

.mixes__stage {
  display: grid; align-items: center;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(1.25rem, 3.5vw, 3rem);
  margin-bottom: clamp(2.75rem, 6vw, 4.5rem);
}
.mixes__screen {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden;
  background: var(--media-bg); border-radius: calc(var(--radius) * 2);
  box-shadow: 0 34px 70px -34px rgb(0 0 0 / 75%), 0 0 0 1px var(--ink-line);
}
.mixes__poster { position: absolute; inset: 0; display: block; }
.mixes__poster img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s var(--ease); }
.mixes__poster::after {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 50%, rgb(0 0 0 / 5%), rgb(0 0 0 / 45%));
}
.mixes__poster:hover img { transform: scale(1.03); }
.mixes__bigplay {
  position: absolute; left: 50%; top: 50%; z-index: 1;
  display: grid; place-items: center;
  width: 4.6rem; height: 4.6rem; margin: -2.3rem 0 0 -2.3rem;
  color: #fff; background: var(--accent); border-radius: 50%;
  box-shadow: 0 12px 30px rgb(0 0 0 / 45%);
  transition: transform 0.25s var(--ease);
}
.mixes__bigplay svg { width: 1.9rem; height: 1.9rem; margin-left: 4px; }
.mixes__poster:hover .mixes__bigplay, .mixes__poster:focus-visible .mixes__bigplay { transform: scale(1.07); }
.mixes__frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.mixes__name { margin: 0 0 0.35rem; font-size: clamp(1.6rem, 1.15rem + 1.7vw, 2.5rem); }
.mixes__sub { margin-bottom: 0.7rem; color: var(--off-white); }
.mixes__info .set__title { margin-top: 1rem; }
.mixes__empty { color: var(--muted); }

.mixes__group + .mixes__group { margin-top: clamp(2.75rem, 6vw, 4rem); }
.mixes__grouphead {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 1.1rem;
  margin-bottom: 1.4rem; padding-top: 1.6rem; border-top: 1px solid var(--ink-line);
}
.mixes__grouphead p { margin: 0; font-size: 0.85rem; color: var(--muted); }
.mixes__grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 1.6rem 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
}
.mixes__cell { min-width: 0; }
.mix { display: grid; gap: 0.25rem; color: inherit; text-decoration: none; border-radius: var(--radius); }
.mix:focus-visible { outline-offset: 5px; }
.mix__thumb {
  position: relative; display: block; aspect-ratio: 16 / 9; overflow: hidden;
  background: var(--media-bg); border-radius: var(--radius);
  box-shadow: 0 0 0 1px var(--ink-line);
}
.mixes__group--show .mix__thumb { box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 40%, var(--ink-line)); }
.mix__thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.45s var(--ease); }
.mix:hover .mix__thumb img { transform: scale(1.04); }
.mix[aria-current='true'] .mix__thumb { box-shadow: 0 0 0 2px var(--accent); }
.mix__thumb .tag--new { position: absolute; top: 0.5rem; left: 0.5rem; }
.mix__ep, .mix__len {
  position: absolute; bottom: 0.5rem;
  padding: 0.18rem 0.5rem; border-radius: 3px;
  font-size: 0.64rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: #fff; background: rgb(0 0 0 / 72%);
}
.mix__ep { left: 0.5rem; background: var(--accent); }
.mix__len { right: 0.5rem; }
.mix__play {
  position: absolute; left: 50%; top: 50%;
  display: grid; place-items: center;
  width: 3rem; height: 3rem; margin: -1.5rem 0 0 -1.5rem;
  color: #fff; background: var(--accent); border-radius: 50%;
  opacity: 0; transform: scale(0.9);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
}
.mix__play svg { width: 1.2rem; height: 1.2rem; margin-left: 3px; }
.mix:hover .mix__play, .mix:focus-visible .mix__play { opacity: 1; transform: none; }
.mix__name { margin-top: 0.5rem; font-weight: 600; line-height: 1.3; color: var(--off-white); overflow-wrap: anywhere; }
.mix__meta { font-size: 0.78rem; color: var(--muted); }

@media (max-width: 860px) {
  .mixes__stage { grid-template-columns: 1fr; }
}
/* On a phone the wall becomes a list — a thumbnail and its words side by
   side — so thirteen sets are a scroll, not thirteen screens. */
@media (max-width: 560px) {
  .mixes__grid { grid-template-columns: 1fr; gap: 0.9rem; }
  .mix { grid-template-columns: 8.5rem minmax(0, 1fr); column-gap: 0.9rem; align-items: center; align-content: center; }
  .mix__thumb { grid-row: 1 / span 2; }
  .mix__name { margin-top: 0; align-self: end; }
  .mix__meta { align-self: start; }
  .mix__thumb .tag--new { top: 0.3rem; left: 0.3rem; padding: 0.15rem 0.4rem; }
  .mix__ep, .mix__len { bottom: 0.3rem; font-size: 0.58rem; }
  .mix__ep { left: 0.3rem; }
  .mix__len { right: 0.3rem; }
}

@media (prefers-reduced-motion: reduce) {
  .disco--live .rel { animation: none; }
  .disco__art img, .mix__thumb img, .mixes__poster img { transition: none; }
}
=======
/* ==========================================================================
   Admin: submissions inbox, artist email, newsletter
   Everything below belongs to the Submissions and Newsletter tabs and the
   Email block in Settings. Kept together so it can be read, or removed, as
   one piece.
   ========================================================================== */

/* The groove rings reach past the section on both sides, and overflow:
   hidden can still be scrolled by script — find-in-page or a scroll into
   view would slide this long page sideways. clip cannot be scrolled. */
.grooves:has(#admin-panel) { overflow: clip; }

/* A grid item will not shrink below its content by default, and one long
   email address would then push the whole panel sideways on a phone. */
.tabs__panel > * { min-width: 0; }

.admin__notice--ok { border-color: color-mix(in srgb, var(--teal) 45%, var(--ink-line)); }

/* ---------- the pipeline: stages as filters, each with its count ---------- */

.pipeline {
  display: grid; gap: 0.5rem;
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
}
.pipeline__stage {
  appearance: none; font: inherit; cursor: pointer; text-align: left;
  display: grid; gap: 0.15rem; padding: 0.75rem 0.9rem;
  background: var(--ink-raised); color: var(--muted);
  border: 1px solid var(--ink-line); border-radius: var(--radius);
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.pipeline__stage:hover { color: var(--off-white); }
.pipeline__stage[aria-pressed='true'] { border-color: var(--accent); color: var(--off-white); }
.pipeline__count { font-size: 1.5rem; font-weight: 600; line-height: 1.1; color: var(--off-white); }
.pipeline__label { font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase; }
#intake-money:empty { display: none; }

/* ---------- search and filters ---------- */

.inbox__tools {
  display: grid; gap: 0.75rem 1rem;
  grid-template-columns: 2fr repeat(5, minmax(0, 1fr));
  align-items: end;
}
.inbox__tools .field input, .inbox__tools .field select { padding: 0.55rem 0.7rem; font-size: 0.85rem; }
@media (max-width: 1080px) { .inbox__tools { grid-template-columns: repeat(3, minmax(0, 1fr)); } .inbox__search { grid-column: 1 / -1; } }
@media (max-width: 560px) { .inbox__tools { grid-template-columns: 1fr 1fr; } }

/* ---------- list and detail ----------
   Side by side on a wide screen. On a phone the open record replaces the
   list, with a way back, because two columns at 390px are two useless ones. */

.inbox { display: grid; gap: 1.25rem; grid-template-columns: minmax(0, 1fr); align-items: start; }
.inbox--open { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.4fr); }
.inbox__list { display: grid; gap: 0.5rem; min-width: 0; }
.inbox__back { display: none; justify-self: start; }
@media (max-width: 900px) {
  .inbox--open { grid-template-columns: minmax(0, 1fr); }
  .inbox--open .inbox__list { display: none; }
  .inbox__back { display: inline-flex; }
}

.inbox__row {
  appearance: none; font: inherit; text-align: left; cursor: pointer; width: 100%;
  display: grid; gap: 0.3rem; padding: 0.8rem 0.95rem; min-width: 0;
  background: var(--ink-raised); color: var(--body);
  border: 1px solid var(--ink-line); border-left: 3px solid transparent; border-radius: var(--radius);
}
.inbox__row:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--ink-line)); }
.inbox__row[aria-current='true'] { border-color: var(--accent); border-left-color: var(--accent); }
.inbox__row-top { display: flex; justify-content: space-between; gap: 0.75rem; min-width: 0; }
.inbox__artist { color: var(--off-white); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inbox__when { flex: none; font-size: 0.75rem; color: var(--muted); }
.inbox__track { font-size: 0.88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inbox__meta { display: flex; flex-wrap: wrap; gap: 0.35rem 0.5rem; align-items: center; }
.inbox__facts { font-size: 0.72rem; color: var(--muted); }

.pill {
  display: inline-block; padding: 0.15rem 0.5rem; border-radius: 999px;
  font-size: 0.64rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  border: 1px solid var(--ink-line); color: var(--muted); white-space: nowrap;
}
.pill--new { color: var(--off-white); border-color: color-mix(in srgb, var(--off-white) 35%, var(--ink-line)); }
.pill--shortlisted { color: var(--teal); border-color: color-mix(in srgb, var(--teal) 50%, var(--ink-line)); }
.pill--approved { color: var(--on-air); border-color: color-mix(in srgb, var(--on-air) 50%, var(--ink-line)); }
.pill--declined, .pill--archived { border-style: dashed; }
.pill--paid { color: var(--on-air); }
.pill--unpaid { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 50%, var(--ink-line)); }

.inbox__detail {
  display: grid; gap: 1rem; min-width: 0; padding: 1.25rem;
  background: var(--ink-raised); border: 1px solid var(--ink-line); border-radius: var(--radius);
}
.inbox__detail[hidden] { display: none; }
.inbox__detail > * { min-width: 0; }
.inbox__detail .admin__row-actions { margin-top: 0; }
.inbox__detail .form__status:empty { display: none; }
.inbox__head { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.inbox__title { margin: 0; font-size: 1.3rem; overflow-wrap: anywhere; }
.inbox__sub {
  margin: 0.5rem 0 0; font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted);
}
.inbox__listen { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.inbox__listen .btn { padding: 0.5rem 0.9rem; font-size: 0.68rem; }
.inbox__player { display: block; width: 100%; border: 0; border-radius: 12px; background: var(--media-bg); }

.inbox__facts-list {
  margin: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.45rem 1rem; font-size: 0.88rem;
}
.inbox__facts-list dt { color: var(--muted); font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; padding-top: 0.15rem; }
.inbox__facts-list dd { margin: 0; color: var(--body); overflow-wrap: anywhere; }
.inbox__story-text { margin: 0.4rem 0 0; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--body); }

/* ---------- composing to an artist, and notes ---------- */

.composer { display: grid; gap: 0.8rem; padding-top: 1rem; border-top: 1px solid var(--ink-line); }
.composer .field textarea { min-height: 170px; }
.composer--note textarea { min-height: 80px; }
.composer--note .btn { justify-self: start; }
.composer__preview { margin: 0; font-size: 0.82rem; color: var(--muted); overflow-wrap: anywhere; }

/* ---------- the contact timeline ---------- */

.timeline { list-style: none; margin: 0; padding: 0 0 0 1rem; border-left: 1px solid var(--ink-line); display: grid; gap: 0.9rem; }
.timeline__item { position: relative; display: grid; gap: 0.25rem; }
.timeline__item::before {
  content: ''; position: absolute; left: calc(-1rem - 4px); top: 0.45rem;
  width: 7px; height: 7px; border-radius: 50%; background: var(--muted);
}
.timeline__item--email::before { background: var(--accent); }
.timeline__item--note::before { background: var(--teal); }
.timeline__item--failed .timeline__head strong { color: var(--accent); }
.timeline__head { margin: 0; display: flex; flex-wrap: wrap; gap: 0.25rem 0.75rem; align-items: baseline; font-size: 0.85rem; }
.timeline__head strong { color: var(--off-white); font-weight: 600; }
.timeline__when { font-size: 0.72rem; color: var(--muted); }
.timeline__subject { margin: 0; font-size: 0.85rem; color: var(--body); overflow-wrap: anywhere; }
.timeline__body {
  margin: 0; font: inherit; font-size: 0.85rem; color: var(--body);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.timeline__more summary { cursor: pointer; font-size: 0.75rem; color: var(--muted); }
.timeline__more[open] summary { margin-bottom: 0.4rem; }

/* ---------- newsletter ---------- */

.nl { display: grid; gap: 1.5rem; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); align-items: start; }
@media (max-width: 980px) { .nl { grid-template-columns: minmax(0, 1fr); } }
.nl__compose { min-width: 0; }
.nl__compose .admin__h, .nl__preview .admin__h { margin-top: 0; }
.nl__buttons { border: 0; margin: 0; padding: 0; display: grid; gap: 0.8rem; min-width: 0; }
.nl__buttons legend { padding: 0; margin-bottom: 0.4rem; font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); }
.nl__send { display: flex; flex-wrap: wrap; gap: 0.5rem; padding-top: 1rem; border-top: 1px solid var(--ink-line); }
.nl__send .btn { white-space: normal; text-align: center; }
.nl__confirm {
  display: grid; gap: 0.8rem; padding: 1rem;
  border: 1px solid var(--accent); border-radius: var(--radius);
}
.nl__confirm[hidden] { display: none; }
.nl__confirm p { margin: 0; color: var(--off-white); overflow-wrap: anywhere; }
.nl__fallback:empty { display: none; }
.nl__fallback { display: grid; gap: 0.6rem; padding-top: 1rem; border-top: 1px dashed var(--ink-line); }
.nl__fallback .admin__row-actions { margin-top: 0; }

.nl__preview { min-width: 0; position: sticky; top: 1rem; }
.nl__preview .admin__bar { padding-bottom: 0.75rem; margin-bottom: 0.75rem; }
.nl__toggle { display: flex; gap: 0.35rem; }
.nl__toggle .btn { padding: 0.45rem 0.8rem; font-size: 0.66rem; }
.nl__toggle .btn[aria-pressed='true'] { border-color: var(--accent); color: var(--accent); }
.nl__inbox-line { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-bottom: 0.6rem; }
.nl__inbox-line strong { color: var(--off-white); }
/* The email itself is light, whatever the dashboard is — that is how it
   arrives. The frame only bounds it. */
.nl__frame {
  max-height: 78vh; overflow: auto; border-radius: var(--radius);
  border: 1px solid var(--ink-line); background: #f2f0ec;
}
.nl__frame[hidden] { display: none; }
.nl__text {
  margin: 0; max-height: 78vh; overflow: auto; padding: 1rem;
  border: 1px solid var(--ink-line); border-radius: var(--radius); background: var(--ink-raised);
  font-size: 0.82rem; line-height: 1.55; color: var(--body); white-space: pre-wrap; overflow-wrap: anywhere;
}
.nl__text[hidden] { display: none; }
@media (max-width: 980px) { .nl__preview { position: static; } }

.nl__list-tools { margin-top: 0; }
.nl__filter { max-width: 22rem; }
.subs .table__row { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) 5rem 6.5rem 11rem; align-items: center; }
#nl-list-status:empty, #nl-status:empty { display: none; }
.subs__row--off { opacity: 0.55; }
.subs__email { color: var(--off-white); }
.subs__actions { display: flex; gap: 0.35rem; justify-content: flex-end; }
.subs__actions .btn { padding: 0.35rem 0.6rem; font-size: 0.62rem; }
@media (max-width: 860px) {
  .subs .table__row { grid-template-columns: minmax(0, 1fr) auto; }
  .subs .table__row > span:nth-child(2), .subs .table__row > span:nth-child(3) { display: none; }
  .subs .table__row > span:nth-child(4) { font-size: 0.72rem; color: var(--muted); }
  .subs__actions { grid-column: 1 / -1; justify-content: flex-start; }
}

#outreach-form .check { align-items: flex-start; }
=======
/* ==========================================================================
   The Vault and the Syndicate hub
   --------------------------------------------------------------------------
   /vault.html (vault.js), the live parts of /syndicate.html (syndicate.js)
   and the dashboard sections admin-extras.js builds. Everything below is
   drawn from the same tokens as the rest of the file, so light mode and the
   accent choice follow without a second set of rules.
   ========================================================================== */

/* ---------- the Vault: controls ---------- */

.vault__stats { font-size: 0.82rem; color: var(--muted); letter-spacing: 0.04em; min-height: 1.3em; }

.vault__controls {
  display: grid; gap: 1rem; align-items: end;
  grid-template-columns: minmax(0, 2.2fr) repeat(3, minmax(0, 1fr));
  padding: 1.25rem; margin-top: -0.5rem;
  background: color-mix(in srgb, var(--ink-raised) 88%, transparent);
  border: 1px solid var(--ink-line); border-radius: var(--radius);
}
.vault__controls .field { min-width: 0; }
.vault__controls .check { margin: 0; grid-column: 1 / -1; }
.vault__search input { font-size: 1rem; }
@media (max-width: 860px) {
  .vault__controls { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .vault__search { grid-column: 1 / -1; }
}
@media (max-width: 560px) {
  .vault__controls { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); padding: 1rem; }
  .vault__controls .field:last-of-type { grid-column: 1 / -1; }
}

.vault__section { padding-top: 2rem; }
.vault__count { font-size: 0.85rem; color: var(--muted); margin: 0 0 1.25rem; }

/* ---------- the Vault: results ---------- */

.vault__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0.9rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 30rem), 1fr));
}
.vault__list[aria-busy='true'] { opacity: 0.55; transition: opacity 0.2s var(--ease); }

.vault__item {
  display: grid; gap: 0.9rem; align-content: start; min-width: 0;
  padding: 1rem;
  background: var(--ink-raised); border: 1px solid var(--ink-line); border-radius: var(--radius);
  transition: border-color 0.25s var(--ease);
}
.vault__item:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--ink-line)); }

.vault__head { display: flex; gap: 1rem; align-items: flex-start; min-width: 0; }
.vault__cover {
  flex: none; width: 96px; height: 96px; object-fit: cover;
  border-radius: calc(var(--radius) - 1px); background: var(--media-bg);
}
/* No cover: the label's grooves, the same motif as the page header. */
.vault__cover--empty {
  display: block;
  background:
    radial-gradient(circle at 50% 50%, var(--accent) 0 4px, transparent 5px),
    repeating-radial-gradient(circle at 50% 50%, #1C1C20 0 2px, #0E0E10 2px 4px);
}
.vault__body { min-width: 0; flex: 1; }
.vault__title {
  margin: 0 0 0.2rem; font-size: 1.05rem; line-height: 1.25;
  overflow-wrap: anywhere;
}
.vault__artist { margin: 0 0 0.35rem; font-size: 0.92rem; color: var(--body); overflow-wrap: anywhere; }
.vault__artist-link { color: inherit; text-decoration: underline; text-decoration-color: var(--ink-line); text-underline-offset: 3px; }
.vault__artist-link:hover { color: var(--accent); text-decoration-color: var(--accent); }
.vault__badge {
  display: inline-block; vertical-align: 0.1em;
  padding: 0.12rem 0.5rem; font-size: 0.58rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent); border-radius: 999px;
}
.vault__meta { margin: 0 0 0.5rem; font-size: 0.8rem; color: var(--muted); }

.vault__shows { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem; }
.vault__show {
  padding: 0.18rem 0.55rem; font-size: 0.66rem; letter-spacing: 0.06em;
  color: var(--body); border: 1px solid var(--ink-line); border-radius: 999px;
}
.vault__show--genre { color: var(--muted); border-style: dashed; }
.vault__show--watch a { color: var(--accent); text-decoration: none; }
.vault__show--watch a:hover { text-decoration: underline; }

.vault__links { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.vault__link {
  display: inline-flex; align-items: center; min-height: 2rem;
  padding: 0.35rem 0.7rem; font-size: 0.68rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none;
  color: var(--off-white); border: 1px solid var(--ink-line); border-radius: var(--radius);
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.vault__link:hover { border-color: var(--accent); color: var(--accent); }
/* A search is a guess, and must not look like the official page. */
.vault__link--search { color: var(--muted); border-style: dashed; font-weight: 500; }
.vault__play { min-height: 2rem; }

.vault__player { width: 100%; aspect-ratio: 16 / 9; border: 0; border-radius: var(--radius); background: var(--media-bg); }
.vault__player--audio { aspect-ratio: auto; height: 152px; }

.vault__more { text-align: center; margin: 2rem 0 0; }

@media (max-width: 420px) {
  .vault__cover { width: 72px; height: 72px; }
  .vault__item { padding: 0.85rem; }
}

/* ---------- the Syndicate: polls ---------- */

.syn__status { margin: -1.25rem 0 1.5rem; }
.syn__who { margin: 0; font-size: 0.92rem; color: var(--body); }
.syn__badge, .syn__mine {
  display: inline-block; vertical-align: 0.1em;
  padding: 0.15rem 0.6rem; font-size: 0.6rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: #fff; background: var(--accent); border-radius: 999px;
}
.syn__mine { color: var(--accent); background: transparent; border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent); }

.syn__polls { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr)); }
.syn__empty { color: var(--muted); margin: 0; }
.syn__poll { display: grid; gap: 0.9rem; align-content: start; min-width: 0; }
.syn__poll:hover { transform: none; }
.syn__poll--closed { opacity: 0.92; }
.syn__poll .card__tag { margin-bottom: 0; }
.syn__question { margin: 0; font-size: 1.25rem; outline: none; overflow-wrap: anywhere; }
.syn__blurb { margin: 0; color: var(--body-soft); font-size: 0.92rem; }

.syn__form { display: grid; gap: 0.9rem; }
.syn__options { border: 0; margin: 0; padding: 0; display: grid; gap: 0.5rem; min-width: 0; }
.syn__option {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 0.4rem 0.75rem; align-items: center;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--ink-line); border-radius: var(--radius);
  background: color-mix(in srgb, var(--ink) 55%, var(--ink-raised));
}
.syn__option:has(input:checked) { border-color: var(--accent); }
.syn__option input { width: 1.05rem; height: 1.05rem; margin: 0; accent-color: var(--accent); }
.syn__option-label { display: grid; gap: 0.1rem; cursor: pointer; min-width: 0; }
.syn__option-name { font-weight: 600; color: var(--off-white); overflow-wrap: anywhere; }
.syn__option-note { font-size: 0.8rem; color: var(--muted); overflow-wrap: anywhere; }
.syn__option-actions { display: flex; gap: 0.4rem; }
.syn__player { grid-column: 1 / -1; width: 100%; aspect-ratio: 16 / 9; border: 0; border-radius: var(--radius); background: var(--media-bg); }
.syn__player--audio { aspect-ratio: auto; height: 152px; }
.syn__foot { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }
.syn__foot .form__status { margin: 0; flex-basis: 100%; min-height: 0; }
.syn__foot .form__status:empty { display: none; }

.syn__results { display: grid; gap: 0.7rem; padding-top: 0.9rem; border-top: 1px solid var(--ink-line); }
.syn__summary { margin: 0; font-size: 0.78rem; color: var(--muted); letter-spacing: 0.04em; }
.syn__bars { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; }
.syn__bar-row { display: grid; gap: 0.3rem; }
.syn__bar-label { font-size: 0.88rem; font-weight: 600; overflow-wrap: anywhere; }
.syn__bar-line {
  display: grid; grid-template-columns: 6.5rem minmax(0, 1fr) 3rem; gap: 0.6rem; align-items: center;
  font-size: 0.7rem; color: var(--muted);
}
.syn__bar-who { letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; }
.syn__bar-track { height: 6px; border-radius: 99px; background: var(--ink-line); overflow: hidden; }
.syn__bar { display: block; height: 100%; width: 0; background: var(--accent); border-radius: inherit; transition: width 0.5s var(--ease); }
.syn__bar--members { background: color-mix(in srgb, var(--accent) 45%, var(--off-white)); }
.syn__bar-pct { text-align: right; font-variant-numeric: tabular-nums; }
.syn__pick { margin: 0; font-size: 0.88rem; color: var(--body); }
.syn__pick strong { color: var(--accent); font-weight: 600; }
@media (max-width: 420px) {
  .syn__bar-line { grid-template-columns: 5.6rem minmax(0, 1fr) 2.6rem; }
}

.syn__count { margin: 1.25rem 0 0; font-size: 0.85rem; color: var(--muted); }

/* ---------- the Syndicate: back an artist ---------- */

.back { display: flex; gap: 1rem; align-items: center; padding: 1.1rem; min-width: 0; }
.back__photo { flex: none; width: 76px; height: 76px; object-fit: cover; border-radius: 50%; background: var(--media-bg); }
.back__photo--empty { display: block; }
.back__body { min-width: 0; }
.back .card__tag { margin-bottom: 0.35rem; overflow-wrap: anywhere; }
.back .set__name { margin: 0 0 0.6rem; font-size: 1.05rem; }
.back .set__name a { color: inherit; text-decoration: none; }
.back .set__name a:hover { color: var(--accent); }
.back__action { margin: 0; font-size: 0.8rem; color: var(--muted); }
.syn__tip { margin: 1.5rem 0 0; display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }
.syn__cta { margin-top: 2rem; display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: center; }

/* ---------- dashboard: The Vault and Syndicate sections ---------- */

.xadmin__grid { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); margin: 1.25rem 0; }
.xadmin__grid .form { max-width: none; }
.xadmin__list { display: grid; gap: 0.75rem; margin-top: 1rem; }
.xadmin__row { display: grid; gap: 0.35rem; }
.xadmin__row .set__name { margin: 0; font-size: 1rem; overflow-wrap: anywhere; }
.xadmin__links { display: flex; flex-wrap: wrap; gap: 0.4rem; font-size: 0.78rem; }
.xadmin__links a { color: var(--body); overflow-wrap: anywhere; }
.xadmin__edit { display: grid; gap: 0.6rem; margin-top: 0.75rem; }
.xadmin__edit .field input { padding: 0.55rem 0.7rem; font-size: 0.85rem; }
.xadmin__table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.xadmin__table th, .xadmin__table td { text-align: left; padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--ink-line); overflow-wrap: anywhere; }
.xadmin__table th { font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
.xadmin__scroll { overflow-x: auto; }
.xadmin__feed { display: grid; gap: 0.5rem; margin-top: 0.75rem; }

/* ---------- The Vault: the members-only door ---------- */
.vault-door { position: relative; margin-top: 1rem; border-radius: 14px; overflow: hidden; min-height: 22rem; display: grid; place-items: center; padding: 2rem 1rem; }
.vault-door__wall { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; filter: blur(14px) saturate(1.1); opacity: 0.55; transform: scale(1.08); }
.vault-door__wall img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 1; }
.vault-door::after { content: ''; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(10, 10, 12, 0.35), rgba(10, 10, 12, 0.85)); }
.vault-door__card { position: relative; z-index: 1; max-width: 34rem; text-align: center; padding: 1.8rem 1.4rem; border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 12px; background: rgba(12, 12, 14, 0.72); backdrop-filter: blur(6px); }
.vault-door__title { margin: 0.4rem 0 0.8rem; }
.vault-door__note { font-size: 0.92rem; opacity: 0.8; }
.vault-door__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem; margin: 1.2rem 0 0; }
@media (max-width: 520px) { .vault-door__wall { grid-template-columns: repeat(2, 1fr); } .vault-door { min-height: 26rem; } }
