/* ================================================================
   BEAT MONGREL — molten signal
   All colors derive from :root variables.
   ================================================================ */
:root {
  --bg: #070605;
  --bg-2: #0e0b09;
  --fg: #f2ece2;
  --muted: #90867a;
  --accent: #f7931d;
  --accent-hot: #fff100;
  --accent-deep: #f16621;
  --sinister: #ff1a2b;
  --serene-blue: #2f7fd6;
  --line: rgba(242, 236, 226, 0.09);
  --card: rgba(242, 236, 226, 0.03);
  --grad: linear-gradient(180deg, var(--accent-hot) 0%, var(--accent) 55%, var(--accent-deep) 100%);
  --grad-x: linear-gradient(90deg, var(--accent-hot) 0%, var(--accent) 55%, var(--accent-deep) 100%);
  --font-display: "Unbounded", sans-serif;
  --font-body: "Space Grotesk", sans-serif;
  --energy: 0; /* live audio energy 0..1, set from JS */
  --beat: 0.465s; /* seconds per beat of the playing track, set from JS */
  --pad: clamp(1.25rem, 4vw, 4rem);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }
html:focus-within { scroll-behavior: smooth; }
section[id] { scroll-margin-top: 84px; }
/* deep-linked blocks need the same offset, or the fixed nav covers them */
[id="danse-macabre"] { scroll-margin-top: 100px; }

html { overflow-x: hidden; } /* decorative ghost text overhangs by ~2px; body alone doesn't stop html scrolling */

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-weight: 300;
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--accent-deep); color: var(--bg); }

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

.grad {
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ---------------- curtain / entrance ---------------- */
#curtain {
  position: fixed; inset: 0; z-index: 200;
  background: var(--bg);
  border-bottom: 2px solid var(--accent-deep);
  display: grid; place-items: center;
  transition: transform 0.85s cubic-bezier(0.76, 0, 0.24, 1) 0.2s, visibility 0.85s ease 0.2s;
}
#curtain.gone { transform: translateY(-101%); visibility: hidden; pointer-events: none; }
#curtain.gone .curtain-mark { opacity: 0; }
.curtain-mark { transition: opacity 0.3s ease; }
.curtain-mark { width: clamp(90px, 14vw, 150px); animation: markPulse 1.1s ease-in-out infinite; }
@keyframes markPulse {
  0%, 100% { transform: scale(1); filter: drop-shadow(0 0 12px rgba(247, 147, 29, 0.25)); }
  50% { transform: scale(1.06); filter: drop-shadow(0 0 34px rgba(247, 147, 29, 0.55)); }
}

/* ---------------- grain ---------------- */
.grain {
  position: fixed; inset: -50%; z-index: 90; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  opacity: 0.05;
  animation: grainShift 0.9s steps(4) infinite;
}
@keyframes grainShift {
  0% { transform: translate(0, 0); }
  25% { transform: translate(-2%, 3%); }
  50% { transform: translate(3%, -2%); }
  75% { transform: translate(-3%, -3%); }
  100% { transform: translate(2%, 2%); }
}

/* ---------------- cursor ---------------- */
.cursor { display: none; }
@media (pointer: fine) {
  .cursor { display: block; position: fixed; inset: 0; z-index: 150; pointer-events: none; }
  .cursor-dot, .cursor-ring {
    position: absolute; top: 0; left: 0; border-radius: 50%;
    transform: translate(-50%, -50%);
  }
  .cursor-dot { width: 6px; height: 6px; background: var(--accent); }
  .cursor-ring {
    width: 34px; height: 34px;
    border: 1px solid var(--accent);
    opacity: 0.5;
    transition: width 0.25s ease, height 0.25s ease, opacity 0.25s ease;
  }
  .cursor.on-link .cursor-ring { width: 56px; height: 56px; opacity: 0.9; }
  .cursor.hidden { opacity: 0; }
}

/* ---------------- nav ---------------- */
#nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  display: flex; align-items: center; gap: 2rem;
  padding: 1rem var(--pad);
  transition: background 0.4s ease, backdrop-filter 0.4s ease, padding 0.4s ease, border-color 0.4s ease;
  border-bottom: 1px solid transparent;
}
#nav.scrolled {
  background: rgba(7, 6, 5, 0.72);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom-color: var(--line);
  padding-top: 0.6rem; padding-bottom: 0.6rem;
}
.nav-brand { display: flex; align-items: center; gap: 0.7rem; margin-right: auto; }
.nav-word {
  font-family: var(--font-display); font-weight: 800;
  font-size: 0.8rem; letter-spacing: 0.14em;
}
.nav-links { display: flex; gap: 1.8rem; }
.nav-links a {
  font-size: 0.8rem; font-weight: 500; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--muted); position: relative; padding: 0.3rem 0;
  transition: color 0.3s ease;
}
.nav-links a::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 1px; width: 100%;
  background: var(--grad-x);
  transform: scaleX(0); transform-origin: right;
  transition: transform 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
.nav-links a:hover, .nav-links a.active { color: var(--fg); }
.nav-links a:hover::after, .nav-links a.active::after { transform: scaleX(1); transform-origin: left; }
/* live BPM readout, 120.00 at the top of the page to 145.00 at the
   bottom — driven 1:1 by scroll position in main.js's onScroll() */
.nav-bpm {
  font-family: var(--font-display); font-weight: 600;
  font-size: 0.78rem; letter-spacing: 0.1em;
  color: var(--accent);
  font-variant-numeric: tabular-nums; /* stops the digits jittering the layout as they count */
  white-space: nowrap;
}
.nav-burger { display: none; }

