/* site.css: tokens, page layout, every section and every state hook listed in plans/contract.md.
   The deck (#deck and everything in it) is styled separately in deck.css.

   Stacking: the fixed canvas#rig paints at --z-rig. Anything that must sit above the beams is
   positioned with --z-content, which is why every band and the hero's copy carry it. The hero
   photo sits UNDER the rig (z-index -1 on .hero-figure), so beams play across the booth instead
   of stopping at the picture's edge. (It used to screen-blend onto the rig; recomputing that blend
   on every rig frame cost about half the frame rate in software-rendered browsers.) Because a
   negative z-index paints below the body's own background, the body is transparent and <html>
   paints the --void base. */

:root {
  /* Colours and fonts (--void ... --smoke, --uv-soft, --line, --band, --lcd-dots, --f-*) live in
     theme.css, generated by tools/theme.py from the profile's theme.json. */
  /* Landscape iPhones: clear the notch or Dynamic Island (the page opts into viewport-fit=cover).
     --gutter, the nav, the hero bleed and club mode all derive from this. 0 in portrait, and on
     Android. */
  --edge: max(clamp(16px, 5vw, 64px), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));
  --deck-h: 89px;       /* deck.js overwrites this on <html> with its measured height */
  --z-rig: 0; --z-content: 1; --z-nav: 30; --z-deck: 40; --z-drawer: 45; --z-modal: 60; --z-club: 80; --z-toast: 90;

  --radius: 14px;
  --radius-sm: 10px;
  --nav-h: 56px;
  --ticker-h: 52px;
  --wrap-max: 1312px;
  /* Side padding that lands content on the same edge as .wrap at every width. */
  --gutter: max(var(--edge), calc((100% - var(--wrap-max)) / 2));
  --ease: cubic-bezier(.2, .7, .2, 1);

  /* Line icons, used as masks over currentColor (so they follow each button's text colour). */
  --i-cal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4M12 13.5v5M9.5 16h5'/%3E%3C/svg%3E");
  --i-out: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7M9 7h8v8'/%3E%3C/svg%3E");
  --i-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='9' y='9' width='12' height='12' rx='2'/%3E%3Cpath d='M5 15V5a2 2 0 0 1 2-2h8'/%3E%3C/svg%3E");
  --i-print: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9V3h12v6'/%3E%3Crect x='3' y='9' width='18' height='8' rx='2'/%3E%3Cpath d='M7 14h10v7H7z'/%3E%3C/svg%3E");
  --i-share: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='18' cy='5' r='3'/%3E%3Ccircle cx='6' cy='12' r='3'/%3E%3Ccircle cx='18' cy='19' r='3'/%3E%3Cpath d='m8.6 13.5 6.8 4M15.4 6.5l-6.8 4'/%3E%3C/svg%3E");
  --i-skim: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M2 5.5 11 12l-9 6.5zM12 5.5l9 6.5-9 6.5z'/%3E%3C/svg%3E");
  --i-disc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9.5'/%3E%3Ccircle cx='12' cy='12' r='2.5'/%3E%3Cpath d='M12 5.5a6.5 6.5 0 0 1 6.5 6.5'/%3E%3C/svg%3E");

  color-scheme: dark;
}

@media (min-width: 900px) {
  :root { --nav-h: 64px; }
}
/* The deck's usual heights (deck.css): the phone bar, and touch tablets' taller strip. deck.js only
   writes --deck-h when its measured height differs (a safe-area inset, a two-line bar), so the
   first paint already has the right padding and boot does not restyle the page to fix it. */
@media (max-width: 719.98px) {
  :root { --deck-h: 69px; }
}
@media (pointer: coarse) and (min-width: 720px) and (max-width: 1199.98px) and (min-height: 600px) {
  :root { --deck-h: 111px; }
}

/* ---------- base ---------- */

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

[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  background: var(--void);
  scroll-padding-top: calc(var(--nav-h) + 12px);
  /* The fixed deck covers the bottom of the viewport: keep keyboard focus (and anything scrolled
     "into view") clear of it (WCAG 2.4.11). */
  scroll-padding-bottom: calc(var(--deck-h) + 16px);
  scrollbar-color: var(--booth-2) var(--void);
}
/* While the "start the music" cue is up it floats above the deck (its top edge is deck + 46 px on
   desktop, deck + 40 px on phones), so focus and anchor jumps stop above it too. Padding only: no
   layout changes, so nothing jumps when the music starts. */
html.sound-armed { scroll-padding-bottom: calc(var(--deck-h) + 56px); }

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  min-height: 100vh;
  background: transparent;   /* html paints --void; see the stacking note at the top */
  color: var(--ink);
  font: 400 1.0625rem/1.6 var(--f-body);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  padding-bottom: var(--deck-h);
}

img, svg, canvas, picture, video { display: block; max-width: 100%; }
img { height: auto; }

h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; }

a { color: var(--uv-soft); }

button, input, select, textarea { font: inherit; color: inherit; }
button { -webkit-tap-highlight-color: transparent; }
/* main.js gives each top-level block a no-op click listener, which is what makes iOS send a click
   for a tap on plain text (the first tap starts the music). WebKit would then flash its grey tap
   highlight over the whole block, so the blocks get none and links keep a subtle UV one. */
body > * { -webkit-tap-highlight-color: transparent; }
a[href] { -webkit-tap-highlight-color: rgb(var(--uv-rgb) / .3); }

::selection { background: var(--uv); color: var(--ink); }

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

main:focus { outline: none; }

.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  top: 10px; left: 10px;
  z-index: calc(var(--z-toast) + 1);
  padding: 10px 16px;
  border-radius: 8px;
  background: var(--lamp);
  color: var(--void);
  font-weight: 700;
  text-decoration: none;
  transform: translateY(-160%);
}
.skip-link:focus { transform: none; }

/* h2 is one scale everywhere; section eyebrows reuse the element with .eyebrow. */
h2 {
  font: var(--display-weight-head) clamp(2rem, 5vw, 3.5rem)/1.02 var(--f-display);
  letter-spacing: -.02em;
  text-wrap: balance;
}

.eyebrow {
  margin: 0 0 16px;
  font: 600 .8125rem/1.2 var(--f-body);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--smoke);
}

.dot-list {
  --sep: 21px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin-left: calc(-1 * var(--sep));
  clip-path: inset(-6px 0 -6px var(--sep));
  list-style: none;
}
.dot-list > * { display: inline-flex; align-items: center; }
.dot-list > *::before {
  content: "";
  flex: none;
  width: 3px;
  height: 3px;
  margin: 0 9px;
  border-radius: 50%;
  background: currentColor;
  opacity: .7;
}

.text-link {
  color: var(--uv-soft);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: rgb(var(--uv-soft-rgb) / .45);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  transition: color .2s, text-decoration-color .2s;
}
.text-link:hover { color: var(--ink); text-decoration-color: currentColor; }

/* ---------- the rig canvas ---------- */

#rig {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: var(--z-rig);
  pointer-events: none;
}

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

.btn {
  --btn-h: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: var(--btn-h);
  padding: 0 22px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font: 700 1rem/1.15 var(--f-body);
  letter-spacing: .005em;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .2s, color .2s, border-color .2s, box-shadow .25s, transform .15s;
}
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--lamp); color: var(--void); }
@media (hover: hover) {
  .btn-primary:hover {
    background: color-mix(in oklab, var(--lamp) 84%, var(--ink));
    box-shadow: 0 0 0 4px rgb(var(--lamp-rgb) / .18);
  }
}

.btn-secondary { background: transparent; color: var(--ink); border-color: rgb(var(--ink-rgb) / .82); }
@media (hover: hover) {
  .btn-secondary:hover { background: rgb(var(--ink-rgb) / .08); border-color: var(--ink); }
}

.btn-sm { --btn-h: 38px; padding: 0 14px; font-size: .875rem; border-radius: 8px; }
.btn-lg { --btn-h: 54px; padding: 0 26px; font-size: 1.0625rem; }

.btn[disabled],
.btn[aria-disabled="true"] { cursor: progress; }

/* Play / pause glyphs drawn with clip-path so they inherit currentColor. */
.glyph {
  flex: none;
  width: 11px;
  height: 13px;
  background: currentColor;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
.feature-card.is-playing .glyph,
.crate-row.is-playing .glyph,
#hero-play.is-playing .glyph {
  clip-path: polygon(0 0, 34% 0, 34% 100%, 66% 100%, 66% 0, 100% 0, 100% 100%, 0 100%);
}
/* A shared moment waits for the first tap (main.js renderCue): any tap plays it, so the hero's own
   Play, which names another set, steps aside until something plays. */
html.sound-armed-link #hero-play { display: none; }

/* ---------- nav + truss ---------- */

.site-nav {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  height: var(--nav-h);
  background: var(--void);
}

.nav-inner {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 100%;
  padding-inline: var(--gutter);
}

.wordmark {
  margin-right: auto;
  /* A 45 px tall link for a 15 px line: the padding grows the target, the negative margin gives
     the height back, so the (flex) layout around it does not change. */
  padding-block: 15px;
  margin-block: -15px;
  font: var(--display-weight-hero) .9375rem/1 var(--f-display);
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

.nav-cta { order: 2; }
.nav-primary { order: 3; }
/* Touch: the small nav button still gets a 44 px target on phones and tablets. */
@media (max-width: 899px) {
  .nav-cta { --btn-h: 44px; }
}

.nav-toggle {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid rgb(var(--ink-rgb) / .28);
  border-radius: 8px;
  background: transparent;
  cursor: pointer;
}
.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  display: block;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform .25s var(--ease), opacity .2s;
}
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before,
.nav-toggle-bars::after { content: ""; position: absolute; left: 0; }
.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after { top: 6px; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after { transform: translateY(-6px) rotate(-45deg); }

.nav-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  list-style: none;
  padding: 10px var(--edge) 22px;
  /* Opaque, and a soft fall-off below, so the hero does not ghost through the links and the panel
     does not end in a hard cut across the name. The negative spread keeps the shadow off the nav. */
  background: var(--void);
  box-shadow: 0 32px 32px -12px rgb(var(--void-rgb) / .9);
  border-bottom: 1px solid var(--line);
}
.nav-menu.is-open { display: block; }
.nav-menu a {
  display: block;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
  font: 600 1.25rem/1.2 var(--f-body);
  color: var(--ink);
  text-decoration: none;
}
/* The menu hangs off the fixed header, so scrolling the page never brings a link out from under the
   deck: it stops at the deck's top edge and scrolls itself if it must. */
@media (max-width: 899px) {
  .nav-menu {
    max-height: calc(100dvh - var(--nav-h) - var(--deck-h));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}
/* Phones held landscape: two columns of shorter rows, so all five links clear the deck. */
@media (max-width: 899px) and (max-height: 500px) {
  .nav-menu.is-open { display: grid; grid-template-columns: 1fr 1fr; column-gap: 24px; }
  .nav-menu a { padding: 10px 0; }
}

@media (min-width: 900px) {
  .nav-inner { gap: clamp(20px, 3vw, 44px); }
  .wordmark { font-size: 1rem; }
  .nav-primary { order: 1; }
  .nav-cta { order: 2; }
  .nav-toggle { display: none; }
  .nav-menu {
    display: flex;
    position: static;
    gap: clamp(18px, 2.4vw, 34px);
    padding: 0;
    background: none;
    box-shadow: none;
    border: 0;
  }
  .nav-menu a {
    padding: 8px 0;
    border: 0;
    font: 500 .9375rem/1 var(--f-body);
    color: var(--smoke);
    transition: color .2s;
  }
  .nav-menu a:hover { color: var(--ink); }
}

/* The truss the rig hangs from: a side-on box truss (two chords, zig-zag bracing) with clamps.
   Lights reads the nav's bottom edge, which is exactly where this sits. */
.truss {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 7px;
  pointer-events: none;
  background: var(--mt-uri-truss) 0 0 / 12px 7px repeat-x;
}
.truss::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 6px;
  height: 5px;
  background: radial-gradient(circle at 48px 2.5px, var(--ink) 0 1.4px, transparent 2px) 0 0 / 96px 5px repeat-x;
  opacity: .35;
  transition: opacity .9s ease;
}
html.is-playing .truss::after { opacity: .85; }
/* Once the hero has scrolled away, page content passes under the truss. Back the truss strip, and
   (through the shadow) the clamp-dot row below it, so nothing shows through the zig-zag. The
   shadow paints under the ::after dots, so they stay visible. sections.js sets html.is-scrolled. */
html.is-scrolled .truss {
  background-color: rgb(var(--void-rgb) / .97);
  box-shadow: 0 5px 0 rgb(var(--void-rgb) / .97);
}

/* ---------- hero ---------- */

.hero {
  position: relative;   /* no z-index: see the stacking note at the top */
  display: flex;
  flex-direction: column;
  padding: 0 var(--gutter) 40px;
  overflow: clip;
  /* Below 900 px the photo sits on top at this height and the name overlaps its lower edge by
     --hero-overlap. */
  --fig-h: min(108vw, 60svh);
  --hero-overlap: clamp(52px, 17vw, 132px);
}

.hero-copy { display: flex; flex-direction: column; }
.hero-copy > * { position: relative; z-index: var(--z-content); }

.hero-figure {
  order: -1;
  position: relative;
  z-index: -1;          /* under canvas#rig: the beams play across the photo */
  height: var(--fig-h);
  margin: 0 calc(-1 * var(--edge));
}

#hero-photo {
  position: absolute;
  inset: 0;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 10%, #000 52%, transparent 96%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 10%, #000 52%, transparent 96%);
}
#hero-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--hero-pos-mobile);
}
@media (min-width: 900px) {
  #hero-photo img { object-position: var(--hero-pos); }
}
/* Scanlines: a static CRT texture inside the masked photo, so it darkens the photo only. */
#hero-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(to bottom, rgb(var(--void-rgb) / .5) 0 1px, transparent 1px 3px);
  pointer-events: none;
}

