/* ==========================================================================
   Neurofunk Radio — "Broadcast Terminal"

   The site is dressed as a piece of rack-mounted transmission gear. Content
   sits in the lit screen areas; everything around it is machined chrome,
   hairline rules, stencil labels and channel numbering. The palette is almost
   entirely black and bone with a single red that only ever means *signal* --
   live, on air, playing, now. Red is never decorative here, which is what
   keeps it loud.

   Type is one family doing three jobs: Archivo, variable on both width and
   weight. The design's headline is a heavy industrial grotesque -- squared
   counters, flat-apex A, moderately condensed -- and three separate faces were
   fighting it. Anton was too narrow and set too tight; Chakra Petch's angular,
   chamfered letterforms read as sci-fi rather than as machinery.

   Archivo covers all of it because the axes do the work:
     display  weight 800, width 84%   the condensed mass of the headline
     ui       weight 600, width 100%  labels, nav and readouts, widely tracked
     body     weight 400, width 100%  article text at a reading size

   One family also means one download, and no mismatched vertical metrics
   between a headline and the label sitting underneath it.
   ========================================================================== */

/* --------------------------------------------------------------- tokens -- */
:root {
  /* Surfaces, darkest to lightest. "pit" is the page, "rack" is a raised
     panel, "rail" is the edge where two panels meet. */
  --void: #08080b;
  --pit: #0b0c10;
  --panel: #101218;
  --rack: #161920;
  --rail: #21242d;
  --hair: rgba(255, 255, 255, 0.07);
  --hair-bright: rgba(255, 255, 255, 0.14);

  /* Signal. */
  --red: #ff2020;
  --red-hot: #ff5142;
  --red-deep: #8f0606;
  --red-glow: rgba(255, 32, 32, 0.45);

  /* Text. */
  --bone: #e9eaee;
  --ash: #8b909b;
  --dim: #565b67;

  --shell: min(1440px, 100% - 3rem);
  --deck-h: 84px;

  /* One family, three roles. The width axis is what separates them, so the
     stack falls back to a condensed system face for display and a normal one
     elsewhere if Archivo fails to load. */
  --f-display: "Archivo", "Archivo Black", "Helvetica Neue Condensed", "Arial Narrow", sans-serif;
  --f-ui: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --f-body: "Archivo", system-ui, sans-serif;

  /* Display setting, kept together so the headline, the section headings and
     the score numbers stay in step. */
  --display-weight: 800;
  --display-width: 84%;

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

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

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--void);
  color: var(--bone);
  font-family: var(--f-body);
  font-size: 16px;
  line-height: 1.6;
  overflow-x: hidden;
  /* The deck is fixed to the bottom on every page; nothing may hide under it. */
  padding-bottom: var(--deck-h);
  /* A faint vertical cast, brighter at the top, so the page reads as lit from
     the masthead rather than evenly flat. */
  background-image:
    radial-gradient(120% 60% at 50% -10%, rgba(255, 32, 32, 0.07), transparent 60%),
    linear-gradient(var(--void), var(--pit) 40%, var(--void));
  background-attachment: fixed;
}

/* ------------------------------------------------------- atmospherics -- */
/* Film grain and scanlines, both inert to the pointer. Together they stop the
   large black fields from looking like flat #000 and give the panels the
   texture of a screen being photographed rather than a page being rendered. */
.grain, .scan { position: fixed; inset: 0; pointer-events: none; z-index: 900; }

.grain {
  opacity: 0.4;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E");
  animation: grain-shift 1.1s steps(3) infinite;
}
@keyframes grain-shift {
  0% { transform: translate(0, 0); }
  33% { transform: translate(-2%, 1%); }
  66% { transform: translate(1%, -2%); }
  100% { transform: translate(0, 0); }
}

/* Kept deliberately faint. At the density and opacity a CRT effect usually
   ships with, the lines beat against large display type and the headline
   reads as struck through rather than scanned. */
.scan {
  opacity: 0.22;
  background: repeating-linear-gradient(
    to bottom, transparent 0 3px, rgba(0, 0, 0, 0.5) 3px 4px
  );
}

/* ------------------------------------------------------------ helpers -- */
.shell { width: var(--shell); margin-inline: auto; }

/* The stencil label. Every technical caption on the site is one of these:
   tiny, uppercase, wide, with a short red rule leading into it. */
.stencil {
  font-family: var(--f-ui);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ash);
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}
.stencil::before {
  content: "";
  width: 18px;
  height: 2px;
  background: var(--red);
  flex: none;
}
.stencil--bare::before { display: none; }

/* Corner brackets. Applied to panels to read as machined frame corners
   rather than CSS borders. */
.brackets { position: relative; }
.brackets::before, .brackets::after {
  content: "";
  position: absolute;
  width: 14px; height: 14px;
  border: 1px solid var(--hair-bright);
  pointer-events: none;
}
.brackets::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.brackets::after { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

/* Hazard striping, used sparingly as an edge accent. */
.hazard {
  background-image: repeating-linear-gradient(
    -45deg, var(--red) 0 6px, transparent 6px 12px
  );
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --------------------------------------------------------------- links -- */
a { color: inherit; text-decoration: none; }

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

/* ------------------------------------------------------------ masthead -- */
.masthead {
  position: sticky;
  top: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  gap: 2rem;
  height: 68px;
  padding-inline: 1.5rem;
  background: rgba(8, 8, 11, 0.82);
  backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid var(--hair);
}
/* The red hairline under the masthead is the "powered" indicator for the
   whole machine. */
.masthead::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, var(--red), transparent 42%, transparent 58%, var(--red));
  opacity: 0.55;
}

.brand { display: flex; align-items: center; gap: 0.8rem; flex: none; }
.brand__mark { width: 34px; color: var(--bone); display: block; transition: color 0.25s; }
.brand:hover .brand__mark { color: var(--red); }
.brand__txt { display: flex; flex-direction: column; line-height: 1; }
.brand__name {
  font-family: var(--f-display);
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.brand__sub {
  font-family: var(--f-ui);
  font-size: 0.52rem;
  letter-spacing: 0.3em;
  color: var(--dim);
  text-transform: uppercase;
  margin-top: 3px;
}

.nav { display: flex; gap: 0.35rem; margin-left: auto; }
.nav a {
  font-family: var(--f-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ash);
  padding: 0.55rem 0.9rem;
  position: relative;
  transition: color 0.2s;
}
.nav a:hover { color: var(--bone); }
.nav a::after {
  content: "";
  position: absolute; left: 0.9rem; right: 0.9rem; bottom: 0.3rem;
  height: 1px; background: var(--red);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.3s var(--ease);
}
.nav a:hover::after, .nav a[aria-current="page"]::after { transform: scaleX(1); }
.nav a[aria-current="page"] { color: var(--bone); }

/* The submit link lives in the button to the right of the nav on desktop.
   That button is hidden on narrow screens, so this copy takes over inside the
   dropdown -- otherwise a phone has no route to the submit form. */
.nav__submit { display: none; }

.btn-submit {
  font-family: var(--f-ui);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red);
  border: 1px solid var(--red);
  padding: 0.6rem 1.1rem;
  flex: none;
  transition: background 0.2s, color 0.2s, box-shadow 0.2s;
}
.btn-submit:hover {
  background: var(--red);
  color: #fff;
  box-shadow: 0 0 24px var(--red-glow);
}

.burger {
  display: none;
  width: 42px; height: 42px;
  background: none; border: 1px solid var(--hair);
  color: var(--bone); cursor: pointer;
  flex-direction: column; gap: 5px; align-items: center; justify-content: center;
  flex: none;
}
.burger span { display: block; width: 18px; height: 2px; background: currentColor; }

/* ---------------------------------------------------------------- hero -- */
.hero {
  position: relative;
  padding: clamp(3rem, 7vw, 6rem) 0 clamp(2rem, 4vw, 3.5rem);
  overflow: hidden;
}
/* Blueprint grid, fading out downward. Sits behind the hero only, so the rest
   of the page feels like it has come off the schematic and into the rack. */
.hero::before {
  content: "";
  position: absolute; inset: 0;
  background-image:
    linear-gradient(var(--hair) 1px, transparent 1px),
    linear-gradient(90deg, var(--hair) 1px, transparent 1px);
  background-size: 68px 68px;
  mask-image: linear-gradient(to bottom, #000 0%, transparent 78%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 78%);
  opacity: 0.55;
  pointer-events: none;
}

.hero__grid {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}

.hero__eyebrow { margin-bottom: 1.4rem; }

.hero h1 {
  font-family: var(--f-display);
  /* Anton is already condensed; the negative tracking and 0.84 leading are
     what turn two words into a single block of mass. */
  font-size: clamp(3.6rem, 11.5vw, 10rem);
  line-height: 0.84;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  margin: 0 0 1.2rem;
  /* A brushed-metal fill rather than flat white -- the highlight runs across
     the middle of the letterforms the way light falls on a steel panel. */
  background: linear-gradient(178deg, #fff 8%, #b9bcc4 38%, #6e727c 55%, #d6d8dd 78%, #85888f 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 4px 26px rgba(0, 0, 0, 0.6));
}
.hero h1 em {
  font-style: normal;
  display: block;
  background: linear-gradient(178deg, var(--red-hot), var(--red) 45%, var(--red-deep));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero__tag {
  font-family: var(--f-ui);
  font-size: clamp(0.85rem, 1.5vw, 1.05rem);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash);
  margin: 0 0 2.2rem;
  max-width: 34ch;
}

.hero__cta { display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap; }

/* The one filled, angular button on the site. The clip-path notch on the
   right echoes the cut corners on the rack panels. */
.btn-live {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  font-family: var(--f-ui);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #fff;
  background: linear-gradient(100deg, var(--red), var(--red-deep));
  border: 0;
  padding: 1.15rem 2.4rem 1.15rem 1.8rem;
  cursor: pointer;
  clip-path: polygon(0 0, 100% 0, calc(100% - 18px) 100%, 0 100%);
  transition: filter 0.2s, box-shadow 0.25s, transform 0.15s;
  box-shadow: 0 0 0 rgba(255, 32, 32, 0);
}
.btn-live:hover { filter: brightness(1.15); box-shadow: 0 8px 40px var(--red-glow); }
.btn-live:active { transform: translateY(1px); }
.btn-live__icon {
  width: 0; height: 0;
  border-left: 13px solid #fff;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
}
/* Swaps to a pause bar-pair when the deck is playing. */
body.is-playing .btn-live__icon {
  width: 13px; height: 15px; border: 0;
  background: linear-gradient(90deg, #fff 0 4px, transparent 4px 9px, #fff 9px 13px);
}

.hero__stripe { width: 46px; height: 26px; opacity: 0.75; }

/* --------------------------------------------------------- signal bay -- */
/* The right half of the hero: a framed instrument showing the live spectrum
   of whatever is actually on air, with the station mark ghosted behind it. */
.bay {
  position: relative;
  aspect-ratio: 4 / 3.1;
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(255, 32, 32, 0.1), transparent 62%),
    linear-gradient(160deg, var(--rack), var(--pit) 70%);
  border: 1px solid var(--rail);
  overflow: hidden;
}
.bay::before {
  /* Ghosted monogram, oversized and bled off the right edge. Sits above the
     readout strip so the spectrum has the lower half of the panel to itself. */
  content: "";
  position: absolute;
  right: -14%; top: 38%; transform: translateY(-50%);
  width: 82%; aspect-ratio: 331 / 240;
  background: currentColor;
  color: rgba(255, 255, 255, 0.075);
  -webkit-mask: url("/img/mark.svg") no-repeat center / contain;
  mask: url("/img/mark.svg") no-repeat center / contain;
}
.bay__canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* Sleeve art for whatever is on air. Sits top-right, cropped square, held
   back with a grayscale wash so it reads as an instrument readout rather than
   as the page's main image. */
.bay__art {
  position: absolute;
  top: 0.9rem; right: 0.9rem;
  width: 92px; height: 92px;
  object-fit: cover;
  border: 1px solid var(--hair-bright);
  filter: grayscale(0.55) contrast(1.12) brightness(0.9);
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.6);
}
.bay__art[hidden] { display: none; }

.bay__readouts {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  gap: 1px;
  background: var(--rail);
  border-top: 1px solid var(--rail);
}
.bay__cell {
  flex: 1;
  background: rgba(8, 8, 11, 0.82);
  padding: 0.7rem 0.9rem;
  min-width: 0;
}
.bay__k {
  font-family: var(--f-ui);
  font-size: 0.55rem; font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--dim);
  display: block; margin-bottom: 0.25rem;
}
.bay__v {
  font-family: var(--f-ui);
  font-size: 0.92rem; font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--bone);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bay__v--red { color: var(--red); }

