/* ==========================================================================
   ANTRUE — Techno DJ / Producer, München
   Design system: "Industrial Void"

   Brand palette, as supplied:
     #FFA500  primary      10.6:1 on black — AAA, and safe for black text on it
     #000000  void
     #FFFFFF  foreground   21:1
     #888888  muted        5.9:1 on black — AA, body-safe

   The surfaces between #000 and #888 are tinted steps off pure black, not
   new brand colours: elevation needs somewhere to go, and stacking panels
   at pure #000 makes them invisible against each other.

   Dark-only by intent. This is a nightlife brand — a light theme would
   fight the product. color-scheme is pinned to dark so native controls
   (scrollbars, form fields, date pickers) render correctly.
   ========================================================================== */

/* ---------------------------------------------------------------
   1. TYPED CUSTOM PROPERTIES
   Registered so the browser treats them as real numbers. The audio
   engine writes these once per frame on :root; every pulsing element
   in the document reads them via calc(). One JS write, GPU does
   the rest — this is what keeps it at 60fps on a mid-range phone.
   --------------------------------------------------------------- */
@property --pulse {
  syntax: '<number>'; inherits: true; initial-value: 0;
}
@property --bass {
  syntax: '<number>'; inherits: true; initial-value: 0;
}
@property --beat {
  syntax: '<number>'; inherits: true; initial-value: 0;
}

/* ---------------------------------------------------------------
   2. TOKENS
   --------------------------------------------------------------- */
:root {
  color-scheme: dark;

  /* --- Live audio bus (written by js/audio-engine.js) --- */
  --pulse: 0;   /* 0..1 smoothed overall energy */
  --bass:  0;   /* 0..1 low-band energy         */
  --beat:  0;   /* 0..1 per-beat decaying flash */

  /* --- Surfaces --- */
  --void:      #000000;   /* page — brand black           */
  --surface:   #0C0C0C;   /* cards, panels                */
  --surface-2: #151515;   /* raised / hover fill          */
  --surface-3: #1F1F1F;   /* pressed / active fill        */

  /* --- Ink (verified against --void) --- */
  --fg:        #FFFFFF;   /* 21.0:1 — AAA                 */
  --fg-soft:   #B4B4B4;   /*  9.7:1 — AAA                 */
  --fg-faint:  #888888;   /*  5.9:1 — AA (body-safe)      */
  --fg-ghost:  #555555;   /* decorative only, never text  */

  /* --- Brand ---
     #FFA500 on black is 10.6:1, and black on #FFA500 is the same, so it
     works as a fill and as text. It is the only colour that means
     "interactive" — nothing else gets to be orange. */
  --brand:      #FFA500;
  --brand-dim:  #B87700;   /* large fills where full orange is too hot */
  --brand-glow: rgba(255, 165, 0, 0.20);
  --on-brand:   #000000;   /* 10.6:1 on brand */

  /* --- Chromatic-aberration channels (glitch only, never text) ---
     A split needs two separated hues or it does not read as a split.
     --chan-r sits next to the brand orange; --chan-b is its complement and
     only ever appears for ~300ms at low opacity during a glitch burst.
     To keep it strictly on-palette, set --chan-b to #FFFFFF. */
  --chan-r:    #FF3D00;
  --chan-b:    #00E5FF;

  /* --- Lines --- */
  --line:        rgba(255, 255, 255, 0.11);
  --line-strong: rgba(255, 255, 255, 0.24);
  --line-brand:  rgba(255, 165, 0, 0.40);

  /* --- Feedback --- */
  --danger:  #FF5C5C;   /*  6.1:1 on void */
  --success: #3FE08F;   /* 11.6:1 on void */

  /* --- Type --- */
  --font-display: "Anton", "Haettenschweiler", "Arial Narrow", sans-serif;
  --font-mono:    "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-body:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --fs-display: clamp(3.25rem, 13vw, 11rem);
  --fs-h1:      clamp(2.25rem, 7vw, 5rem);
  --fs-h2:      clamp(1.875rem, 4.6vw, 3.25rem);
  --fs-h3:      clamp(1.25rem, 2vw, 1.5rem);
  --fs-lead:    clamp(1.0625rem, 1.5vw, 1.25rem);
  --fs-body:    1rem;        /* never below 16px — avoids iOS auto-zoom */
  --fs-sm:      0.9375rem;
  --fs-label:   0.75rem;     /* mono uppercase micro-labels only */

  --lh-tight:  0.86;
  --lh-snug:   1.15;
  --lh-body:   1.6;

  --track-label: 0.18em;

  /* --- Spacing (4/8 rhythm) --- */
  --space-1: 0.5rem;  --space-2: 1rem;   --space-3: 1.5rem;
  --space-4: 2rem;    --space-5: 3rem;   --space-6: 4rem;
  --space-7: 6rem;    --space-8: 8rem;

  --gutter: clamp(1.25rem, 5vw, 4rem);
  --maxw:   80rem;
  --maxw-prose: 42rem;   /* ~68 chars — keeps long copy readable */

  /* --- Geometry ---
     Zero radius everywhere. Softness is the enemy here. Corners are
     cut at 45° via clip-path instead — the signature shape. */
  --radius: 0;
  --chamfer: 0.75rem;
  --chamfer-sm: 0.375rem;

  /* --- Motion ---
     Sharp, not silky. Short durations, aggressive easing, hard cuts.
     --ease-cut has a strong overshoot-free snap; --ease-snap is the
     expo-out used for anything that travels distance. */
  --ease-cut:  cubic-bezier(0.2, 0, 0, 1);
  --ease-snap: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in:   cubic-bezier(0.6, 0, 0.9, 0.2);

  --dur-instant: 90ms;
  --dur-fast:    160ms;
  --dur-base:    240ms;
  --dur-slow:    420ms;

  /* --- Layers ---
     The nav sits ABOVE the mobile panel on purpose. The burger morphs into
     the close X, so if the panel outranked it the only control that shuts
     the menu would be painted behind the thing it is meant to close —
     leaving a phone visitor with no way out but the back button. */
  --z-canvas:  0;
  --z-content: 10;
  --z-overlay: 20;
  --z-panel:   50;
  --z-nav:     60;
  --z-toast:   100;
}

/* ---------------------------------------------------------------
   3. RESET & BASE
   --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  min-height: 100dvh;
  background: var(--void);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

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

img, video { height: auto; }

button, input, textarea, select {
  font: inherit;
  color: inherit;
}

button { background: none; border: 0; cursor: pointer; }

a { color: inherit; text-decoration: none; }

ul, ol { list-style: none; padding: 0; }

:target { scroll-margin-top: 6rem; }

/* --- Focus: never removed, always obvious --- */
:focus-visible {
  outline: 3px solid var(--brand);
  outline-offset: 3px;
}
:focus:not(:focus-visible) { outline: none; }

::selection {
  background: var(--brand);
  color: var(--on-brand);
}

/* --- Skip link --- */
.skip-link {
  position: absolute;
  top: -100%;
  left: var(--gutter);
  z-index: var(--z-toast);
  padding: var(--space-2) var(--space-3);
  background: var(--brand);
  color: var(--on-brand);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.skip-link:focus { top: var(--space-2); }

/* ---------------------------------------------------------------
   4. ATMOSPHERE
   Static grain + scanlines. Deliberately not animated — an animated
   grain layer is a per-frame full-screen repaint and the single
   fastest way to kill battery on mobile. Static costs nothing.
   --------------------------------------------------------------- */
.atmos {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  pointer-events: none;
}

.atmos::before {
  /* grain */
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.35;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.32'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
}

.atmos::after {
  /* scanlines — barely there, reads as texture not gimmick */
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.22) 0 1px,
    transparent 1px 3px
  );
  opacity: 0.34;
}