.photo-credit {
  position: absolute;
  z-index: var(--z-content);
  top: 18px;
  right: max(10px, env(safe-area-inset-right, 0px));   /* clear of a landscape notch */
  writing-mode: vertical-rl;
  white-space: nowrap;   /* one column even when a short phone holds the photo near its floor */
  /* A p beside the figure, not a figcaption inside it: the figure paints under canvas#rig, and the
     beams washed out anything in it. Here (absolute against .hero, --z-content) it sits above the
     rig, on a dark plate that keeps --smoke at AA over the photo. */
  padding: 8px 3px;
  border-radius: 4px;
  background: rgb(var(--void-rgb) / .85);
  font: 500 .6875rem/1 var(--f-body);
  letter-spacing: .08em;
  color: var(--smoke);
}

.hero-copy { margin-top: calc(-1 * var(--hero-overlap)); }

/* The thesis. Base colour stays --ink so it reads with the lights Off; the rig lights it through
   --glow (0..1, smoothed energy) and --sweep (rotation) that lights.js sets every frame.
   Per-frame cost matters here (the harness holds the page to 30 fps in software rendering):
   the UV halo is static, because animating a blur re-rasterises it every frame, and --glow only
   drives the opacity of the beam light on the letters. */
.hero-title {
  font: var(--display-weight-hero) calc(clamp(4rem, 15vw, 13rem) * var(--hero-k))/var(--hero-leading) var(--f-display);
  letter-spacing: var(--hero-track);
  text-transform: uppercase;
  color: var(--ink);
  text-shadow: 0 0 .2em rgb(var(--uv-rgb) / .28);
  /* Only acts when a line would overflow, which in practice means a reader's own text-spacing
     stylesheet (WCAG 1.4.12): a long surname then breaks rather than running off the edge. */
  overflow-wrap: anywhere;
  --hero-beams:
    transparent 0deg 16deg,
    rgb(var(--uv-rgb) / .95) 25deg, transparent 34deg 76deg,
    rgb(var(--lamp-rgb) / .78) 86deg, transparent 96deg 120deg;
}
.hero-line { position: relative; display: block; }
/* Below 900 px the name has the width to itself (no height budget to share), so a condensed
   display face can take a larger multiplier there: --hero-k-narrow (theme.css). */
@media (max-width: 899px) {
  .hero-title { --hero-k: var(--hero-k-narrow); }
}

@media (max-width: 400px) {
  .hero-title { font-size: calc(16.2vw * var(--hero-k)); }
}

/* Beam light on the letters: a conic gradient clipped to the glyphs, turned by --sweep, which
   lights.js writes as a unitless number of degrees. Each line's box hugs its text (less to repaint)
   and both lines place the light source at the same point, about half a line above the name, so the
   beams run on unbroken from the first name into the surname. `content: ... / ""` keeps the duplicate text out of
   the heading's accessible name. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero-line { width: fit-content; }
  .hero-line::before {
    --beam-y: -.6em;
    content: attr(data-text);
    content: attr(data-text) / "";
    position: absolute;
    inset: 0;
    color: transparent;
    -webkit-text-fill-color: transparent;
    text-shadow: none;
    background-image: repeating-conic-gradient(from calc(var(--sweep, 0) * 1deg) at 3em var(--beam-y), var(--hero-beams));
    -webkit-background-clip: text;
            background-clip: text;
    opacity: calc(var(--glow, 0) * .92);
    pointer-events: none;
    /* Own layer: the per-frame repaint stays here and never re-rasterises the haloed letters. */
    will-change: transform;
  }
  .hero-line + .hero-line::before { --beam-y: -1.46em; }   /* one line (.86em) further below the source */
  /* lights.js sets this at its lowest quality level (a device that cannot hold the frame rate):
     the light layers go and the name stays --ink with its static halo. */
  .hero-title[data-rig-lite] .hero-line::before { display: none; }
}

/* The tagline and the tour line sit straight on the transparent hero, over the rig, so each has a
   static void halo (a tight layer under the glyphs, a soft one around them) that keeps it at AA
   when a bright beam passes behind it in Rave. Static: nothing here changes per frame. */
.hero-tagline {
  margin-top: 22px;
  max-width: 34ch;
  font: 500 clamp(1.0625rem, 1.5vw, 1.3125rem)/1.5 var(--f-body);
  color: var(--ink);
  text-wrap: pretty;
  text-shadow: 0 0 3px rgb(var(--void-rgb) / .95), 0 0 8px rgb(var(--void-rgb) / .95), 0 1px 2px rgb(var(--void-rgb) / .95);
}
/* Portrait tablets: the name and both CTAs span the column, so a 34ch tagline left most of its
   block empty. */
@media (min-width: 600px) and (max-width: 899px) {
  .hero-tagline { max-width: 46ch; font-size: 1.1875rem; }
}

.hero-tour {
  margin-top: 12px;
  font: 600 .8125rem/1.4 var(--f-body);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--smoke);
  text-shadow: 0 0 3px rgb(var(--void-rgb) / .95), 0 0 8px rgb(var(--void-rgb) / .95), 0 1px 2px rgb(var(--void-rgb) / .95);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 28px;
}

/* The way into the booth, under the CTAs: a text link with a tiny record for an icon. It sits on the
   transparent hero, so it gets the same static void halo as the tagline. */
.hero-booth-line {
  margin-top: 16px;
  font-size: .9375rem;
  line-height: 1.45;
  text-shadow: 0 0 3px rgb(var(--void-rgb) / .95), 0 0 8px rgb(var(--void-rgb) / .95), 0 1px 2px rgb(var(--void-rgb) / .95);
}
.hero-booth { display: inline-flex; align-items: center; gap: 10px; }
.hero-booth-icon {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background:
    radial-gradient(circle, var(--void) 0 7%, var(--lamp) 8% 26%, transparent 27%),
    repeating-radial-gradient(circle, var(--mt-hero-booth-icon) 0 1px, var(--mt-hero-booth-icon-2) 1px 2.5px);
  box-shadow: 0 0 0 1px rgb(var(--uv-rgb) / .55);
}
@media (prefers-reduced-motion: no-preference) {
  .hero-booth:hover .hero-booth-icon { animation: tt-spin 1.8s linear infinite; }
}
@media (max-width: 899px) {
  /* The first screen ends exactly at the deck: the genre ticker starts at or below its top edge
     instead of being cut through its letters. */
  .hero { min-height: calc(100svh - var(--nav-h) - var(--deck-h)); }
  .hero-actions { margin-top: 20px; }
  .hero-actions .btn { flex: 1 1 auto; }
  /* The first-screen height budget: the photo gives way so everything under it clears the fixed
     deck, sound cue included (the phone cue below is a 32 px strip 8 px above the deck). With the
     two buttons side by side (from about 465 px), 402 px covers the copy from the name down to
     them, the booth link under them and the cue. It follows --deck-h, so a taller deck (touch tablets) shrinks the photo to
     match. It is constant, not tied to html.sound-armed, so the photo never jumps when the music
     starts. The 200 px floor keeps landscape phones about where they were. */
  .hero { --fig-h: max(200px, min(108vw, 60svh, calc(100svh - var(--nav-h) - var(--deck-h) - 402px))); }
}
@media (max-width: 479.98px) {
  /* Phones: the sticky nav already shows an orange "Book <first>" about 50 px above, so the hero's
     own copy of it goes, and its 66 px go to the photo. Play then stands alone, full width. 392 px
     covers the name down to Play, the two-line booth link under it and the cue. The floor is the
     smallest photo that still shows the DJ's face. */
  .hero-actions .btn-secondary { display: none; }
  /* While a shared moment waits for the first tap, Play steps aside (html.sound-armed-link) and
     Book <first> takes its place, so the hero keeps its one full-width button and nothing moves. */
  html.sound-armed-link .hero-actions .btn-secondary { display: inline-flex; }
  .hero { --fig-h: max(185px, min(108vw, 60svh, calc(100svh - var(--nav-h) - var(--deck-h) - 392px))); }
}
/* Very short portrait phones (an iPhone SE with Safari's toolbars, in-app browsers): the photo is
   at its floor, so the copy tightens instead, keeping Play clear of the sound cue and the deck. */
@media (max-width: 479.98px) and (max-height: 650px) {
  .hero-title { font-size: min(16.2vw, 8.5svh); }
  .hero-tagline { margin-top: 12px; }
  .hero-tour { margin-top: 8px; }
  .hero-actions { margin-top: 12px; }
  .hero-actions .btn-lg { --btn-h: 48px; }
  .hero-booth-line { margin-top: 10px; font-size: .875rem; }
}
.nowrap { white-space: nowrap; }

@media (min-width: 900px) {
  .hero {
    min-height: max(560px, calc(100svh - var(--nav-h) - var(--deck-h) - var(--ticker-h)));
    padding: clamp(16px, 2.6vh, 32px) var(--gutter) clamp(22px, 3.6vh, 40px);
  }
  .hero-figure {
    order: 0;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 64%;
    height: auto;
    margin: 0;
  }
  #hero-photo {
    -webkit-mask-image:
      linear-gradient(to right, transparent 0, #000 44%),
      linear-gradient(to bottom, transparent 0, #000 7%, #000 60%, transparent 100%);
    -webkit-mask-composite: source-in;
            mask-image:
      linear-gradient(to right, transparent 0, #000 44%),
      linear-gradient(to bottom, transparent 0, #000 7%, #000 60%, transparent 100%);
            mask-composite: intersect;
  }
  .photo-credit { top: 22px; right: max(16px, env(safe-area-inset-right, 0px)); }
  .hero-copy { flex: 1; margin-top: 0; }
  .hero-title { margin-top: clamp(14px, 2.4vh, 24px); }
  .hero-tagline { margin-top: 18px; max-width: 30em; }
  .hero-actions { margin-top: 24px; }
}

/* Short laptop viewports (1366x768 and 1080p at 125%, minus browser chrome): the name is sized by
   height too, so the tagline and both CTAs still clear the deck on the first screen. Taller
   viewports are untouched. Must come after the block above to win. */
@media (min-width: 900px) and (max-height: 820px) {
  .hero {
    min-height: calc(100svh - var(--nav-h) - var(--deck-h) - var(--ticker-h));
    padding-top: 12px;
    padding-bottom: 16px;
  }
  /* The rest of the hero column is about 260 px, so below about 740 px of height 21svh alone
     overflows the budget and the ticker slides under the deck: the third term (--hero-rest, theme.css) keeps the name inside
     it (1366x657, 1280x650, 1440x700 and 1100x650 all end with the ticker on the deck). */
  .hero-title { font-size: calc(clamp(3.5rem, min(15vw, 21svh, 58svh - var(--hero-rest)), 13rem) * var(--hero-k)); margin-top: 10px; }
  .hero-tagline { margin-top: 12px; }
  .hero-actions { margin-top: 16px; }
}

/* ---------- genre ticker ---------- */

.ticker {
  position: relative;
  z-index: var(--z-content);
  display: flex;
  align-items: center;
  height: var(--ticker-h);
  overflow: hidden;
  border-block: 1px solid var(--line);
  background: linear-gradient(to bottom, rgb(var(--void-rgb) / .55), var(--band));
}
.ticker-track {
  display: flex;
  width: max-content;
  animation: ticker 80s linear infinite;
}
.ticker:hover .ticker-track { animation-play-state: paused; }
/* Lights Off stops the page moving, the marquee included (WCAG 2.2.2): main.js mirrors the lights
   mode onto <html data-lights>. */
html[data-lights="off"] .ticker-track { animation-play-state: paused; }
.ticker-list { display: flex; flex: none; list-style: none; }
.ticker-list li {
  display: flex;
  align-items: center;
  font: 700 .9375rem/1 var(--f-body);
  letter-spacing: .22em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--smoke);
}
.ticker-list li::after {
  content: "";
  width: 5px;
  height: 5px;
  margin: 0 28px;
  border-radius: 50%;
  background: var(--uv);
}
@keyframes ticker { to { transform: translateX(-50%); } }

@media (prefers-reduced-motion: reduce) {
  .ticker { display: block; height: auto; padding-block: 16px; }
  .ticker-track { display: block; width: auto; animation: none; }
  .ticker-dup { display: none; }
  .ticker-list { flex-wrap: wrap; justify-content: center; gap: 12px 28px; padding-inline: var(--edge); }
  .ticker-list li::after { display: none; }
}

/* ---------- bands (every section after the hero) ---------- */

.band {
  position: relative;
  z-index: var(--z-content);
  background: var(--band);
  padding-block: clamp(56px, 7.5vw, 108px);
}
/* Phones: the five heavy sections below the fold skip style, layout and paint while they are far
   from the viewport, so the page's first layouts, and the whole-page restyle when the phone sheet
   makes the page inert, only cover what is near the screen. Each keeps its last real height once
   drawn (auto); the lengths are measured first guesses of the content box (the padding comes on
   top). sections.js's reveal allows for it. Phones only: a desktop "capture the full page"
   screenshot draws beyond the viewport without rendering skipped sections, so they came out blank. */
@media (max-width: 719.98px) {
  #dates, #mixes, #radio, #about, #promoters { content-visibility: auto; }
  #dates { contain-intrinsic-block-size: auto 2720px; }
  #mixes { contain-intrinsic-block-size: auto 3250px; }
  #radio { contain-intrinsic-block-size: auto 3020px; }
  #about { contain-intrinsic-block-size: auto 3890px; }
  #promoters { contain-intrinsic-block-size: auto 4720px; }
}

.wrap {
  width: 100%;
  max-width: calc(var(--wrap-max) + 2 * var(--edge));
  margin-inline: auto;
  padding-inline: var(--edge);
}

.section-head { max-width: 52rem; margin-bottom: clamp(32px, 4.5vw, 56px); }
.section-sub {
  margin-top: 14px;
  font-size: clamp(1.0625rem, 1.3vw, 1.1875rem);
  color: var(--smoke);
  text-wrap: pretty;
}

/* ---------- next gig ---------- */

.next-gig { padding-block: clamp(40px, 6vw, 80px) clamp(24px, 3vw, 40px); }
.next-gig + .band { padding-top: clamp(48px, 6vw, 88px); }

.ng-panel {
  display: grid;
  gap: 28px;
  padding: clamp(20px, 3.4vw, 44px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}

.ng-eyebrow { margin-bottom: 14px; }
.ng-name { font: 700 clamp(1.5rem, 2.6vw, 2.25rem)/1.1 var(--f-body); letter-spacing: -.01em; margin-bottom: 12px; }
.ng-where { font-weight: 600; }
.ng-when, .ng-price { color: var(--smoke); }

.ng-countdown {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: clamp(4px, 1vw, 12px);
  padding: 16px clamp(10px, 2vw, 24px) 14px;
  border: 1px solid rgb(var(--lamp-rgb) / .2);
  border-radius: var(--radius-sm);
  background: var(--lcd-dots), var(--void);
  color: var(--lamp);
}
.ng-unit { display: flex; flex-direction: column; align-items: center; min-width: 1.25em; }
.ng-num {
  font: 900 clamp(2.125rem, 8.5vw, 5.25rem)/1 var(--f-lcd);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 22px rgb(var(--lamp-rgb) / .45);
}
/* A colon as two single LCD pixels: Doto's own colon is a pair of five-pixel crosses. */
.lcd-colon {
  display: inline-block;
  width: .1em;
  height: 1em;
  margin: 0 .07em;
  vertical-align: top;
  background:
    linear-gradient(currentColor 0 0) 0 37% / 100% .1em no-repeat,
    linear-gradient(currentColor 0 0) 0 67% / 100% .1em no-repeat;
}
.ng-sep {
  flex: none;
  margin: 0;
  font-size: clamp(2.125rem, 8.5vw, 5.25rem);
  opacity: .55;
}
.ng-label {
  margin-top: 10px;
  font: 600 .75rem/1 var(--f-body);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--smoke);
}

/* Gig night: while the gig is under way sections.js sets #next-gig[data-state="live"], hides the
   countdown and shows #ng-live in the same LCD slot ("On now until 2am"). */
.ng-live {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 16px clamp(10px, 2vw, 24px) 14px;
  border: 1px solid rgb(var(--lamp-rgb) / .2);
  border-radius: var(--radius-sm);
  background: var(--lcd-dots), var(--void);
  color: var(--lamp);
  text-align: center;
}
.ng-live-now {
  font: 900 clamp(2.125rem, 8.5vw, 5.25rem)/1 var(--f-lcd);
  text-transform: uppercase;
  text-shadow: 0 0 22px rgb(var(--lamp-rgb) / .45);
}
.ng-live-until {
  font: 600 .75rem/1 var(--f-body);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--smoke);
}
#next-gig[data-state="live"] .ng-panel { border-color: rgb(var(--lamp-rgb) / .45); }

.ng-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.ng-actions #ng-tickets { grid-column: 1 / -1; }
.ng-actions .btn { padding-inline: 10px; }
@media (max-width: 400px) {
  .ng-actions .btn { font-size: .875rem; }
}
@media (min-width: 600px) {
  .ng-actions { display: flex; flex-wrap: wrap; }
}

@media (min-width: 900px) {
  .ng-panel {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "info count" "actions count";
    align-items: end;
    column-gap: clamp(32px, 4vw, 64px);
  }
  .ng-info { grid-area: info; }
  .ng-countdown { grid-area: count; align-self: stretch; align-items: center; }
  .ng-live { grid-area: count; align-self: stretch; padding-inline: clamp(24px, 3vw, 48px); }
  .ng-actions { grid-area: actions; }
}
/* Small laptops and landscape tablets: an auto-sized LCD with 8.5vw numerals took most of the row
   and squeezed the gig details into a 230 px column. Here the two share the row, and the numerals
   scale to the column they have. From 1200 px the layout above stands as it is. */
@media (min-width: 900px) and (max-width: 1199.98px) {
  .ng-panel { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); }
  .ng-num, .ng-sep { font-size: clamp(2.125rem, 4.6vw, 5.25rem); }
}