.bay__tag {
  position: absolute; top: 0.9rem; left: 0.9rem;
  display: flex; align-items: center; gap: 0.5rem;
  font-family: var(--f-ui);
  font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--bone);
  background: rgba(8, 8, 11, 0.75);
  border: 1px solid var(--hair);
  padding: 0.35rem 0.7rem;
}

/* The pulsing red dot that means "this is not a recording". */
.led {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 8px var(--red);
  animation: led-pulse 2s ease-in-out infinite;
  flex: none;
}
@keyframes led-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* -------------------------------------------------------------- ticker -- */
.ticker {
  border-block: 1px solid var(--hair);
  background: var(--pit);
  overflow: hidden;
  padding: 0.75rem 0;
  position: relative;
  /* Fade the ends so the loop never visibly pops at the edges. */
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.ticker__track {
  display: flex;
  gap: 3.5rem;
  width: max-content;
  animation: ticker-run 46s linear infinite;
}
.ticker:hover .ticker__track { animation-play-state: paused; }
@keyframes ticker-run { to { transform: translateX(-50%); } }
.ticker__item {
  font-family: var(--f-ui);
  font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--ash);
  display: flex; align-items: center; gap: 3.5rem;
  white-space: nowrap;
}
.ticker__item::after { content: "◆"; color: var(--red); font-size: 0.5rem; }

/* --------------------------------------------------------- section head -- */
.sec { padding: clamp(3rem, 6vw, 5rem) 0; }
.sec__head {
  display: flex; align-items: baseline; gap: 1.5rem;
  margin-bottom: 2.2rem; flex-wrap: wrap;
}
.sec__title {
  font-family: var(--f-display);
  font-size: clamp(1.7rem, 3.6vw, 2.9rem);
  line-height: 1;
  letter-spacing: 0.005em;
  text-transform: uppercase;
  margin: 0;
}
.sec__title::after {
  content: "";
  display: inline-block;
  width: 34px; height: 3px;
  background: var(--red);
  margin-left: 0.9rem;
  vertical-align: 0.28em;
}
.sec__more {
  margin-left: auto;
  font-family: var(--f-ui);
  font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ash);
  display: inline-flex; align-items: center; gap: 0.5rem;
  transition: color 0.2s, gap 0.2s;
}
.sec__more:hover { color: var(--red); gap: 0.9rem; }

/* The three-column home layout. Reviews get the wide column because the
   cards carry artwork; news and events are lists and read fine narrow. */
.home-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 1px;
  background: var(--hair);
  border: 1px solid var(--hair);
}
.home-col { background: var(--pit); padding: 1.6rem; min-width: 0; }
.home-col__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; margin-bottom: 1.4rem;
  padding-bottom: 0.9rem; border-bottom: 1px solid var(--hair);
}
.home-col__title {
  font-family: var(--f-display);
  font-size: 1.35rem; text-transform: uppercase; letter-spacing: 0.01em;
  margin: 0;
}
.home-col__all {
  font-family: var(--f-ui); font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--red);
  white-space: nowrap;
}
.home-col__all:hover { text-decoration: underline; text-underline-offset: 4px; }

/* --------------------------------------------------------------- cards -- */
.card {
  display: block;
  position: relative;
  background: var(--panel);
  border: 1px solid var(--hair);
  transition: border-color 0.25s, transform 0.25s var(--ease), box-shadow 0.25s;
}
.card:hover {
  border-color: rgba(255, 32, 32, 0.5);
  transform: translateY(-3px);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55);
}
.card__media {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--rack);
}
.card__media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* Desaturated until hover, so the grid reads as one dark field and colour
     only appears where the reader is looking. */
  filter: grayscale(1) contrast(1.15) brightness(0.72);
  transition: filter 0.4s, transform 0.6s var(--ease);
}
.card:hover .card__media img { filter: grayscale(0.15) contrast(1.05) brightness(0.95); transform: scale(1.04); }
.card__media::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(11, 12, 16, 0.95), transparent 55%);
}
.card__kind {
  position: absolute; top: 0.7rem; left: 0.7rem; z-index: 2;
  font-family: var(--f-ui); font-size: 0.55rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: #fff; background: var(--red);
  padding: 0.28rem 0.55rem;
}
.card__body { padding: 1.1rem; }
.card__title {
  font-family: var(--f-ui);
  font-size: 1rem; font-weight: 700; line-height: 1.3;
  letter-spacing: 0.01em;
  margin: 0 0 0.5rem;
  text-transform: uppercase;
  transition: color 0.2s;
}
.card:hover .card__title { color: var(--red-hot); }
.card__dek {
  font-size: 0.86rem; color: var(--ash); line-height: 1.55;
  margin: 0 0 0.8rem;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.card__meta {
  display: flex; align-items: center; gap: 0.8rem; flex-wrap: wrap;
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--dim);
}
.card__meta .dot { color: var(--red); }

/* Compact list rows, used by the news and events columns on the home page. */
.row {
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr);
  gap: 0.95rem;
  padding: 0.95rem 0;
  border-bottom: 1px solid var(--hair);
  align-items: start;
  transition: background 0.2s;
}
.row:last-child { border-bottom: 0; }
.row:hover { background: rgba(255, 255, 255, 0.02); }
.row__thumb {
  aspect-ratio: 1; overflow: hidden; background: var(--rack);
  border: 1px solid var(--hair);
}
.row__thumb img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(1) brightness(0.75); transition: filter 0.3s;
}
.row:hover .row__thumb img { filter: grayscale(0.2) brightness(0.95); }
.row__kind {
  font-family: var(--f-ui); font-size: 0.55rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--red);
  display: block; margin-bottom: 0.3rem;
}
.row__title {
  font-family: var(--f-ui); font-size: 0.86rem; font-weight: 600;
  line-height: 1.35; text-transform: uppercase; margin: 0 0 0.35rem;
  transition: color 0.2s;
}
.row:hover .row__title { color: var(--red-hot); }
.row__date {
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 500;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--dim);
}

/* --------------------------------------------------------------- event -- */
/* The date block is the whole identity of an event row: a torn-off calendar
   chip with the month stencilled above the day. */
.evt {
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr);
  gap: 1rem;
  padding: 0.95rem 0;
  border-bottom: 1px solid var(--hair);
  align-items: start;
}
.evt:last-child { border-bottom: 0; }
.evt__date {
  border: 1px solid var(--rail);
  background: var(--rack);
  text-align: center;
  padding: 0.4rem 0.2rem 0.45rem;
  position: relative;
}
.evt__date::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px; background: var(--red);
}
.evt__mon {
  font-family: var(--f-ui); font-size: 0.55rem; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--red);
  display: block;
}
.evt__day {
  font-family: var(--f-display); font-size: 1.5rem; line-height: 1;
  display: block; margin-top: 1px;
}
.evt__name {
  font-family: var(--f-ui); font-size: 0.88rem; font-weight: 700;
  text-transform: uppercase; line-height: 1.3; margin: 0 0 0.3rem;
}
.evt__where {
  font-family: var(--f-ui); font-size: 0.62rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ash);
  margin: 0 0 0.3rem;
}
.evt__lineup {
  font-size: 0.78rem; color: var(--dim); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.evt__tix {
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 700;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--red);
  display: inline-flex; align-items: center; gap: 0.4rem; margin-top: 0.45rem;
}
.evt__tix:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Full events page.
   Elsewhere on the site a grid gets its dividing lines from a 1px gap over a
   tinted background. That trick cannot be used here: a month with one or two
   events leaves the remaining tracks empty, and the tint renders those empty
   tracks as visible grey blocks sitting next to the real ones. Each card
   carries its own border instead, so a short month simply ends. */
.evt-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 1rem;
}
.evt-grid .evt {
  background: var(--pit);
  border: 1px solid var(--hair);
  padding: 1.3rem;
  grid-template-columns: 66px minmax(0, 1fr);
  transition: background 0.2s, border-color 0.2s;
}
.evt-grid .evt:hover { background: var(--panel); border-color: rgba(255, 32, 32, 0.4); }

/* --------------------------------------------------------------- feeds -- */
.feed {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 1.4rem;
}

/* Page header for the section pages. */
.pagehead {
  padding: clamp(2.5rem, 5vw, 4rem) 0 0;
  border-bottom: 1px solid var(--hair);
  position: relative;
}
.pagehead__inner { padding-bottom: 2rem; }
.pagehead h1 {
  font-family: var(--f-display);
  font-size: clamp(2.4rem, 7vw, 5rem);
  line-height: 0.9; text-transform: uppercase; letter-spacing: -0.005em;
  margin: 0.8rem 0 0.7rem;
}
.pagehead p {
  color: var(--ash); max-width: 56ch; margin: 0;
  font-size: 1rem;
}

/* ------------------------------------------------------------- article -- */
.article { padding: clamp(2rem, 5vw, 3.5rem) 0 4rem; }
.article__shell { width: min(72ch, 100% - 3rem); margin-inline: auto; }

.article__head { margin-bottom: 2.5rem; }
.article__kind { margin-bottom: 1.1rem; }
.article h1 {
  font-family: var(--f-display);
  font-size: clamp(2.1rem, 5.5vw, 3.8rem);
  line-height: 0.95;
  text-transform: uppercase;
  letter-spacing: -0.005em;
  margin: 0 0 1rem;
}
.article__dek {
  font-family: var(--f-ui);
  font-size: 1.05rem; font-weight: 400; line-height: 1.5;
  color: var(--ash); margin: 0 0 1.4rem;
}
.article__byline {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding-top: 1.1rem; border-top: 1px solid var(--hair);
  font-family: var(--f-ui); font-size: 0.62rem; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--dim);
}
.article__hero {
  margin: 0 0 2.5rem;
  border: 1px solid var(--hair);
  aspect-ratio: 16 / 9; overflow: hidden; background: var(--rack);
}
.article__hero img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Body copy. This is the only place on the site set in a reading face at a
   reading size, and it gets the measure and leading to match. */
.prose { font-size: 1.06rem; line-height: 1.75; color: #d3d5db; }
.prose > * + * { margin-top: 1.35em; }
.prose h2 {
  font-family: var(--f-display);
  font-size: 1.75rem; line-height: 1.15; text-transform: uppercase;
  color: var(--bone); margin-top: 2.4em; letter-spacing: 0.01em;
}
.prose h3 {
  font-family: var(--f-ui);
  font-size: 1.1rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--bone); margin-top: 2em;
}
.prose a {
  color: var(--red-hot);
  border-bottom: 1px solid rgba(255, 81, 66, 0.35);
  transition: border-color 0.2s, color 0.2s;
}
.prose a:hover { border-bottom-color: var(--red-hot); color: #fff; }
.prose strong { color: var(--bone); font-weight: 600; }
.prose blockquote {
  border-left: 2px solid var(--red);
  padding: 0.2rem 0 0.2rem 1.4rem;
  margin-inline: 0;
  font-family: var(--f-ui);
  font-size: 1.12rem; font-weight: 500; font-style: normal;
  color: var(--bone);
}
.prose ul, .prose ol { padding-left: 1.3rem; }
.prose li + li { margin-top: 0.5em; }
.prose li::marker { color: var(--red); }
.prose img, .prose iframe {
  max-width: 100%; display: block; border: 1px solid var(--hair);
}
.prose iframe { width: 100%; aspect-ratio: 16 / 9; height: auto; }
.prose figure { margin-inline: 0; }
.prose figcaption {
  font-family: var(--f-ui); font-size: 0.68rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--dim); margin-top: 0.7rem;
}
.prose hr { border: 0; border-top: 1px solid var(--hair); margin-block: 2.5em; }