/* Vignette that breathes with the low end. Sub-pixel, but you feel it.
   The gradient itself is static and only opacity changes — recolouring a
   full-screen gradient every frame is a full-viewport repaint and would
   melt a mid-range phone. Opacity on a promoted layer is composite-only. */
.atmos-pulse {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(
    ellipse 120% 80% at 50% 50%,
    transparent 40%,
    rgba(8, 8, 11, 0.92) 100%
  );
  opacity: calc(0.62 - var(--bass) * 0.28);
  will-change: opacity;
}

/* ---------------------------------------------------------------
   5. LAYOUT
   --------------------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  position: relative;
  z-index: var(--z-content);
  padding-block: clamp(4rem, 11vw, 8rem);
}

.section--tight { padding-block: clamp(3rem, 7vw, 5rem); }

.section + .section { border-top: 1px solid var(--line); }

.prose { max-width: var(--maxw-prose); }

/* --- Section header: mono index + display title --- */
.sec-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.sec-index {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--brand);
}

.sec-index::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(to right, var(--line-brand), transparent);
}

.sec-title {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: 400;
  line-height: var(--lh-snug);
  letter-spacing: 0.01em;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------
   6. TYPOGRAPHY UTILITIES
   --------------------------------------------------------------- */
.label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
}

.label--brand { color: var(--brand); }

.lead {
  font-size: var(--fs-lead);
  line-height: 1.55;
  color: var(--fg-soft);
}

.mono { font-family: var(--font-mono); }

.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------
   7. CHAMFER — the signature shape
   45° cut corners instead of rounding. Applied via clip-path so it
   costs nothing and scales with the element.
   --------------------------------------------------------------- */
.chamfer {
  clip-path: polygon(
    var(--chamfer) 0, 100% 0,
    100% calc(100% - var(--chamfer)),
    calc(100% - var(--chamfer)) 100%,
    0 100%, 0 var(--chamfer)
  );
}

.chamfer-sm {
  clip-path: polygon(
    var(--chamfer-sm) 0, 100% 0,
    100% calc(100% - var(--chamfer-sm)),
    calc(100% - var(--chamfer-sm)) 100%,
    0 100%, 0 var(--chamfer-sm)
  );
}

/* HUD corner brackets — drawn with gradients, no extra DOM */
.brackets { position: relative; }

.brackets::before,
.brackets::after {
  content: "";
  position: absolute;
  width: 14px;
  height: 14px;
  border-color: var(--brand);
  border-style: solid;
  opacity: 0.7;
  pointer-events: none;
}
.brackets::before {
  top: -1px; left: -1px;
  border-width: 2px 0 0 2px;
}
.brackets::after {
  right: -1px; bottom: -1px;
  border-width: 0 2px 2px 0;
}

/* ---------------------------------------------------------------
   8. BUTTONS
   Hover is a hard cut, not a fade — 90ms, no easing curve you can
   perceive. That is the whole personality of this site in one rule.
   --------------------------------------------------------------- */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--fg);
  --btn-line: var(--line-strong);

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: 3rem;               /* ≥44px touch target */
  padding: 0.875rem var(--space-3);
  background: var(--btn-bg);
  color: var(--btn-fg);
  box-shadow: inset 0 0 0 1px var(--btn-line);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  touch-action: manipulation;
  transition:
    background-color var(--dur-instant) var(--ease-cut),
    color            var(--dur-instant) var(--ease-cut),
    box-shadow       var(--dur-instant) var(--ease-cut),
    transform        var(--dur-instant) var(--ease-cut);
  clip-path: polygon(
    var(--chamfer-sm) 0, 100% 0,
    100% calc(100% - var(--chamfer-sm)),
    calc(100% - var(--chamfer-sm)) 100%,
    0 100%, 0 var(--chamfer-sm)
  );
}

.btn:hover {
  --btn-bg: var(--surface-2);
  --btn-line: var(--fg-faint);
}

.btn:active { transform: translateY(1px); }

.btn--primary {
  --btn-bg: var(--brand);
  --btn-fg: var(--on-brand);
  --btn-line: var(--brand);
}
.btn--primary:hover {
  --btn-bg: var(--fg);
  --btn-fg: var(--void);
  --btn-line: var(--fg);
}

.btn--ghost { --btn-line: var(--line); }

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.42;
  cursor: not-allowed;
  pointer-events: none;
}

.btn__icon {
  width: 1rem;
  height: 1rem;
  flex: none;
  stroke-width: 2;
}

/* Text link with a brand underline that wipes in from the left */
.link {
  position: relative;
  color: var(--fg);
  padding-bottom: 2px;
  background-image: linear-gradient(var(--brand), var(--brand));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 2px;
  transition: background-size var(--dur-fast) var(--ease-cut),
              color var(--dur-fast) var(--ease-cut);
}
.link:hover { background-size: 100% 2px; color: var(--brand); }

/* ---------------------------------------------------------------
   9. NAVIGATION
   --------------------------------------------------------------- */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--gutter);
  padding-top: max(var(--space-2), env(safe-area-inset-top));
  background: rgba(8, 8, 11, 0.72);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-base) var(--ease-cut),
              background-color var(--dur-base) var(--ease-cut);
}

.nav.is-stuck {
  border-bottom-color: var(--line);
  background: rgba(8, 8, 11, 0.9);
}

.nav__brand {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-display);
  font-size: 1.375rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1;
}

/* The live indicator dot — scales with the beat */
.nav__dot {
  width: 9px;
  height: 9px;
  flex: none;
  background: var(--brand);
  transform: scale(calc(1 + var(--beat) * 0.85));
  box-shadow: 0 0 calc(4px + var(--beat) * 14px) var(--brand);
  transition: transform 40ms linear;
}

.nav__links {
  display: none;
  align-items: center;
  gap: var(--space-3);
}

.nav__link {
  position: relative;
  padding: var(--space-1) 0;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-soft);
  transition: color var(--dur-instant) var(--ease-cut);
}

.nav__link::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 2px;
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease-cut);
}

.nav__link:hover { color: var(--fg); }
.nav__link:hover::after,
.nav__link[aria-current="true"]::after { transform: scaleX(1); }
.nav__link[aria-current="true"] { color: var(--brand); }

.nav__actions { display: flex; align-items: center; gap: var(--space-2); }

/* --- Language switch ---
   Two real buttons rather than a select: the whole control is two taps
   wide, and aria-pressed makes the current language announced rather
   than merely coloured. */
.langswitch {
  display: flex;
  align-items: center;
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

.langswitch__btn {
  min-width: 2.75rem;
  min-height: 2.75rem;          /* ≥44px touch target */
  padding: 0 0.625rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-faint);
  touch-action: manipulation;
  transition: background-color var(--dur-instant) var(--ease-cut),
              color var(--dur-instant) var(--ease-cut);
}

.langswitch__btn:hover { color: var(--fg); background: var(--surface-2); }

.langswitch__btn[aria-pressed="true"] {
  background: var(--brand);
  color: var(--on-brand);
}