/* ---------- dates timeline ---------- */

.timeline { position: relative; list-style: none; }
/* The rail: gigs in the order they happen. */
.timeline::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 8px;
  bottom: 0;
  width: 1px;
  background: linear-gradient(to bottom, var(--uv), rgb(var(--uv-rgb) / 0));
}

.gig {
  position: relative;
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr);
  gap: 16px 18px;
  padding: 4px 0 44px 30px;
}
.gig::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  width: 11px;
  height: 11px;
  border: 2px solid var(--uv);
  border-radius: 50%;
  background: var(--void);
}
/* The first gig still showing is the next one: its node is lit. */
.gig:not([hidden]):not(.gig:not([hidden]) ~ .gig)::before {
  border-color: var(--lamp);
  background: var(--lamp);
  box-shadow: 0 0 0 5px rgb(var(--lamp-rgb) / .16);
}

.gig-date { display: flex; flex-direction: column; align-items: flex-start; line-height: 1; }
.gig-dow, .gig-mon {
  font: 700 .75rem/1 var(--f-body);
  letter-spacing: .2em;
  color: var(--smoke);
}
.gig-day {
  margin: 6px 0 7px;
  font: var(--display-weight-hero) clamp(2.25rem, 4.2vw, 3.25rem)/.9 var(--f-display);
  letter-spacing: -.03em;
  color: var(--ink);
}

.gig-title { font: 700 clamp(1.25rem, 2vw, 1.625rem)/1.2 var(--f-body); margin-bottom: 8px; }
.gig-venue { display: flex; flex-wrap: wrap; gap: 2px 12px; margin-bottom: 10px; }
.gig-place { font-weight: 600; }
.gig-addr { color: var(--smoke); }

.gig-facts { --sep: 25px; font-variant-numeric: tabular-nums; }
.gig-facts > *::before { margin: 0 11px; background: var(--smoke); }
.gig-status { color: var(--lamp); font-weight: 600; }
/* The list separator (::before) stays first, so it is the part clipped at a line start. */
.gig-status::before { order: -2; }
/* The status light is its own dot (::after, drawn after the separator) so it survives a line wrap. */
.gig-status::after {
  content: "";
  order: -1;
  flex: none;
  width: 7px;
  height: 7px;
  margin-right: 8px;
  border-radius: 50%;
  background: var(--lamp);
  box-shadow: 0 0 8px rgb(var(--lamp-rgb) / .6);
}

.gig-links {
  grid-column: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 20px;
}
.gig-more { display: flex; flex-wrap: wrap; gap: 8px 18px; list-style: none; font-size: .9375rem; }
.gig-more a {
  color: var(--uv-soft);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: rgb(var(--uv-soft-rgb) / .4);
  text-underline-offset: 4px;
}
.gig-more a:hover { color: var(--ink); }

/* Touch: Tickets, Add to calendar, Google Calendar and Directions are what people tap at a gig,
   so each gets a 44 px target. */
@media (max-width: 899px) {
  .gig-links .btn { --btn-h: 44px; }
  .gig-more { gap: 0 20px; }
  .gig-more a { display: inline-flex; align-items: center; min-height: 44px; }
}

.dates-empty { padding: 8px 0 0 30px; color: var(--smoke); font-size: 1.125rem; }

@media (min-width: 900px) {
  .gig { grid-template-columns: 84px minmax(0, 1fr) auto; column-gap: 28px; padding-left: 40px; }
  .gig-links { grid-column: 3; flex-direction: column; align-items: flex-end; align-self: start; gap: 12px; }
  .gig-more { flex-direction: column; align-items: flex-end; gap: 6px; }
}

/* Subscribe to every date at once (the build writes ics/<slug>-dates.ics). Aligned with the gig text. */
.dates-subscribe {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 4px 0 0 30px;
  font-size: .9375rem;
  color: var(--smoke);
}
.dates-subscribe a { white-space: nowrap; }
.dates-subscribe-icon {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  background: currentColor;
  -webkit-mask: var(--i-cal) center / contain no-repeat;
          mask: var(--i-cal) center / contain no-repeat;
}
@media (min-width: 900px) {
  .dates-subscribe { margin-left: 152px; }
}
/* No dates listed: there is no rail to line up with, so the empty-state line and the subscribe line
   under it sit flush with the section heading. */
.timeline:not(:has(.gig:not([hidden]))) + .dates-empty { padding-left: 0; }
.timeline:not(:has(.gig:not([hidden]))) ~ .dates-empty:not([hidden]) + .dates-subscribe { margin: 10px 0 0; }

/* ---------- played at (dates with proof, inside #dates) ---------- */

.played-at {
  margin-top: clamp(56px, 7vw, 96px);
  padding-top: clamp(32px, 4vw, 48px);
  border-top: 1px solid var(--line);
}
.played-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px 32px;
  margin-bottom: clamp(18px, 2.4vw, 28px);
}
.played-title {
  font: var(--display-weight-head) clamp(1.625rem, 3.4vw, 2.5rem)/1.02 var(--f-display);
  letter-spacing: -.02em;
}
.played-summary {
  font-size: .9375rem;
  color: var(--smoke);
  font-variant-numeric: tabular-nums;
  text-wrap: pretty;
}
.played-summary [data-slot="count"] { color: var(--ink); font-weight: 700; }

.played-list { list-style: none; border-top: 1px solid var(--line); }
.played {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  padding: 22px 0 24px;
  border-bottom: 1px solid var(--line);
}
.played-date {
  font: 600 .8125rem/1.3 var(--f-body);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--smoke);
  font-variant-numeric: tabular-nums;
}
/* The newest date is lit, like the next gig's node on the timeline above. */
.played:first-child .played-date { color: var(--lamp); }
.played-venue {
  font: var(--display-weight-head) clamp(1.25rem, 2.3vw, 1.75rem)/1.12 var(--f-display);
  letter-spacing: -.01em;
  text-transform: uppercase;
  overflow-wrap: anywhere;
}
.played-town { color: var(--uv-soft); }
.played-event { margin-top: 6px; color: rgb(var(--ink-rgb) / .86); text-wrap: pretty; }
.played-quote {
  max-width: 62ch;
  margin-top: 12px;
  padding-left: 14px;
  border-left: 2px solid var(--uv);
  font-size: .9375rem;
  font-style: italic;
  color: var(--ink);
  text-wrap: pretty;
}
.played-links { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; }
.played-links .btn { --btn-h: 40px; }
/* Hear the set is a lamp outline, like a release's Play: solid lamp stays for the one main action
   on each screen (the hero, the featured Play, Tickets, the nav and booth calls to action). */
.btn.played-hear { border-color: rgb(var(--lamp-rgb) / .6); background: transparent; color: var(--lamp); }
@media (hover: hover) {
  .btn.played-hear:hover { border-color: var(--lamp); background: var(--lamp); color: var(--void); box-shadow: none; }
}
.btn.played-hear.is-playing { border-color: var(--lamp); background: var(--lamp); color: var(--void); }
.played-hear .glyph { width: 9px; height: 11px; }
.played-hear.is-playing .glyph {
  clip-path: polygon(0 0, 34% 0, 34% 100%, 66% 100%, 66% 0, 100% 0, 100% 100%, 0 100%);
}
.played-watch-icon {
  position: relative;
  flex: none;
  width: 18px;
  height: 13px;
  border-radius: 4px;
  background: currentColor;
}
.played-watch-icon::after {
  content: "";
  position: absolute;
  left: 7px;
  top: 3.5px;
  width: 5px;
  height: 6px;
  background: var(--void);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
@media (hover: hover) {
  .played-watch:hover .played-watch-icon::after { background: var(--void); }
}
.played-watch[aria-expanded="true"] { border-color: var(--uv); background: rgb(var(--uv-rgb) / .16); }
.played-listing {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  color: var(--uv-soft);
  font-weight: 600;
  font-size: .9375rem;
  text-decoration: underline;
  text-decoration-color: rgb(var(--uv-soft-rgb) / .4);
  text-underline-offset: 4px;
}
.played-listing:hover { color: var(--ink); }
.played-listing::after, .release-store::after, a.anthem-link::after {
  content: "";
  width: 10px;
  height: 10px;
  margin-left: 6px;
  background: currentColor;
  -webkit-mask: var(--i-out) center / contain no-repeat;
          mask: var(--i-out) center / contain no-repeat;
}
.played-video { grid-column: 1 / -1; width: 100%; max-width: 760px; }
@media (max-width: 899px) {
  .played-links .btn, .played-listing { min-height: 44px; }
  .played-links .btn { --btn-h: 44px; }
}
@media (min-width: 900px) {
  .played {
    /* 140 px keeps every date to two lines ("Saturday 1st November 2025" included). */
    grid-template-columns: 140px minmax(0, 1fr) auto;
    column-gap: 28px;
    align-items: start;
    padding: 26px 0 28px;
  }
  .played-date { padding-top: 6px; }
  /* Two columns, right-aligned: Hear the set, then Watch, so every Watch sits at the same x and a
     Listing (or the lone SoundCloud link) takes its own right-aligned row. */
  .played-links { display: grid; grid-template-columns: auto auto; justify-content: end; gap: 4px 16px; padding-top: 2px; }
  .played-hear { grid-column: 1; }
  .played-watch { grid-column: 2; }
  .played-listing { grid-column: 1 / -1; justify-self: end; }
  .played-video { grid-column: 2 / -1; }
}

/* ---------- the crate ---------- */

.crate-stats {
  margin-top: 18px;
  font-size: .9375rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  opacity: .8;
}

.featured-block { margin-bottom: clamp(48px, 6vw, 80px); container-type: inline-size; }

.featured {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 330px), 1fr));
}
/* Two columns (a 676 to 1021 px wide block): the third card would sit alone at half width, so it
   takes the whole row. One column and three columns are left alone. */
@container (min-width: 676px) and (max-width: 1021.98px) {
  .featured > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}
.featured:empty { min-height: 190px; }

.feature-card {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  /* The text row takes the slack, so every card's play row sits at its foot and the Play buttons
     line up whatever the title's length or tags. */
  grid-template-rows: 1fr auto;
  grid-template-areas: "art text" "play wave";
  gap: 16px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
  transition: border-color .3s var(--ease), background-color .3s var(--ease);
}
@media (hover: hover) { .feature-card:hover { background: var(--booth-2); } }