/* Review scoring panel. */
.score {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 1.5rem;
  align-items: center;
  background: var(--panel);
  border: 1px solid var(--rail);
  padding: 1.4rem;
  margin-bottom: 2.5rem;
}
.score__num {
  font-family: var(--f-display);
  font-size: 3.2rem; line-height: 0.85;
  color: var(--red);
  display: flex; align-items: baseline; gap: 0.15rem;
}
.score__num small {
  font-family: var(--f-ui); font-size: 0.85rem; font-weight: 600;
  color: var(--dim); letter-spacing: 0.1em;
}
.score--unrated { grid-template-columns: minmax(0, 1fr); }
.score__meta { min-width: 0; }
.score__row { display: flex; gap: 0.6rem; margin-bottom: 0.4rem; align-items: baseline; }
.score__k {
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--dim);
  width: 62px; flex: none;
}
.score__v {
  font-family: var(--f-ui); font-size: 0.82rem; font-weight: 600;
  text-transform: uppercase; color: var(--bone);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Ten segments; the lit ones are the score, rounded to the nearest half. */
.score__bar { display: flex; gap: 3px; margin-top: 0.7rem; }
.score__seg { height: 6px; flex: 1; background: var(--rail); }
.score__seg--on { background: var(--red); box-shadow: 0 0 8px var(--red-glow); }
.score__seg--half {
  background: linear-gradient(90deg, var(--red) 50%, var(--rail) 50%);
}

/* Provenance. Every AI-written piece prints what it was built from, so a
   reader can check the desk's work. */
.sources {
  margin-top: 3rem;
  border: 1px solid var(--hair);
  background: var(--pit);
  padding: 1.3rem;
}
.sources__head { margin-bottom: 0.9rem; }
.sources ol { margin: 0; padding-left: 1.2rem; }
.sources li { margin-bottom: 0.45rem; }
.sources a {
  font-family: var(--f-ui); font-size: 0.78rem; color: var(--ash);
  border-bottom: 1px solid transparent; word-break: break-word;
}
.sources a:hover { color: var(--red-hot); border-bottom-color: currentColor; }

/* Next/previous. */
.adjacent {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
  background: var(--hair); border: 1px solid var(--hair);
  margin-top: 3rem;
}
.adjacent a {
  background: var(--pit); padding: 1.2rem; min-width: 0;
  transition: background 0.2s;
}
.adjacent a:hover { background: var(--panel); }
.adjacent a:last-child { text-align: right; }
.adjacent__k {
  font-family: var(--f-ui); font-size: 0.55rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--red);
  display: block; margin-bottom: 0.4rem;
}
.adjacent__t {
  font-family: var(--f-ui); font-size: 0.85rem; font-weight: 600;
  text-transform: uppercase; line-height: 1.35;
}

/* --------------------------------------------------------------- forms -- */
.form { display: grid; gap: 1.3rem; max-width: 620px; }
.field { display: grid; gap: 0.5rem; }
.field label {
  font-family: var(--f-ui); font-size: 0.62rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--ash);
}
.field input, .field textarea, .field select {
  background: var(--panel);
  border: 1px solid var(--rail);
  color: var(--bone);
  font-family: var(--f-ui); font-size: 0.92rem;
  padding: 0.85rem 1rem;
  width: 100%;
  transition: border-color 0.2s, background 0.2s;
}
.field input:focus, .field textarea:focus, .field select:focus {
  outline: 0; border-color: var(--red); background: var(--rack);
}
.field textarea { min-height: 130px; resize: vertical; font-family: var(--f-body); }
.field__hint { font-size: 0.78rem; color: var(--dim); }

.btn {
  font-family: var(--f-ui); font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.18em; text-transform: uppercase;
  background: var(--red); color: #fff; border: 0;
  padding: 0.95rem 1.8rem; cursor: pointer;
  justify-self: start;
  transition: filter 0.2s, box-shadow 0.2s;
}
.btn:hover { filter: brightness(1.15); box-shadow: 0 6px 26px var(--red-glow); }
.btn--ghost {
  background: transparent; border: 1px solid var(--rail); color: var(--ash);
}
.btn--ghost:hover { border-color: var(--red); color: var(--red); box-shadow: none; }
.btn--sm { padding: 0.5rem 0.9rem; font-size: 0.62rem; }

.notice {
  border: 1px solid var(--rail); border-left: 2px solid var(--red);
  background: var(--panel); padding: 1rem 1.2rem;
  font-family: var(--f-ui); font-size: 0.84rem; color: var(--bone);
}
.notice--ok { border-left-color: #2fbf6b; }

/* --------------------------------------------------------------- empty -- */
.empty {
  border: 1px dashed var(--rail);
  padding: 2.5rem 1.5rem;
  text-align: center;
  color: var(--dim);
  font-family: var(--f-ui); font-size: 0.8rem;
  letter-spacing: 0.1em; text-transform: uppercase;
}

/* ------------------------------------------------------------ pagination -- */
.pager { display: flex; gap: 0.6rem; justify-content: center; margin-top: 3rem; }
.pager a, .pager span {
  font-family: var(--f-ui); font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase;
  padding: 0.7rem 1.1rem; border: 1px solid var(--rail); color: var(--ash);
  transition: border-color 0.2s, color 0.2s;
}
.pager a:hover { border-color: var(--red); color: var(--red); }
.pager span { opacity: 0.35; }

/* -------------------------------------------------------------- footer -- */
.foot {
  border-top: 1px solid var(--hair);
  background: var(--pit);
  padding: 3.5rem 0 2rem;
  margin-top: 2rem;
}
.foot__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
  gap: 2.5rem;
  margin-bottom: 2.5rem;
}
.foot__mark { width: 54px; color: var(--bone); display: block; margin-bottom: 1.1rem; }
.foot__blurb { color: var(--ash); font-size: 0.9rem; max-width: 40ch; margin: 0 0 1.2rem; }
.foot__h {
  font-family: var(--f-ui); font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--bone);
  margin: 0 0 1rem;
}
.foot__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.foot__list a {
  font-family: var(--f-ui); font-size: 0.78rem; color: var(--ash);
  transition: color 0.2s;
}
.foot__list a:hover { color: var(--red); }
.foot__bar {
  display: flex; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap;
  padding-top: 1.5rem; border-top: 1px solid var(--hair);
  font-family: var(--f-ui); font-size: 0.6rem; font-weight: 500;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--dim);
}

/* ================================================================= deck == */
/* The player. Fixed to the bottom of every page and never re-rendered by
   navigation -- see app.js -- so the audio survives moving around the site.
   Styled as the front face of a rack unit: brushed gradient, inset seams,
   a jog-dial transport and a lit meter. */
.deck {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 800;
  height: var(--deck-h);
  display: grid;
  grid-template-columns: auto minmax(190px, 1.1fr) minmax(0, 2fr) auto auto;
  align-items: center;
  gap: 1.4rem;
  padding: 0 1.4rem;
  background: linear-gradient(180deg, var(--rack), #0a0b0e);
  border-top: 1px solid var(--rail);
  box-shadow: 0 -18px 50px rgba(0, 0, 0, 0.75);
}
.deck::before {
  /* Seam highlight along the top edge of the unit. */
  content: "";
  position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--hair-bright) 30%, var(--hair-bright) 70%, transparent);
}

/* Jog-dial transport button. */
.deck__play {
  position: relative;
  width: 52px; height: 52px;
  border-radius: 50%;
  border: 1px solid var(--rail);
  background: radial-gradient(circle at 34% 28%, #2b2f39, #101218 68%);
  color: var(--bone);
  cursor: pointer;
  display: grid; place-items: center;
  flex: none;
  transition: box-shadow 0.25s, border-color 0.25s;
}
.deck__play::before {
  /* Tick ring around the dial. */
  content: "";
  position: absolute; inset: -5px;
  border-radius: 50%;
  background: conic-gradient(from 0deg, var(--rail) 0 2deg, transparent 2deg 12deg);
  opacity: 0.85;
  -webkit-mask: radial-gradient(circle, transparent 62%, #000 64%);
  mask: radial-gradient(circle, transparent 62%, #000 64%);
  transition: background 0.3s;
}
.deck__play:hover { border-color: rgba(255, 32, 32, 0.6); box-shadow: 0 0 26px rgba(255, 32, 32, 0.28); }
body.is-playing .deck__play::before {
  background: conic-gradient(from 0deg, var(--red) 0 2deg, transparent 2deg 12deg);
  animation: dial-spin 8s linear infinite;
}
@keyframes dial-spin { to { transform: rotate(360deg); } }
body.is-playing .deck__play { border-color: rgba(255, 32, 32, 0.5); }

.deck__icon { width: 0; height: 0; border-left: 14px solid currentColor; border-top: 9px solid transparent; border-bottom: 9px solid transparent; margin-left: 3px; }
body.is-playing .deck__icon {
  width: 14px; height: 16px; margin-left: 0; border: 0;
  background: linear-gradient(90deg, currentColor 0 4px, transparent 4px 10px, currentColor 10px 14px);
}
/* Buffering: the dial ring sweeps while the stream connects. */
body.is-loading .deck__play::before {
  background: conic-gradient(from 0deg, var(--red), transparent 45%);
  animation: dial-spin 0.9s linear infinite;
}

.deck__meta { min-width: 0; }
.deck__label {
  display: flex; align-items: center; gap: 0.5rem;
  font-family: var(--f-ui); font-size: 0.55rem; font-weight: 700;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--red);
  margin-bottom: 0.25rem;
}
.deck__track {
  font-family: var(--f-ui); font-size: 0.9rem; font-weight: 600;
  /* Matches the rack readout. Track metadata arrives in whatever case the
     encoder was given, and the two players must not disagree about it. */
  text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--bone);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.deck__artist { color: var(--ash); font-weight: 400; }

/* The live waveform. Painted from the actual audio when the browser allows
   it; see app.js for the fallback. */
.deck__wave {
  position: relative;
  height: 46px;
  min-width: 0;
  border: 1px solid var(--hair);
  background:
    linear-gradient(90deg, rgba(255, 32, 32, 0.05), transparent 30%, transparent 70%, rgba(255, 32, 32, 0.05)),
    var(--pit);
  overflow: hidden;
}
.deck__wave canvas { display: block; width: 100%; height: 100%; }
/* Centre line, visible through the silence between transients. */
.deck__wave::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; background: var(--hair); pointer-events: none;
}

.deck__stat { text-align: right; flex: none; }
.deck__stat .bay__k { text-align: right; }
.deck__count {
  font-family: var(--f-ui); font-size: 1rem; font-weight: 700; color: var(--bone);
  display: flex; align-items: center; gap: 0.45rem; justify-content: flex-end;
}

.deck__vol { display: flex; align-items: center; gap: 0.7rem; flex: none; }
.deck__vol svg { width: 17px; color: var(--dim); flex: none; }
.vol {
  -webkit-appearance: none; appearance: none;
  width: 92px; height: 3px; background: var(--rail); cursor: pointer;
}
.vol::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--bone); border: 0; cursor: pointer;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
  transition: background 0.2s, box-shadow 0.2s;
}
.vol:hover::-webkit-slider-thumb { background: var(--red); box-shadow: 0 0 12px var(--red-glow); }
.vol::-moz-range-thumb {
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--bone); border: 0; cursor: pointer;
}
.vol:hover::-moz-range-thumb { background: var(--red); }

/* ---------------------------------------------------------------- admin -- */
.admin-table { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
.admin-table th {
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--dim);
  text-align: left; padding: 0.7rem 0.8rem; border-bottom: 1px solid var(--rail);
  white-space: nowrap;
}
.admin-table td {
  padding: 0.8rem; border-bottom: 1px solid var(--hair); vertical-align: top;
}
.admin-table tr:hover td { background: rgba(255, 255, 255, 0.02); }
.admin-table a { color: var(--bone); }
.admin-table a:hover { color: var(--red-hot); }
.tag {
  font-family: var(--f-ui); font-size: 0.55rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  border: 1px solid var(--rail); padding: 0.15rem 0.45rem; color: var(--ash);
  white-space: nowrap;
}
.tag--live { border-color: var(--red); color: var(--red); }
.tag--draft { border-color: #b6892a; color: #e0ad46; }
.tag--ai { border-color: #2f6fbf; color: #63a0e8; }
.admin-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.admin-actions form { display: inline; }

.stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px; background: var(--hair); border: 1px solid var(--hair);
  margin-bottom: 2.5rem;
}
.stat { background: var(--pit); padding: 1.2rem; }
.stat__n { font-family: var(--f-display); font-size: 2.2rem; line-height: 1; }
.stat__k {
  font-family: var(--f-ui); font-size: 0.55rem; font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--dim);
  margin-top: 0.4rem;
}

/* ------------------------------------------------------------- reveals -- */
/* One orchestrated entrance per page load. Elements carry --i for stagger. */
@media (prefers-reduced-motion: no-preference) {
  .rise {
    opacity: 0;
    transform: translateY(18px);
    animation: rise 0.7s var(--ease) forwards;
    animation-delay: calc(var(--i, 0) * 70ms);
  }
  @keyframes rise { to { opacity: 1; transform: none; } }

  /* Scroll-triggered for anything below the fold; app.js adds .in.
     Gated on .js (set by an inline script in <head>) so that with scripting
     off, or if app.js fails to load, the content is simply visible rather
     than permanently transparent. */
  .js .obs { opacity: 0; transform: translateY(22px); transition: opacity 0.6s var(--ease), transform 0.6s var(--ease); }
  .js .obs.in { opacity: 1; transform: none; }
}