.mobile-menu {
  position: fixed; inset: 0; z-index: 99;
  background: rgba(7, 6, 5, 0.96);
  display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 2rem;
  opacity: 0; visibility: hidden; transition: opacity 0.4s ease, visibility 0.4s ease;
}
.mobile-menu.open { opacity: 1; visibility: visible; }
.mobile-menu a {
  font-family: var(--font-display); font-weight: 800; font-size: 1.6rem; letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ---------------- hero ---------------- */
.hero {
  position: relative; min-height: 100svh;
  display: flex; flex-direction: column; justify-content: center;
  padding: 6rem var(--pad) 4rem;
  overflow: hidden;
  overflow-anchor: none;
  /* the touch-and-hold journey works anywhere over the hero, so the
     whole section opts out of text selection — otherwise iOS answers a
     long press with the loupe + Copy/Look Up callout on the subtitle
     (the title alone being unselectable isn't enough) */
  user-select: none; -webkit-user-select: none;
  -webkit-touch-callout: none;
}
#gl { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(90deg, rgba(7, 6, 5, 0.82) 0%, rgba(7, 6, 5, 0.45) 38%, rgba(7, 6, 5, 0) 68%),
    linear-gradient(180deg, rgba(7, 6, 5, 0.75) 0%, rgba(7, 6, 5, 0) 30%, rgba(7, 6, 5, 0) 72%, rgba(7, 6, 5, 0.65) 100%);
}
/* text column + mark sit as flex siblings so the mark can be truly
   centered in whatever space remains beside the text, rather than
   pinned by a guessed right-offset */
.hero-layout {
  position: relative; z-index: 2; display: flex; align-items: center; gap: 2rem;
  /* the shared camera for the whole 3D hero scene: title extrusion,
     ember-mote depth field and mark tilt all render through this one
     perspective so their parallax stays physically consistent */
  perspective: 1100px;
  transform-style: preserve-3d;
}
.hero-inner { max-width: 960px; position: relative; transform-style: preserve-3d; }
/* ember light thrown up from below onto the title — the shader's fire
   lives at the bottom of the frame, so this glow plane sits under the
   letters and breathes; its opacity is driven per-frame from JS by the
   same energy/heat/storm values the shader reads, so the light on the
   type always agrees with the light in the smoke */
.hero-underglow {
  position: absolute; left: -12%; right: -8%; top: 8%; bottom: -18%;
  z-index: -1; pointer-events: none;
  background:
    radial-gradient(58% 52% at 26% 82%, color-mix(in srgb, var(--accent-deep) 52%, transparent), transparent 68%),
    radial-gradient(40% 40% at 62% 90%, color-mix(in srgb, var(--accent) 34%, transparent), transparent 70%);
  mix-blend-mode: screen;
  opacity: 0.55;
  animation: emberBreathe 4.6s ease-in-out infinite;
}
@keyframes emberBreathe {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.07) translateY(-1%); }
}
/* floating ember motes at staggered depths between the camera and the
   title — the whole field is rotated slightly harder than the title
   each frame (main.js), so near motes sweep faster than far ones:
   real perspective parallax, not a flat drift */
.hero-depth-field {
  position: absolute; inset: 0; pointer-events: none;
  transform-style: preserve-3d;
}
.mote {
  position: absolute; border-radius: 50%;
  background: color-mix(in srgb, var(--accent-hot) 70%, var(--accent));
  box-shadow: 0 0 8px var(--accent), 0 0 2px var(--accent-hot);
  animation: moteFloat var(--dur, 9s) ease-in-out var(--delay, 0s) infinite alternate;
}
@keyframes moteFloat {
  from { transform: translateZ(var(--mz, 0px)) translateY(0) ; opacity: var(--mo, 0.5); }
  to   { transform: translateZ(var(--mz, 0px)) translateY(-7vh); opacity: calc(var(--mo, 0.5) * 0.35); }
}
/* position:relative + an absolutely-positioned .hero-mark inside, rather
   than flex-centering it directly — a flex item's grown size feeds into
   its container's height even with overflow hidden set, which is what
   inflated .hero's own height during the growth journey. Taking the mark
   fully out of flow means its size can never do that, on any viewport. */
.hero-mark-slot { position: relative; flex: 1 1 0; min-width: 0; transform-style: preserve-3d; }
/* slow-breathing ember halo behind the mark — grounds it in the smoke
   instead of floating flatly on top of it */
.hero-mark-slot::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 150%; aspect-ratio: 1;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle,
    color-mix(in srgb, var(--accent-deep) 32%, transparent) 0%,
    color-mix(in srgb, var(--accent) 14%, transparent) 40%,
    transparent 68%);
  mix-blend-mode: screen;
  pointer-events: none;
  animation: haloPulse 5.4s ease-in-out infinite;
}
@keyframes haloPulse {
  0%, 100% { opacity: 0.55; transform: translate(-50%, -50%) scale(1); }
  50% { opacity: 0.95; transform: translate(-50%, -50%) scale(1.12); }
}
.hero-eyebrow {
  display: flex; align-items: center; gap: 0.6rem;
  font-size: 0.78rem; font-weight: 500; letter-spacing: 0.32em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 1.6rem;
}
.pulse-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 rgba(247, 147, 29, 0.6);
  animation: dotPulse 1.8s ease-out infinite;
}
@keyframes dotPulse {
  0% { box-shadow: 0 0 0 0 rgba(247, 147, 29, 0.6); }
  70% { box-shadow: 0 0 0 12px rgba(247, 147, 29, 0); }
  100% { box-shadow: 0 0 0 0 rgba(247, 147, 29, 0); }
}
/* while a track plays, the eyebrow dot pulse locks to its BPM */
body.is-playing .pulse-dot { animation-duration: var(--beat); }
.hero-title {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(2.6rem, 9vw, 7.8rem);
  line-height: 0.98;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  /* touch-and-hold drives the hover journey on mobile (main.js) — without
     these, iOS tries to select the text or start a page scroll from a
     press that lands here, both of which would cancel the interaction */
  touch-action: none;
  user-select: none; -webkit-user-select: none;
  /* the whole monument rotates in 3D every frame (main.js attitude loop);
     preserve-3d must run unbroken from .hero-layout's perspective all the
     way down to the .ch-depth slices or the extrusion flattens */
  transform-style: preserve-3d;
  will-change: transform;
}
.hero-title .line { display: block; transform-style: preserve-3d; }
.hero-title .ch {
  display: inline-block;
  transform-style: preserve-3d;
  /* entrance: letters slam forward out of the smoke from deep Z,
     one per beat, instead of the old flat mask-slide */
  opacity: 0;
  transform: translateZ(-480px) translateY(0.35em) rotateX(52deg);
  animation: chIn 1.05s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  animation-delay: calc(var(--i) * 65ms + 400ms);
}
@keyframes chIn {
  60% { opacity: 1; }
  78% { transform: translateZ(0.04em) translateY(0) rotateX(-4deg); }
  100% { opacity: 1; transform: translateZ(0) translateY(0) rotateX(0); }
}
/* the cursor-magnet effect (main.js) drives this element's transform
   every frame — kept separate from .ch so it never fights the
   one-time entrance animation above */