.feature-art {
  grid-area: art;
  width: 92px;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 8px;
  /* A platter stands in when the artwork is missing or fails to load. */
  background:
    radial-gradient(circle, var(--lamp) 0 11%, var(--void) 11.5% 14%, transparent 14.5%),
    repeating-radial-gradient(circle, var(--booth-2) 0 2px, var(--void) 2px 4px);
}
.feature-art img { width: 100%; height: 100%; object-fit: cover; }

.feature-text { grid-area: text; align-self: center; min-width: 0; }
.feature-label {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  margin-bottom: 6px;
  font: 700 1.0625rem/1.3 var(--f-body);
  text-wrap: balance;
}
.feature-meta { font-size: .875rem; color: var(--smoke); font-variant-numeric: tabular-nums; }

.feature-play {
  grid-area: play;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 44px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--lamp);
  color: var(--void);
  font: 700 .9375rem/1 var(--f-body);
  cursor: pointer;
  transition: background-color .2s, color .2s, box-shadow .2s;
}
@media (hover: hover) {
  .feature-play:hover { background: color-mix(in oklab, var(--lamp) 84%, var(--ink)); }
}

.feature-wave {
  grid-area: wave;
  align-self: center;
  width: 100%;
  height: 44px;
}

.feature-card.is-current { border-color: rgb(var(--uv-rgb) / .65); }
.feature-card.is-playing { border-color: rgb(var(--lamp-rgb) / .7); background: var(--booth-2); }
.feature-card.is-playing .feature-play {
  background: transparent;
  color: var(--lamp);
  box-shadow: inset 0 0 0 1.5px var(--lamp);
}

.crate-controls {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-bottom: 6px;
}

.crate-tabs {
  display: flex;
  gap: 2px;
  min-width: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  border-bottom: 1px solid var(--line);
}
.crate-tabs::-webkit-scrollbar { display: none; }
@media (max-width: 1199px) {
  .crate-tabs {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent);
    padding-right: 32px;
  }
}

.crate-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  min-height: 46px;
  padding: 0 12px;
  border: 0;
  background: none;
  color: var(--smoke);
  font: 600 .9375rem/1 var(--f-body);
  white-space: nowrap;
  cursor: pointer;
  transition: color .2s;
}
.crate-tab:hover { color: var(--ink); }
.crate-tab[aria-selected="true"] { color: var(--ink); }
.crate-tab[aria-selected="true"]::after {
  content: "";
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: var(--uv);
}
.crate-tab:focus-visible { outline-offset: -2px; border-radius: 8px; }
.crate-tab-count {
  min-width: 1.75em;
  padding: 3px 7px;
  border-radius: 999px;
  background: var(--booth-2);
  color: var(--smoke);
  font-size: .75rem;
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.crate-tab[aria-selected="true"] .crate-tab-count { background: rgb(var(--uv-rgb) / .28); color: var(--ink); }

/* Phones: the five filters wrap as chips instead of scrolling sideways, where "Studio mixes" and
   "Productions & bootlegs" sat off-screen with nothing to say they were there. */
@media (max-width: 599px) {
  .crate-tabs {
    flex-wrap: wrap;
    gap: 8px;
    overflow: visible;
    padding-right: 0;
    border-bottom: 0;
    -webkit-mask-image: none;
            mask-image: none;
  }
  /* Sized so the five fit two rows from about 385 px ("Studio mixes" beside "Productions &
     bootlegs"); narrower phones take three. */
  .crate-tab {
    min-height: 40px;
    padding: 0 8px 0 11px;
    gap: 6px;
    font-size: .875rem;
    border: 1px solid rgb(var(--smoke-rgb) / .32);
    border-radius: 999px;
    transition: color .2s, border-color .2s, background-color .2s;
  }
  .crate-tab[aria-selected="true"] {
    border-color: var(--uv);
    background: rgb(var(--uv-rgb) / .16);
  }
  .crate-tab[aria-selected="true"]::after { display: none; }
  .crate-tab:focus-visible { outline-offset: 2px; border-radius: 999px; }
}

.crate-search {
  width: 100%;
  height: 46px;
  padding: 0 14px 0 42px;
  border: 1px solid rgb(var(--smoke-rgb) / .32);
  border-radius: var(--radius-sm);
  background:
    var(--mt-uri-crate-search) 15px 50% / 16px no-repeat,
    var(--booth);
  font-size: 1rem;
  transition: border-color .2s;
}
.crate-search::placeholder { color: var(--smoke); opacity: 1; }
.crate-search:hover { border-color: rgb(var(--smoke-rgb) / .6); }
.crate-search:focus-visible { outline-offset: 1px; border-color: var(--uv); }

@media (min-width: 1200px) {
  .crate-controls { flex-direction: row; align-items: flex-end; gap: 24px; }
  .crate-tabs { flex: 1; }
  .crate-search-wrap { flex: none; width: 300px; padding-bottom: 6px; }
}

.crate-list { list-style: none; }

.crate-row {
  position: relative;
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto;
  grid-template-areas:
    "play title title"
    "play tags  link"
    "play facts facts";
  align-items: center;
  gap: 4px 14px;
  padding: 12px 8px;
  border-bottom: 1px solid var(--line);
  transition: background-color .25s;
}
@media (hover: hover) { .crate-row:hover { background: rgb(var(--booth-rgb) / .7); } }

.row-play {
  grid-area: play;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid rgb(var(--ink-rgb) / .3);
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background-color .2s, border-color .2s, color .2s;
}
.row-play .glyph { width: 10px; height: 12px; margin-left: 2px; }
@media (hover: hover) {
  .row-play:hover { border-color: var(--lamp); background: var(--lamp); color: var(--void); }
}

.row-main, .row-meta { display: contents; }
.row-title { grid-area: title; min-width: 0; font-weight: 600; line-height: 1.35; overflow-wrap: anywhere; text-wrap: pretty; }

.row-tags { grid-area: tags; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; min-width: 0; font-size: .8125rem; color: var(--smoke); }
.row-tags:empty, .row-genre:empty { display: none; }
.row-free {
  padding: 2px 8px;
  border: 1px solid rgb(var(--lamp-rgb) / .55);
  border-radius: 999px;
  color: var(--lamp);
  font-size: .75rem;
  font-weight: 700;
  text-decoration: none;
  transition: background-color .2s, color .2s;
}
@media (hover: hover) {
  .row-free:hover { background: var(--lamp); color: var(--void); }
}

.row-facts { grid-area: facts; min-width: 0; font-size: .8125rem; font-variant-numeric: tabular-nums; color: var(--smoke); }
.row-link {
  grid-area: link;
  justify-self: end;
  font-size: .8125rem;
  color: var(--uv-soft);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: rgb(var(--uv-soft-rgb) / .4);
  text-underline-offset: 3px;
}
.row-link:hover { color: var(--ink); }

/* Touch: a 44 px play button, and 44 px tall hit areas on the row's links that do not change the
   row's rhythm (an invisible extension; the links sit mid-row, so the extensions only reach over
   plain text). */
@media (max-width: 899px) {
  .crate-row { grid-template-columns: 44px minmax(0, 1fr) auto; }
  .row-play { width: 44px; height: 44px; }
  .row-free, .row-link { position: relative; }
  .row-free::after, .row-link::after { content: ""; position: absolute; }
  .row-free::after { inset: -10px -4px; }
  .row-link::after { inset: -12px -4px; }
}

.crate-row.is-current,
.crate-row.is-playing { background: rgb(var(--booth-rgb) / .85); }
.crate-row.is-current .row-play { border-color: var(--lamp); color: var(--lamp); }
.crate-row.is-playing .row-play { border-color: var(--lamp); background: var(--lamp); color: var(--void); }
.crate-row.is-playing .row-play .glyph { margin-left: 0; }
.crate-row.is-playing .row-title { color: var(--lamp); }
.crate-row.is-playing::before {
  content: "";
  position: absolute;
  left: 0;
  top: 10px;
  bottom: 10px;
  width: 2px;
  border-radius: 2px;
  background: var(--lamp);
}

@media (min-width: 1100px) {
  .crate-row { grid-template-areas: none; column-gap: 18px; padding: 10px 12px; }
  .row-play, .row-title, .row-tags, .row-facts, .row-link { grid-area: auto; }
  .row-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
  .row-facts { font-size: inherit; color: inherit; }
  .row-link { font-size: inherit; }
  .row-meta {
    display: grid;
    align-items: center;
    grid-template-columns: 10.5rem 9rem 4.25rem 6rem;
    column-gap: 20px;
    font-size: .875rem;
    font-variant-numeric: tabular-nums;
    color: var(--smoke);
  }
  .row-facts { display: contents; }
  .row-facts > *::before { display: none; }
  .row-dur { justify-content: flex-end; }
  .row-link { justify-self: end; }
}

/* overflow-wrap: a pasted URL or one long word in the echoed query wraps instead of widening the page. */
.crate-empty { padding: 36px 0 12px; font-size: 1.0625rem; color: var(--smoke); text-align: center; overflow-wrap: anywhere; }
.crate-more { display: flex; margin: 28px auto 0; }
.crate-profile { margin-top: 28px; text-align: center; }

/* Track ID tags on crate rows and featured cards (sections.js fills [data-slot="tl"] from the
   track's `tl`): "Track IDs" for timed tracklists, "Tracklist" for untimed ones. */
.tl-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: fit-content;
  padding: 2px 8px 2px 7px;
  border: 1px solid rgb(var(--uv-rgb) / .55);
  border-radius: 999px;
  color: var(--uv-soft);
  font: 700 .75rem/1.35 var(--f-body);
  white-space: nowrap;
}
.tl-tag::before {
  content: "";
  flex: none;
  width: 9px;
  height: 8px;
  background:
    linear-gradient(currentColor 0 0) 0 0 / 100% 1.5px no-repeat,
    linear-gradient(currentColor 0 0) 0 50% / 70% 1.5px no-repeat,
    linear-gradient(currentColor 0 0) 0 100% / 85% 1.5px no-repeat;
}
.feature-tl { margin-top: 8px; }

/* The search box shares its row with "Drop the needle" (dig.js wires the button). */
.crate-search-wrap { display: flex; align-items: center; gap: 8px; min-width: 0; }
.crate-search-wrap .crate-search { flex: 1 1 12rem; min-width: 0; }
.crate-search-wrap .dig-needle { flex: none; --btn-h: 46px; border-radius: var(--radius-sm); }
@media (min-width: 1200px) {
  .crate-search-wrap { width: 480px; }
}

/* ---------- the booth teaser ---------- */

.booth-teaser {
  overflow: clip;
  border-block: 1px solid var(--line);
  background:
    radial-gradient(55% 90% at 78% 55%, rgb(var(--uv-rgb) / .2), transparent 70%),
    radial-gradient(30% 50% at 70% 80%, rgb(var(--lamp-rgb) / .08), transparent 70%),
    var(--band);
}
.booth-teaser-inner { display: grid; gap: clamp(36px, 5vw, 64px); align-items: center; }
.booth-teaser-eyebrow { margin-bottom: 14px; color: var(--lamp); }
.booth-teaser-sub { margin-top: 14px; color: var(--ink); }
.booth-teaser-text { max-width: 40ch; margin-top: 12px; color: var(--smoke); font-size: 1.0625rem; text-wrap: pretty; }
.booth-teaser-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 22px; margin-top: 28px; }
.booth-teaser-note { font-size: .875rem; color: var(--smoke); }
.booth-open-icon {
  flex: none;
  width: 20px;
  height: 20px;
  background: currentColor;
  -webkit-mask: var(--i-disc) center / contain no-repeat;
          mask: var(--i-disc) center / contain no-repeat;
}

/* Two 1210s and a mixer, top down. Decoration only (aria-hidden): a click on them opens the booth
   like the button does. The records turn at 33 and a third (1.8 s a turn), a motion with no
   brightness change; they stop under reduced motion and when the lights are Off, like the ticker. */