/* --------------------------------------------------------- breakpoints -- */
@media (max-width: 1180px) {
  .home-grid { grid-template-columns: 1fr 1fr; }
  .home-col:first-child { grid-column: 1 / -1; }
  .foot__grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 900px) {
  :root { --shell: min(1440px, 100% - 2rem); --deck-h: 76px; }

  .nav {
    position: fixed; inset: 68px 0 auto 0;
    flex-direction: column; gap: 0;
    background: var(--pit); border-bottom: 1px solid var(--rail);
    padding: 0.5rem 1.5rem 1.5rem;
    transform: translateY(-120%);
    transition: transform 0.35s var(--ease);
    z-index: 490;
  }
  body.nav-open .nav { transform: none; }
  .nav a { padding: 0.9rem 0; font-size: 0.9rem; border-bottom: 1px solid var(--hair); }
  .nav a::after { display: none; }
  .burger { display: flex; margin-left: auto; }
  .btn-submit { display: none; }
  .nav__submit { display: block; color: var(--red); }

  .hero__grid { grid-template-columns: 1fr; }
  .bay { order: -1; aspect-ratio: 16 / 10; }

  /* Three readouts do not fit across a phone without the last one clipping
     mid-word. The bitrate is the least useful of the three, so it goes and
     the track title takes the room. */
  .bay__cell--signal { display: none; }

  .home-grid { grid-template-columns: 1fr; }
  .home-col:first-child { grid-column: auto; }

  /* The deck sheds its meter and volume on narrow screens; transport, track
     and listener count are what matter on a phone. */
  .deck {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 0.9rem; padding: 0 0.9rem;
  }
  .deck__wave, .deck__vol { display: none; }
  .deck__play { width: 44px; height: 44px; }

  .adjacent { grid-template-columns: 1fr; }
  .adjacent a:last-child { text-align: left; }
  .foot__grid { grid-template-columns: 1fr; gap: 2rem; }
}

@media (max-width: 520px) {
  .hero h1 { font-size: clamp(2.9rem, 15vw, 4.5rem); }
  .article__shell { width: min(72ch, 100% - 2rem); }
  .evt-grid { grid-template-columns: 1fr; }
  .feed { grid-template-columns: 1fr; }
  .deck__count { font-size: 0.85rem; }
  .score { grid-template-columns: 1fr; gap: 1rem; }
}

/* Print: strip the machine, keep the words. */
@media print {
  .masthead, .deck, .foot, .grain, .scan, .ticker, .adjacent { display: none !important; }
  body { background: #fff; color: #000; padding: 0; }
  .prose, .article h1 { color: #000; }
}

/* ==========================================================================
   HOMEPAGE — the long-form layout
   Nine sections, each with its own frame. Everything below is specific to the
   homepage; the shared components above are unchanged.
   ========================================================================== */

/* ------------------------------------------------------------- kicker -- */
/* "DRONE MANIPULATION PRESENTS" — centred label with a rule running out of
   both sides, the way a broadcast slate is set. */
.kicker {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  font-family: var(--f-ui);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--red);
  margin: 0 0 1.6rem;
}
.kicker__rule {
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--red));
  flex: 0 0 34px;
}
.kicker__rule:last-child {
  flex: 1;
  background: linear-gradient(90deg, var(--red), transparent);
  max-width: 220px;
}

/* Section headings: "SUBMIT YOUR MIX —" with the trailing red dash. */
.secheading {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  font-family: var(--f-display);
  font-size: clamp(1.5rem, 3vw, 2.15rem);
  line-height: 1;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  margin: 0 0 1.6rem;
}
.secheading__dash { width: 26px; height: 3px; background: var(--red); flex: none; }
.secheading--center { justify-content: center; margin-bottom: 2.6rem; }

/* ================================================================ hero == */
.hero { padding: clamp(2rem, 4vw, 3rem) 0 0; }

/* Blueprint grid plus a red bloom behind the artwork side. */
.hero__bg {
  position: absolute; inset: 0;
  background-image:
    radial-gradient(60% 70% at 76% 34%, rgba(255, 32, 32, 0.13), transparent 62%),
    linear-gradient(var(--hair) 1px, transparent 1px),
    linear-gradient(90deg, var(--hair) 1px, transparent 1px);
  background-size: auto, 74px 74px, 74px 74px;
  mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 92%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 92%);
  opacity: 0.7;
  pointer-events: none;
}

.hero__grid {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 3.5vw, 3rem);
  align-items: center;
  min-height: clamp(300px, 34vw, 430px);
}
.hero__copy { position: relative; z-index: 2; }

.hero h1 {
  font-family: var(--f-display);
  font-size: clamp(2.9rem, 7vw, 6.1rem);
  line-height: 0.86;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  margin: 0 0 1rem;
  background: linear-gradient(176deg, #fff 6%, #c3c6cd 34%, #7c8089 54%, #e2e4e8 74%, #8d9098 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 6px 30px rgba(0, 0, 0, 0.75));
}

.hero__tag {
  font-family: var(--f-ui);
  font-size: clamp(0.78rem, 1.18vw, 1.02rem);
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ash);
  margin: 0 0 2rem;
  max-width: none;
  white-space: nowrap;
}

.btn-live--hero { font-size: 0.9rem; padding: 1rem 2.6rem 1rem 1.7rem; }

/* ------------------------------------------------------------ hero art -- */
/* The design puts a large piece of chrome/mech artwork here. Until that art
   exists the slot holds the live spectrum and the current sleeve, framed the
   same way — so the composition is right and swapping in a picture is a
   background-image change, nothing more. */
.hero__art { position: relative; z-index: 1; }
/* .hero__figure removed: the panel-era element it styled no longer exists;
   the artwork is now an <img> in .hero__frame. */

/* Seats the photograph into the page: a wash toward black on the left so the
   headline has somewhere to sit, and a lift from the bottom so the spectrum
   and readouts stay legible over it. */
.hero__figure::after {
  content: "";
  position: absolute; inset: 0;
  z-index: 1;
  background:
    linear-gradient(90deg, rgba(8, 8, 11, 0.85) 0%, rgba(8, 8, 11, 0.2) 9%, transparent 22%),
    linear-gradient(to top, rgba(8, 8, 11, 0.78) 0%, transparent 30%);
  pointer-events: none;
}
/* The spectrum, sleeve and readouts sit above that wash. */
.hero__spectrum, .hero__cover, .hero__badge, .hero__corner { z-index: 2; }
/* The ghosted monogram that used to sit here was standing in for artwork
   that now exists. Two focal points in one panel is one too many. */
.hero__spectrum { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

.hero__cover {
  position: absolute;
  right: 1.3rem; bottom: 1.5rem;
  width: 108px; height: 108px;
  object-fit: cover;
  border: 1px solid var(--hair-bright);
  filter: grayscale(0.5) contrast(1.15) brightness(0.92);
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.7);
}
.hero__cover[hidden] { display: none; }

.hero__corner { position: absolute; width: 22px; height: 22px; border: 1px solid rgba(255,32,32,.5); }
.hero__corner--tr { top: 14px; right: 14px; border-left: 0; border-bottom: 0; }
.hero__corner--br { bottom: 14px; left: 14px; border-right: 0; border-top: 0; }

.hero__badge {
  position: absolute; top: 1rem; left: 1rem;
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 700;
  letter-spacing: 0.24em; text-transform: uppercase; color: var(--bone);
  background: rgba(8, 8, 11, 0.78);
  border: 1px solid var(--hair);
  padding: 0.35rem 0.7rem;
}

/* =============================================================== rack === */
/* The rack-mount player under the hero. This is the primary transport on the
   homepage; the slim deck at the foot of the window takes over once this
   scrolls out of view. */
.hero__rack {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 1.4rem;
  align-items: center;
  padding: clamp(1.5rem, 3vw, 2.6rem) 0 clamp(2rem, 4vw, 3rem);
}
.hero__broadcasting { display: grid; gap: 0.3rem; flex: none; }
.hero__live {
  font-family: var(--f-display);
  font-size: 1.35rem; line-height: 1; text-transform: uppercase; color: var(--bone);
}

.rack {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: clamp(0.9rem, 2vw, 1.8rem);
  padding: 0.9rem 1.2rem;
  background: linear-gradient(180deg, #23262e 0%, #14161c 18%, #0c0d11 100%);
  border: 1px solid var(--rail);
  border-radius: 3px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.09),
    inset 0 -1px 0 rgba(0, 0, 0, 0.8),
    0 22px 50px rgba(0, 0, 0, 0.7);
}
/* Rack-ear screws. */
.rack::before, .rack::after {
  content: "";
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 5px; height: 5px; border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #4a4f5a, #14161c);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6);
}
.rack::before { left: 6px; }
.rack::after { right: 6px; }