.hero-title .ch-inner { display: inline-block; position: relative; transform-style: preserve-3d; }
/* the word wrapper's own .grad text-clip gradient would paint at the
   letters' z=0 plane, on top of the extrusion slices and fighting the
   per-letter faces — the faces below own all the color now */
.hero-title .w { background: none; }
/* the visible front face of each letter — both words are gradient-lit
   from BELOW, because that's where the embers are: hot at the baseline,
   falling off toward the cap height */
.hero-title .ch-face {
  display: block; position: relative;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--fg) 36%, var(--bg)) 0%,
    color-mix(in srgb, var(--fg) 90%, var(--bg)) 48%,
    color-mix(in srgb, var(--accent) 68%, var(--fg)) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hero-title .grad .ch-face {
  background: linear-gradient(180deg,
    var(--accent-deep) 0%,
    var(--accent) 46%,
    var(--accent-hot) 100%);
  -webkit-background-clip: text;
  background-clip: text;
}
/* extrusion slices stacked behind the face at descending translateZ —
   real geometry, so the sides of the letters appear and shift as the
   camera drifts. Depth + per-slice color are set inline from main.js
   (color-mix over --accent-deep/--bg, so the palette stays var-driven). */
.hero-title .ch-depth {
  display: block; position: absolute; inset: 0;
  pointer-events: none; user-select: none;
}
/* the hot under-lip: the slice nearest the face is ember-bright and
   nudged down a touch, reading as firelight catching the bottom bevel */
.hero-title .ch-edge {
  color: var(--accent-hot);
  opacity: 0.85;
}
/* touch-only devices: extrusion is painted, not geometric — one span
   per letter whose own glyph + text-shadow stack step down-left in the
   same shaded ramp the 3D slices use. Zero extra compositor layers,
   which is what keeps mobile WebKit from crash-looping (see main.js). */
.hero-title .ch-extrude {
  transform: translate(-0.011em, 0.028em);
  color: color-mix(in srgb, var(--accent-deep) 44%, var(--bg));
  text-shadow:
    -0.011em 0.028em 0 color-mix(in srgb, var(--accent-deep) 36%, var(--bg)),
    -0.022em 0.056em 0 color-mix(in srgb, var(--accent-deep) 27%, var(--bg)),
    -0.033em 0.084em 0 color-mix(in srgb, var(--accent-deep) 18%, var(--bg)),
    -0.044em 0.112em 0 color-mix(in srgb, var(--accent-deep) 9%, var(--bg));
}
@media (hover: none) {
  /* collapse the per-letter 3D contexts — the title still tilts as one
     plane inside .hero-layout's perspective, so the monument reads 3D,
     but letters stop being individual composited layers */
  .hero-title .line, .hero-title .ch, .hero-title .ch-inner { transform-style: flat; }
  /* the deep-Z slam entrance needs a local vanishing point once the
     chain above is flat */
  .hero-title .line { perspective: 800px; }
  .hero-depth-field { transform-style: flat; }
}
.hero-sub {
  margin-top: 1.8rem;
  font-size: clamp(1rem, 1.8vw, 1.3rem);
  color: var(--muted); max-width: 34em;
}
.hero-sub em { font-style: normal; color: var(--fg); border-bottom: 1px solid var(--accent-deep); }
.hero-actions { display: flex; gap: 1rem; margin-top: 2.4rem; flex-wrap: wrap; }

/* while a track plays, the hero button's label gives way to the same
   little eq-bars graphic the tracklist uses — the label keeps its
   space (visibility, not display) so the box never changes size */
#heroPlay { position: relative; }
body.is-playing #heroPlay .hero-play-label { visibility: hidden; }
#heroPlay .hero-eq {
  position: absolute; left: 0; right: 0; top: 50%;
  transform: translateY(-50%);
  height: 20px;
  display: none; align-items: flex-end; justify-content: center; gap: 4px;
}
body.is-playing #heroPlay .hero-eq { display: flex; }
#heroPlay .hero-eq i {
  width: 4px; background: var(--bg); height: 30%;
  animation: eq 0.55s ease-in-out infinite alternate;
}
#heroPlay .hero-eq i:nth-child(2) { animation-delay: 0.12s; }
#heroPlay .hero-eq i:nth-child(3) { animation-delay: 0.24s; }
#heroPlay .hero-eq i:nth-child(4) { animation-delay: 0.36s; }

.btn {
  display: inline-block;
  font-family: var(--font-display); font-weight: 600;
  font-size: 0.8rem; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 1rem 1.8rem;
  transition: transform 0.25s ease, box-shadow 0.3s ease, background 0.3s ease, color 0.3s ease;
}
.btn-solid {
  background: var(--grad-x); color: var(--bg);
  box-shadow: 0 4px 30px rgba(241, 102, 33, 0.35);
}
.btn-solid:hover { transform: translateY(-3px); box-shadow: 0 10px 44px rgba(241, 102, 33, 0.55); }
.btn-ghost { border: 1px solid var(--line); color: var(--fg); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-3px); }