/* Hidden on the narrowest phones — it lives in the menu panel there. */
.nav .langswitch { display: none; }

@media (min-width: 30rem) {
  .nav .langswitch { display: flex; }
}

/* Language handover: a hard horizontal cut across the content, matching
   the wipe vocabulary used everywhere else. 240ms, then gone. */
main.is-switching {
  animation: lang-cut 240ms var(--ease-cut) 1;
}

@keyframes lang-cut {
  0%   { opacity: 1;    clip-path: inset(0 0 0 0); }
  35%  { opacity: 0.55; clip-path: inset(0 0 0 6%); transform: translate3d(3px, 0, 0); }
  36%  { opacity: 0.9;  clip-path: inset(0 4% 0 0); transform: translate3d(-2px, 0, 0); }
  100% { opacity: 1;    clip-path: inset(0 0 0 0); transform: none; }
}

/* --- Mobile trigger --- */
.nav__burger {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  margin-right: -0.5rem;
  color: var(--fg);
  touch-action: manipulation;
}

.nav__burger-box {
  position: relative;
  width: 22px;
  height: 14px;
}

.nav__burger-box span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  background: currentColor;
  transition: transform var(--dur-base) var(--ease-snap),
              opacity var(--dur-instant) var(--ease-cut);
}
.nav__burger-box span:nth-child(1) { top: 0; }
.nav__burger-box span:nth-child(2) { top: 6px; }
.nav__burger-box span:nth-child(3) { top: 12px; }

/* Scoped to the bars inside the box. Without .nav__burger-box in the
   selector, `span:nth-child(1)` also matches the box itself — it is the
   button's first child — so the whole icon rotated 45° while the top bar
   rotated another 45° on top of it, and the X never formed. */
.nav__burger[aria-expanded="true"] .nav__burger-box span:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}
.nav__burger[aria-expanded="true"] .nav__burger-box span:nth-child(2) {
  opacity: 0;
}
.nav__burger[aria-expanded="true"] .nav__burger-box span:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

/* --- Mobile panel: hard diagonal wipe, no fade --- */
.navpanel {
  position: fixed;
  inset: 0;
  z-index: var(--z-panel);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--gutter);
  padding-top: max(var(--space-7), env(safe-area-inset-top));
  padding-bottom: max(var(--space-6), env(safe-area-inset-bottom));
  background: var(--void);
  clip-path: polygon(0 0, 100% 0, 100% 0, 0 0);
  transition: clip-path var(--dur-slow) var(--ease-snap);
  visibility: hidden;
}

.navpanel.is-open {
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  visibility: visible;
}

.navpanel__link {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  font-family: var(--font-display);
  font-size: clamp(2rem, 11vw, 3.5rem);
  line-height: 1.05;
  text-transform: uppercase;
  color: var(--fg);
  opacity: 0;
  transform: translateY(1.25rem);
  transition: opacity var(--dur-base) var(--ease-cut),
              transform var(--dur-base) var(--ease-snap),
              color var(--dur-instant) var(--ease-cut);
}

.navpanel.is-open .navpanel__link {
  opacity: 1;
  transform: none;
  /* staggered by --i set in the markup — sharp, 40ms apart */
  transition-delay: calc(140ms + var(--i, 0) * 40ms);
}

.navpanel__link:hover,
.navpanel__link:focus-visible { color: var(--brand); }

.navpanel__link i {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-style: normal;
  font-weight: 700;
  letter-spacing: var(--track-label);
  color: var(--brand);
}

.navpanel__foot {
  margin-top: var(--space-5);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

@media (min-width: 62rem) {
  .nav__links { display: flex; }
  .nav__burger { display: none; }
  .navpanel { display: none; }
}

/* ---------------------------------------------------------------
   10. HERO
   --------------------------------------------------------------- */
/* ---------------------------------------------------------------
   HERO — scroll-driven reveal
   The section is (100svh + --hero-scroll) tall. .hero__stage sticks to
   the top for that distance while js/motion.js writes --sp (0→1) onto
   the section. Three things read it:

     · the frame's clip-path opens from an inset window to full bleed
     · the photo scales down and pans, so you travel *through* the image
     · the headline drifts and fades as the frame takes over

   The photo moves with transform only (compositor), so the sole paint
   cost per frame is the clip-path on one element.
   --------------------------------------------------------------- */
.hero {
  --sp: 0;                       /* scroll progress, written by JS   */
  --hero-scroll: 900px;          /* how far the reveal plays out     */
  --clip-inset: 24%;             /* window size at --sp: 0           */
  --photo-zoom: 1.32;            /* photo scale at --sp: 0           */
  --photo-pan: 7%;               /* horizontal travel through frame  */

  position: relative;
  height: calc(100svh + var(--hero-scroll));
}

.hero__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-top: 7rem;
  padding-bottom: max(var(--space-4), env(safe-area-inset-bottom));
  overflow: hidden;
}

/* The window. clip-path travels from inset(24%) to inset(0). */
.hero__frame {
  position: absolute;
  inset: 0;
  z-index: var(--z-canvas);
  overflow: hidden;
  background: var(--void);
  clip-path: inset(calc(var(--clip-inset) * (1 - var(--sp))));
}

.hero__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 42%;      /* keeps him in frame while panning */
  transform:
    scale(calc(var(--photo-zoom) - (var(--photo-zoom) - 1) * var(--sp)))
    translate3d(calc(var(--photo-pan) * (1 - var(--sp)) * -1), 0, 0);
  will-change: transform;
}

/* Measured, not guessed: white on the raw photo is 3.03:1 in the
   headline area — below AA. This gradient keeps the text side at ~0.75+
   coverage (≈8:1) while staying light enough on the right that the
   portrait is not crushed. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top,
      rgba(0, 0, 0, 0.88) 0%,
      rgba(0, 0, 0, 0.45) 42%,
      rgba(0, 0, 0, 0.18) 72%),
    linear-gradient(to right,
      rgba(0, 0, 0, 0.90) 0%,
      rgba(0, 0, 0, 0.78) 38%,
      rgba(0, 0, 0, 0.45) 68%,
      rgba(0, 0, 0, 0.30) 100%);
}

/* Canvas visualiser rides on top of the photo inside the same window.
   Masked toward the right, where the portrait is and the headline is not:
   screen-blended bars behind white text would claw back the contrast the
   scrim is there to guarantee. */
.hero__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.5;
  mix-blend-mode: screen;
  -webkit-mask-image: radial-gradient(ellipse 58% 62% at 74% 50%, #000 20%, transparent 74%);
  mask-image: radial-gradient(ellipse 58% 62% at 74% 50%, #000 20%, transparent 74%);
}

.hero__grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, var(--line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  background-size: clamp(3rem, 8vw, 6rem) clamp(3rem, 8vw, 6rem);
  -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, #000 10%, transparent 70%);
  mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, #000 10%, transparent 70%);
  opacity: calc((0.5 + var(--pulse) * 0.5) * (1 - var(--sp) * 0.7));
  will-change: opacity;
}

.hero__inner {
  position: relative;
  z-index: var(--z-content);
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  /* Drifts up and clears out as the frame opens, so the last stretch of
     the scroll is the photograph alone before the ticker arrives. */
  transform: translate3d(0, calc(var(--sp) * -3rem), 0);
  opacity: calc(1 - var(--sp) * 1.35);
  will-change: transform, opacity;
}

/* Fully faded — remove it from hit-testing and the tab order rather than
   leaving invisible controls behind. */
.hero.is-past .hero__inner {
  visibility: hidden;
  pointer-events: none;
}

.hero__status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.hero__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem 0.75rem;
  box-shadow: inset 0 0 0 1px var(--line-strong);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-soft);
}