/* Jog dial. */
.rack__dial { position: relative; flex: none; }
.rack__play {
  position: relative;
  width: 74px; height: 74px;
  border-radius: 50%;
  border: 1px solid #2b2f39;
  background:
    radial-gradient(circle at 36% 30%, rgba(51,56,68,.5), transparent 60%),
    radial-gradient(circle at 50% 50%, #16191f 40%, #0a0b0e 72%);
  color: var(--bone);
  cursor: pointer;
  display: grid; place-items: center;
  box-shadow: inset 0 2px 6px rgba(255,255,255,.06), 0 6px 18px rgba(0,0,0,.7);
  transition: box-shadow 0.25s, border-color 0.25s;
  padding: 0;
}
/* Tick ring around the dial — red and turning while the stream plays. */
.rack__play::before {
  content: "";
  position: absolute; inset: -7px;
  border-radius: 50%;
  background: conic-gradient(from 0deg, #2b2f39 0 3deg, transparent 3deg 15deg);
  -webkit-mask: radial-gradient(circle, transparent 63%, #000 65%);
  mask: radial-gradient(circle, transparent 63%, #000 65%);
  transition: background 0.3s;
}
.rack__play:hover { border-color: rgba(255,32,32,.55); box-shadow: 0 0 30px rgba(255,32,32,.25); }
body.is-playing .rack__play::before {
  background: conic-gradient(from 0deg, var(--red) 0 3deg, transparent 3deg 15deg);
  animation: dial-spin 9s linear infinite;
}
body.is-loading .rack__play::before {
  background: conic-gradient(from 0deg, var(--red), transparent 45%);
  animation: dial-spin 0.9s linear infinite;
}
.rack__icon {
  width: 0; height: 0;
  border-left: 17px solid currentColor;
  border-top: 11px solid transparent;
  border-bottom: 11px solid transparent;
  margin-left: 5px;
}
body.is-playing .rack__icon {
  width: 16px; height: 19px; margin-left: 0; border: 0;
  background: linear-gradient(90deg, currentColor 0 5px, transparent 5px 11px, currentColor 11px 16px);
}

/* The lit screen area. */
.rack__screen {
  min-width: 0;
  background: linear-gradient(180deg, #0a0b0f, #060709);
  border: 1px solid #191c23;
  box-shadow: inset 0 0 24px rgba(0, 0, 0, 0.9);
  padding: 0.6rem 0.9rem 0.5rem;
}
.rack__meta { display: flex; align-items: center; gap: 0.7rem; margin-bottom: 0.35rem; }
.rack__label {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-family: var(--f-ui); font-size: 0.53rem; font-weight: 700;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--red);
  flex: none;
}
.rack__track {
  font-family: var(--f-ui); font-size: 0.82rem; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--bone);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0;
}
.rack__dash { flex: 1; height: 1px; background: var(--red); opacity: 0.5; min-width: 12px; }
.rack__wave { height: 46px; position: relative; }
.rack__wave canvas { display: block; width: 100%; height: 100%; }

.rack__k {
  font-family: var(--f-ui); font-size: 0.5rem; font-weight: 600;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--dim);
  display: block;
}
.rack__stat { text-align: center; flex: none; }
.rack__count {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-family: var(--f-ui); font-size: 0.95rem; font-weight: 700; color: var(--bone);
  margin-top: 0.2rem;
}

/* Volume knob — drag or arrow-key, styled as a machined pot. */
.rack__vol { text-align: center; flex: none; }
.knob {
  width: 46px; height: 46px; border-radius: 50%;
  margin: 0.35rem auto 0;
  position: relative;
  cursor: grab;
  background:
    radial-gradient(circle at 38% 30%, #3a3f4a, #141720 62%),
    #0d0f14;
  border: 1px solid #2b2f39;
  box-shadow: inset 0 2px 5px rgba(255,255,255,.08), 0 4px 12px rgba(0,0,0,.7);
}
.knob:active { cursor: grabbing; }
.knob::before {
  content: "";
  position: absolute; inset: -6px;
  border-radius: 50%;
  background: conic-gradient(from 200deg, var(--red) 0deg var(--vol-deg, 224deg), #23262e var(--vol-deg, 224deg) 280deg, transparent 280deg);
  -webkit-mask: radial-gradient(circle, transparent 64%, #000 66%);
  mask: radial-gradient(circle, transparent 64%, #000 66%);
}
.knob__pointer {
  position: absolute; left: 50%; top: 6px;
  width: 2px; height: 13px; background: var(--bone);
  transform-origin: 50% 17px;
  transform: translateX(-50%) rotate(var(--vol-rot, 44deg));
  border-radius: 1px;
}

/* Ten-segment level meter at the right end of the unit. */
.rack__meter { display: flex; flex-direction: column-reverse; gap: 2px; flex: none; padding-right: 4px; }
.rack__meter span { width: 12px; height: 3px; background: #23262e; }
body.is-playing .rack__meter span { background: var(--red-deep); }
body.is-playing .rack__meter span:nth-child(-n+6) { background: var(--red); box-shadow: 0 0 6px var(--red-glow); }
body.is-playing .rack__meter span:nth-child(n+9) { background: #23262e; }

/* =============================================================== board == */
.sec--board { padding-top: clamp(2rem, 4vw, 3rem); }
.board {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.4rem;
}
.board__col {
  background: linear-gradient(180deg, rgba(20, 22, 28, 0.9), rgba(11, 12, 16, 0.9));
  border: 1px solid var(--hair);
  padding: 1.4rem;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.board__head { margin-bottom: 1.2rem; }
.board__title {
  display: flex; align-items: center; gap: 0.8rem;
  font-family: var(--f-display);
  font-size: 1.25rem; letter-spacing: 0.02em; text-transform: uppercase;
  margin: 0;
}
.board__dash { width: 20px; height: 2px; background: var(--red); flex: none; }
.board__all {
  margin-top: auto; padding-top: 1.1rem;
  font-family: var(--f-ui); font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--red);
  display: inline-flex; align-items: center; gap: 0.45rem;
  transition: gap 0.2s;
}
.board__all:hover { gap: 0.8rem; }

/* Featured mix card. */
.mixcard { display: block; margin-bottom: 0.4rem; }
.mixcard__media {
  position: relative; display: block;
  aspect-ratio: 16 / 9; overflow: hidden; background: var(--rack);
  border: 1px solid var(--hair);
}
.mixcard__media img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(1) contrast(1.1) brightness(0.7);
  transition: filter 0.4s, transform 0.6s var(--ease);
}
.mixcard:hover .mixcard__media img { filter: grayscale(0.2) brightness(0.92); transform: scale(1.04); }
.mixcard__play {
  position: absolute; left: 0; bottom: 0;
  width: 46px; height: 38px;
  background: var(--red);
  display: grid; place-items: center;
}
.mixcard__play::after {
  content: "";
  border-left: 12px solid #fff;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
  margin-left: 3px;
}
.mixcard__body { display: block; padding: 0.9rem 0 0; }
.mixcard__title {
  display: block;
  font-family: var(--f-ui); font-size: 0.82rem; font-weight: 700;
  text-transform: uppercase; line-height: 1.35; color: var(--bone);
  transition: color 0.2s;
}
.mixcard:hover .mixcard__title { color: var(--red-hot); }
.mixcard__artist {
  display: block; margin-top: 0.25rem;
  font-family: var(--f-ui); font-size: 0.6rem; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--red);
}
.mixcard__meta { display: flex; gap: 1.2rem; margin-top: 0.6rem; }
.mixcard__stat {
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--dim);
}

/* ============================================================== submit == */
.submitpanel { position: relative; overflow: hidden; }
.submitpanel__bg {
  position: absolute; inset: 0;
  background:
    radial-gradient(70% 100% at 8% 50%, rgba(255, 32, 32, 0.1), transparent 62%),
    linear-gradient(90deg, #0e1015 0%, #0a0b0e 45%, #0b0c10 100%);
}
.submitpanel__bg::after {
  /* Ghosted mark standing in for the design's photographic panel. */
  content: "";
  position: absolute; left: -4%; top: 50%; transform: translateY(-50%);
  width: 30%; aspect-ratio: 331 / 240;
  background: currentColor; color: rgba(255,255,255,.04);
  -webkit-mask: url("/img/mark.svg") no-repeat center / contain;
  mask: url("/img/mark.svg") no-repeat center / contain;
}
.submitpanel__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
}
.submitpanel__lede { color: var(--ash); max-width: 46ch; margin: 0 0 2rem; font-size: 0.95rem; }
.submitpanel__stripe { display: inline-block; width: 44px; height: 22px; margin-left: 1rem; vertical-align: middle; opacity: .75; }

.tform { display: grid; gap: 1.2rem; }
.tform__row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; }
.tform__row--join { grid-template-columns: 1fr 1fr auto; align-items: end; }

/* Angled submit button, matching the hero CTA's cut. */
.btn-cut {
  display: inline-flex; align-items: center;
  font-family: var(--f-ui); font-size: 0.82rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: #fff;
  background: linear-gradient(100deg, var(--red), var(--red-deep));
  border: 0; cursor: pointer;
  padding: 1rem 2.6rem 1rem 1.8rem;
  clip-path: polygon(0 0, 100% 0, calc(100% - 18px) 100%, 0 100%);
  justify-self: start;
  transition: filter 0.2s, box-shadow 0.25s;
}
.btn-cut:hover { filter: brightness(1.15); box-shadow: 0 8px 34px var(--red-glow); }
.btn-cut--join { padding: 0.95rem 2.4rem 0.95rem 1.6rem; height: max-content; }

/* The "file upload" panel. The design shows a drop zone; we take links
   instead, so the panel says so rather than presenting a control that would
   silently do nothing. */
.submitpanel__drop { padding-top: 2.4rem; }
.drop {
  margin-top: 0.6rem;
  border: 1px dashed rgba(255, 32, 32, 0.45);
  background: rgba(255, 32, 32, 0.03);
  padding: 2rem 1.4rem;
  text-align: center;
}
.drop svg { width: 34px; color: var(--red); margin-bottom: 0.9rem; }
.drop__lede {
  font-family: var(--f-ui); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--red);
  margin: 0 0 0.6rem;
}
.drop__hint { font-size: 0.8rem; color: var(--dim); line-height: 1.55; margin: 0; }

/* ============================================================= reviews == */
.revgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.4rem; }
.rev {
  display: block;
  background: linear-gradient(180deg, rgba(20,22,28,.92), rgba(11,12,16,.92));
  border: 1px solid var(--hair);
  transition: border-color 0.25s, transform 0.25s var(--ease), box-shadow 0.25s;
}
.rev:hover {
  border-color: rgba(255, 32, 32, 0.5);
  transform: translateY(-4px);
  box-shadow: 0 18px 46px rgba(0, 0, 0, 0.6);
}
.rev__media { display: block; aspect-ratio: 16 / 11; overflow: hidden; background: var(--rack); }
.rev__media img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(1) contrast(1.15) brightness(0.72);
  transition: filter 0.4s, transform 0.6s var(--ease);
}
.rev:hover .rev__media img { filter: grayscale(0.1) brightness(0.95); transform: scale(1.05); }
.rev__body { display: block; padding: 1.2rem; }
.rev__title {
  display: block;
  font-family: var(--f-ui); font-size: 0.95rem; font-weight: 700;
  text-transform: uppercase; line-height: 1.3; color: var(--bone);
}
.rev__credit {
  display: block; margin-top: 0.35rem;
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--dim);
}
.rev__blurb {
  display: block; margin-top: 0.8rem;
  font-size: 0.85rem; line-height: 1.55; color: var(--ash);
}
.rev__foot {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; margin-top: 1.2rem;
}
.rev__score {
  font-family: var(--f-display); font-size: 1.7rem; line-height: 1; color: var(--red);
}
.rev__score small { font-family: var(--f-ui); font-size: 0.62rem; font-weight: 600; color: var(--dim); }
.rev__score--none { font-size: 0.9rem; }
.rev__more {
  font-family: var(--f-ui); font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--red);
}

/* ============================================================= podcast == */
.podcast__grid {
  display: grid;
  grid-template-columns: 200px minmax(0, 1.5fr) minmax(0, 0.85fr);
  gap: clamp(1.2rem, 3vw, 2.4rem);
  align-items: start;
}
.podcast__cover {
  position: relative;
  aspect-ratio: 1;
  border: 1px solid var(--rail);
  overflow: hidden;
  background: var(--rack);
}
.podcast__cover img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(0.85) contrast(1.15) brightness(0.62);
}
.podcast__coverTag {
  position: absolute; left: 0.9rem; top: 0.9rem;
  font-family: var(--f-display); font-size: 0.95rem; line-height: 1.15;
  text-transform: uppercase; color: var(--bone);
  text-shadow: 0 2px 12px rgba(0,0,0,.9);
}

.podcast__deck { min-width: 0; }
.podcast__nowlabel {
  font-family: var(--f-ui); font-size: 0.55rem; font-weight: 700;
  letter-spacing: 0.24em; text-transform: uppercase; color: var(--red);
}
.podcast__title {
  font-family: var(--f-display);
  font-size: clamp(1.4rem, 2.6vw, 2rem);
  line-height: 1; text-transform: uppercase; margin: 0.5rem 0 0.3rem;
}
.podcast__artist {
  font-family: var(--f-ui); font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--ash);
  margin: 0 0 1.2rem;
}
.podcast__wave { height: 62px; border-bottom: 1px solid var(--hair); margin-bottom: 1rem; }
.podcast__wave canvas { display: block; width: 100%; height: 100%; }

.podcast__transport { display: flex; align-items: center; gap: 1.2rem; }
.podcast__play {
  width: 52px; height: 52px; border-radius: 50%; flex: none;
  border: 1px solid var(--rail);
  background: radial-gradient(circle at 36% 30%, #2b2f39, #101218 68%);
  color: var(--bone); cursor: pointer;
  display: grid; place-items: center;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.podcast__play:hover { border-color: rgba(255,32,32,.6); box-shadow: 0 0 22px rgba(255,32,32,.25); }
.podcast__icon {
  width: 0; height: 0;
  border-left: 13px solid currentColor;
  border-top: 9px solid transparent; border-bottom: 9px solid transparent;
  margin-left: 3px;
}
.podcast__play[aria-pressed="true"] .podcast__icon {
  width: 13px; height: 15px; margin-left: 0; border: 0;
  background: linear-gradient(90deg, currentColor 0 4px, transparent 4px 9px, currentColor 9px 13px);
}
.podcast__scrub { flex: 1; min-width: 0; }
.podcast__times {
  display: flex; justify-content: space-between;
  font-family: var(--f-ui); font-size: 0.6rem; font-weight: 600;
  letter-spacing: 0.1em; color: var(--bone);
  margin-bottom: 0.35rem;
}
.podcast__times--sub { color: var(--dim); margin: 0.35rem 0 0; }

.seek {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 3px; background: var(--rail); cursor: pointer;
}
.seek::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--red); border: 0; box-shadow: 0 0 10px var(--red-glow);
}
.seek::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%; background: var(--red); border: 0;
}

.podcast__skips { display: flex; gap: 0.5rem; flex: none; }
.podcast__skip {
  width: 36px; height: 36px;
  border: 1px solid var(--rail); background: none; color: var(--ash);
  font-family: var(--f-ui); font-size: 0.6rem; font-weight: 700;
  cursor: pointer; transition: border-color 0.2s, color 0.2s;
}
.podcast__skip:hover { border-color: var(--red); color: var(--red); }

.podcast__listen {
  display: flex; align-items: center; gap: 1.4rem; flex-wrap: wrap;
  margin-top: 1.6rem; padding-top: 1.2rem; border-top: 1px solid var(--hair);
}
.podcast__listen .rack__k { display: inline; }
.podcast__listen a {
  font-family: var(--f-ui); font-size: 0.65rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ash);
  transition: color 0.2s;
}
.podcast__listen a:hover { color: var(--red); }