.hero-mark {
  /* grown via real width, not transform: scale() — scaling a composited
     SVG layer stretches its cached raster and goes pixelated at the big
     sizes the hover journey reaches; resizing forces a crisp vector
     repaint at every size instead */
  width: calc(clamp(300px, 32vw, 560px) * var(--mark-scale, 1));
  height: auto; display: block;
  /* positioned out of flow and self-centered, rather than centered via
     the slot's flexbox — see .hero-mark-slot comment: this is what
     actually keeps the growth from ever affecting ancestor layout */
  position: absolute; top: 50%; left: 50%;
  overflow-anchor: none;
  opacity: 0.92;
  /* one glow shadow by default; the second, grounding shadow is added
     below for fine-pointer devices only — a second full-size blur pass
     on the (grown) mark is exactly the kind of render-memory load that
     kills mobile WebKit */
  filter: drop-shadow(0 0 60px var(--mark-glow, rgba(241, 102, 33, 0.4)));
  /* the float bob, parallax offset and hover-journey scale are all
     composed into one transform set directly from JS every frame (see
     main.js) rather than a @keyframes animation reading live custom
     properties — mixing a native CSS animation with per-frame
     JS-updated custom properties feeding its transform is a known
     rough edge in WebKit specifically; this is just a static fallback
     for the instant before that loop's first frame runs */
  transform: translate(-50%, -50%);
}
@media (pointer: fine) {
  .hero-mark {
    filter: drop-shadow(0 0 60px var(--mark-glow, rgba(241, 102, 33, 0.4)))
            drop-shadow(0 26px 40px rgba(0, 0, 0, 0.55));
  }
}
/* driven from JS by proximity to the hero title (main.js "magnet" loop) */
.mark-eye-glow {
  fill: var(--eye-glow, var(--sinister));
  opacity: 0;
  filter: blur(6px);
  mix-blend-mode: screen;
  pointer-events: none;
}

/* a black cat clinging to the wall, claws out, slowly losing its grip —
   only shows itself during the storm phase of a long hero-title hover
   (see main.js "hover journey"); always looping, just invisible at
   rest so nothing needs to start/stop on a timer. */
.rain-cats {
  position: absolute; inset: 0; z-index: 1; overflow: hidden;
  opacity: var(--storm, 0);
  pointer-events: none;
  transition: opacity 0.4s ease;
}
.rain-cats img {
  position: absolute; top: -18%; opacity: 0.8;
  height: auto;
  filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.5));
  animation: catSlide linear infinite;
}
/* clings and wobbles near the top for the first quarter of the cycle,
   then loses its grip and slides the rest of the way down, faster */
@keyframes catSlide {
  0%   { transform: translateY(0) rotate(-3deg); }
  8%   { transform: translateY(0.5vh) rotate(3deg); }
  16%  { transform: translateY(1vh) rotate(-3deg); }
  24%  { transform: translateY(2.5vh) rotate(2deg); }
  42%  { transform: translateY(14vh) rotate(-4deg); }
  65%  { transform: translateY(40vh) rotate(3deg); }
  100% { transform: translateY(118vh) rotate(-6deg); }
}
/* the three originals, now scaled with the viewport so they read big on a
   desktop screen without overwhelming a phone */
.rain-cats img:nth-child(1) { left: 8%;  width: clamp(80px, 10vw, 165px); animation-duration: 7.5s; animation-delay: 0s; }
.rain-cats img:nth-child(2) { left: 46%; width: clamp(120px, 14vw, 230px); animation-duration: 9.5s; animation-delay: 2.8s; }
.rain-cats img:nth-child(3) { left: 80%; width: clamp(68px, 9vw, 145px);  animation-duration: 6.8s; animation-delay: 5s; }

/* a scatter of small ones behind them for depth. No drop-shadow on these —
   at this size it wouldn't read, and twelve extra filtered layers is real
   render cost on the hero for nothing. */
.rain-cats img:nth-child(n+4) {
  width: clamp(20px, 2.4vw, 40px);
  opacity: 0.62;
  /* a black cat on a near-black hero disappears at this size, so give it a
     faint light edge instead of the big soft drop-shadow the large ones use —
     a 3px blur is cheap enough to run twelve times */
  filter: drop-shadow(0 0 3px rgba(255, 214, 170, 0.45));
}
.rain-cats img:nth-child(4)  { left: 3%;  animation-duration: 5.4s;  animation-delay: 1.2s; }
.rain-cats img:nth-child(5)  { left: 17%; animation-duration: 8.2s;  animation-delay: 3.6s; }
.rain-cats img:nth-child(6)  { left: 24%; animation-duration: 6.1s;  animation-delay: 0.4s; }
.rain-cats img:nth-child(7)  { left: 33%; animation-duration: 9.8s;  animation-delay: 5.9s; }
.rain-cats img:nth-child(8)  { left: 39%; animation-duration: 5.9s;  animation-delay: 2.1s; }
.rain-cats img:nth-child(9)  { left: 55%; animation-duration: 7.7s;  animation-delay: 4.4s; }
.rain-cats img:nth-child(10) { left: 61%; animation-duration: 6.5s;  animation-delay: 0.9s; }
.rain-cats img:nth-child(11) { left: 68%; animation-duration: 10.4s; animation-delay: 3.1s; }
.rain-cats img:nth-child(12) { left: 74%; animation-duration: 5.7s;  animation-delay: 6.3s; }
.rain-cats img:nth-child(13) { left: 87%; animation-duration: 8.8s;  animation-delay: 1.7s; }
.rain-cats img:nth-child(14) { left: 92%; animation-duration: 6.3s;  animation-delay: 4.9s; }
.rain-cats img:nth-child(15) { left: 97%; animation-duration: 9.1s;  animation-delay: 2.5s; }
.hero-foot {
  position: absolute; z-index: 2; left: var(--pad); bottom: 1.6rem;
  font-size: 0.7rem; letter-spacing: 0.34em; color: var(--muted);
}
.hero-scroll { animation: scrollHint 2.2s ease-in-out infinite; }
@keyframes scrollHint { 0%, 100% { transform: translateY(0); opacity: 0.6; } 50% { transform: translateY(6px); opacity: 1; } }

/* ---------------- sections ---------------- */
.section { padding: clamp(5rem, 10vw, 9rem) var(--pad); max-width: 1500px; margin: 0 auto; position: relative; }
.section-ghost {
  position: absolute; top: 0.5em; right: -0.06em; z-index: -1;
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(5rem, 16vw, 15rem); line-height: 1;
  color: transparent;
  -webkit-text-stroke: 1px rgba(242, 236, 226, 0.05);
  pointer-events: none; user-select: none;
  white-space: nowrap;
}
.section-head {
  display: flex; align-items: baseline; gap: 1.4rem; flex-wrap: wrap;
  margin-bottom: clamp(2.5rem, 5vw, 4.5rem);
  border-bottom: 1px solid var(--line); padding-bottom: 1.2rem;
}
.section-index {
  font-family: var(--font-display); font-size: 0.85rem; font-weight: 600;
  color: var(--accent-deep);
}
.section-title {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(2rem, 5.5vw, 4rem); letter-spacing: 0.02em; line-height: 1;
}
.section-note { margin-left: auto; font-size: 0.78rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted); }