.booth-rig {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: clamp(8px, 1.4vw, 16px);
  width: 100%;
  max-width: 620px;
  justify-self: center;
  padding: clamp(10px, 1.8vw, 20px);
  border: 1px solid var(--line);
  border-radius: 18px;
  background: linear-gradient(180deg, var(--mt-booth-rig), var(--mt-booth-rig-2));
  box-shadow: inset 0 1px 0 rgb(var(--ink-rgb) / .06), 0 30px 80px rgba(0, 0, 0, .45);
  cursor: pointer;
}
.tt {
  position: relative;
  aspect-ratio: 1 / .84;
  border-radius: 12px;
  background: linear-gradient(150deg, var(--mt-tt), var(--mt-tt-2) 70%);
  box-shadow: inset 0 0 0 1px rgb(var(--ink-rgb) / .07);
}
.tt-platter {
  position: absolute;
  left: 6%;
  top: 8%;
  width: 70%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: repeating-conic-gradient(var(--mt-tt-platter) 0 3deg, var(--mt-tt-platter-2) 3deg 6deg);
  box-shadow: 0 0 0 2px var(--mt-tt-platter-shadow), 0 8px 18px rgba(0, 0, 0, .55);
}
.tt-record {
  position: absolute;
  inset: 5%;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background:
    radial-gradient(circle, transparent 0 60%, rgb(var(--uv-rgb) / .08) 61% 62%, transparent 63%),
    repeating-radial-gradient(circle, var(--mt-tt-record) 0 1.2px, var(--mt-tt-record-2) 1.2px 2.6px);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .6);
}
.tt-label {
  width: 38%;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 50%;
  background: radial-gradient(circle, var(--void) 0 7%, var(--lamp) 8%);
  box-shadow: 0 0 0 2px rgb(var(--void-rgb) / .8);
}
.tt-label img { width: 100%; height: 100%; object-fit: cover; }
.tt-arm { position: absolute; right: 6%; top: 8%; width: 30%; height: 64%; pointer-events: none; }
.tt-arm-base {
  position: absolute;
  right: 0;
  top: 0;
  width: 42%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, var(--mt-tt-arm-base) 0 30%, var(--mt-tt-arm-base-2) 32% 60%, var(--mt-tt-arm-base-3) 62%);
}
.tt-arm-bar {
  position: absolute;
  right: 18%;
  top: 14%;
  width: 5%;
  height: 76%;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--mt-tt-arm-bar), var(--mt-tt-arm-bar-2) 50%, var(--mt-tt-arm-bar));
  transform: rotate(22deg);
  transform-origin: top center;
}
.tt-arm-head {
  position: absolute;
  right: 44%;
  bottom: 4%;
  width: 16%;
  height: 12%;
  border-radius: 2px;
  background: var(--mt-tt-arm-head);
  transform: rotate(22deg);
}
.tt-lamp {
  position: absolute;
  left: 5%;
  bottom: 6%;
  width: 7%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--lamp);
  box-shadow: 0 0 10px rgb(var(--lamp-rgb) / .8);
}
.tt-pitch {
  position: absolute;
  right: 8%;
  bottom: 8%;
  width: 6%;
  height: 26%;
  border-radius: 3px;
  background: var(--mt-tt-record);
  box-shadow: inset 0 0 0 1px rgb(var(--ink-rgb) / .1);
}
.tt-pitch span { position: absolute; left: -40%; right: -40%; top: 42%; height: 16%; border-radius: 2px; background: var(--mt-tt-pitch-span); }
.tt-tag { position: absolute; left: 6%; top: 3%; font: 900 1.125rem/1 var(--f-lcd); color: var(--lamp); opacity: .9; }
.tt-mixer {
  display: grid;
  justify-items: center;
  gap: 8px;
  width: clamp(38px, 7vw, 64px);
  padding: 10px 6px;
  border-radius: 10px;
  background: linear-gradient(180deg, var(--mt-tt-mixer), var(--mt-tt-mixer-2));
  box-shadow: inset 0 0 0 1px rgb(var(--ink-rgb) / .07);
}
.tt-knob {
  width: 60%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 30%, var(--mt-tt-arm-head) 0 14%, var(--mt-tt-knob) 16% 70%, var(--mt-tt-knob-2) 72%);
}
.tt-faders { display: flex; gap: 22%; justify-content: center; width: 100%; height: 44px; }
.tt-faders span { position: relative; width: 12%; min-width: 3px; border-radius: 2px; background: var(--mt-tt-record); }
.tt-faders span::after { content: ""; position: absolute; left: -150%; right: -150%; top: 30%; height: 7px; border-radius: 2px; background: var(--mt-tt-pitch-span); }
.tt-faders span + span::after { top: 55%; }
.tt-xfader { position: relative; width: 86%; height: 5px; border-radius: 3px; background: var(--mt-tt-record); }
.tt-xfader span { position: absolute; left: 36%; top: -4px; width: 28%; height: 13px; border-radius: 2px; background: var(--lamp); }

@keyframes tt-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: no-preference) {
  .booth-rig[data-spin] .tt-record { animation: tt-spin 1.8s linear infinite; }
  .booth-rig[data-spin] .tt-b .tt-record { animation-duration: 1.74s; }   /* pitched up a touch: two decks never quite in step */
  html[data-lights="off"] .tt-record { animation-play-state: paused; }
}
@media (hover: hover) {
  .booth-rig { transition: border-color .3s, box-shadow .3s; }
  .booth-rig:hover { border-color: rgb(var(--uv-rgb) / .6); box-shadow: inset 0 1px 0 rgb(var(--ink-rgb) / .06), 0 30px 80px rgba(0, 0, 0, .45), 0 0 0 4px rgb(var(--uv-rgb) / .12); }
}
@media (min-width: 900px) {
  .booth-teaser-inner { grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr); }
}

/* ---------- hard dance radio ---------- */
/* #tuner is drawn and styled by tuner.js and tuner.css; this file styles what sits around it. */

.tuner-note { max-width: 64ch; margin-top: 16px; font-size: .875rem; color: var(--smoke); text-wrap: pretty; }
/* #radio-now is screen-reader text (.sr-only): the tuner's LCD shows the same line on screen. */

/* The DJ on the radio: past-tense cards. The tag sits inline in each heading, so a station's
   name never stands alone without what the DJ did there. */
.radio-history {
  margin-top: clamp(56px, 7vw, 96px);
  padding-top: clamp(32px, 4vw, 48px);
  border-top: 1px solid var(--line);
}
.history-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 32px;
  margin-bottom: clamp(18px, 2.4vw, 28px);
}
.history-title { font: var(--display-weight-head) clamp(1.625rem, 3.4vw, 2.5rem)/1.02 var(--f-display); letter-spacing: -.02em; }
.history-sub { font-size: .9375rem; color: var(--smoke); }

.history-list { display: grid; gap: 16px; list-style: none; }
.history-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: clamp(20px, 2.6vw, 28px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}
.history-name { font: 700 clamp(1.25rem, 1.8vw, 1.5rem)/1.3 var(--f-body); text-wrap: pretty; }
.history-tag {
  /* Under the name on every card (it stays inside the h4, so the heading still says it). */
  display: table;
  margin-top: 8px;
  padding: 4px 8px 3px;
  border: 1px solid rgb(var(--smoke-rgb) / .45);
  border-radius: 6px;
  font: 700 .6875rem/1.25 var(--f-body);
  letter-spacing: .12em;
  text-transform: uppercase;
  vertical-align: baseline;
  color: var(--smoke);
}
.history-tag-lamp { border-color: rgb(var(--lamp-rgb) / .65); color: var(--lamp); }
.history-text { color: var(--smoke); text-wrap: pretty; }
.history-count { margin-top: auto; padding-top: 4px; font-size: .875rem; color: var(--smoke); font-variant-numeric: tabular-nums; }
.history-count span { color: var(--ink); font-weight: 700; }
@media (max-width: 899px) {
  .history-find { --btn-h: 44px; }
}

.history-founder {
  border-color: rgb(var(--lamp-rgb) / .35);
  background: linear-gradient(135deg, rgb(var(--lamp-rgb) / .07), transparent 55%), var(--booth);
}
.history-founder .history-name { font: var(--display-weight-head) clamp(1.5rem, 2.8vw, 2.25rem)/1.15 var(--f-display); letter-spacing: -.015em; }
.history-founder .history-text { max-width: 56ch; font-size: 1.0625rem; color: var(--ink); }
.history-founder .history-count { margin-top: 8px; }

/* Day strip: which day the weekly show went out (UV: a run that has ended). */
.week { display: flex; gap: 4px; list-style: none; }
.week li {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border: 1px solid var(--line);
  border-radius: 5px;
  font: 700 .6875rem/1 var(--f-body);
  color: var(--smoke);
}
.week .on { border-color: var(--lamp); background: var(--lamp); color: var(--void); }
.week-past .on { border-color: var(--uv); background: rgb(var(--uv-rgb) / .35); color: var(--ink); }

@media (min-width: 700px) {
  .history-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .history-founder { grid-column: 1 / -1; }
}
@media (min-width: 1100px) {
  .history-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .history-founder { grid-column: span 2; }
}

/* ---------- about ---------- */

.about-grid { display: grid; gap: clamp(40px, 5vw, 72px); }

.about-bio p { max-width: 62ch; text-wrap: pretty; }
.about-bio p + p { margin-top: 1.1em; }
.about-bio p:not(.about-lede) { color: rgb(var(--ink-rgb) / .84); }
.about-lede { font: 500 clamp(1.1875rem, 1.7vw, 1.4375rem)/1.45 var(--f-body); }

.pull-quote { margin-bottom: 28px; }
.pull-quote p {
  font: 600 clamp(1.5rem, 2.5vw, 2.125rem)/1.22 var(--f-body);
  letter-spacing: -.015em;
  text-indent: -.45em;
  text-wrap: balance;
}
.pull-quote p::before { content: "\201C"; color: var(--uv); }
.pull-quote p::after { content: "\201D"; color: var(--uv); }
.pull-quote figcaption {
  margin-top: 16px;
  font: 600 .8125rem/1 var(--f-body);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--smoke);
}

.fact-card {
  display: grid;
  gap: 16px;
  padding: clamp(20px, 2.6vw, 28px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}
.fact-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.fact-head .eyebrow { margin: 0; }
.fact-readout {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 12px 7px;
  border: 1px solid rgb(var(--lamp-rgb) / .2);
  border-radius: 8px;
  background: var(--lcd-dots), var(--void);
  color: var(--lamp);
}
.fact-bpm { font: 900 2.25rem/1 var(--f-lcd); text-shadow: 0 0 16px rgb(var(--lamp-rgb) / .45); }
.fact-unit { font: 700 1.125rem/1 var(--f-lcd); }
.fact-card .btn { justify-self: start; }

.walls {
  display: grid;
  gap: 44px;
  margin-top: clamp(56px, 7vw, 96px);
  padding-top: clamp(32px, 4vw, 48px);
  border-top: 1px solid var(--line);
}

.lineup {
  --sep: 1.3em;
  display: flex;
  flex-wrap: wrap;
  row-gap: 8px;
  margin-left: calc(-1 * var(--sep));
  clip-path: inset(0 0 0 var(--sep));
  list-style: none;
  font: 700 clamp(1.3125rem, 2.4vw, 1.875rem)/1.2 var(--f-body);
  letter-spacing: -.005em;
  text-transform: uppercase;
}
.lineup li { display: inline-flex; align-items: center; }
.lineup li::before {
  content: "";
  flex: none;
  width: .26em;
  height: .26em;
  margin: 0 .52em;
  border-radius: 50%;
  background: var(--uv);
}

.labels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; list-style: none; }
.labels li {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.labels .label-own { border-color: rgb(var(--lamp-rgb) / .5); }
.label-name { font-weight: 600; line-height: 1.3; }
.label-note { font-size: .8125rem; color: var(--smoke); }
.label-own .label-note { color: var(--lamp); }

@media (min-width: 960px) {
  .about-grid { grid-template-columns: minmax(0, 1.12fr) minmax(0, .88fr); align-items: start; }
  .walls { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 64px; }
  .labels { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- track record (inside #about) ---------- */

.track-record {
  display: grid;
  gap: 48px;
  margin-top: clamp(56px, 7vw, 96px);
  padding-top: clamp(32px, 4vw, 48px);
  border-top: 1px solid var(--line);
}
.releases {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr));
  list-style: none;
}
/* Each release is a sleeve with its record peeking out of the right edge (lamp label on the vinyl). */
.release {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 16px 14px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--booth);
}
.release::after {
  content: "";
  position: absolute;
  top: 14px;
  right: -34px;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  background:
    radial-gradient(circle, var(--void) 0 5%, var(--uv) 6% 22%, transparent 23%),
    repeating-radial-gradient(circle, var(--mt-release-after) 0 1px, var(--mt-release-after-2) 1px 3px);
  box-shadow: 0 0 0 1px rgb(var(--uv-rgb) / .3);
  opacity: .8;
}
.release-vinyl::after { background: radial-gradient(circle, var(--void) 0 5%, var(--lamp) 6% 22%, transparent 23%), repeating-radial-gradient(circle, var(--mt-release-after) 0 1px, var(--mt-release-after-2) 1px 3px); }
.release > * { position: relative; z-index: 1; }
.release-date { font: 600 .75rem/1.3 var(--f-body); letter-spacing: .12em; text-transform: uppercase; color: var(--smoke); }
.release-title { padding-right: 40px; font: 700 1.125rem/1.25 var(--f-body); text-wrap: pretty; }
.release-with { display: block; margin-top: 2px; font-size: .875rem; font-weight: 500; color: var(--smoke); }
.release-label { font-size: .9375rem; font-weight: 600; color: var(--uv-soft); }
.release-links { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin-top: auto; padding-top: 10px; }
.release-store {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  font-size: .9375rem;
  font-weight: 600;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: rgb(var(--ink-rgb) / .35);
  text-underline-offset: 4px;
}
.release-store:hover { color: var(--uv-soft); }
.release-play {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 0 14px;
  border: 1px solid rgb(var(--lamp-rgb) / .6);
  border-radius: 999px;
  background: transparent;
  color: var(--lamp);
  font: 700 .875rem/1 var(--f-body);
  cursor: pointer;
  transition: background-color .2s, color .2s;
}
.release-play .glyph { width: 8px; height: 10px; }
.release-play.is-playing { background: var(--lamp); color: var(--void); }
.release-play.is-playing .glyph,
.settype.is-playing .glyph {
  clip-path: polygon(0 0, 34% 0, 34% 100%, 66% 100%, 66% 0, 100% 0, 100% 100%, 0 100%);
}
@media (hover: hover) {
  .release-play:hover { background: var(--lamp); color: var(--void); }
}
@media (max-width: 899px) {
  .release-store, .release-play { min-height: 44px; }
  .release { padding: 14px 14px 8px; }
  .release-links { padding-top: 4px; }
}
.compilations { max-width: 72ch; margin-top: 18px; font-size: .9375rem; color: var(--smoke); text-wrap: pretty; }

.highlights { display: grid; gap: 14px; list-style: none; }
.highlights li { display: flex; align-items: baseline; gap: 12px; color: rgb(var(--ink-rgb) / .88); text-wrap: pretty; }
.highlights li::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 1px;
  background: var(--lamp);
  transform: translateY(-2px) rotate(45deg);
}