.hero__chip--live {
  color: var(--brand);
  box-shadow: inset 0 0 0 1px var(--line-brand);
}

.hero__chip-dot {
  width: 6px; height: 6px;
  background: currentColor;
  transform: scale(calc(1 + var(--beat) * 0.9));
  transition: transform 40ms linear;
}

/* The name. Set enormous, cropped tight, one word per line. */
.hero__title {
  font-family: var(--font-display);
  font-size: var(--fs-display);
  font-weight: 400;
  line-height: var(--lh-tight);
  letter-spacing: -0.005em;
  text-transform: uppercase;
  margin-bottom: var(--space-3);
}

.hero__title span { display: block; }

/* Second line rendered as outline — the classic poster move */
.hero__title .outline {
  color: transparent;
  -webkit-text-stroke: clamp(1px, 0.22vw, 2.5px) var(--fg);
  transition: -webkit-text-stroke-color var(--dur-base) var(--ease-cut);
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.hero__blurb {
  max-width: 32rem;
  color: var(--fg-soft);
  font-size: var(--fs-lead);
  line-height: 1.5;
}

.hero__cta { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* Scroll cue */
.hero__cue {
  position: absolute;
  right: var(--gutter);
  bottom: max(var(--space-4), env(safe-area-inset-bottom));
  z-index: var(--z-content);
  display: none;
  align-items: center;
  gap: var(--space-1);
  writing-mode: vertical-rl;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
}

.hero__cue-line {
  width: 1px;
  height: 3rem;
  background: linear-gradient(to bottom, var(--brand), transparent);
}

@media (min-width: 62rem) {
  .hero__cue { display: flex; }
}

/* Shorter reveal on phones — 900px of scrolling before the first real
   content is a long time on a small screen. */
@media (max-width: 47.99rem) {
  .hero {
    --hero-scroll: 520px;
    --clip-inset: 16%;
    --photo-zoom: 1.22;
    --photo-pan: 4%;
  }

  /* Portrait crop puts him top-centre and the headline runs full width,
     so the bars move above the text rather than beside it. */
  .hero__canvas {
    -webkit-mask-image: radial-gradient(ellipse 80% 34% at 50% 26%, #000 15%, transparent 78%);
    mask-image: radial-gradient(ellipse 80% 34% at 50% 26%, #000 15%, transparent 78%);
    opacity: 0.42;
  }

  /* Stronger floor on the scrim: the phone crop is filled by his face and
     the bright wall, with none of the dark negative space the wide frame
     gives the desktop layout. */
  .hero__scrim {
    background:
      linear-gradient(to top,
        rgba(0, 0, 0, 0.94) 0%,
        rgba(0, 0, 0, 0.72) 38%,
        rgba(0, 0, 0, 0.34) 68%,
        rgba(0, 0, 0, 0.22) 100%);
  }
}

/* ---------------------------------------------------------------
   11. TICKER
   A single translate on one element. Duplicated content makes the
   loop seamless. Pauses when off-screen (class toggled from JS).
   --------------------------------------------------------------- */
.ticker {
  position: relative;
  z-index: var(--z-content);
  overflow: hidden;
  padding-block: var(--space-2);
  border-block: 1px solid var(--line);
  background: var(--surface);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}

.ticker__track {
  display: flex;
  width: max-content;
  gap: var(--space-4);
  animation: ticker-scroll 38s linear infinite;
  will-change: transform;
}

.ticker.is-paused .ticker__track { animation-play-state: paused; }

.ticker__item {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2.6vw, 2rem);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--fg-ghost);
  white-space: nowrap;
}

.ticker__item b {
  font-weight: 400;
  color: var(--brand);
}

.ticker__sep {
  width: 8px; height: 8px;
  flex: none;
  background: var(--brand);
  transform: rotate(45deg) scale(calc(0.8 + var(--bass) * 0.9));
  transition: transform 60ms linear;
}

@keyframes ticker-scroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* ---------------------------------------------------------------
   12. ABOUT
   --------------------------------------------------------------- */
.about {
  display: grid;
  gap: var(--space-5);
}

.about__body p + p { margin-top: var(--space-2); }
.about__body p { color: var(--fg-soft); }

.about__stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--line);
  box-shadow: 0 0 0 1px var(--line);
}

.stat {
  padding: var(--space-3);
  background: var(--surface);
  transition: background-color var(--dur-fast) var(--ease-cut);
}

.stat:hover { background: var(--surface-2); }

.stat__label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
}

.stat__val {
  display: block;
  margin-top: var(--space-1);
  font-family: var(--font-display);
  font-size: clamp(1.125rem, 2.4vw, 1.5rem);
  line-height: 1.15;
  text-transform: uppercase;
  color: var(--brand);
}

.stat__val a { color: inherit; }

@media (min-width: 48rem) {
  .about { grid-template-columns: 1.15fr 0.85fr; gap: var(--space-6); }
  .about__stats { grid-template-columns: repeat(2, 1fr); align-self: start; }
}

/* ---------------------------------------------------------------
   13. RELEASES
   --------------------------------------------------------------- */
.releases {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
}

@media (min-width: 40rem) { .releases { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .releases { grid-template-columns: repeat(3, 1fr); } }

/* With six streaming links per release, four narrow columns would force the
   links into a cramped stack. Two wide cards give the real cover art room
   and let the link row breathe. */
@media (min-width: 64rem) {
  .releases--4 { grid-template-columns: repeat(2, 1fr); }

  .releases--4 .release { flex-direction: row; }

  /* Fluid so the art scales with the card instead of eating a fixed 240px
     out of a 441px card at 1024 and squeezing the links into four rows. */
  .releases--4 .release__art {
    width: clamp(9rem, 17vw, 15rem);
    flex: none;
    aspect-ratio: 1 / 1;
  }

  .releases--4 .release__body {
    flex: 1;
    border-top: 0;
    border-left: 1px solid var(--line);
  }
}

.release {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--line);
  transition: background-color var(--dur-fast) var(--ease-cut),
              box-shadow var(--dur-fast) var(--ease-cut),
              transform var(--dur-fast) var(--ease-cut);
}

.release:hover,
.release:focus-within {
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--line-brand);
  transform: translateY(-3px);
}

.release__art {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--surface-2);
}

.release__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.15);
  transition: filter var(--dur-base) var(--ease-cut),
              transform var(--dur-slow) var(--ease-snap);
}

.release:hover .release__art img {
  filter: grayscale(0) contrast(1.05);
  transform: scale(1.04);
}

/* RGB channel split on hover — two tinted copies offset by 2px.
   Pure CSS, compositor-only, no JS per frame. */
.release__art::before,
.release__art::after {
  content: "";
  position: absolute;
  inset: 0;
  background: inherit;
  opacity: 0;
  mix-blend-mode: screen;
  pointer-events: none;
  transition: opacity var(--dur-instant) var(--ease-cut);
}
.release__art::before { background: var(--chan-r); }
.release__art::after  { background: var(--chan-b); }