/* ---------------- featured ---------------- */
.featured {
  display: grid; grid-template-columns: minmax(280px, 520px) 1fr;
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
  margin-bottom: clamp(3rem, 7vw, 6rem);
}
.featured-art {
  position: relative;
  border: 1px solid var(--line);
  box-shadow: 0 30px 90px rgba(241, 102, 33, 0.18);
}
.featured-art img { width: 100%; }
.featured-info .tag-row { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 1.4rem; }
.tag {
  font-size: 0.68rem; font-weight: 600; letter-spacing: 0.18em;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--line); color: var(--muted);
}
.tag-hot { border-color: var(--accent-deep); color: var(--accent); }
.featured-title {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(3rem, 7vw, 6rem); line-height: 1; margin-bottom: 1.2rem;
}
.featured-desc { color: var(--muted); max-width: 34em; margin-bottom: 2rem; font-size: 1.05rem; }

.tracklist { list-style: none; margin-bottom: 2.2rem; border-top: 1px solid var(--line); }
.track {
  display: flex; align-items: center; gap: 1rem;
  padding: 0.9rem 0.4rem;
  border-bottom: 1px solid var(--line);
  transition: background 0.3s ease, padding 0.3s ease;
}
.track:hover { background: var(--card); padding-left: 0.9rem; }
.track.playing { background: var(--card); padding-left: 0.9rem; }
.track-idx {
  font-family: var(--font-display); font-size: 0.7rem; font-weight: 600;
  color: var(--muted); flex: 0 0 1.4rem;
}
.track.playing .track-idx { color: var(--accent); }
.track-name { font-weight: 500; letter-spacing: 0.04em; }
.track-meta { margin-left: auto; font-size: 0.75rem; letter-spacing: 0.14em; color: var(--muted); }

/* play buttons */
.play {
  width: 42px; height: 42px; flex: 0 0 42px;
  border: 1px solid var(--accent-deep); border-radius: 50%;
  display: grid; place-items: center;
  transition: background 0.3s ease, box-shadow 0.3s ease, transform 0.2s ease;
}
.play svg { width: 16px; height: 16px; fill: var(--accent); transition: fill 0.3s ease; }
.play:hover { background: var(--accent-deep); box-shadow: 0 0 22px rgba(241, 102, 33, 0.5); transform: scale(1.06); }
.play:hover svg { fill: var(--bg); }
.play .ico-pause { display: none; }
.play.playing .ico-play { display: none; }
.play.playing .ico-pause { display: block; }
.play.playing {
  background: var(--accent-deep);
  box-shadow: 0 0 calc(14px + var(--energy) * 30px) rgba(241, 102, 33, 0.7);
}
.play.playing svg { fill: var(--bg); }

/* eq bars in tracklist */
.track-eq { display: none; align-items: flex-end; gap: 2px; height: 14px; margin-left: 0.6rem; }
.track.playing .track-eq { display: inline-flex; }
.track-eq i {
  width: 3px; background: var(--grad); height: 30%;
  animation: eq 0.55s ease-in-out infinite alternate;
}
.track-eq i:nth-child(2) { animation-delay: 0.12s; }
.track-eq i:nth-child(3) { animation-delay: 0.24s; }
.track-eq i:nth-child(4) { animation-delay: 0.36s; }
@keyframes eq { from { height: 20%; } to { height: 100%; } }

/* ---------------- release grid ---------------- */
.release-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(1.2rem, 2.5vw, 2rem);
}
.release {
  background: var(--card);
  border: 1px solid var(--line);
  transition: border-color 0.35s ease, transform 0.35s ease, box-shadow 0.35s ease;
}
.release:hover {
  border-color: var(--accent-deep);
  box-shadow: 0 18px 60px rgba(241, 102, 33, 0.16);
}
.release-art { position: relative; overflow: hidden; }
.release-art img { transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
.release:hover .release-art img { transform: scale(1.05); }
.play-overlay {
  position: absolute; right: 1rem; bottom: 1rem;
  width: 52px; height: 52px; flex-basis: 52px;
  background: rgba(7, 6, 5, 0.6); backdrop-filter: blur(6px);
}
.release-body { padding: 1.3rem 1.4rem 1.5rem; }
.release-body h3 {
  font-family: var(--font-display); font-weight: 800; font-size: 1.15rem; letter-spacing: 0.04em;
  margin-bottom: 0.5rem;
}
/* RMX badge. As a plain inline element its painted box ignores line-height,
   so it stood ~19px tall against 14px caps and hung below the baseline.
   inline-block lets line-height + padding size it to the cap band; the -0.15em
   nudge is optical, centring it on the caps rather than on the line box
   (which sits low because of descender space). Measured symmetric at both the
   1.15rem card titles and the 2.2rem free-download title. */
.rmx {
  display: inline-block;
  font-size: 0.6em; line-height: 1;
  vertical-align: middle; position: relative; top: -0.15em;
  border: 1px solid var(--accent-deep); color: var(--accent);
  padding: 0.12em 0.42em 0.1em; letter-spacing: 0.12em;
}
.release-meta { font-size: 0.85rem; color: var(--muted); margin-bottom: 0.4rem; }
.release-data { font-size: 0.72rem; letter-spacing: 0.16em; color: var(--accent); margin-bottom: 1rem; }
.release-link {
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.2em;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
  transition: color 0.3s ease, border-color 0.3s ease;
}
.release-link:hover { color: var(--accent); border-color: var(--accent); }

/* ---------------- free download ---------------- */
/* narrower art than .featured — this sits between the featured EP and the
   release grid, so it needs to read as a step down from the EP, not a rival */
.freedl {
  display: grid; grid-template-columns: minmax(200px, 320px) 1fr;
  gap: clamp(1.6rem, 4vw, 3.5rem);
  align-items: center;
  padding: clamp(1.6rem, 3vw, 2.4rem);
  border: 1px solid var(--line);
  /* sits below the release grid, so it needs space above it, not below */
  margin-top: clamp(3rem, 7vw, 6rem);
}
.freedl-art {
  position: relative;
  border: 1px solid var(--line);
  box-shadow: 0 20px 60px rgba(241, 102, 33, 0.16);
}
.freedl-art img { width: 100%; display: block; }
.freedl-info .tag-row { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 1.2rem; }
.freedl-title {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(1.4rem, 3vw, 2.2rem); letter-spacing: 0.02em;
  margin-bottom: 0.5rem;
}
.freedl-desc { color: var(--muted); font-size: 0.95rem; margin-bottom: 1.6rem; max-width: 34em; }
.freedl-actions { display: flex; gap: 1rem; flex-wrap: wrap; }
.freedl-actions .btn { display: inline-flex; align-items: baseline; gap: 0.6rem; }
/* the size sits on the button so nobody starts a 56 MB pull on mobile data
   without knowing what they are in for */
.freedl-size { font-size: 0.68em; opacity: 0.75; letter-spacing: 0.08em; }

/* tilt + glare */
.tilt { transform-style: preserve-3d; will-change: transform; }
.glare {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(circle at var(--gx, 50%) var(--gy, 50%), rgba(242, 236, 226, 0.18), transparent 55%);
  opacity: 0; transition: opacity 0.3s ease;
}
.tilt:hover .glare { opacity: 1; }

/* ---------------- gigs ---------------- */
.gig-board { border-top: 1px solid var(--line); }
.gig-row {
  display: grid; grid-template-columns: 8rem 1fr auto; align-items: center; gap: 1.5rem;
  padding: 1.6rem 0.5rem;
  border-bottom: 1px solid var(--line);
  transition: background 0.3s ease, padding 0.3s ease;
}
.gig-row:hover { background: var(--card); padding-left: 1.2rem; }
.gig-date {
  font-family: var(--font-display); font-weight: 800; font-size: 1.3rem;
  color: var(--accent);
}
.gig-venue { font-size: 1.15rem; font-weight: 500; display: flex; flex-direction: column; }
.gig-venue em { font-style: normal; font-size: 0.8rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted); margin-top: 0.2rem; }
.gig-status {
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.22em;
  border: 1px dashed var(--muted); color: var(--muted);
  padding: 0.45rem 0.9rem;
}
.gig-note { margin-top: 2.2rem; color: var(--muted); }
.gig-note a,
.gig-book {
  font: inherit; background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--accent); border-bottom: 1px solid var(--accent-deep);
  transition: color 0.3s ease;
}
.gig-note a:hover,
.gig-book:hover { color: var(--accent-hot); }