@media (min-width: 960px) {
  .track-record { grid-template-columns: minmax(0, 1.4fr) minmax(0, .8fr); gap: 64px; }
  /* Releases alone (no highlights column): they take the full width. */
  .track-record:not(:has(> .tr-highlights)) { grid-template-columns: minmax(0, 1fr); }
  .track-record:not(:has(> .tr-highlights)) .releases { grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
}

/* ---------- watch ---------- */

.watch-grid { display: grid; gap: 28px 16px; }

.yt-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}
.yt-facade {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
/* The thumbnail's filter makes it a layer painted over the button's own outline, and .yt-frame
   clips anything outside, so the ring goes on the frame instead (same 2 px UV, 3 px out, as the
   global ring). */
.yt-facade:focus-visible { outline: none; }
.yt-frame:has(> .yt-facade:focus-visible) { outline: 2px solid var(--uv); outline-offset: 3px; }
.yt-facade picture, .yt-facade img { width: 100%; height: 100%; }
.yt-facade img {
  object-fit: cover;
  filter: saturate(.85) brightness(.8);
  transition: filter .4s ease, transform .7s var(--ease);
}
@media (hover: hover) { .yt-facade:hover img { filter: saturate(1) brightness(.92); transform: scale(1.02); } }
.yt-play {
  position: absolute;
  left: 50%;
  top: 50%;
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--lamp);
  box-shadow: 0 8px 30px rgb(var(--void-rgb) / .55);
  transform: translate(-50%, -50%);
  transition: transform .3s var(--ease);
}
.yt-play::before {
  content: "";
  width: 20px;
  height: 23px;
  margin-left: 5px;
  background: var(--void);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
@media (hover: hover) { .yt-facade:hover .yt-play { transform: translate(-50%, -50%) scale(1.06); } }
.yt-iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.video-title { margin-top: 12px; font: 600 1rem/1.4 var(--f-body); text-wrap: pretty; }
.watch-more { margin-top: 32px; }

@media (min-width: 900px) {
  .watch-grid { grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); align-items: start; }
  .video-lead { grid-row: span 2; }
  .video-lead .yt-play { width: 80px; height: 80px; }
  .video-lead .video-title { font-size: 1.125rem; }
}

/* ---------- requests ---------- */

.requests {
  display: grid;
  gap: 32px;
  align-items: center;
  padding: clamp(24px, 4vw, 56px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}
.requests-copy h2 { margin-bottom: 18px; }
.requests-text { max-width: 44ch; margin-bottom: 26px; font-size: clamp(1.0625rem, 1.4vw, 1.25rem); text-wrap: pretty; }
.fine-print { margin-top: 14px; font-size: .8125rem; color: var(--smoke); }

.qr-tile {
  width: min(100%, 220px);
  padding: 12px 12px 10px;
  border-radius: var(--radius);
  background: var(--ink);
}
.qr-tile img { width: 100%; }
.qr-tile figcaption { margin-top: 6px; font: 600 .75rem/1.2 var(--f-body); color: var(--void); text-align: center; }

@media (min-width: 760px) {
  .requests { grid-template-columns: minmax(0, 1fr) auto; }
  .qr-tile { width: 236px; }
}

/* ---------- for promoters ---------- */
/* The promoter's 30-second test: the first card answers "what does he sound like" and the second
   "how do I book him", side by side on desktop, stacked in that order on phones (the email is on
   the first phone screen of the section). */

.promoters {
  border-top: 1px solid rgb(var(--lamp-rgb) / .25);
  background:
    radial-gradient(70% 40% at 15% 0, rgb(var(--lamp-rgb) / .07), transparent 70%),
    var(--band);
}
.promo-eyebrow { margin-bottom: 14px; color: var(--lamp); }

.promo-grid { display: grid; gap: 16px; }
.promo-card {
  min-width: 0;
  padding: clamp(20px, 3vw, 36px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}
.promo-h {
  margin-bottom: 14px;
  font: 600 .8125rem/1.2 var(--f-body);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--smoke);
}

.promo-hear-block { display: grid; justify-items: start; gap: 12px; }
/* The set's own baked waveform, lit up to where it has played (promo.js draws it). Decoration: the
   deck is the control. Phones leave it out, so the email stays on the first screen. */
.promo-wave { display: none; width: 100%; height: 88px; }
@media (min-width: 700px) {
  .promo-listen { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; column-gap: clamp(24px, 4vw, 48px); }
  .promo-listen .promo-caption { max-width: 34ch; }
  .promo-wave { display: block; }
}
.promo-hear { --btn-h: 60px; padding: 0 30px; font-size: 1.1875rem; box-shadow: 0 10px 34px rgb(var(--lamp-rgb) / .28); }
.promo-hear .glyph { width: 12px; height: 14px; }
/* While its set plays, "Playing now" does nothing when pressed (the deck pauses), so it steps down
   from the orange primary to an outline in lamp: never the loudest thing on the panel as a dead
   button. The idle "Hear him now" keeps the orange. */
.promo-hear.is-busy { background: transparent; color: var(--lamp); border-color: var(--lamp); box-shadow: none; cursor: default; }
@media (hover: hover) {
  .promo-hear.is-busy:hover { background: rgb(var(--lamp-rgb) / .08); border-color: var(--lamp); box-shadow: none; }
}
/* It only ever plays, so while the set is on it shows a level meter rather than a pause sign. */
.promo-hear.is-playing .glyph {
  width: 14px;
  clip-path: polygon(0 40%, 22% 40%, 22% 100%, 39% 100%, 39% 0, 61% 0, 61% 100%, 78% 100%, 78% 25%, 100% 25%, 100% 100%, 0 100%);
}
.promo-caption { max-width: 48ch; color: var(--smoke); text-wrap: pretty; }
.promo-textbtn {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: -8px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--uv-soft);
  font: 600 1rem/1.3 var(--f-body);
  text-align: left;
  text-decoration: underline;
  text-decoration-color: rgb(var(--uv-soft-rgb) / .45);
  text-underline-offset: 4px;
  cursor: pointer;
}
.promo-textbtn:hover { color: var(--ink); }
.promo-textbtn.is-playing { color: var(--lamp); }

.settypes { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr)); }
.settype {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  min-width: 0;
  min-height: 44px;
  padding: 13px 16px 14px;
  border: 1px solid rgb(var(--smoke-rgb) / .32);
  border-radius: 12px;
  background: rgb(var(--void-rgb) / .6);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  transition: border-color .2s, background-color .2s, box-shadow .2s;
}
@media (hover: hover) {
  .settype:hover { border-color: rgb(var(--uv-rgb) / .7); background: var(--booth-2); }
}
.settype[aria-pressed="true"] {
  border-color: var(--uv);
  background: rgb(var(--uv-rgb) / .14);
  box-shadow: inset 0 0 0 1px var(--uv);
}
.settype.is-playing { border-color: var(--lamp); box-shadow: inset 0 0 0 1px var(--lamp); }
.settype-top { display: flex; align-items: center; gap: 10px; }
.settype .glyph { width: 10px; height: 12px; color: var(--lamp); }
.settype-name { font: 700 1.0625rem/1.2 var(--f-body); }
.settype-meta { font-size: .8125rem; line-height: 1.45; color: var(--smoke); font-variant-numeric: tabular-nums; }

.skim { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 14px; }
.skim .btn { gap: 8px; }
/* Touch: the 38 px Skim keys reach 44 px (the set types sit 14 px above, the status beside). */
@media (pointer: coarse) {
  .skim .btn { position: relative; }
  .skim .btn::after { content: ""; position: absolute; inset: -3px 0; }
}
.skim-icon {
  flex: none;
  width: 16px;
  height: 16px;
  background: currentColor;
  -webkit-mask: var(--i-skim) center / contain no-repeat;
          mask: var(--i-skim) center / contain no-repeat;
}
/* The skim readout is a small LCD: "SKIM 2 OF 4 · 23:10". Empty until a skim starts. */
.skim-status {
  padding: 8px 12px 7px;
  border: 1px solid rgb(var(--lamp-rgb) / .22);
  border-radius: 8px;
  background: var(--lcd-dots), var(--void);
  color: var(--lamp);
  font: 900 1.125rem/1 var(--f-lcd);
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  text-transform: uppercase;
}
.skim-status:empty { display: none; }
.skim-help { margin-top: 10px; font-size: .8125rem; color: var(--smoke); }

.promo-contact {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  border-color: rgb(var(--lamp-rgb) / .38);
  background: linear-gradient(160deg, rgb(var(--lamp-rgb) / .1), transparent 55%), var(--booth);
}
.promo-contact-title {
  margin-bottom: 0;
  font: var(--display-weight-head) clamp(1.5rem, 2.6vw, 2rem)/1.05 var(--f-display);
  letter-spacing: -.015em;
  text-transform: none;
  color: var(--ink);
}
.promo-email {
  max-width: 100%;
  font: 700 clamp(1.1875rem, 2.2vw, 1.625rem)/1.25 var(--f-body);
  letter-spacing: -.005em;
  overflow-wrap: anywhere;
  color: var(--ink);
  -webkit-user-select: all;
          user-select: all;
}
.promo-contact-actions { display: flex; flex-wrap: wrap; gap: 10px; }
/* The buttons share out the row at every width, so one that wraps (Save contact) fills its own row
   and reads as deliberate rather than orphaned. */
.promo-contact-actions .btn { gap: 8px; flex: 1 1 auto; }
.promo-rider { font-size: .9375rem; color: var(--ink); }
.promo-form-link { font-size: .9375rem; }
@media (max-width: 899px) {
  .promo-contact-actions .btn { --btn-h: 44px; }
}

.copy-icon, .print-icon, .share-icon {
  flex: none;
  width: 16px;
  height: 16px;
  background: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
.copy-icon { --icon: var(--i-copy); }
.print-icon { --icon: var(--i-print); }
.share-icon { --icon: var(--i-share); }

.promo-proof { display: grid; gap: 16px; margin-top: 16px; }
.promo-facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 24px;
  margin: 0;
  padding: clamp(8px, 1.6vw, 18px) clamp(20px, 3vw, 36px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}
.fact { min-width: 0; padding: 14px 0; border-bottom: 1px solid var(--line); }
.fact-wide { grid-column: 1 / -1; }
.promo-facts > :last-child { border-bottom: 0; }
.fact dt { margin-bottom: 4px; font: 600 .75rem/1.2 var(--f-body); letter-spacing: .14em; text-transform: uppercase; color: var(--smoke); }
.fact dd { margin: 0; text-wrap: pretty; }

.promo-plays {
  padding: clamp(20px, 3vw, 36px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}
.plays-intro { font-size: .9375rem; color: var(--smoke); text-wrap: pretty; }
.plays-list { display: grid; gap: 12px; margin: 16px 0; list-style: none; }
.plays-list li { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 5px 12px; }
.plays-name { font-weight: 700; }
.plays-n { font-size: .875rem; color: var(--smoke); font-variant-numeric: tabular-nums; }
.plays-bar { grid-column: 1 / -1; height: 5px; border-radius: 3px; background: rgb(var(--uv-rgb) / .14); }
.plays-bar::after {
  content: "";
  display: block;
  width: calc(var(--sets) / 18 * 100%);
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--uv), var(--lamp));
}
.plays-own { padding-top: 12px; border-top: 1px solid var(--line); font-weight: 600; }

.bios { margin-top: clamp(40px, 5vw, 64px); }
.bio-grid { display: grid; gap: 16px; }
.bio {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
  padding: clamp(20px, 2.6vw, 28px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}
.bio-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.bio-label { font: 700 1.0625rem/1.2 var(--f-body); }
.bio-count { margin-right: auto; font-size: .8125rem; color: var(--smoke); font-variant-numeric: tabular-nums; }
.bio-copy { gap: 8px; }
.bio-text { color: rgb(var(--ink-rgb) / .88); text-wrap: pretty; }
.bio-text p + p { margin-top: .9em; }
.bio-poster .bio-text { font: 600 clamp(1.1875rem, 1.8vw, 1.4375rem)/1.35 var(--f-body); color: var(--ink); }
.bio.is-copied { border-color: rgb(var(--uv-rgb) / .7); }
@media (max-width: 899px) {
  .bio-copy { --btn-h: 44px; }
}

.promo-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin-top: clamp(28px, 3.4vw, 40px); }
.promo-actions .btn { gap: 10px; }
.promo-photo { font-size: .875rem; color: var(--smoke); }

@media (min-width: 1000px) {
  .promo-grid, .promo-proof { grid-template-columns: minmax(0, 1.4fr) minmax(0, .85fr); }
  .promo-grid { grid-template-areas: "listen contact" "types contact"; }
  .promo-listen { grid-area: listen; }
  .promo-types { grid-area: types; }
  .promo-contact { grid-area: contact; position: sticky; top: calc(var(--nav-h) + 24px); align-self: start; }
  .bio-grid { grid-template-columns: minmax(0, .85fr) minmax(0, 1.4fr); grid-template-rows: auto 1fr; grid-template-areas: "poster long" "short long"; align-items: start; }
  .bio-poster { grid-area: poster; }
  .bio-short { grid-area: short; }
  .bio-long { grid-area: long; }
  .promo-photo { margin-left: auto; }
}

/* ---------- booking ---------- */

.booking { display: grid; gap: clamp(40px, 5vw, 72px); }
.booking-copy > p { max-width: 46ch; }
.booking-copy .section-head { margin-bottom: 24px; }
.booking-list { display: grid; gap: 10px; margin: 24px 0; list-style: none; }
.booking-list li { display: flex; align-items: baseline; gap: 12px; }
.booking-list li::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--uv);
  transform: translateY(-2px);
}
.booking-email { color: var(--smoke); }

.booking-card {
  padding: clamp(20px, 3.4vw, 40px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
}

.booking-form { display: grid; gap: 18px 16px; }

.field { display: flex; flex-direction: column; gap: 7px; min-width: 0; transition: opacity .3s; }
.field label { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; font-size: .9375rem; font-weight: 600; }
.req { font: 600 .6875rem/1 var(--f-body); letter-spacing: .12em; text-transform: uppercase; color: var(--smoke); }

.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 48px;
  padding: 11px 14px;
  border: 1px solid rgb(var(--smoke-rgb) / .34);
  border-radius: var(--radius-sm);
  background: var(--void);
  font-size: 1rem;
  line-height: 1.4;
  transition: border-color .2s, box-shadow .2s;
}
.field textarea { min-height: 136px; resize: vertical; line-height: 1.5; }
.field select {
  appearance: none;
  padding-right: 42px;
  background: var(--mt-uri-field-select) right 16px center / 12px 8px no-repeat, var(--void);
  cursor: pointer;
}
.field input::placeholder, .field textarea::placeholder { color: var(--smoke); opacity: 1; }
.field input::-webkit-calendar-picker-indicator { opacity: .7; cursor: pointer; }
.field :is(input, select, textarea):hover { border-color: rgb(var(--smoke-rgb) / .6); }
.field :is(input, select, textarea):focus-visible { outline-offset: 1px; border-color: var(--uv); }
/* The date field has a second tab stop, the calendar button inside it, where the host matches
   neither :focus nor :focus-visible. :focus-within covers both stops (a text-like input shows the
   ring on any focus anyway), and the button itself gets its own ring at full opacity. */