.release:hover .release__art::before {
  opacity: 0.14;
  transform: translate3d(-2px, 0, 0);
}
.release:hover .release__art::after {
  opacity: 0.14;
  transform: translate3d(2px, 0, 0);
}

.release__badge {
  position: absolute;
  top: var(--space-1);
  left: var(--space-1);
  z-index: 2;
  padding: 0.25rem 0.5rem;
  background: var(--brand);
  color: var(--on-brand);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.release__play {
  position: absolute;
  right: var(--space-2);
  bottom: var(--space-2);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  background: var(--void);
  color: var(--brand);
  box-shadow: inset 0 0 0 1px var(--line-brand);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur-fast) var(--ease-cut),
              transform var(--dur-fast) var(--ease-snap),
              background-color var(--dur-instant) var(--ease-cut),
              color var(--dur-instant) var(--ease-cut);
  touch-action: manipulation;
}

.release:hover .release__play,
.release__play:focus-visible {
  opacity: 1;
  transform: none;
}

.release__play:hover {
  background: var(--brand);
  color: var(--on-brand);
}

/* On touch devices there is no hover — the control is always visible */
@media (hover: none) {
  .release__play { opacity: 1; transform: none; }
}

.release__play svg { width: 1.125rem; height: 1.125rem; }

.release__body {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  padding: var(--space-3);
  border-top: 1px solid var(--line);
}

.release__meta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
}

.release__meta span:not(:last-child)::after {
  content: " ·";
  color: var(--fg-ghost);
}

.release__title {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 400;
  line-height: 1.1;
  text-transform: uppercase;
}

/* Two tracks per catalogue number (radio edit + extended), worth saying
   plainly — it is the first thing a DJ checks. */
.release__format {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-faint);
}

.release__listen {
  margin-top: auto;
  padding-top: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.release__listen > span {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-ghost);
}

/* Chips rather than a text run: six platform names in a row would read as
   a wall of words, and each chip is its own comfortable tap target. */
.release__links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}

.release__links a {
  display: inline-flex;
  align-items: center;
  min-height: 1.875rem;
  padding: 0 0.5rem;
  box-shadow: inset 0 0 0 1px var(--line-strong);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--fg-soft);
  transition: background-color var(--dur-instant) var(--ease-cut),
              color var(--dur-instant) var(--ease-cut),
              box-shadow var(--dur-instant) var(--ease-cut);
}

.release__links a:hover {
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: inset 0 0 0 1px var(--brand);
}

/* The chips sit below the 44px guidance because they are a dense
   secondary cluster, so give them a bigger hit area on touch devices
   where there is no cursor to aim with. */
@media (hover: none) {
  .release__links a { min-height: 2.75rem; padding: 0 0.75rem; }
}

/* ---------------------------------------------------------------
   13b. UPCOMING RELEASE
   The one time-critical thing on the page, so it gets a feature block
   rather than a grid card — brand-outlined, cover large, one primary
   action (pre-save) and a short preview beside it.
   --------------------------------------------------------------- */
.upnext {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--line-brand);
}

@media (min-width: 56rem) {
  .upnext {
    flex-direction: row;
    align-items: stretch;
    gap: var(--space-4);
    padding: var(--space-4);
  }
}

.upnext__art {
  position: relative;
  flex: none;
  width: 100%;
  max-width: 20rem;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--surface-2);
}

@media (min-width: 56rem) {
  .upnext__art { width: clamp(12rem, 22vw, 18rem); max-width: none; }
}

.upnext__art img { width: 100%; height: 100%; object-fit: cover; }

.upnext__flag {
  position: absolute;
  top: 0; left: 0;
  padding: 0.375rem 0.75rem;
  background: var(--brand);
  color: var(--on-brand);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.upnext__body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
  flex: 1;
}

.upnext__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
}

.upnext__meta span:not(:last-child)::after {
  content: " ·";
  color: var(--fg-ghost);
}

.upnext__title {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 5vw, 3rem);
  font-weight: 400;
  line-height: 1.05;
  text-transform: uppercase;
}

.upnext__sub {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-faint);
}

.upnext__when {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin-top: var(--space-1);
  padding-top: var(--space-2);
  border-top: 1px solid var(--line);
}

.upnext__date {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg);
}

.upnext__date time { color: var(--brand); font-weight: 700; }

.upnext__count {
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
}

.upnext__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: auto;
  padding-top: var(--space-2);
}

.upnext__play {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 3rem;
  padding: 0 var(--space-3);
  box-shadow: inset 0 0 0 1px var(--line-strong);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg);
  touch-action: manipulation;
  transition: background-color var(--dur-instant) var(--ease-cut),
              color var(--dur-instant) var(--ease-cut);
  clip-path: polygon(
    var(--chamfer-sm) 0, 100% 0,
    100% calc(100% - var(--chamfer-sm)),
    calc(100% - var(--chamfer-sm)) 100%,
    0 100%, 0 var(--chamfer-sm)
  );
}

.upnext__play:hover { background: var(--surface-2); }
.upnext__play svg { width: 0.875rem; height: 0.875rem; flex: none; }
.upnext__play .icon-pause { display: none; }
.upnext__play[data-state="playing"] .icon-play { display: none; }
.upnext__play[data-state="playing"] .icon-pause { display: block; }
.upnext__play[data-state="playing"] { background: var(--brand); color: var(--on-brand); }

/* Progress readout for the preview. Scales on the compositor rather than
   animating width, so a 45s clip costs nothing to track. */
.upnext__bar {
  flex: 1 1 6rem;
  min-width: 5rem;
  height: 3px;
  background: var(--surface-3);
  overflow: hidden;
}

.upnext__bar i {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--brand);
  transform: scaleX(var(--p, 0));
  transform-origin: left;
}

.upnext__note {
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--fg-ghost);
}

/* --- Nothing announced ---
   Same treatment as an unavailable press asset: clearly a real slot that
   is simply empty, not a broken component. */
.upnext--empty {
  align-items: center;
  box-shadow: inset 0 0 0 1px var(--line);
  text-align: center;
  flex-direction: column;
  padding-block: var(--space-5);
}

.upnext--empty .upnext__body { align-items: center; text-align: center; flex: none; }

.upnext__mark {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  color: var(--fg-ghost);
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

.upnext__mark svg { width: 1.5rem; height: 1.5rem; }

.upnext--empty .upnext__title { color: var(--fg-soft); }

.upnext__blurb {
  max-width: 34rem;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--fg-faint);
}

.upnext__soon {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 var(--space-3);
  margin-top: var(--space-1);
  background: var(--surface-3);
  color: var(--fg-faint);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* ---------------------------------------------------------------
   14. MIX PLAYER
   --------------------------------------------------------------- */
.player {
  display: grid;
  gap: var(--space-4);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--line);
  padding: var(--space-3);
}

@media (min-width: 56rem) {
  .player { grid-template-columns: 1fr 20rem; padding: var(--space-4); }
}

.player__stage { display: flex; flex-direction: column; gap: var(--space-3); }

.player__now {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
}

.player__track {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 4vw, 2.25rem);
  line-height: 1.1;
  text-transform: uppercase;
}

.player__sub {
  margin-top: 0.25rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
}

/* Bars are real DOM elements; the engine sets --v on each one.
   Transform-only, so the compositor handles all of it. */
.player__viz {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: clamp(5rem, 14vw, 8rem);
  padding: 1px;
  background:
    linear-gradient(to top, rgba(215,255,62,0.05), transparent 60%);
  box-shadow: inset 0 0 0 1px var(--line);
}