/* ---------------- bio ---------------- */
.bio-grid {
  display: grid; grid-template-columns: minmax(280px, 480px) 1fr;
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
}
.bio-photo { position: relative; }
.bio-photo img {
  filter: grayscale(0.9) contrast(1.1) brightness(0.9);
  transition: filter 0.6s ease;
}
.bio-photo:hover img { filter: grayscale(0.2) contrast(1.05) brightness(1); }
.bio-photo::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(200deg, rgba(241, 102, 33, 0.28), transparent 55%);
  mix-blend-mode: overlay; pointer-events: none;
}
.bio-lede {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.4rem, 3vw, 2.2rem); line-height: 1.25;
  margin-bottom: 1.8rem;
}
.bio-copy > p:not(.bio-lede) { color: var(--muted); max-width: 38em; margin-bottom: 1.2rem; font-size: 1.05rem; }

/* ---------------- contact ---------------- */
.contact-grid {
  display: grid; grid-template-columns: 1.25fr 1fr;
  gap: clamp(2.5rem, 6vw, 6rem);
  margin-top: 2.5rem; align-items: start;
}

/* honeypot — hidden from people, still filled in by bots */
.form-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* floating-label field: the label sits over an empty input and lifts away as
   soon as there's a value, so the form reads clean with no visible chrome */
.field { position: relative; margin-bottom: 1.6rem; }
.field input,
.field textarea {
  width: 100%; display: block;
  font-family: var(--font-body); font-size: 1rem; color: var(--fg);
  background: transparent; border: 0;
  border-bottom: 1px solid var(--line);
  padding: 1.6rem 0 0.7rem;
  transition: border-color 0.3s ease;
}
.field textarea { resize: vertical; min-height: 7rem; line-height: 1.5; }
.field input:focus,
.field textarea:focus { outline: none; border-bottom-color: var(--accent); }
.field label {
  position: absolute; left: 0; top: 1.5rem;
  font-size: 0.95rem; color: var(--muted);
  pointer-events: none;
  transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1),
              font-size 0.28s cubic-bezier(0.16, 1, 0.3, 1),
              color 0.28s ease, letter-spacing 0.28s ease;
}
.field input:focus + label,
.field textarea:focus + label,
.field input:not(:placeholder-shown) + label,
.field textarea:not(:placeholder-shown) + label {
  transform: translateY(-1.5rem);
  font-size: 0.65rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--accent);
}
.contact-form .btn { border: 0; cursor: pointer; }

.form-status {
  margin-top: 1rem; min-height: 1.2em;
  font-size: 0.8rem; letter-spacing: 0.12em; color: var(--muted);
}
.form-status.ok { color: var(--accent); }
.form-status.err { color: #ff6b5e; }

.contact-aside { display: flex; flex-direction: column; gap: 2.8rem; }
.mailing {
  border: 1px solid var(--line);
  padding: clamp(1.6rem, 3vw, 2.4rem);
}
.mailing-title {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(1.4rem, 2.4vw, 2rem); line-height: 1.1;
  margin-bottom: 0.7rem;
}
.mailing-copy { color: var(--muted); font-size: 0.95rem; margin-bottom: 1.2rem; }
.mailing-form .field { margin-bottom: 1.4rem; }
.mailing-submit { border: 0; cursor: pointer; width: 100%; padding: 0.9rem 1.5rem; }

.contact-links { display: flex; gap: 0.9rem 1.6rem; flex-wrap: wrap; }

/* ---------------- mixes (Techno Salad) ---------------- */
.mix-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: clamp(1.2rem, 2.5vw, 2rem);
  /* without this, opening one tracklist stretches every other card in that
     row to match, dragging their titles down the page */
  align-items: start;
}
.mix {
  display: grid; grid-template-columns: 96px 1fr;
  gap: 1.1rem; align-items: center;
  border: 1px solid var(--line); padding: 1rem;
  transition: border-color 0.3s ease;
}
.mix:hover { border-color: var(--accent-deep); }
/* the whole card is the link now — lift the art slightly on hover so it
   reads as clickable without needing button chrome inside it */