.podcast__list { min-width: 0; }
.eplist { list-style: none; margin: 0.7rem 0 0; padding: 0; }
.eplist__row {
  display: grid; grid-template-columns: 46px minmax(0, 1fr);
  gap: 0.7rem; align-items: center;
  padding: 0.65rem 0.7rem;
  border-bottom: 1px solid var(--hair);
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
  position: relative;
}
.eplist__row:hover { background: rgba(255, 255, 255, 0.03); }
.eplist__row.is-current { background: rgba(255, 32, 32, 0.07); }
.eplist__row.is-current::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 2px; background: var(--red);
}
.eplist__n {
  font-family: var(--f-ui); font-size: 0.62rem; font-weight: 700;
  letter-spacing: 0.08em; color: var(--red);
}
.eplist__name {
  font-family: var(--f-ui); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--bone);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ============================================================= support == */
.supportsec { position: relative; overflow: hidden; }
.supportsec__bg {
  position: absolute; inset: 0;
  background:
    radial-gradient(80% 120% at 78% 50%, rgba(255, 32, 32, 0.16), transparent 58%),
    linear-gradient(90deg, #0b0c10 0%, #0a0b0e 40%, #14161c 100%);
}
.supportsec__inner { position: relative; max-width: 640px; }
.supportsec__lede { color: var(--ash); margin: 0 0 2rem; font-size: 0.95rem; max-width: 50ch; }
.supportsec__stripe { display: inline-block; width: 44px; height: 22px; margin-left: 1rem; vertical-align: middle; opacity: .75; }

.strength { margin-bottom: 2rem; }
.strength__bar {
  position: relative;
  height: 14px;
  background: #14161c;
  border: 1px solid var(--rail);
  margin-top: 0.5rem;
  max-width: 420px;
}
.strength__fill {
  height: 100%;
  background: repeating-linear-gradient(90deg, var(--red) 0 4px, rgba(255,32,32,.35) 4px 7px);
  box-shadow: 0 0 14px var(--red-glow);
  transition: width 0.6s var(--ease);
}
.strength__pct {
  position: absolute; right: -46px; top: 50%; transform: translateY(-50%);
  font-family: var(--f-ui); font-size: 0.68rem; font-weight: 700; color: var(--bone);
}

/* ============================================================ partners == */
.partners__row {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 1.5rem;
  align-items: center;
  padding: 1.5rem 0;
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
}
/* Wordmarks, not logo files. Label marks are trademarks and hotlinking them
   from label sites would break and misrepresent; these read as a partner row
   without borrowing anyone's artwork. */
.partner {
  display: grid; justify-items: center; gap: 0.15rem;
  text-align: center;
  filter: grayscale(1); opacity: 0.5;
  transition: opacity 0.3s, filter 0.3s;
}
.partner:hover { opacity: 1; filter: none; }
.partner__name {
  font-family: var(--f-display); font-size: 1.15rem;
  letter-spacing: 0.02em; text-transform: uppercase; color: var(--bone);
  line-height: 1;
}
.partner__sub {
  font-family: var(--f-ui); font-size: 0.5rem; font-weight: 600;
  letter-spacing: 0.3em; text-transform: uppercase; color: var(--ash);
}

/* ================================================================ join == */
.joinsec { position: relative; overflow: hidden; }
.joinsec__bg {
  position: absolute; inset: 0;
  background:
    radial-gradient(60% 110% at 14% 50%, rgba(255, 32, 32, 0.14), transparent 60%),
    linear-gradient(90deg, #101218 0%, #0a0b0e 55%);
}
.joinsec__inner {
  position: relative;
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}
.joinsec__art { aspect-ratio: 1; display: grid; place-items: center; }
/* Concentric rings with a lit core — the design's circular hardware motif. */
.joinsec__ring {
  width: 74%; aspect-ratio: 1; border-radius: 50%;
  background:
    radial-gradient(circle, var(--red) 0 6%, transparent 7%),
    radial-gradient(circle, rgba(255,32,32,.28) 0 16%, transparent 17%),
    conic-gradient(from 0deg, #23262e 0 2deg, transparent 2deg 10deg);
  border: 1px solid var(--rail);
  box-shadow: 0 0 60px rgba(255, 32, 32, 0.25), inset 0 0 50px rgba(0,0,0,.9);
  position: relative;
}
.joinsec__ring::after {
  content: ""; position: absolute; inset: 22%;
  border: 1px solid var(--hair-bright); border-radius: 50%;
}
.joinsec__lede { color: var(--ash); margin: 0 0 1.8rem; font-size: 0.95rem; }

/* ============================================================== footer == */
.foot__grid { grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr)); }
.foot__name {
  font-family: var(--f-display); font-size: 1.05rem;
  text-transform: uppercase; margin: 0.9rem 0 0.3rem; color: var(--bone);
}
.foot__tag {
  font-family: var(--f-ui); font-size: 0.62rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ash); margin: 0;
}
.foot__live {
  font-family: var(--f-ui); font-size: 0.62rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--red);
  margin: 0.7rem 0 0;
}
.foot__locked {
  font-family: var(--f-ui); font-size: 0.72rem; font-weight: 500;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ash);
  margin: 0 0 1.2rem; line-height: 1.7;
}
.foot__desk { font-size: 0.72rem; color: var(--dim); margin: 1.2rem 0 0; line-height: 1.55; }

.signal {
  border: 1px solid var(--rail);
  background: var(--panel);
  padding: 0.6rem 0.75rem;
  display: grid; gap: 0.3rem;
}
.signal__k {
  font-family: var(--f-ui); font-size: 0.5rem; font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--dim);
}
.signal__v {
  font-family: var(--f-ui); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--red);
}
.signal__bars { display: flex; gap: 2px; align-items: flex-end; height: 14px; }
.signal__bars i {
  flex: 1; background: var(--red); opacity: 0.9;
  animation: signal-pulse 2.4s ease-in-out infinite;
}
.signal__bars i:nth-child(1) { height: 30%; animation-delay: 0s; }
.signal__bars i:nth-child(2) { height: 45%; animation-delay: .1s; }
.signal__bars i:nth-child(3) { height: 60%; animation-delay: .2s; }
.signal__bars i:nth-child(4) { height: 75%; animation-delay: .3s; }
.signal__bars i:nth-child(5) { height: 90%; animation-delay: .4s; }
.signal__bars i:nth-child(6) { height: 75%; animation-delay: .5s; }
.signal__bars i:nth-child(7) { height: 55%; animation-delay: .6s; }
.signal__bars i:nth-child(8) { height: 35%; animation-delay: .7s; }
@keyframes signal-pulse { 0%,100% { opacity: .9; } 50% { opacity: .35; } }

.foot__legal { display: inline-flex; gap: 0.7rem; align-items: center; }
.foot__legal a { transition: color 0.2s; }
.foot__legal a:hover { color: var(--red); }
.foot__sep { color: var(--red); }

/* ========================================================= docking deck == */
/* On the homepage the rack is the player, so the fixed deck stays out of the
   way until the rack has scrolled past. app.js adds .deck-docked to <body>. */
body.has-rack .deck {
  transform: translateY(110%);
  transition: transform 0.4s var(--ease);
}
body.has-rack.deck-docked .deck { transform: none; }
body.has-rack { padding-bottom: 0; }
body.has-rack.deck-docked { padding-bottom: var(--deck-h); }

/* ========================================================== breakpoints == */
@media (max-width: 1180px) {
  .podcast__grid { grid-template-columns: 160px minmax(0, 1fr); }
  .podcast__list { grid-column: 1 / -1; }
  .partners__row { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2rem 1rem; }
}

@media (max-width: 980px) {
  .board { grid-template-columns: 1fr; }
  .revgrid { grid-template-columns: 1fr; }
  .submitpanel__inner { grid-template-columns: 1fr; }
  .submitpanel__drop { padding-top: 0; }
  .joinsec__inner { grid-template-columns: 1fr; }
  .joinsec__art { display: none; }
  .hero__grid { grid-template-columns: 1fr; }
  .hero__art { order: -1; }
  .hero__figure { aspect-ratio: 16 / 9; }
}

@media (max-width: 780px) {
  .hero__rack { grid-template-columns: 1fr; }
  .hero__broadcasting { display: flex; align-items: baseline; gap: 0.8rem; }
  /* The meter and knob are the first things to go — transport, track and
     listener count are what a phone needs. */
  .rack { grid-template-columns: auto minmax(0, 1fr) auto; padding: 0.8rem; }
  .rack__vol, .rack__meter { display: none; }
  .rack__play { width: 58px; height: 58px; }
  .tform__row, .tform__row--join { grid-template-columns: 1fr; }
  .podcast__grid { grid-template-columns: 1fr; }
  .podcast__cover { max-width: 220px; }
  .podcast__transport { flex-wrap: wrap; }
  .podcast__scrub { order: 3; width: 100%; flex-basis: 100%; }
  .partners__row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .strength__pct { right: 0; top: -22px; transform: none; }
}