.player__bar {
  flex: 1;
  min-width: 2px;
  height: 100%;
  background: linear-gradient(to top, var(--brand-dim), var(--brand));
  transform: scaleY(var(--v, 0.04));
  transform-origin: bottom;
  opacity: calc(0.35 + var(--v, 0) * 0.65);
  will-change: transform;
}

.player__transport {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.player__play {
  position: relative;
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  flex: none;
  background: var(--brand);
  color: var(--on-brand);
  transition: background-color var(--dur-instant) var(--ease-cut),
              transform var(--dur-instant) var(--ease-cut);
  touch-action: manipulation;
  clip-path: polygon(
    var(--chamfer-sm) 0, 100% 0,
    100% calc(100% - var(--chamfer-sm)),
    calc(100% - var(--chamfer-sm)) 100%,
    0 100%, 0 var(--chamfer-sm)
  );
}

/* Glow is a separate promoted layer whose opacity tracks the low end.
   Animating box-shadow directly would repaint the button every frame. */
.player__play::after {
  content: "";
  position: absolute;
  inset: -6px;
  z-index: -1;
  background: var(--brand);
  filter: blur(14px);
  opacity: calc(var(--bass) * 0.55);
  will-change: opacity;
  pointer-events: none;
}

.player__play:hover { background: var(--fg); }
.player__play:active { transform: scale(0.96); }
.player__play svg { width: 1.25rem; height: 1.25rem; }
.player__play .icon-pause { display: none; }
.player__play[data-state="playing"] .icon-play { display: none; }
.player__play[data-state="playing"] .icon-pause { display: block; }

/* Seek bar — a real range input, restyled, so keyboard + AT work free */
.player__seek {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.player__time {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-variant-numeric: tabular-nums;
  color: var(--fg-faint);
  flex: none;
}

.player__range {
  flex: 1;
  height: 2.75rem;         /* generous touch target */
  background: none;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
}

.player__range::-webkit-slider-runnable-track {
  height: 4px;
  background: linear-gradient(
    to right,
    var(--brand) var(--progress, 0%),
    var(--surface-3) var(--progress, 0%)
  );
}
.player__range::-moz-range-track {
  height: 4px;
  background: linear-gradient(
    to right,
    var(--brand) var(--progress, 0%),
    var(--surface-3) var(--progress, 0%)
  );
}

.player__range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 4px;
  height: 18px;
  margin-top: -7px;
  background: var(--fg);
  border: 0;
}
.player__range::-moz-range-thumb {
  width: 4px;
  height: 18px;
  background: var(--fg);
  border: 0;
  border-radius: 0;
}

/* --- Tracklist --- */
.player__list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--line);
}

@media (min-width: 56rem) {
  .player__list {
    border-top: 0;
    border-left: 1px solid var(--line);
    padding-left: var(--space-3);
    max-height: 22rem;
    overflow-y: auto;
  }
}

.cue {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 3rem;
  padding: var(--space-2) 0;
  text-align: left;
  border-bottom: 1px solid var(--line);
  color: var(--fg-soft);
  transition: color var(--dur-instant) var(--ease-cut),
              padding-left var(--dur-fast) var(--ease-cut);
  touch-action: manipulation;
}

.cue:hover { color: var(--fg); padding-left: 0.5rem; }

.cue[aria-current="true"] {
  color: var(--brand);
  padding-left: 0.5rem;
}

.cue__idx {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-variant-numeric: tabular-nums;
  color: var(--fg-ghost);
  flex: none;
  width: 1.75rem;
}

.cue[aria-current="true"] .cue__idx { color: var(--brand); }

.cue__name {
  flex: 1;
  font-size: var(--fs-sm);
  line-height: 1.3;
}

.cue__len {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-variant-numeric: tabular-nums;
  color: var(--fg-ghost);
  flex: none;
}

/* Equaliser glyph on the active cue */
.cue__eq {
  display: none;
  align-items: flex-end;
  gap: 2px;
  width: 12px;
  height: 12px;
  flex: none;
}
.cue[aria-current="true"] .cue__eq { display: flex; }

.cue__eq i {
  flex: 1;
  background: var(--brand);
  transform-origin: bottom;
  transform: scaleY(calc(0.25 + var(--pulse) * 0.75));
  transition: transform 60ms linear;
}
.cue__eq i:nth-child(2) { transform: scaleY(calc(0.25 + var(--bass) * 0.75)); }
.cue__eq i:nth-child(3) { transform: scaleY(calc(0.35 + var(--pulse) * 0.5)); }

/* ---------------------------------------------------------------
   15. PRESS KIT
   --------------------------------------------------------------- */
.kit {
  display: grid;
  gap: 1px;
  background: var(--line);
  box-shadow: 0 0 0 1px var(--line);
  grid-template-columns: 1fr;
}

@media (min-width: 40rem) { .kit { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .kit { grid-template-columns: repeat(4, 1fr); } }

.kit__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-height: 11rem;
  padding: var(--space-3);
  background: var(--surface);
  color: var(--fg);
  transition: background-color var(--dur-fast) var(--ease-cut);
}

.kit__item:hover { background: var(--surface-2); }

.kit__icon {
  width: 1.5rem;
  height: 1.5rem;
  color: var(--brand);
  stroke-width: 1.75;
}

.kit__name {
  font-family: var(--font-display);
  font-size: 1.25rem;
  text-transform: uppercase;
  line-height: 1.15;
}

.kit__desc {
  font-size: var(--fs-sm);
  color: var(--fg-soft);
  line-height: 1.45;
}

.kit__file {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1);
  padding-top: var(--space-2);
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-faint);
}

.kit__item:hover .kit__file { color: var(--brand); }

/* --- Full EPK feature block ---
   The one thing most promoters actually came for, so it sits above the
   individual assets and carries the brand fill rather than competing as
   a fifth equal tile. */
.epk {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  padding: var(--space-3);
  background: var(--surface);
  color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--line-brand);
  transition: background-color var(--dur-fast) var(--ease-cut),
              box-shadow var(--dur-fast) var(--ease-cut);
}

.epk:hover { background: var(--surface-2); }

.epk__icon {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  flex: none;
  background: var(--brand);
  color: var(--on-brand);
  clip-path: polygon(
    var(--chamfer-sm) 0, 100% 0,
    100% calc(100% - var(--chamfer-sm)),
    calc(100% - var(--chamfer-sm)) 100%,
    0 100%, 0 var(--chamfer-sm)
  );
}

.epk__icon svg { width: 1.75rem; height: 1.75rem; }

.epk__text {
  flex: 1 1 16rem;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.epk__name {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2.6vw, 1.75rem);
  line-height: 1.1;
  text-transform: uppercase;
}

.epk__desc {
  font-size: var(--fs-sm);
  line-height: 1.45;
  color: var(--fg-soft);
}

.epk__meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
  margin-left: auto;
}

.epk__size {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
}

.epk__cta {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 var(--space-3);
  background: var(--brand);
  color: var(--on-brand);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
  transition: background-color var(--dur-instant) var(--ease-cut);
  clip-path: polygon(
    var(--chamfer-sm) 0, 100% 0,
    100% calc(100% - var(--chamfer-sm)),
    calc(100% - var(--chamfer-sm)) 100%,
    0 100%, 0 var(--chamfer-sm)
  );
}