.field input[type="date"]:focus-within { outline: 2px solid var(--uv); outline-offset: 1px; border-color: var(--uv); }
.field input[type="date"]:focus-within:not(:focus)::-webkit-calendar-picker-indicator {
  opacity: 1;
  outline: 2px solid var(--uv);
  outline-offset: 2px;
  border-radius: 4px;
}

.field.has-error :is(input, select, textarea),
.field [aria-invalid="true"] { border-color: var(--lamp); box-shadow: 0 0 0 1px var(--lamp); }
.field-error { font-size: .875rem; font-weight: 600; line-height: 1.35; color: var(--lamp); }
.field-error:empty { display: none; }

.form-foot { display: flex; flex-direction: column; gap: 16px; margin-top: 6px; }
.form-submit { align-self: flex-start; min-width: 13.5rem; }

.send-progress {
  height: 3px;
  overflow: hidden;
  border-radius: 3px;
  background: rgb(var(--lamp-rgb) / .14);
  opacity: 0;
  transition: opacity .2s;
}
.send-progress-bar {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--lamp);
  transform: scaleX(0);
  transform-origin: left center;
}
.booking-form.is-sending .send-progress { opacity: 1; }
.booking-form.is-sending .send-progress-bar { animation: send-sweep 1.4s cubic-bezier(.45, .05, .35, 1) forwards; }
.booking-form.is-sending .field { opacity: .55; pointer-events: none; }
.booking-form.is-sending .form-submit { background: var(--lamp); box-shadow: none; opacity: .85; }
@keyframes send-sweep { to { transform: scaleX(1); } }

.booking-success { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; padding-block: 8px; }
.success-mark {
  position: relative;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--lamp);
  box-shadow: 0 0 0 8px rgb(var(--lamp-rgb) / .14);
}
.success-mark::after {
  content: "";
  position: absolute;
  left: 20px;
  top: 14px;
  width: 12px;
  height: 22px;
  border: solid var(--void);
  border-width: 0 4px 4px 0;
  transform: rotate(45deg);
}
.success-title { margin-top: 8px; font: 700 clamp(1.75rem, 3vw, 2.25rem)/1.1 var(--f-body); letter-spacing: -.01em; }
.success-title { scroll-margin-top: calc(var(--nav-h) + 110px); }
.success-title:focus { outline: none; }
.success-text { max-width: 48ch; font-size: 1.125rem; text-wrap: pretty; }
.booking-success .btn { margin-top: 10px; }

@media (prefers-reduced-motion: no-preference) {
  .booking-success .success-mark { animation: success-in .5s var(--ease) both; }
  @keyframes success-in { from { opacity: 0; transform: scale(.6); } }
}

@media (min-width: 600px) {
  .booking-form { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .field-wide { grid-column: 1 / -1; }
}

@media (min-width: 1024px) {
  .booking { grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr); align-items: start; }
  .booking-copy { position: sticky; top: calc(var(--nav-h) + 32px); }
}

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

.site-footer {
  position: relative;
  z-index: var(--z-content);
  padding: 44px 0 40px;
  border-top: 1px solid var(--line);
  background: var(--void);
}
.footer-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 20px 40px; }
.footer-mark { margin-right: 0; font-size: 1.25rem; }
.footer-links { display: flex; flex-wrap: wrap; gap: 10px 24px; list-style: none; }
.footer-links a { color: var(--ink); font-weight: 500; text-decoration: none; transition: color .2s; }
.footer-links a:hover { color: var(--uv-soft); }
@media (max-width: 899px) {
  .footer-links { row-gap: 0; }
  .footer-links a { display: inline-block; padding-block: 11px; }   /* 44 px touch targets */
}
.footer-legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 6px 24px;
  flex-basis: 100%;
  padding-top: 20px;
  border-top: 1px solid var(--line);
  font-size: .8125rem;
  color: var(--smoke);
}

/* ---------- toast ---------- */

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--deck-h) + 16px);
  z-index: var(--z-toast);
  width: max-content;
  max-width: min(calc(100vw - 32px), 30rem);
  padding: 12px 16px;
  border: 1px solid rgb(var(--lamp-rgb) / .45);
  border-radius: 12px;
  background: var(--booth-2);
  box-shadow: 0 12px 40px rgb(var(--void-rgb) / .6);
  font-weight: 500;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 12px);
  transition: opacity .25s ease, transform .25s ease;
}
.toast:empty { padding: 0; border: 0; }
.toast.is-visible { opacity: 1; pointer-events: auto; transform: translate(-50%, 0); }

/* ---------- sound cue ---------- */
/* Autoplay was refused by the browser: the first click, tap or key press anywhere starts the set.
   Clicks pass straight through it (any click starts the music anyway). The dot's slow opacity
   pulse is an 8 px area, far below any flash threshold. */

.sound-cue {
  position: fixed;
  left: 50%;
  bottom: calc(var(--deck-h) + 16px);   /* --deck-h already includes the bottom safe-area inset */
  z-index: var(--z-deck);
  display: inline-flex;
  align-items: center;
  gap: 10px;
  width: max-content;
  max-width: calc(100vw - 32px);
  margin: 0;
  padding: 10px 18px 10px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgb(var(--booth-rgb) / .94);
  box-shadow: 0 12px 40px rgb(var(--void-rgb) / .6);
  color: var(--ink);
  font-weight: 600;
  font-size: .95rem;
  line-height: 1.2;
  pointer-events: none;
  transform: translateX(-50%);
  animation: sound-cue-in .4s ease both;
}
.sound-cue[hidden] { display: none; }
.sound-cue-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--lamp);
  box-shadow: 0 0 10px rgb(var(--lamp-rgb) / .7);
  animation: sound-cue-pulse 1.6s ease-in-out infinite;
}
@keyframes sound-cue-in { from { opacity: 0; transform: translate(-50%, 8px); } }
@keyframes sound-cue-pulse { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .sound-cue, .sound-cue-dot { animation: none; }
}
/* Phones and portrait tablets: a slimmer cue, closer to the deck. Still above it (Scott's autoplay
   ruling), and the hero's height budget leaves room for it under the buttons. It follows the
   visitor down the page (scrolling is not a gesture, so it stays up), so its plate is solid: no
   text from the section underneath shows through it. */
@media (max-width: 899px) {
  .sound-cue {
    bottom: calc(var(--deck-h) + 8px);
    padding: 7px 14px 7px 12px;
    font-size: .8125rem;
    background: var(--booth);
  }
}
/* Desktop: the first screen ends with the genre ticker sitting on the deck, so the cue sits inside
   that band, centred (40 px is the pill's own height), on a solid plate so no lettering shows
   through. After a scroll it floats 6 px above the deck. */
@media (min-width: 900px) {
  .sound-cue {
    bottom: calc(var(--deck-h) + (var(--ticker-h) - 40px) / 2);
    background: var(--booth);
  }
}
/* Reduced motion stops the ticker, and a pill parked over a frozen word reads as a typo, so on
   wide screens the cue rises just above the band. Below 1280 px that would reach the CTAs, so there
   it stays in the band. */
@media (min-width: 1280px) and (prefers-reduced-motion: reduce) {
  .sound-cue { bottom: calc(var(--deck-h) + var(--ticker-h) + 12px); }
}
/* Short screens (phones held landscape, very short windows): the first screen is mostly the name,
   so the cue moves up under the nav at the right, clear of the name, the tagline and the photo
   credit, and fades in rather than rising. */
@media (max-height: 500px) {
  .sound-cue {
    top: calc(var(--nav-h) + 14px);
    bottom: auto;
    left: auto;
    right: max(var(--edge), env(safe-area-inset-right, 0px));
    transform: none;
    animation-name: sound-cue-fade;
  }
}
@keyframes sound-cue-fade { from { opacity: 0; } }

/* ---------- strobe dialog ---------- */

.strobe-dialog {
  width: min(calc(100vw - 32px), 30rem);
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--booth);
  color: var(--ink);
  box-shadow: 0 24px 80px rgba(0, 0, 0, .6);
}
.strobe-dialog::backdrop { background: rgb(var(--void-rgb) / .8); }
.strobe-mark {
  display: grid;
  place-items: center;
  width: 40px;
  height: 36px;
  background: var(--lamp);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
.strobe-mark::after {
  content: "";
  width: 4px;
  height: 14px;
  margin-top: 10px;
  border-radius: 2px;
  background: linear-gradient(var(--void) 0 9px, transparent 9px 11px, var(--void) 11px);
}
.strobe-title { margin: 16px 0 10px; font: 700 1.375rem/1.2 var(--f-body); letter-spacing: 0; }
.strobe-body { margin-bottom: 24px; color: var(--smoke); }
.strobe-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 12px; }

/* ---------- club mode ---------- */

.club {
  position: fixed;
  inset: 0;
  z-index: var(--z-club);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background: var(--void);
}
/* main.js focuses the overlay itself on open (so Space stays play/pause); the Exit button keeps
   its own focus ring. */
#club:focus { outline: none; }
.club-rig { position: absolute; inset: 0; width: 100%; height: 100%; }
.club > :not(.club-rig) { position: relative; }
/* main.js moves #toast into the overlay while it is up (only the fullscreen element renders in
   fullscreen): it stays fixed, above the club readouts, rather than becoming a grid row. */
.club > .toast { position: fixed; bottom: max(24px, env(safe-area-inset-bottom)); }

.club-top {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
  padding: max(16px, env(safe-area-inset-top)) var(--edge) 0;
}
/* On the same dark pill as the Exit button, in --ink: readable where a beam crosses it. */
.club-hint {
  padding: 4px 10px;
  border-radius: 999px;
  background: rgb(var(--void-rgb) / .6);
  font-size: .875rem;
  color: var(--ink);
  text-shadow: 0 1px 8px var(--void);
}
/* Touch-only devices have no Esc key; the Exit button is the way out there. */
@media (hover: none) and (pointer: coarse) {
  .club-hint { display: none; }
}
.club-exit { background: rgb(var(--void-rgb) / .6); }
/* On an iPhone (no Esc key, no Fullscreen API) this button is the only way out: a full 44 px. */
@media (pointer: coarse) {
  .club-exit { --btn-h: 44px; }
}

.club-name {
  align-self: center;
  /* Sized by height as well, so a phone held landscape keeps the readouts on screen. */
  font: var(--display-weight-hero) calc(clamp(3rem, min(14vw, 22dvh), 15rem) * var(--hero-k))/var(--hero-leading) var(--f-display);
  letter-spacing: var(--hero-track);
  text-align: center;
  color: var(--ink);
  text-shadow: 0 0 60px rgb(var(--uv-rgb) / .45);
}
.club-name span { display: block; }

.club-bottom {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding: 0 var(--edge) max(24px, env(safe-area-inset-bottom));
}
.club-readouts { min-width: 0; color: var(--lamp); }
.club-now {
  max-width: 40ch;
  /* 900: at 700 each stroke is one LCD dot wide, too faint to read off a big screen. */
  font: 900 clamp(1.125rem, 2.2vw, 2.25rem)/1.25 var(--f-lcd);
  letter-spacing: .02em;
  overflow-wrap: anywhere;
  text-wrap: balance;
  /* A tight void halo lifts the thin dot-matrix off bright beams, for reading from the floor. */
  text-shadow: 0 0 10px rgb(var(--void-rgb) / .95), 0 1px 2px rgb(var(--void-rgb) / .95);
}
.club-bpm {
  margin-top: 10px;
  font: 900 clamp(2.25rem, 5vw, 4.5rem)/1 var(--f-lcd);
  text-shadow: 0 0 24px rgb(var(--lamp-rgb) / .5);
}
.club-qr-tile {
  flex: none;
  width: clamp(112px, 13vw, 196px);
  padding: 10px 10px 9px;
  border-radius: var(--radius);
  background: var(--ink);
}
.club-qr { width: 100%; }
.club-qr-tile figcaption { margin-top: 7px; font: 700 .8125rem/1.2 var(--f-body); color: var(--void); text-align: center; text-wrap: balance; }

/* Phones: the QR on its own row above the now-playing line, which then gets the full width. */
@media (max-width: 599px) {
  .club-bottom { flex-direction: column; align-items: stretch; }
  .club-qr-tile { order: -1; align-self: flex-end; }
  .club-now { max-width: none; }
}

/* Short screens (a phone held landscape as a gig screen): tighter chrome, a smaller QR that is
   still scannable, and clear of the notch and the home indicator. */
@media (max-height: 500px) {
  .club-top { padding-top: max(10px, env(safe-area-inset-top)); }
  .club-qr-tile { width: clamp(88px, 24dvh, 112px); padding: 7px 7px 6px; }
  .club-qr-tile figcaption { font-size: .6875rem; margin-top: 4px; }
  .club-bpm { font-size: 1.75rem; margin-top: 6px; }
  .club-bottom { padding-bottom: max(14px, env(safe-area-inset-bottom)); }
}

/* Club mode 2.0: the tune playing, the next date and Room mode. main.js fills #club-tune and
   #club-next, unhides #club-room where the microphone is available, and shows #club-mic while it
   listens. The privacy note goes wherever the button goes. */