.mix-art img { transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1); }
.mix:hover .mix-art img { transform: scale(1.05); }
.mix-art { overflow: hidden; }
.mix:hover .mix-title { color: var(--accent); }
.mix-title { transition: color 0.3s ease; }
.mix-art { position: relative; align-self: start; }
.mix-art img { width: 100%; display: block; }
.mix-title {
  font-family: var(--font-display); font-weight: 800;
  font-size: 0.95rem; letter-spacing: 0.04em; margin-bottom: 0.35rem;
}
.mix-num { color: var(--accent); }
.mix-meta { font-size: 0.72rem; letter-spacing: 0.14em; color: var(--muted); margin-bottom: 0.7rem; }

@media (max-width: 900px) {
  .mix-grid { grid-template-columns: 1fr; }
}

/* ---------------- booking modal ---------------- */
/* native <dialog> so focus trapping and Esc-to-close come for free */
.modal {
  width: min(560px, calc(100vw - 2rem));
  /* the global `* { margin: 0 }` reset wipes out the UA stylesheet's
     `margin: auto`, which is what centres a native modal dialog */
  margin: auto;
  max-height: calc(100vh - 2rem); overflow-y: auto;
  padding: clamp(1.8rem, 4vw, 2.8rem);
  border: 1px solid var(--line);
  background: var(--bg); color: var(--fg);
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
}
.modal::backdrop { background: rgba(0, 0, 0, 0.72); backdrop-filter: blur(6px); }
.modal[open] { animation: modalIn 0.32s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes modalIn {
  from { opacity: 0; transform: translateY(14px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}
.modal-close {
  position: absolute; top: 0.6rem; right: 0.9rem;
  background: none; border: 0; cursor: pointer;
  font-size: 1.8rem; line-height: 1; color: var(--muted);
  transition: color 0.3s ease;
}
.modal-close:hover { color: var(--accent); }
.modal-title {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(1.5rem, 3.5vw, 2.2rem); line-height: 1.1;
  margin-bottom: 0.6rem;
}
.modal-copy { color: var(--muted); font-size: 0.95rem; margin-bottom: 1.8rem; }
.contact-links a {
  font-size: 0.85rem; font-weight: 600; letter-spacing: 0.24em;
  color: var(--muted);
  border-bottom: 1px solid var(--line); padding-bottom: 4px;
  transition: color 0.3s ease, border-color 0.3s ease;
}
.contact-links a:hover { color: var(--accent); border-color: var(--accent-deep); }

/* ---------------- footer ---------------- */
footer {
  display: flex; align-items: center; gap: 1.2rem; flex-wrap: wrap;
  padding: 2.5rem var(--pad) calc(2.5rem + 70px);
  border-top: 1px solid var(--line);
  color: var(--muted); font-size: 0.75rem; letter-spacing: 0.22em;
}
.foot-links { display: flex; gap: 1.6rem; margin-left: auto; }
.foot-links a { transition: color 0.3s ease; }
.foot-links a:hover { color: var(--accent); }

/* scrollbar */
::-webkit-scrollbar { width: 10px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--bg-2); border: 1px solid var(--line); }
::-webkit-scrollbar-thumb:hover { background: var(--accent-deep); }

/* ---------------- now playing bar ---------------- */
.nowbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 110;
  display: flex; align-items: center; gap: 1rem;
  padding: 0.7rem var(--pad);
  background: rgba(14, 11, 9, 0.85);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  border-top: 1px solid var(--line);
  transform: translateY(110%);
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.nowbar.visible { transform: translateY(0); }
.nowbar-play { width: 38px; height: 38px; flex: 0 0 38px; }
.nowbar-info { display: flex; flex-direction: column; min-width: 0; }
.nowbar-title { font-weight: 600; font-size: 0.9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nowbar-meta { font-size: 0.7rem; letter-spacing: 0.16em; color: var(--muted); }
.nowbar-viz { flex: 0 0 120px; height: 36px; }
/* elapsed / total — tabular so the digits don't shuffle the bar as they tick */
.nowbar-time {
  flex: 0 0 auto; font-size: 0.7rem; letter-spacing: 0.08em; color: var(--muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.nowbar-progress {
  flex: 1; height: 22px; display: flex; align-items: center; cursor: pointer;
  position: relative;
}
.nowbar-progress::before {
  content: ""; position: absolute; left: 0; right: 0; height: 2px;
  background: var(--line);
}
.nowbar-fill {
  position: absolute; left: 0; height: 2px; width: 0%;
  background: var(--grad-x);
  box-shadow: 0 0 10px rgba(247, 147, 29, 0.6);
}

/* ---------------- reveals ---------------- */
.reveal {
  opacity: 0; transform: translateY(34px);
  transition: opacity 0.9s cubic-bezier(0.16, 1, 0.3, 1), transform 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}
.reveal.in { opacity: 1; transform: translateY(0); }
/* stagger applied per-batch from JS via transition-delay inline style */

/* ---------------- responsive ---------------- */
@media (max-width: 900px) {
  .nav-links, .nav-bpm { display: none; }
  .nav-burger {
    display: flex; flex-direction: column; gap: 6px; padding: 0.5rem;
  }
  .nav-burger span { width: 26px; height: 2px; background: var(--fg); transition: transform 0.3s ease; display: block; }
  .nav-burger[aria-expanded="true"] span:first-child { transform: translateY(4px) rotate(45deg); }
  .nav-burger[aria-expanded="true"] span:last-child { transform: translateY(-4px) rotate(-45deg); }

  .hero { justify-content: center; }
  /* stack mark above text instead of sitting beside it — a fixed
     % offset looked fine in emulators but collided with the title on
     real devices, where Safari's dynamic toolbar changes viewport
     height, so both pieces now just flow top-to-bottom. */
  .hero-layout { flex-direction: column; }
  /* fixed reserved height, not the base flex:1 1 0 (which in this
     column layout would mean main-axis = height, growing to fill
     space) — .hero-mark is absolutely positioned now, so this box's
     own height no longer follows its child; it needs an explicit
     value to keep reserving the same visual gap above the text that
     the stacked layout depends on, regardless of how big the mark
     grows during a touch-and-hold */
  .hero-mark-slot { order: -1; margin-bottom: 0.5rem; flex: none; width: 100%; height: min(56vw, 280px); }
  .hero-mark { width: calc(min(56vw, 280px) * var(--mark-scale, 1)); opacity: 0.9; }
  .hero-title { font-size: clamp(2.4rem, 11vw, 4.2rem); }
  /* tighter camera on small screens so the extrusion still reads
     at the smaller type size */
  .hero-layout { perspective: 760px; }
  .featured { grid-template-columns: 1fr; }
  .freedl { grid-template-columns: 1fr; }
  /* full-width buttons so the two downloads don't squash side by side */
  .freedl-actions { flex-direction: column; align-items: stretch; }
  .freedl-actions .btn { justify-content: center; }
  .bio-grid { grid-template-columns: 1fr; }
  .contact-grid { grid-template-columns: 1fr; }
  .gig-row { grid-template-columns: 5.5rem 1fr auto; gap: 0.8rem; }
  .gig-date { font-size: 1rem; }
  .gig-venue { font-size: 1rem; }
  .track-meta { display: none; }
  .nowbar-viz { display: none; }
  .section-note { display: none; }
}

/* ---------------- reduced motion ---------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .hero-title .ch { transform: none; animation: none; opacity: 1; }
  .hero-depth-field, .hero-underglow { display: none; }
  /* the cursor-follow loop never runs under reduced motion, which
     would leave the ring parked visibly at the viewport corner */
  .cursor { display: none; }
  .hero-mark-slot::before { animation: none; opacity: 0.5; }
  .reveal { opacity: 1; transform: none; }
  #gl { display: none; }
}

/* focus visibility */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---------------- individual mix pages (/mixes/techno-salad-NN/) ---------- */
/* These pages don't load main.js, so anything main.js normally drives (the
   custom cursor, reveal-on-scroll) must not be relied on here — content is
   visible by default rather than waiting for a class to be added. */
.mixpage { background: var(--bg); }
.mixpage .reveal { opacity: 1; transform: none; }
.mixpage-main {
  max-width: 1100px; margin: 0 auto;
  padding: calc(70px + clamp(2rem, 6vw, 5rem)) var(--pad) clamp(3rem, 6vw, 6rem);
}
.mixpage-back {
  display: inline-block; margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--muted); transition: color 0.3s ease;
}
.mixpage-back:hover { color: var(--accent); }

.mixpage-head {
  display: grid; grid-template-columns: minmax(220px, 340px) 1fr;
  gap: clamp(1.6rem, 4vw, 3.5rem); align-items: center;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}
.mixpage-art {
  border: 1px solid var(--line);
  box-shadow: 0 30px 90px rgba(241, 102, 33, 0.18);
}
.mixpage-art img { width: 100%; display: block; }
.mixpage-title {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(1.8rem, 5vw, 3.2rem); line-height: 1.05;
  margin: 0.4rem 0 0.5rem;
}
.mixpage-meta { color: var(--muted); font-size: 0.9rem; margin-bottom: 1.6rem; }
.mixpage-actions { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.6rem; }
.mixpage-actions .btn { border: 0; cursor: pointer; display: inline-flex; align-items: baseline; gap: 0.5rem; }

.mixpage-player { display: flex; align-items: center; gap: 1rem; }
.mixpage-bar {
  flex: 1; height: 6px; background: rgba(242, 236, 226, 0.12);
  cursor: pointer; position: relative; touch-action: none;
}
.mixpage-fill { height: 100%; width: 0; background: var(--grad-x); }
.mixpage-time {
  font-size: 0.75rem; color: var(--muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

.mixpage-tl-head {
  font-family: var(--font-display); font-weight: 800;
  font-size: 1.1rem; letter-spacing: 0.16em; text-transform: uppercase;
  padding-bottom: 0.8rem; border-bottom: 1px solid var(--line); margin-bottom: 1.4rem;
}
/* two columns on wide screens — 25 tracks in one column is a long scroll */
.mixpage-tracks {
  columns: 2; column-gap: clamp(2rem, 5vw, 4rem);
  padding-left: 1.4rem; color: var(--muted);
  font-size: 0.92rem; line-height: 2;
}
.mixpage-tracks li { break-inside: avoid; }
.mixpage-tracks li::marker { color: var(--accent-deep); font-size: 0.85em; }

.mixnav {
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  margin-top: clamp(2.5rem, 5vw, 4rem); padding-top: 1.6rem;
  border-top: 1px solid var(--line);
}
.mixnav-link {
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); transition: color 0.3s ease;
}
.mixnav-link:hover { color: var(--accent); }

@media (max-width: 800px) {
  .mixpage-head { grid-template-columns: 1fr; }
  .mixpage-tracks { columns: 1; }
}

/* play button on a mix page swaps its label for animated EQ bars while the
   mix runs — same pattern as #heroPlay on the homepage */
#mixPlay { position: relative; }
#mixPlay .mix-eq {
  position: absolute; left: 0; right: 0; top: 50%;
  transform: translateY(-50%);
  height: 16px; /* 20px less 20% */
  display: none; align-items: flex-end; justify-content: center; gap: 4px;
}
#mixPlay.playing .mix-eq { display: flex; }
#mixPlay.playing .mix-play-label { visibility: hidden; }
#mixPlay .mix-eq i {
  width: 4px; background: var(--bg); height: 30%;
  animation: eq 0.55s ease-in-out infinite alternate;
}
#mixPlay .mix-eq i:nth-child(2) { animation-delay: 0.12s; }
#mixPlay .mix-eq i:nth-child(3) { animation-delay: 0.24s; }
#mixPlay .mix-eq i:nth-child(4) { animation-delay: 0.36s; }
#mixPlay .mix-eq i:nth-child(5) { animation-delay: 0.48s; }
#mixPlay .mix-eq i:nth-child(6) { animation-delay: 0.60s; }