.epk:hover .epk__cta { background: var(--fg); }

/* --- Not yet uploaded ---
   Set by js/motion.js when the probe finds nothing at the path. The href
   is removed too, so this is genuinely inert rather than just looking it. */
.kit__item.is-unavailable,
.epk.is-unavailable {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

.epk.is-unavailable {
  box-shadow: inset 0 0 0 1px var(--line);
}

.epk.is-unavailable .epk__icon,
.epk.is-unavailable .epk__cta {
  background: var(--surface-3);
  color: var(--fg-faint);
}

/* ---------------------------------------------------------------
   16. BOOKING FORM
   --------------------------------------------------------------- */
.booking {
  display: grid;
  gap: var(--space-5);
}

@media (min-width: 56rem) {
  .booking { grid-template-columns: 0.9fr 1.1fr; gap: var(--space-6); }
}

.form { display: flex; flex-direction: column; gap: var(--space-3); }

.form__row { display: grid; gap: var(--space-3); }

@media (min-width: 40rem) {
  .form__row--2 { grid-template-columns: repeat(2, 1fr); }
}

.field { display: flex; flex-direction: column; gap: 0.5rem; }

.field__label {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-soft);
}

.field__req { color: var(--brand); }

.field__input,
.field__select,
.field__textarea {
  width: 100%;
  min-height: 3rem;         /* ≥44px */
  padding: 0.75rem var(--space-2);
  background: var(--surface);
  color: var(--fg);
  border: 0;
  box-shadow: inset 0 0 0 1px var(--line-strong);
  font-family: var(--font-mono);
  font-size: var(--fs-body);  /* 16px — no iOS zoom on focus */
  transition: box-shadow var(--dur-instant) var(--ease-cut),
              background-color var(--dur-instant) var(--ease-cut);
}

.field__textarea {
  min-height: 8rem;
  resize: vertical;
  line-height: 1.5;
}

.field__input::placeholder,
.field__textarea::placeholder { color: var(--fg-ghost); }

.field__input:focus,
.field__select:focus,
.field__textarea:focus {
  outline: none;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 2px var(--brand);
}

.field__select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B4B4B4' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  background-size: 1.125rem;
  padding-right: 2.5rem;
  cursor: pointer;
}

.field__hint {
  font-size: 0.8125rem;
  color: var(--fg-faint);
  line-height: 1.4;
}

/* Error state: colour is never the only signal — there is an icon
   and a text message, and the field gets a left rule. */
.field__error {
  display: none;
  align-items: flex-start;
  gap: 0.375rem;
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--danger);
}

.field__error svg {
  width: 0.9375rem;
  height: 0.9375rem;
  flex: none;
  margin-top: 0.15rem;
}

.field.is-invalid .field__error { display: flex; }

.field.is-invalid .field__input,
.field.is-invalid .field__select,
.field.is-invalid .field__textarea {
  box-shadow: inset 0 0 0 1px var(--danger), inset 3px 0 0 var(--danger);
}

.field.is-invalid .field__input:focus,
.field.is-invalid .field__select:focus,
.field.is-invalid .field__textarea:focus {
  box-shadow: inset 0 0 0 2px var(--danger), inset 3px 0 0 var(--danger);
}

/* Consent checkbox
   A 20px box is well under the 44px touch minimum, and a checkbox is a
   replaced element so padding will not grow its hit area. Instead the
   label is stretched over the whole panel — the entire block becomes the
   toggle target — and the privacy link is lifted back above that overlay
   so it stays independently clickable. */
.consent {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  min-height: 3rem;
  padding: var(--space-2);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--line);
  transition: background-color var(--dur-instant) var(--ease-cut);
}

.consent:hover { background: var(--surface-2); }

.consent input {
  width: 1.5rem;
  height: 1.5rem;
  margin: 0;
  flex: none;
  accent-color: var(--brand);
  cursor: pointer;
}

.consent label {
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--fg-soft);
  cursor: pointer;
}

.consent label::before {
  content: "";
  position: absolute;
  inset: 0;
  cursor: pointer;
}

.consent a {
  position: relative;
  z-index: 1;
  color: var(--brand);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Submit feedback */
.form__status {
  display: none;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-sm);
  line-height: 1.5;
}

.form__status.is-visible { display: flex; }

.form__status--ok {
  background: rgba(63, 224, 143, 0.10);
  box-shadow: inset 0 0 0 1px rgba(63, 224, 143, 0.35);
  color: var(--success);
}

.form__status--err {
  background: rgba(255, 92, 92, 0.10);
  box-shadow: inset 0 0 0 1px rgba(255, 92, 92, 0.35);
  color: var(--danger);
}

.form__status svg { width: 1.125rem; height: 1.125rem; flex: none; margin-top: 0.15rem; }

/* Spinner shown while the request is in flight */
.btn__spinner {
  display: none;
  width: 1rem;
  height: 1rem;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 640ms linear infinite;
}

.btn.is-busy .btn__spinner { display: block; }
.btn.is-busy .btn__label { opacity: 0.7; }

@keyframes spin { to { transform: rotate(360deg); } }

/* --- Booking side panel --- */
.booking__aside {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-self: start;
}

.infocard {
  padding: var(--space-3);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--line);
}

.infocard__title {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--brand);
  margin-bottom: var(--space-2);
}

.infocard dl { display: grid; gap: var(--space-2); }

.infocard dt {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
}

.infocard dd { margin: 0.15rem 0 0; font-size: var(--fs-sm); }

/* ---------------------------------------------------------------
   17. FOOTER
   --------------------------------------------------------------- */
.footer {
  position: relative;
  z-index: var(--z-content);
  border-top: 1px solid var(--line);
  padding-block: var(--space-5);
  padding-bottom: max(var(--space-5), calc(env(safe-area-inset-bottom) + var(--space-3)));
  background: var(--surface);
}

.footer__grid {
  display: grid;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}

@media (min-width: 48rem) {
  .footer__grid { grid-template-columns: 1.4fr repeat(2, 1fr); gap: var(--space-5); }
}

.footer__wordmark {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 9vw, 5rem);
  line-height: 0.9;
  text-transform: uppercase;
  color: transparent;
  -webkit-text-stroke: 1px var(--fg-ghost);
  transition: -webkit-text-stroke-color var(--dur-base) var(--ease-cut);
}

.footer:hover .footer__wordmark { -webkit-text-stroke-color: var(--brand); }

.footer__col h3 {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
  margin-bottom: var(--space-2);
}

.footer__col li + li { margin-top: 0.625rem; }

.footer__col a {
  font-size: var(--fs-sm);
  color: var(--fg-soft);
  transition: color var(--dur-instant) var(--ease-cut);
}
.footer__col a:hover { color: var(--brand); }

.footer__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-ghost);
}

.footer__legal { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.footer__legal a { color: var(--fg-faint); }
.footer__legal a:hover { color: var(--brand); }

/* ---------------------------------------------------------------
   18. THIRD-PARTY EMBED CONSENT (DSGVO two-click)
   Nothing loads from SoundCloud/Spotify until the visitor asks.
   --------------------------------------------------------------- */
.embed {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--line);
}

.embed__gate {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3);
  text-align: center;
}

.embed__gate p {
  max-width: 34rem;
  font-size: var(--fs-sm);
  color: var(--fg-soft);
  line-height: 1.5;
}

.embed iframe { width: 100%; height: 100%; border: 0; display: block; }