/* ------------------------------------------------------ podcast archive -- */
.eparchive { list-style: none; margin: 0 0 2.5rem; padding: 0; border-top: 1px solid var(--hair); }
.eparchive__row {
  display: grid;
  grid-template-columns: 62px minmax(0, 1fr) auto;
  gap: 1.1rem;
  align-items: center;
  padding: 0.85rem 0.7rem;
}
.eparchive__art {
  position: relative; aspect-ratio: 1; overflow: hidden;
  background: var(--rack); border: 1px solid var(--hair);
}
.eparchive__art img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(1) brightness(0.6); transition: filter 0.3s;
}
.eparchive__row:hover .eparchive__art img { filter: grayscale(0.2) brightness(0.9); }
.eparchive__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  opacity: 0; transition: opacity 0.2s; background: rgba(255, 32, 32, 0.35);
}
.eparchive__play::after {
  content: ""; border-left: 11px solid #fff;
  border-top: 7px solid transparent; border-bottom: 7px solid transparent; margin-left: 3px;
}
.eparchive__row:hover .eparchive__play,
.eparchive__row.is-current .eparchive__play { opacity: 1; }
.eparchive__meta { min-width: 0; }
.eparchive__n {
  display: block;
  font-family: var(--f-ui); font-size: 0.55rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--red);
}
.eparchive__name {
  display: block; margin-top: 0.2rem;
  font-family: var(--f-ui); font-size: 0.88rem; font-weight: 600;
  text-transform: uppercase; color: var(--bone);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.eparchive__time {
  font-family: var(--f-ui); font-size: 0.62rem; font-weight: 600;
  letter-spacing: 0.12em; color: var(--dim);
}
/* The archive's deck sits in the flow under the list rather than docking --
   the persistent player already follows the reader, and two fixed bars would
   fight for the bottom of the window. */
.eparchive__deck {
  border: 1px solid var(--rail);
  background: linear-gradient(180deg, rgba(20,22,28,.95), rgba(11,12,16,.95));
  padding: 1.4rem;
}
@media (max-width: 520px) {
  .eparchive__row { grid-template-columns: 48px minmax(0, 1fr) auto; gap: 0.8rem; }
}

/* =========================================================== display type ==
   Archivo is variable on width and weight, and both axes default to the
   regular setting. Anton needed neither -- it had exactly one cut -- so every
   display rule inherited its mass from the font file rather than from CSS.
   These are the elements that carry the display voice, and this is where they
   are given it, in one place, so the headline and the section heading under it
   cannot drift apart.

   font-stretch on a variable font maps to the wdth axis, so 84% is a real
   interpolated width rather than a synthetic squash.
   ========================================================================== */
.hero h1,
.article h1,
.pagehead h1,
.secheading,
.sec__title,
.board__title,
.home-col__title,
.prose h2,
.brand__name,
.foot__name,
.hero__live,
.evt__day,
.score__num,
.rev__score,
.stat__n,
.podcast__title,
.podcast__coverTag,
.partner__name {
  font-weight: var(--display-weight);
  font-stretch: var(--display-width);
  /* Heavy condensed caps need a touch of negative tracking or the counters
     close up and the word reads as a block. */
  letter-spacing: -0.005em;
  font-synthesis: none;
}

/* The two largest settings take a little more negative tracking still --
   optical, not linear: what looks right at 1.2rem is loose at 7rem. */
.hero h1 { letter-spacing: -0.02em; }
.article h1,
.pagehead h1 { letter-spacing: -0.015em; }

/* The mark in the masthead is small enough that condensing it hurts
   legibility more than it helps the family resemblance. */
.brand__name { font-stretch: 92%; letter-spacing: 0.01em; }

/* Numerals in readouts must not shift the layout as they count up and down.
   Archivo has tabular figures; without them the listener count jitters
   sideways every poll. */
.rack__count, .deck__count, .score__num, .rev__score, .stat__n,
.evt__day, .podcast__times, .strength__pct, .eparchive__time {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ------------------------------------------------------- headline texture --
   The design's headline is not flat metal: it is cast concrete, pitted and
   scratched. That texture is what makes it read as an object rather than as
   text with a gradient on it.

   Both layers are clipped to the glyphs -- a noise field multiplied over the
   metal ramp -- so the wear sits inside the letterforms and the background
   behind them stays clean.
   ========================================================================== */
.hero h1 {
  background-image:
    /* Coarse, low-contrast pitting. Multiplied at full strength this turned
       the letters grey and stripy; overlay keeps the metal bright and only
       varies it, which is what wear actually looks like. */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.55' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='260' height='260' filter='url(%23g)' opacity='0.32'/%3E%3C/svg%3E"),
    linear-gradient(174deg, #fff 3%, #e9ebef 22%, #babec5 44%, #fdfdfe 58%, #c7cbd2 78%, #969aa3 100%);
  background-size: 260px 260px, 100% 100%;
  background-blend-mode: overlay, normal;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 4px 22px rgba(0, 0, 0, 0.75));
}

/* ------------------------------------------------------- hero full bleed --
   In the design the artwork runs off the right edge of the screen; it is not
   a framed panel sitting in a column. Reproducing that means breaking the
   hero row out of the centred shell: full viewport width, with the left side
   padded to where the shell's gutter would have been so the headline still
   lines up with everything below it, and nothing on the right so the image
   reaches the edge.

   The rack underneath stays inside the shell -- it is a piece of equipment
   with two ends, and running it off the screen would read as broken rather
   than as bleed. */
.hero__grid.shell {
  width: 100%;
  max-width: none;
  margin-inline: 0;
  padding-left: max(1.5rem, calc((100vw - 1440px) / 2));
  padding-right: 0;
  gap: clamp(1rem, 3vw, 2.5rem);
}

/* Bled to the edge, the cut corner has nothing to sit against. */
.hero__figure {
  clip-path: none;
  aspect-ratio: 4 / 3.15;
}

/* The corner brackets marked the panel's frame. With no frame, only the one
   away from the bleed still reads. */
.hero__corner--tr { display: none; }
.hero__corner--br { bottom: 18px; left: 18px; }

@media (max-width: 980px) {
  /* Stacked, the image is a banner again and wants both gutters back. */
  .hero__grid.shell {
    width: var(--shell);
    margin-inline: auto;
    padding-left: 0;
  }
  .hero__figure { aspect-ratio: 16 / 9; }
}

/* The artwork's own background is lighter than the page, so a short blend
   left a visible vertical seam where the panel began. Carry the fade further
   in -- past the machinery and into the empty half of the frame -- so the
   image resolves out of the black instead of starting at an edge. */
.hero__figure::after {
  background:
    /* The skull sits at roughly 15-35% across the crop, so the fade has to be
       spent before it reaches there: opaque at the seam, gone by a third. Any
       longer and it dissolves the face; any shorter and the artwork starts at
       a visible vertical edge. */
    linear-gradient(90deg,
      var(--void) 0%,
      rgba(8, 8, 11, 0.78) 5%,
      rgba(8, 8, 11, 0.3) 13%,
      transparent 27%),
    linear-gradient(to top, rgba(8, 8, 11, 0.72) 0%, transparent 26%);
}

/* The sleeve was sized for an empty panel. Against the artwork it competes
   with the face, so it steps back to a readout rather than a second picture. */
.hero__cover {
  width: 84px; height: 84px;
  right: 1.1rem; bottom: 1.1rem;
  filter: grayscale(0.7) contrast(1.1) brightness(0.85);
  opacity: 0.92;
}

/* ==========================================================================
   HERO — full bleed
   The artwork is the section, not a picture inside it. Everything below
   overrides the earlier panel-in-a-column treatment.
   ========================================================================== */
.hero {
  position: relative;
  overflow: hidden;
  padding: 0;
  isolation: isolate;
}

/* The artwork. Sized by HEIGHT rather than `cover` so the head scales with
   the section and stays huge on a wide screen -- `cover` on a 21:9 monitor
   would have scaled it down to fit the width and left the skull small in a
   sea of black. Anchored right so the face lands mid-section and the
   machinery bleeds off the edge. */
.hero__art {
  position: absolute;
  inset: 0;
  z-index: -3;
  /* artwork moved to an <img> in .hero__frame -- see the glow section */
}

/* The live spectrum, dropped behind the veil so it reads as instrumentation
   inside the picture rather than a graph drawn over it. */
.hero__spectrum {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  width: 100%; height: 46%;
  z-index: -2;
  opacity: 0.62;
  pointer-events: none;
}

/* Two jobs: hold the left half dark enough to set a headline on, and sink the
   bottom into the page so the rack has something to sit against. The stops
   are placed around the subject -- the skull sits just past halfway, so the
   veil is spent before it reaches the face. */
.hero__veil {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(90deg,
      var(--void) 0%,
      rgba(8, 8, 11, 0.96) 22%,
      rgba(8, 8, 11, 0.72) 38%,
      rgba(8, 8, 11, 0.22) 52%,
      transparent 64%),
    linear-gradient(to top, var(--void) 0%, rgba(8, 8, 11, 0.5) 12%, transparent 34%),
    linear-gradient(to bottom, rgba(8, 8, 11, 0.7) 0%, transparent 18%);
}

/* Blueprint grid, kept faint and only over the dark side. */
.hero__grid-lines {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.5;
  background-image:
    linear-gradient(var(--hair) 1px, transparent 1px),
    linear-gradient(90deg, var(--hair) 1px, transparent 1px);
  background-size: 74px 74px;
  mask-image: linear-gradient(90deg, #000 0%, #000 34%, transparent 58%);
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 34%, transparent 58%);
}

/* Back to one column: the copy sits on the artwork, so there is no second
   column for it to share the row with. */
.hero__grid.shell {
  width: var(--shell);
  max-width: 1440px;
  margin-inline: auto;
  padding: clamp(3.5rem, 8vw, 7rem) 0 clamp(2rem, 4vw, 3.5rem);
  display: block;
  min-height: 0;
  position: relative;
}
.hero__copy { max-width: min(52ch, 54%); }

.hero__badge {
  position: absolute;
  top: clamp(1.2rem, 3vw, 2.2rem);
  right: clamp(1.5rem, 4vw, 3rem);
  z-index: 2;
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 700;
  letter-spacing: 0.24em; text-transform: uppercase; color: var(--bone);
  background: rgba(8, 8, 11, 0.72);
  border: 1px solid var(--hair-bright);
  padding: 0.4rem 0.8rem;
  backdrop-filter: blur(6px);
}

.hero__rack { position: relative; z-index: 2; padding-top: 0; }

/* Headline can grow again now that it has the whole left half to itself. */
.hero h1 { font-size: clamp(3rem, 7.6vw, 7rem); }

@media (max-width: 980px) {
  /* Stacked on a phone the artwork cannot both fill the width and leave room
     for the words, so it becomes a band across the top of the section and the
     copy sits under it on black. */
  .hero__art {
    inset: 0 0 auto 0;
    height: 46%;
    background-size: cover;
    background-position: 62% 12%;
  }
  .hero__veil {
    background:
      linear-gradient(to bottom, rgba(8, 8, 11, 0.35) 0%, rgba(8, 8, 11, 0.8) 34%, var(--void) 48%);
  }
  .hero__grid.shell { padding-top: 44%; }
  .hero__copy { max-width: none; }
  .hero__spectrum { height: 30%; bottom: auto; top: 30%; opacity: 0.5; }
  .hero__grid-lines { display: none; }
}

/* `52ch` measured against the 16px body size, not the headline, so it capped
   the column near 430px and clipped NEUROFUNK. The constraint that matters
   here is the share of the section the artwork is not using. */
.hero__copy { max-width: 60%; }

/* The panel-era rule set `left`, and an absolutely positioned element with
   both left and right resolved stretches between them -- which turned the
   badge into a full-width bar. */
.hero__badge { left: auto; }

@media (max-width: 980px) {
  .hero__copy { max-width: none; }
  .hero__badge { top: 1rem; right: 1rem; }
}

@media (max-width: 780px) {
  /* At this width the stat column resolves to about 60px and "LISTENERS" at
     full tracking needs closer to 75, so the label was clipping inside its
     own box. Tighten the tracking rather than shrink the count. */
  .rack__stat .rack__k { letter-spacing: 0.1em; font-size: 0.46rem; }
}

/* ------------------------------------------------------------- lockup -- */
/* The hero wordmark is a supplied image, not type. It carries the kicker, the
   textured NEUROFUNK RADIO and the tagline in one file, so the three live
   elements it replaces are gone from the markup. Sized by width against the
   copy column and left to keep its own aspect from the intrinsic attributes,
   which also stops it shifting layout while it loads. */
.hero__lockup {
  margin: 0 0 clamp(1.6rem, 3vw, 2.4rem);
  max-width: min(660px, 100%);
  /* Reset the display-type rules -- there is no text in here to set. */
  background: none;
  filter: none;
  letter-spacing: normal;
  line-height: 0;
}
.hero__lockup img {
  display: block;
  width: 100%;
  height: auto;
  /* Lifts the concrete off a black field; the source was authored on white. */
  filter: drop-shadow(0 6px 26px rgba(0, 0, 0, 0.85)) brightness(1.06);
}

@media (max-width: 980px) {
  .hero__lockup { max-width: 520px; }
}

/* Push the figure further off the right edge so the wordmark has clean black
   behind it. Anchored past 100% rather than scaled up -- growing the image
   would extend it leftward, straight back under the type. */
/* .hero__art background-position superseded by .hero__frame */

@media (max-width: 980px) {
  /* mobile framing handled on .hero__frame > img */
}

/* The lockup was set to fill the copy column, which put it at roughly 42% of
   the viewport. In the reference the wordmark is about 30% -- it reads as a
   mark sitting on the artwork, not as a slab competing with it. Sized in vw
   so that ratio holds across widths rather than only at one.

   The reference also insets the hero copy past the site gutter, so the
   wordmark starts inboard of the masthead logo rather than aligning with it. */
.hero__lockup {
  max-width: clamp(240px, 30vw, 470px);
  margin-bottom: clamp(1.3rem, 2.4vw, 1.9rem);
}
.hero__copy { padding-left: clamp(0rem, 4vw, 4.5rem); }

@media (max-width: 980px) {
  .hero__lockup { max-width: min(360px, 80%); }
  .hero__copy { padding-left: 0; }
}

/* The meter was pinned to the top of the hero, not the bottom. The panel-era
   rule set `inset: 0`, and when top, bottom and height are all resolved the
   browser drops `bottom` -- so the later `bottom: 0` never applied. Clearing
   `top` is what actually anchors it.

   Sized so the bars stand just above the rack: the baseline sits at ~89% of
   the hero and they rise into the lower half, which puts the movement around
   the player rather than across the wordmark. */
.hero__spectrum {
  top: auto;
  bottom: 0;
  height: 60%;
  opacity: 0.55;
}

@media (max-width: 980px) {
  .hero__spectrum { top: auto; bottom: 0; height: 34%; opacity: 0.45; }
}

/* ------------------------------------------------------- masthead logo -- */
/* One supplied image replaces the SVG mark and the stacked name/strapline.
   Sized by height so it sits to the masthead's rhythm; the intrinsic width
   and height attributes hold its aspect and reserve the space while it loads,
   so the nav does not jump. */
.brand__logo {
  display: block;
  height: clamp(20px, 1.9vw, 27px);
  width: auto;
  transition: opacity 0.2s;
}
.brand:hover .brand__logo { opacity: 0.82; }

@media (max-width: 900px) {
  .brand__logo { height: 22px; }
}

/* ==========================================================================
   HERO — glowing eyes
   The artwork is now an <img> in a frame rather than a background, because a
   background has no element whose coordinate space the glows could be pinned
   to. Percentages of the hero itself would drift off the sockets at every
   other viewport width; percentages of the frame track the picture exactly.

   The frame reproduces what the background shorthand was doing: 122% of the
   hero's height, the source's own aspect, anchored 70px past the right edge.
   The -1.3% top is the old `background-position-y: 6%` resolved -- with the
   image 22% taller than the box, 6% of the overflow is 1.32% of the height.
   ========================================================================== */
.hero__art { overflow: hidden; }

.hero__frame {
  position: absolute;
  top: -1.3%;
  right: -70px;
  height: 122%;
  aspect-ratio: 898 / 941;
}
.hero__frame > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Painted after the veil and at the same depth, so the light sits on top of
   the darkening rather than under it. */
.hero__glows {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

/* ------------------------------------------------------------- the light -- */
/* `screen` is what makes these read as emission rather than as coloured discs
   laid over the picture: it can only ever lighten, so the machined detail
   underneath still shows through the glow. */
.glow {
  position: absolute;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  mix-blend-mode: screen;
  will-change: opacity;
}

/* Positions measured off the source: these are where the red actually is. */
.glow--lens {                       /* the large mechanical iris */
  left: 49.5%; top: 26.5%;
  width: 23%; aspect-ratio: 1;
  background: radial-gradient(circle,
    rgba(255, 214, 196, 0.95) 0%,
    rgba(255, 74, 46, 0.85) 12%,
    rgba(255, 26, 18, 0.5) 30%,
    rgba(150, 8, 8, 0.22) 52%,
    transparent 72%);
  animation: ember 8.4s ease-in-out infinite;
}
.glow--socket {                     /* the skull's own eye */
  left: 10.45%; top: 32.5%;
  width: 9%; aspect-ratio: 1;
  background: radial-gradient(circle,
    rgba(255, 190, 170, 0.95) 0%,
    rgba(255, 60, 34, 0.8) 16%,
    rgba(190, 12, 10, 0.4) 40%,
    transparent 70%);
  /* Its own rhythm -- two bulbs in one head failing in unison would read as a
     CSS animation rather than as broken hardware. */
  animation: ember 6.6s ease-in-out infinite;
  animation-delay: -2.9s;
}
.glow--jaw {                        /* circuitry under the cheek */
  left: 21%; top: 46%;
  width: 15%; aspect-ratio: 1.4;
  background: radial-gradient(ellipse,
    rgba(255, 48, 30, 0.5) 0%,
    rgba(160, 10, 10, 0.22) 42%,
    transparent 72%);
  animation: ember 9.8s ease-in-out infinite;
  animation-delay: -4.1s;
}
.glow--core {                       /* the chest plate */
  left: 40%; top: 58%;
  width: 19%; aspect-ratio: 1.2;
  background: radial-gradient(ellipse,
    rgba(255, 40, 26, 0.42) 0%,
    rgba(140, 8, 8, 0.18) 45%,
    transparent 74%);
  animation: ember 11.5s ease-in-out infinite;
  animation-delay: -1.7s;
}

/* ------------------------------------------------------------- flicker -- */
/* A failing fluorescent tube does not fade -- it cuts, stutters and catches.
   `steps(1, end)` on the animation is what gives the hard on/off edges;
   interpolated opacity would read as a gentle throb instead.

   Most of the cycle is steady. Two bursts, at roughly a third and
   three-quarters through, keep it occasional rather than nervous. */
@keyframes eye-flicker {
  0%,   30%   { opacity: 1; }
  30.4%       { opacity: 0.12; }
  31%         { opacity: 0.92; }
  31.5%       { opacity: 0.08; }
  32.2%       { opacity: 1; }
  33%         { opacity: 0.3; }
  33.6%       { opacity: 0.95; }
  34%         { opacity: 0.06; }
  35%         { opacity: 1; }

  36%,  71%   { opacity: 1; }
  71.4%       { opacity: 0.1; }
  72%         { opacity: 0.85; }
  72.4%       { opacity: 0.15; }
  73.2%       { opacity: 1; }
  74%         { opacity: 0.42; }
  74.5%       { opacity: 1; }

  76%,  100%  { opacity: 1; }
}

/* The slow swell underneath, so the light is never quite static between
   stutters. */
@keyframes eye-breathe {
  0%, 100% { transform: translate(-50%, -50%) scale(1); filter: brightness(1); }
  50%      { transform: translate(-50%, -50%) scale(1.06); filter: brightness(1.22); }
}

@media (prefers-reduced-motion: reduce) {
  /* Keep the light, drop the stutter -- a flickering element is exactly what
     this preference exists to suppress. */
  .glow { animation: none !important; opacity: 1; }
}

@media (max-width: 980px) {
  /* The artwork becomes a band across the top; the frame follows it. */
  .hero__frame {
    top: 0; right: 0; left: 0;
    height: 100%; width: 100%;
    aspect-ratio: auto;
  }
  .hero__frame > img { object-fit: cover; object-position: 62% 12%; }
  /* Cropped this hard the sockets no longer land where the percentages say. */
  .hero__glows { display: none; }
}

/* ==========================================================================
   HERO LIGHTING — two behaviours, deliberately different
   The red is powered and idling; the tubes over the metal are failing. Giving
   both the same stutter made the whole head read as one broken thing. So the
   red swells slowly and never cuts, and the flicker moved onto the chrome.
   ========================================================================== */

/* The red lights. A long ease from a low ember to full and back -- no steps
   anywhere, so it never blinks. Each element runs a different period so the
   sockets, jaw and chest drift out of phase instead of pulsing as one. */
@keyframes ember {
  0%, 100% { opacity: 0.34; transform: translate(-50%, -50%) scale(0.94); filter: brightness(0.82); }
  50%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.07); filter: brightness(1.25); }
}

/* The tube flicker, over the metal.
   `color-dodge` is what confines it to the highlights: dodge divides by the
   inverse of the blend colour, so a pixel already near white flares hard and a
   black one cannot move at all. A plain white overlay in `screen` would have
   lifted the shadows too and turned the background grey.

   Anchored to the top-left, where the light in the artwork comes from -- an
   even wash across the whole frame reads as the page brightening rather than
   as a fixture above the head. */
.hero__flare {
  position: absolute;
  inset: 0;
  pointer-events: none;
  mix-blend-mode: color-dodge;
  opacity: 0;
  background:
    radial-gradient(120% 90% at 34% 8%,
      rgba(255, 252, 245, 0.5) 0%,
      rgba(220, 232, 255, 0.22) 34%,
      rgba(120, 140, 170, 0.06) 62%,
      transparent 82%);
  animation: tube-flicker 13s steps(1, end) infinite;
}

/* A failing tube cuts and catches -- it does not fade. `steps(1, end)` is what
   gives the hard edges; interpolated opacity would read as a throb. Most of
   the cycle is dark, with two short bursts, so it stays occasional. */
@keyframes tube-flicker {
  0%,   26%   { opacity: 0; }
  26.3%       { opacity: 0.9; }
  26.7%       { opacity: 0.05; }
  27.2%       { opacity: 0.75; }
  27.5%       { opacity: 0; }
  28.1%       { opacity: 1; }
  28.6%       { opacity: 0.15; }
  29.3%       { opacity: 0.55; }
  29.9%       { opacity: 0; }

  30%,  67%   { opacity: 0; }
  67.2%       { opacity: 0.7; }
  67.6%       { opacity: 0; }
  68.1%       { opacity: 1; }
  68.4%       { opacity: 0.2; }
  68.9%       { opacity: 0.85; }
  69.6%       { opacity: 0.1; }
  70.2%       { opacity: 0.4; }
  70.8%       { opacity: 0; }

  71%,  100%  { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  /* Keep a hint of the light, drop every cut. */
  .hero__flare { animation: none !important; opacity: 0.12; }
  .glow { animation: none !important; opacity: 0.85; }
}

@media (max-width: 980px) {
  /* The frame is a cropped band on a phone; the flare has nothing to sit on. */
  .hero__flare { display: none; }
}

/* ------------------------------------------------------------ the cut -- */
/* A tube does not only flash -- it drops out. This is the other half of the
   flicker: the metal goes almost black while the red keeps burning, because
   the glows are painted on their own layer above this one.

   Not fully opaque. At 0.87 the skull is still faintly readable in the dark,
   which is the point: the head does not disappear, it goes unlit.

   Timed into the gaps of the flare on the same 13s cycle, so the two read as
   one failing fixture rather than as two effects that happen to share a
   frame. The second burst ends on a long hold -- the moment the light is
   properly out before it catches again. */
.hero__blackout {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: #05060a;
  opacity: 0;
  animation: tube-cut 13s steps(1, end) infinite;
}

@keyframes tube-cut {
  0%,   26.5% { opacity: 0; }
  26.7%       { opacity: 0.9; }
  27.1%       { opacity: 0; }
  27.6%       { opacity: 0.87; }
  28.0%       { opacity: 0; }
  28.7%       { opacity: 0.86; }
  29.2%       { opacity: 0; }
  29.9%       { opacity: 0.92; }
  30.7%       { opacity: 0; }

  31%,  67.4% { opacity: 0; }
  67.7%       { opacity: 0.9; }
  68.0%       { opacity: 0; }
  68.5%       { opacity: 0.87; }
  68.8%       { opacity: 0; }
  69.7%       { opacity: 0.88; }
  70.1%       { opacity: 0; }
  /* The long one: roughly half a second of darkness. */
  70.9%       { opacity: 0.87; }
  74.2%       { opacity: 0.87; }
  74.6%       { opacity: 0.35; }
  75.0%       { opacity: 0.9; }
  75.6%       { opacity: 0; }

  76%,  100%  { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .hero__blackout { animation: none !important; opacity: 0; }
}

@media (max-width: 980px) {
  .hero__blackout { display: none; }
}

/* The artwork left edge is no longer masked. The feather was there for the
   still, whose color-dodge flare lit the picture up to a hard border; the
   loop is square, so the skull sits further left and the feather was cutting
   into the face. With the flare off there is no bright edge to hide. */


.hero__frame > video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The footage already pulses the eyes and drifts smoke through the frame.
   Leaving the painted glows on would light the same head twice, and at the
   wrong coordinates -- they were measured against the still's crop. */
.hero__glows,
.hero__flare,
.hero__blackout { display: none; }

@media (prefers-reduced-motion: reduce) {
  /* app.js pauses it; this makes sure the poster is what shows if the pause
     lands a frame late. */
  .hero__frame > video { opacity: 1; }
}

/* ------------------------------------------------- artwork edge, take 2 -- */
/* The loop is a rectangle on a black page and its content runs right to its
   own border, so it ended on a visible vertical line.

   The first attempt feathered a third of the frame and washed the face out.
   The subject reaches within a few percent of the left edge, so the fade has
   to be narrow enough to catch only the border itself -- eight percent, fully
   opaque by the time it reaches any skull. Same treatment on the bottom edge,
   which met the board section on a hard line for the same reason. */
.hero__frame {
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0%, rgba(0,0,0,0.55) 3.5%, #000 8%),
    linear-gradient(to bottom, #000 82%, transparent 100%);
  mask-image:
    linear-gradient(90deg, transparent 0%, rgba(0,0,0,0.55) 3.5%, #000 8%),
    linear-gradient(to bottom, #000 82%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* Slightly taller so less of the square is lost to the hero's crop -- more of
   the loop is actually on screen. */
.hero__frame { height: 128%; top: -3%; }

@media (max-width: 980px) {
  .hero__frame {
    -webkit-mask-image: linear-gradient(to bottom, #000 58%, transparent 97%);
    mask-image: linear-gradient(to bottom, #000 58%, transparent 97%);
    -webkit-mask-composite: source-over;
    mask-composite: add;
    height: 100%; top: 0;
  }
}

/* ---------------------------------------------------------- event page -- */
/* The listing row is a link now, so it needs the affordances a link has. */
.evt { text-decoration: none; }
.evt__name, .evt__where, .evt__lineup { display: block; }
.evt__more,
.evt .evt__tix { display: inline-block; margin-top: 0.45rem; }
.evt:hover .evt__name { color: var(--red-hot); }
.evt:hover .evt__tix { text-decoration: underline; text-underline-offset: 3px; }

/* The date block on the event's own page: the listing chip, enlarged, with
   the year that the listing has no room for. */
.evpage__date {
  border: 1px solid var(--rail);
  background: var(--rack);
  text-align: center;
  padding: 0.6rem 0.9rem 0.7rem;
  position: relative;
  align-self: start;
}
.evpage__date::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: var(--red);
}
.evpage__date .evt__mon { font-size: 0.62rem; }
.evpage__date .evt__day { font-size: 2.4rem; }
.evpage__year {
  display: block;
  font-family: var(--f-ui); font-size: 0.58rem; font-weight: 600;
  letter-spacing: 0.18em; color: var(--dim); margin-top: 0.2rem;
}

/* A passed event says so before anything else on the page. */
.evpage__passed { margin-bottom: 2rem; }
.evpage .score { align-items: start; }
.evpage .score__v { white-space: normal; }

/* ------------------------------------------------ on-air badge, moved -- */
/* It now sits inside the CTA row rather than floating over the artwork, so
   the absolute positioning that put it top-right has to be undone -- and the
   `left: auto` that was compensating for an inherited `left` goes with it. */
.hero__badge {
  position: static;
  top: auto;
  right: auto;
  left: auto;
  align-self: center;
}

@media (max-width: 980px) {
  .hero__badge { position: static; top: auto; right: auto; }
}