.club-top { flex-wrap: wrap; align-items: flex-start; }
.club-room-box { display: grid; gap: 8px; margin-right: auto; max-width: min(100%, 30rem); }
.club-room-box:has(#club-room[hidden]) { display: none; }
.club-room-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.club-room { background: rgb(var(--void-rgb) / .6); }
@media (pointer: coarse) {
  .club-room { --btn-h: 44px; }
}
.club-mic {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px 6px 10px;
  border: 1px solid rgb(var(--lamp-rgb) / .65);
  border-radius: 999px;
  background: rgb(var(--void-rgb) / .75);
  color: var(--lamp);
  font: 700 .8125rem/1 var(--f-body);
  letter-spacing: .1em;
  text-transform: uppercase;
}
.club-mic::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--lamp); box-shadow: 0 0 8px rgb(var(--lamp-rgb) / .7); }
.club-room-note {
  padding: 6px 10px;
  border-radius: 10px;
  background: rgb(var(--void-rgb) / .66);
  font-size: .8125rem;
  line-height: 1.45;
  color: var(--ink);
  text-wrap: pretty;
}
.club-tune {
  max-width: 34ch;
  margin-top: 10px;
  font: 900 clamp(1.5rem, 3.4vw, 3.25rem)/1.12 var(--f-lcd);
  color: var(--ink);
  overflow-wrap: anywhere;
  text-wrap: balance;
  text-shadow: 0 0 12px rgb(var(--void-rgb) / .95), 0 1px 2px rgb(var(--void-rgb) / .95);
}
.club-tune:empty { display: none; }
/* main.js fills #club-tune with these spans: a small label line, then artist and title. */
.club-tune-kind {
  display: block;
  margin-bottom: 4px;
  font: 700 clamp(.75rem, 1vw, .9375rem)/1.2 var(--f-body);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--lamp);
}
.club-tune-artist { color: var(--ink); }
.club-tune-sep { color: var(--uv-soft); }
.club-tune-title { font-weight: 700; color: var(--ink); }
/* Above the mix name, so the name, the tune and the BPM read as one LCD block. */
.club-next {
  width: fit-content;
  max-width: 100%;
  margin-bottom: 12px;
  padding: 6px 12px;
  border-radius: 10px;
  background: rgb(var(--void-rgb) / .7);
  font: 600 clamp(.9375rem, 1.3vw, 1.1875rem)/1.35 var(--f-body);
  color: var(--ink);
}
/* Phones: the room button and Exit share the top row, the privacy note runs under both. */
@media (max-width: 599px) {
  .club-top {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "room exit" "note note";
    gap: 8px 10px;
  }
  .club-room-box { display: contents; }
  .club-room-row { grid-area: room; }
  .club-room-note { grid-area: note; font-size: .75rem; }
  .club-exit { grid-area: exit; }
  .club-hint { display: none; }
}
@media (max-height: 500px) {
  .club-room-note { max-width: 44ch; padding: 4px 8px; font-size: .6875rem; }
  .club-tune { font-size: 1.25rem; margin-top: 4px; }
  .club-next { margin-bottom: 6px; }
}
/* The venue screen goes quiet (main.js sets data-idle after 4 s with no pointer, touch, key or
   focus activity): the Esc hint, Exit, the privacy note and the room button fade out, and so does
   the pointer. A keyboard-focused control stays, and so does the room button while it listens (the
   Mic on pill and "Stop listening" must stay visible, F8). Any activity brings them straight back. */
.club-hint, .club-room-note { transition: opacity .6s ease; }
.club-exit, .club-room { transition: opacity .6s ease, background-color .2s, color .2s, border-color .2s, box-shadow .25s, transform .15s; }
.club[data-idle] :is(.club-hint, .club-room-note),
.club[data-idle] .club-exit:not(:focus-visible),
.club[data-idle]:not(:has(#club-mic:not([hidden]))) .club-room:not(:focus-visible) {
  opacity: 0;
  pointer-events: none;
}
.club[data-idle] { cursor: none; }
@media (prefers-reduced-motion: reduce) {
  .club-hint, .club-room-note, .club-exit, .club-room { transition: none; }
}

/* ---------- reveal on scroll (sections.js adds html.reveal-ready, then .is-in) ---------- */

@media (prefers-reduced-motion: no-preference) {
  html.reveal-ready [data-reveal] { transition: opacity .7s var(--ease), transform .7s var(--ease); }
  html.reveal-ready [data-reveal]:not(.is-in) { opacity: 0; transform: translateY(12px); }
}


@media (prefers-reduced-motion: reduce) {
  .yt-facade img, .yt-play { transition: none; }
  .yt-facade:hover img { transform: none; }
}

/* ---------- on air (a DJ with a radio show: sections.js initOnAir) ---------- */

/* The hero lamp: a studio ON AIR sign. --onair (red) is the lamp only; it lights only while the
   show is on (the schedule, not a live signal), and it never flashes: a steady glow, no animation
   of its brightness. Off air the lamp is dim and the pill reads as a plain chip. */
.onair {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  align-self: flex-start;
  gap: 6px 10px;
  max-width: 100%;
  min-height: 36px;
  margin-bottom: 18px;
  padding: 3px 4px 3px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgb(var(--booth-rgb) / .8);
  color: var(--ink);
  font: 600 .875rem/1.2 var(--f-body);
}
.onair-lamp {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgb(var(--onair-rgb) / .28);
  box-shadow: inset 0 0 0 1px rgb(var(--onair-rgb) / .35);
}
.onair-text { min-width: 0; }
.onair-state { font-weight: 700; }
.onair-long { color: var(--smoke); }
/* A compact button, so the pill keeps one height whether the show is on or off (nothing below it
   moves at 07:00); its hit area reaches past the pill to 44 px. */
.onair-listen { --btn-h: 28px; position: relative; margin-left: 4px; padding-inline: 12px; border-radius: 999px; font-size: .8125rem; }
.onair-listen::after { content: ""; position: absolute; inset: -8px -4px; }
.onair:not(:has(.onair-listen:not([hidden]))) { padding-right: 14px; }
.onair[data-state="live"] { border-color: rgb(var(--onair-rgb) / .55); }
.onair[data-state="live"] .onair-lamp {
  background: var(--onair);
  box-shadow: 0 0 10px rgb(var(--onair-rgb) / .7);
}
/* Lit: the sign itself, void text on the red (about 5.5:1). */
.onair[data-state="live"] .onair-state {
  display: inline-block;
  padding: 3px 7px 2px;
  border-radius: 4px;
  background: var(--onair);
  color: var(--void);
  font: var(--display-weight-head) 1rem/1 var(--f-display);
  letter-spacing: .06em;
  text-transform: uppercase;
}
.onair[data-state="live"] .onair-long { color: var(--ink); }
@media (max-width: 599.98px) {
  .onair { margin-bottom: 12px; }
  .onair-long { display: none; }
}
/* The lamp's row comes out of the photo's share of the first screen (the budget above), so Play
   and the booth link still clear the deck. */
@media (max-width: 899px) {
  .hero:has(> .hero-copy > .onair) { --fig-h: max(200px, min(108vw, 60svh, calc(100svh - var(--nav-h) - var(--deck-h) - 402px - 54px))); }
}
@media (max-width: 479.98px) {
  .hero:has(> .hero-copy > .onair) { --fig-h: max(185px, min(108vw, 60svh, calc(100svh - var(--nav-h) - var(--deck-h) - 392px - 54px))); }
}

/* The nav wordmark carries a small steady lamp while the show is on. */
html[data-onair="live"] .site-nav .wordmark::after {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-left: 8px;
  vertical-align: .12em;
  border-radius: 50%;
  background: var(--onair);
  box-shadow: 0 0 8px rgb(var(--onair-rgb) / .6);
}

/* The on-air panel reuses the next-gig panel; live, the "On air now" readout takes the lamp's red
   (text only) and the panel's edge follows it. */
#next-gig.next-show[data-state="live"] .ng-live-now {
  color: var(--onair);
  text-shadow: 0 0 22px rgb(var(--onair-rgb) / .4);
}
#next-gig.next-show[data-state="live"] .ng-live { border-color: rgb(var(--onair-rgb) / .35); }
#next-gig.next-show[data-state="live"] .ng-panel { border-color: rgb(var(--onair-rgb) / .45); }
#next-gig.next-show .ng-when { max-width: 36em; }
/* Phones (the two-column button grid): Listen takes the full row, the two links share the next. */
#next-gig.next-show .ng-actions #ng-listen { grid-column: 1 / -1; }
@media (max-width: 479.98px) {
  #next-gig.next-show .ng-actions { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- listen again (#catchup, in #radio between the tuner and the history) ---------- */

.catchup { margin-top: 40px; }
.catchup-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 32px;
  margin-bottom: clamp(12px, 1.6vw, 18px);
}
.catchup-head .catchup-title { font: var(--display-weight-head) clamp(1.625rem, 3.4vw, 2.5rem)/1.02 var(--f-display); letter-spacing: -.02em; }
.catchup-sub { font-size: .9375rem; color: var(--smoke); }
.catchup-list { list-style: none; border-top: 1px solid var(--line); }
.catchup-item {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto;
  align-items: center;
  gap: 4px 12px;
  padding: 10px 8px;
  border-bottom: 1px solid var(--line);
  transition: background-color .25s;
}
@media (hover: hover) { .catchup-item:hover { background: rgb(var(--booth-rgb) / .7); } }
.catchup-play {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid rgb(var(--ink-rgb) / .3);
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background-color .2s, border-color .2s, color .2s;
}
.catchup-play .glyph { width: 10px; height: 12px; margin-left: 2px; }
.catchup-play:focus-visible { outline: 2px solid var(--uv); outline-offset: 2px; }
@media (hover: hover) {
  .catchup-play:hover { border-color: var(--lamp); background: var(--lamp); color: var(--void); }
}
.catchup-item .catchup-title { min-width: 0; font-weight: 600; line-height: 1.35; color: var(--ink); overflow-wrap: anywhere; }
.catchup-meta { justify-self: end; font-size: .875rem; font-variant-numeric: tabular-nums; color: var(--smoke); white-space: nowrap; }
.catchup-item.is-playing { background: rgb(var(--booth-rgb) / .85); }
.catchup-item.is-playing .catchup-play { border-color: var(--lamp); background: var(--lamp); color: var(--void); }
.catchup-item.is-playing .catchup-play .glyph {
  margin-left: 0;
  clip-path: polygon(0 0, 34% 0, 34% 100%, 66% 100%, 66% 0, 100% 0, 100% 100%, 0 100%);
}
.catchup-item.is-playing .catchup-title { color: var(--lamp); }
@media (max-width: 599.98px) {
  .catchup-item { grid-template-columns: 44px minmax(0, 1fr); padding-inline: 4px; }
  .catchup-play { grid-row: 1 / span 2; width: 44px; height: 44px; }
  .catchup-meta { grid-column: 2; justify-self: start; }
}

/* ---------- type-only hero (no photo: #hero.hero-type) ---------- */

.hero-type .hero-copy { margin-top: 0; padding-top: clamp(28px, 6vh, 64px); }
@media (min-width: 900px) {
  .hero-type .hero-copy { max-width: none; padding-top: 0; }
}

/* ---------- small pieces ---------- */

.success-alt { max-width: 48ch; margin-top: -6px; color: var(--smoke); text-wrap: pretty; }
.release-yt { flex: none; margin-top: 10px; border-radius: var(--radius-sm); }
.promo-contact-actions .btn { max-width: 100%; white-space: normal; text-align: center; }

/* Club mode: the set plays in SoundCloud's player (widget tracks). */
.club-now .club-sc {
  display: block;
  margin-top: .35em;
  color: var(--smoke);
  font: 600 clamp(12px, 1.3vw, 15px)/1.3 var(--f-body);
  letter-spacing: .04em;
}

/* Only on a page without the deck (never on ours): SoundCloud's player stays visible. */
#sc-widget-holder { position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 5; height: 20px; }

/* ---------- the one-sheet (print only) ---------- */
/* #onesheet never shows on screen. In print it is the only thing on the page: black on white, one
   A4 sheet, whether it came from "Print one-sheet" or Ctrl+P. */

.onesheet { display: none; }

/* White to the paper's edge: the page is color-scheme dark on screen, and with "Background
   graphics" on (or a PDF printer that prints backgrounds) the @page margin took the dark canvas,
   framing the sheet in black. */
@page { size: A4; margin: 14mm 15mm 13mm; background: #fff; }

@media print {
  :root { color-scheme: light !important; }
  html, body { background: #fff !important; color: #111 !important; }
  html { scroll-padding: 0; }
  body { min-height: 0 !important; padding: 0 !important; }
  body > :not(#onesheet) { display: none !important; }
  #deck, #rig, #club, #toast, #booth, #strobe-dialog, #sound-cue, .site-nav { display: none !important; }

  .onesheet {
    display: block !important;
    color: #111;
    font: 400 10.4pt/1.45 var(--f-body);
    -webkit-print-color-adjust: exact;
            print-color-adjust: exact;
  }
  .os-head {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: end;
    gap: 6pt 14pt;
    padding-bottom: 9pt;
    border-bottom: 2.4pt solid #111;
  }
  .os-mark { font: var(--display-weight-hero) 36pt/.86 var(--f-display); letter-spacing: -.02em; text-transform: uppercase; }
  .os-poster { padding-bottom: 2pt; font: 600 12pt/1.35 var(--f-body); }
  .os-bio { margin: 14pt 0 18pt; font-size: 11.6pt; line-height: 1.55; }
  /* Hear him across the full width, so every set URL fits on one line; Played at on the left,
     Releases then Book <first> on the right. The markup order (hear, book, played, releases) stays. */
  .os-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: "hear hear" "played rel" "played book";
    grid-template-rows: auto auto 1fr;
    gap: 20pt 20pt;
  }
  .os-block:nth-child(1) { grid-area: hear; }
  .os-block:nth-child(2) { grid-area: book; }
  .os-block:nth-child(3) { grid-area: played; }
  .os-block:nth-child(4) { grid-area: rel; }
  .os-block { break-inside: avoid; }
  .os-h {
    margin: 0 0 5pt;
    padding-bottom: 3pt;
    border-bottom: .8pt solid #111;
    font: var(--display-weight-head) 9.4pt/1.2 var(--f-display);
    letter-spacing: .08em;
    text-transform: uppercase;
    text-wrap: initial;
  }
  .os-list { list-style: none; margin: 0; padding: 0; }
  .os-list li { padding: 3.6pt 0; border-bottom: .5pt solid #d6d6d6; overflow-wrap: anywhere; }
  .os-list b { font-weight: 700; }
  .os-block p + p { margin-top: 3pt; }
  .os-email { font: 700 16pt/1.25 var(--f-body); }
  .os-site { color: #444; }
  .os-foot { margin-top: 20pt; padding-top: 7pt; border-top: .8pt solid #111; font-size: 9pt; color: #444; }
}