/* ---------------------------------------------------------------
   19. KINETIC REVEALS
   Sharp clip-path wipes, not fades. Elements are visible by default
   and only hidden once JS confirms it will animate them — so with
   JS disabled or broken, everything still renders.
   --------------------------------------------------------------- */
.js [data-reveal] {
  opacity: 0;
  clip-path: inset(0 100% 0 0);
  transform: translate3d(0, 10px, 0);
  transition:
    opacity   var(--dur-fast) var(--ease-cut),
    clip-path var(--dur-slow) var(--ease-snap),
    transform var(--dur-base) var(--ease-snap);
  transition-delay: calc(var(--d, 0) * 1ms);
}

.js [data-reveal].is-in {
  opacity: 1;
  clip-path: inset(0 0 0 0);
  transform: none;
}

/* Vertical variant for stacked cards */
.js [data-reveal="up"] {
  clip-path: inset(100% 0 0 0);
  transform: translate3d(0, 24px, 0);
}
.js [data-reveal="up"].is-in { clip-path: inset(0 0 0 0); }

/* Per-character hero entrance. Widths are reserved by the spans
   themselves, so nothing reflows — CLS stays at 0. */
.js .kinetic-char {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, 0.4em, 0);
  transition: opacity 120ms var(--ease-cut),
              transform 260ms var(--ease-snap);
  transition-delay: calc(var(--ci, 0) * 26ms);
}
.js .kinetic-char.is-in { opacity: 1; transform: none; }

/* Preserve the space between words when characters are split */
.kinetic-char--space { white-space: pre; }

/* ---------------------------------------------------------------
   20. GLITCH
   Used surgically: section entry, hover, and hard state changes.
   Never ambient, never looping — a permanent glitch reads as broken,
   not as design.
   --------------------------------------------------------------- */
.glitch { position: relative; }

.glitch[data-glitch]::before,
.glitch[data-glitch]::after {
  content: attr(data-glitch);
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  clip-path: inset(0 0 100% 0);
}

.glitch.is-glitching::before {
  color: var(--chan-r);
  animation: glitch-r 320ms steps(2, end) 1;
}

.glitch.is-glitching::after {
  color: var(--chan-b);
  animation: glitch-b 320ms steps(2, end) 1;
}

@keyframes glitch-r {
  0%   { opacity: 0.9; transform: translate3d(-3px, 0, 0); clip-path: inset(12% 0 62% 0); }
  25%  { opacity: 0.8; transform: translate3d(3px, 0, 0);  clip-path: inset(58% 0 18% 0); }
  50%  { opacity: 0.9; transform: translate3d(-2px, 0, 0); clip-path: inset(30% 0 45% 0); }
  75%  { opacity: 0.6; transform: translate3d(2px, 0, 0);  clip-path: inset(75% 0 8% 0); }
  100% { opacity: 0;   transform: none; }
}

@keyframes glitch-b {
  0%   { opacity: 0.9; transform: translate3d(3px, 0, 0);  clip-path: inset(70% 0 12% 0); }
  25%  { opacity: 0.8; transform: translate3d(-3px, 0, 0); clip-path: inset(20% 0 58% 0); }
  50%  { opacity: 0.9; transform: translate3d(2px, 0, 0);  clip-path: inset(48% 0 30% 0); }
  75%  { opacity: 0.6; transform: translate3d(-2px, 0, 0); clip-path: inset(6% 0 78% 0); }
  100% { opacity: 0;   transform: none; }
}

/* Horizontal tear that fires on beat for the hero title only */
.beat-shift {
  transform: translate3d(calc(var(--beat) * 2.5px), 0, 0);
  transition: transform 50ms linear;
}

/* ---------------------------------------------------------------
   21. UTILITIES
   --------------------------------------------------------------- */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.stack { display: flex; flex-direction: column; }
.stack-2 > * + * { margin-top: var(--space-2); }
.stack-3 > * + * { margin-top: var(--space-3); }
.stack-4 > * + * { margin-top: var(--space-4); }

.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

/* ---------------------------------------------------------------
   22. LEGAL PAGES
   --------------------------------------------------------------- */
.legal { padding-top: 9rem; }

.legal__body { max-width: var(--maxw-prose); }

.legal__body h2 {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3vw, 2rem);
  text-transform: uppercase;
  line-height: 1.15;
  margin-top: var(--space-5);
  margin-bottom: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.legal__body h2:first-child { margin-top: 0; border-top: 0; padding-top: 0; }

.legal__body h3 {
  font-family: var(--font-mono);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--brand);
  margin-top: var(--space-3);
  margin-bottom: var(--space-1);
}

.legal__body p,
.legal__body li {
  color: var(--fg-soft);
  line-height: 1.7;
}

.legal__body p + p { margin-top: var(--space-2); }

.legal__body ul {
  list-style: none;
  margin-top: var(--space-2);
  display: grid;
  gap: 0.5rem;
}

.legal__body li {
  position: relative;
  padding-left: 1.25rem;
}

.legal__body li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.65em;
  width: 6px; height: 6px;
  background: var(--brand);
  transform: rotate(45deg);
}

.legal__body a { color: var(--brand); text-decoration: underline; text-underline-offset: 3px; }

.legal__body strong { color: var(--fg); font-weight: 500; }

.legal__body code {
  padding: 0.1em 0.4em;
  background: var(--surface-2);
  font-family: var(--font-mono);
  font-size: 0.875em;
  color: var(--brand);
}

.legal__body address {
  font-style: normal;
  color: var(--fg);
  line-height: 1.7;
}

/* ---------------------------------------------------------------
   23. REDUCED MOTION
   Everything stops. The pulse freezes at a readable mid value, the
   glitch is removed entirely, reveals become instant. Nothing is
   hidden and nothing becomes unreachable.
   --------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  :root {
    --pulse: 0.4;
    --bass: 0.3;
    --beat: 0;
  }

  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  .js [data-reveal],
  .js [data-reveal="up"],
  .js .kinetic-char {
    opacity: 1 !important;
    clip-path: none !important;
    transform: none !important;
  }

  .ticker__track { animation: none; transform: none; }
  .ticker { -webkit-mask-image: none; mask-image: none; }

  /* No scroll-driven reveal: the hero is a single screen, the photo is
     already full-bleed and unscaled, and the headline stays put. */
  .hero {
    --sp: 1;
    --hero-scroll: 0px;
    height: 100svh;
  }
  .hero__frame { clip-path: none !important; }
  .hero__photo { transform: none !important; }
  .hero__inner { transform: none !important; opacity: 1 !important; }

  .glitch.is-glitching::before,
  .glitch.is-glitching::after { display: none; }

  .beat-shift { transform: none; }

  .navpanel {
    transition: none;
    clip-path: none;
    display: none;
  }
  .navpanel.is-open { display: flex; }
}

/* ---------------------------------------------------------------
   24. PRINT
   Promoters print riders and press sheets. Make it not embarrassing.
   --------------------------------------------------------------- */
@media print {
  .nav, .navpanel, .atmos, .atmos-pulse, .ticker,
  .hero__canvas, .hero__grid, .hero__cue, .player__viz { display: none !important; }

  body { background: #fff; color: #000; }
  .section { padding-block: 1.5rem; border-top: 1px solid #ccc; }
  a { color: #000; text-decoration: underline; }
  .hero { min-height: auto; padding-top: 0; }
}
