/* =========================================================
   JUNKSISTA — one page
   Palette sampled from the "0A" artwork and press photos.
   ========================================================= */

:root {
  /* colour */
  --bg:        #08080a;
  --bg-2:      #0e0e11;
  --surface:   #131317;
  --line:      rgba(255,255,255,.10);
  --line-soft: rgba(255,255,255,.06);
  --text:      #f3f1ef;
  --muted:     #9c9ca4;
  --red:       #c53238;   /* from the 0A cover */
  --red-hot:   #ff3b45;
  --teal:      #7fd4cd;   /* from the JUNKSISTA wordmark */

  /* type */
  --display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --body:    "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* motion */
  --ease:     cubic-bezier(.16,1,.3,1);
  --ease-out: cubic-bezier(.22,.61,.36,1);
  --dur:      .9s;

  /* layout */
  --wrap: 1180px;
  --gut:  clamp(1.25rem, 4vw, 3rem);
  --header-h: 72px;
}

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  font-size: clamp(1rem, .95rem + .2vw, 1.075rem);
  line-height: 1.65;
  font-weight: 300;
  letter-spacing: .002em;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
a   { color: inherit; }
p   { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }
em  { font-style: italic; }

::selection { background: var(--red); color: #fff; }

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

.skip-link {
  position: fixed; top: .5rem; left: .5rem; z-index: 999;
  padding: .7rem 1.1rem; background: var(--red); color: #fff;
  font-weight: 600; text-decoration: none;
  transform: translateY(-160%);
  transition: transform .25s var(--ease);
}
.skip-link:focus { transform: none; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gut);
}

/* ---------- typography helpers ---------- */

/* Zwei Register, nicht eins, und jetzt ueber die ganze Achse. Archivo traegt
   wdth 62..125; benutzt wurden erst nur 106–125 (eine einzige Breite, entspre-
   chend gleichfoermig), dann 74 als schmales Gegenstueck. 74 war aber nicht das
   Ende der Achse, sondern ein Wert in ihrer Mitte — die Wortmarke auf dem
   0A-Cover steht deutlich schmaler als alles, was hier stand. Der schmale Pol
   sitzt deshalb jetzt auf 62 %, dem echten Boden der Achse: darunter klemmt die
   Datei, 56 % und 50 % rendern messbar identisch zu 62 %. Zusammen mit den
   125 % von .hero__title spannt die Seite damit die Breitenachse von Anschlag
   zu Anschlag, ohne eine zweite Schriftdatei.

   Der Deckel steigt von 6,4rem auf 7,5rem, und das ist gerechnet: bei 62 %
   statt 74 % braucht derselbe Satz 85,5 % der Breite, die Schrift kann also um
   1/0,855 hoch. Das Ergebnis ist breitenneutral — "Death By Heartache" misst
   919 px gegen 915 px vorher, "Recent Releases" 782 gegen 780 —, aber die Zeile
   ist 120 px hoch statt 102. Die zwei unteren clamp-Terme steigen im selben
   Verhaeltnis mit.

   Nach oben ist bei 7,5rem Schluss, und die Grenze setzt nicht die Spalte: bei
   8rem waechst "Recent Releases" auf 834 px, und dann wrappt der
   Bandcamp-Link in .section__head--row unter die Headline. Wer hoeher will,
   muss dort vorher hinsehen.

   Auf 375 px steht .display damit auf 54 px und .hero__title auf 51 px — die
   Section-Headline ist dort nominell die groessere. Das ist kein Fehler und
   will nicht "korrigiert" werden: die Hero-Zeile ist auf schmalen Fenstern
   breitengebunden (13,53vw, damit sie beide Fensterkanten trifft), sie laesst
   sich also nicht anheben, ohne die Randbuendigkeit zu verlieren. Sie dominiert
   dort ueber die Breite, nicht ueber die Hoehe. Wer stattdessen .display
   herunterzieht, nimmt die 1,17 wieder weg, um die es hier ging. */
.display {
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 62%;
  font-size: clamp(3.35rem, 1.4rem + 8.2vw, 7.5rem);
  line-height: .88;
  letter-spacing: -.01em;
  text-transform: uppercase;
  margin: 0 0 .4em;
}
/* Die Gegenseite des Kontrasts: "0A" bleibt breit und fett. Es setzt
   font-stretch bewusst neu — ohne die Zeile erbte es die 62 % von .display. */
.display--xl {
  font-stretch: 118%;
  font-size: clamp(4rem, 2rem + 12vw, 10rem);
  letter-spacing: -.03em;
  line-height: .82;
  margin-bottom: .25em;
}

.lede {
  font-size: clamp(1.06rem, 1rem + .45vw, 1.3rem);
  line-height: 1.55;
  color: #dfdedd;
}

.eyebrow {
  display: flex; align-items: center; gap: .7rem;
  font-family: var(--display);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--teal);
  margin: 0 0 1.1rem;
}
.eyebrow i {
  width: 26px; height: 1px; background: var(--teal); opacity: .6;
  flex: none;
}

/* ---------- chrome ---------- */

/* Static film grain: a single 140px tile, no animation.
   An animated grain layer costs a full-screen repaint every frame — not worth
   it on a page that is mostly large photography. */
.grain {
  position: fixed; inset: 0;
  z-index: 200;
  pointer-events: none;
  opacity: .04;
  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='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.scroll-progress {
  position: fixed; inset: 0 0 auto 0; height: 2px; z-index: 150;
  pointer-events: none;
}
.scroll-progress i {
  display: block; height: 100%; width: 100%;
  background: linear-gradient(90deg, var(--red), var(--teal));
  transform: scaleX(0); transform-origin: 0 50%;
}

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

.site-header {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  height: var(--header-h);
  display: flex; align-items: center;
  transition: background .4s var(--ease), border-color .4s var(--ease);
  border-bottom: 1px solid transparent;
}
.site-header.is-stuck {
  background: rgba(8,8,10,.72);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border-bottom-color: var(--line-soft);
}

.header__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
}

.brand {
  display: inline-flex; align-items: center; gap: .7rem;
  text-decoration: none;
}
/* Die Wortmarke ist ein weißes PNG auf transparent, kein Text und keine
   CSS-Maske: als Maske war sie in Safari zusammen mit dem drop-shadow
   unsichtbar, und ein fehlendes Bild fällt hier wenigstens als alt-Text auf. */
.brand__logo {
  display: block;
  width: 116px; height: auto;
  /* Haarlinien über einem Foto: ohne den Schatten verschwindet der
     Schriftzug, sobald im Hero ein helleres Gesicht darunter liegt. */
  filter: drop-shadow(0 1px 5px rgba(0,0,0,.6));
  transition: opacity .3s var(--ease);
}
.brand:hover .brand__logo { opacity: .75; }
.brand__mark {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 0 0 rgba(197,50,56,.6);
  animation: pulse 2.6s var(--ease) infinite;
}
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(197,50,56,.55); }
  70%  { box-shadow: 0 0 0 12px rgba(197,50,56,0); }
  100% { box-shadow: 0 0 0 0 rgba(197,50,56,0); }
}

.nav { display: flex; align-items: center; gap: 2rem; }
.nav ul {
  display: flex; align-items: center; gap: 1.9rem;
  list-style: none; margin: 0; padding: 0;
}
.nav__link {
  position: relative;
  text-decoration: none;
  font-size: .82rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
  transition: color .3s var(--ease);
  padding-block: .35rem;
}
.nav__link::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 1px; background: var(--red);
  transform: scaleX(0); transform-origin: 100% 50%;
  transition: transform .45s var(--ease);
}
.nav__link:hover, .nav__link.is-active { color: var(--text); }
.nav__link:hover::after, .nav__link.is-active::after {
  transform: scaleX(1); transform-origin: 0 50%;
}

.burger {
  display: none;
  width: 44px; height: 44px;
  background: none; border: 0; cursor: pointer;
  padding: 0; position: relative;
}
.burger span {
  position: absolute; left: 11px; width: 22px; height: 1.5px;
  background: var(--text);
  transition: transform .4s var(--ease), opacity .25s;
}
.burger span:nth-child(1) { top: 19px; }
.burger span:nth-child(2) { top: 25px; }
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(3px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { transform: translateY(-3px) rotate(-45deg); }

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

/* Keine Radien mehr, hier und an allen sechs anderen Stellen (.tilt, .quote,
   .pq--lead, .player, .release__art, .skip-link). Die Pille war der eine Wert,
   an dem die Seite wie ein Produkt-Template aussah: im Hero stand ein
   abgerundetes rotes Oval auf einem Foto mit zwei harten Gesichtern, und die
   vier Cover — Slice-Glitch, gepinselter Titel — lagen in Kacheln mit 4 px
   Eckenradius. Plakate haben rechte Winkel.
   Rund bleiben nur die Punkte (.brand__mark, .album__stat i,
   .news__date::before) und .player__play: die Punkte sind Interpunktion, kein
   Behaelter, und der Kreis um das Dreieck ist die Play-Konvention — quadratisch
   liest er als Stop. */
.btn {
  --btn-bg: var(--red);
  position: relative;
  display: inline-flex; align-items: center; gap: .6rem;
  padding: .95rem 1.7rem;
  border: 1px solid transparent;
  font-family: var(--display);
  font-size: .82rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  overflow: hidden;
  isolation: isolate;
  transition: transform .35s var(--ease), color .35s var(--ease), border-color .35s var(--ease);
}
.btn em { font-style: normal; }
.btn svg {
  width: 15px; height: 15px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round;
  transition: transform .4s var(--ease);
}
.btn:hover svg { transform: translate(2px,-2px); }
.btn::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--btn-bg);
  transform: translateY(0);
  transition: transform .45s var(--ease);
}
.btn::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--text);
  transform: translateY(101%);
  transition: transform .45s var(--ease);
}
.btn--solid { color: #fff; }
.btn--solid:hover { color: #08080a; }
.btn--solid:hover::after { transform: translateY(0); }

.btn--ghost {
  color: var(--text);
  border-color: var(--line);
}
.btn--ghost::before { background: transparent; }
.btn--ghost::after { background: rgba(255,255,255,.08); }
.btn--ghost:hover { border-color: rgba(255,255,255,.28); }
.btn--ghost:hover::after { transform: translateY(0); }

.btn--sm  { padding: .62rem 1.15rem; font-size: .72rem; }
.btn--lg  { padding: 1.15rem 2.2rem; font-size: .9rem; }

.link-arrow {
  display: inline-flex; align-items: center; gap: .5rem;
  text-decoration: none;
  font-size: .8rem; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  padding-bottom: .35rem;
  transition: color .3s var(--ease), border-color .3s var(--ease);
}
.link-arrow svg {
  width: 14px; height: 14px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round;
  transition: transform .4s var(--ease);
}
.link-arrow:hover { color: var(--text); border-color: var(--red); }
.link-arrow:hover svg { transform: translate(3px,-3px); }

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

.hero {
  position: relative;
  min-height: 100svh;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding-block: calc(var(--header-h) + 4rem) clamp(4rem, 10vh, 7rem);
  overflow: hidden;
}
/* Der Überstand muss den Parallax-Weg tragen: das JS verschiebt um höchstens
   speed/2 der Medienhöhe, bei 0.18 also 9 % — sonst wandert die Bildkante ins
   Bild und darunter steht nur noch der Seitenhintergrund. */
.hero__media { position: absolute; inset: -12% 0; z-index: 0; }
.hero__picture { display: block; width: 100%; height: 100%; }
.hero__img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 22%;
  filter: grayscale(.25) contrast(1.08) brightness(.82);
  transform: translate3d(0, var(--parallax-y, 0px), 0);
  will-change: transform;
}
/* the clip is graded in the file itself, so only the brightness is matched
   to .hero__img here — enough that the cross-fade does not jump */
.hero__video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 45%;
  filter: contrast(1.02) brightness(.84);
  opacity: 0;
  transform: translate3d(0, var(--parallax-y, 0px), 0);
  transition: opacity 1.4s var(--ease);
  will-change: transform, opacity;
}
.hero__video.is-ready { opacity: 1; }

.hero__scrim {
  position: absolute; inset: 0;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(197,50,56,.20), transparent 55%),
    linear-gradient(to top, var(--bg) 4%, rgba(8,8,10,.78) 38%, rgba(8,8,10,.35) 72%, rgba(8,8,10,.65) 100%);
}

/* Keine Deckelung mehr: die Headline soll die Fensterbreite fuellen, und ein
   zentrierter 1560er-Block haette sie ab 1656 px Fensterbreite wieder
   eingefangen. Eyebrow, Zitat und Buttons haben eigene Maximalbreiten
   (40ch bzw. Inhalt), die ruecken dadurch nur an den Gutter statt an die Kante
   eines unsichtbaren Kastens — auf breiten Fenstern die plakativere Setzung. */
/* max-width: none, nicht bloss die 1560 weg — sonst greift wieder die 1180 aus
   .wrap und die Headline endet bei 1230 statt an der Fensterkante. */
.hero__inner { position: relative; z-index: 1; max-width: none; }

.hero__eyebrow {
  font-family: var(--display);
  font-size: .78rem; font-weight: 600;
  letter-spacing: .34em; text-transform: uppercase;
  color: var(--teal);
  margin: 0 0 1.2rem;
}

/* Randbuendig statt brav in der Spalte. Der negative Gutter zieht die Zeile an
   die Fensterkanten; die Groesse ist darauf gerechnet und nicht geraten:
   "JUNKSISTA" misst in Archivo 800 / wdth 125 genau 7,3926em, fuer 100vw also
   100/7,3926 = 13,53vw. letter-spacing muss dafuer auf 0 — die .01em davor
   verteilten sich auf neun Zeichen und schoben die Zeile ueber die Kante.
   Gedeckelt wird nicht ueber eine feste rem-Zahl, sondern ueber die Fenster-
   hoehe: ein fester Deckel haette die Zeile ab ~1780 px Breite bei fast, aber
   eben nicht ganz voller Breite stehen lassen — und knapp danebenliegend sieht
   schlechter aus als deutlich kleiner. 32svh greift nur auf flachen Fenstern,
   wo sonst Zitat und Buttons unter die Kante rutschen. */
.hero__title {
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 125%;
  font-size: max(2.2rem, min(13.53vw, 32svh));
  line-height: .84;
  letter-spacing: 0;
  text-transform: uppercase;
  margin: 0 0 1.4rem;
  margin-inline: calc(-1 * var(--gut));
  display: flex; flex-wrap: nowrap; white-space: nowrap;
}
/* Chromatischer Versatz, rot links und tuerkis rechts. Der Effekt ist belegt
   statt erfunden: das 0A-Cover zeigt genau diesen Slice-Versatz auf den beiden
   Gesichtern, und die zwei Farben der Palette sind dieselben. Er kommt zweimal
   vor — einmal beim Einlaufen (dieser Block) und danach als wiederkehrender
   Aussetzer (@keyframes chGlitch, weiter unten). Drei Punkte zum Einlaufen:
   - Er faehrt auf 0 zusammen und bleibt nicht stehen. Ein dauerhafter Farbsaum
     an 195-px-Versalien liest sich als kaputte Schriftdatei, nicht als Geste.
   - Beide Schatten muessen auch im Zielzustand als Schatten dastehen, nur mit
     Offset 0 und Alpha 0: text-shadow interpoliert ausschliesslich zwischen
     zwei Listen gleicher Laenge — gegen `none` springt der Wert hart.
   - Der Versatz steht in em, nicht in px. Die Headline laeuft von 2,2rem bis
     13,53vw; ein fester Pixelwert waere auf dem Handy ein Klotz und auf 1920
     nicht mehr zu sehen.
   Reduced Motion braucht hier keine eigene Regel: das JS zerlegt die Headline
   dann gar nicht erst (Punkt 1 in main.js), es gibt also kein .ch. */
.hero__title .ch {
  display: inline-block;
  transform: translateY(110%) rotate(6deg);
  opacity: 0;
  text-shadow: -.055em 0 0 var(--red), .055em 0 0 var(--teal);
  transition: transform 1s var(--ease), opacity .7s var(--ease),
              text-shadow .8s var(--ease);
  transition-delay: calc(var(--i) * .045s + .15s);
}
.hero__title.is-in .ch {
  transform: none; opacity: 1;
  text-shadow: 0 0 0 rgba(197,50,56,0), 0 0 0 rgba(127,212,205,0);
}

/* Der wiederkehrende Aussetzer. Einmal beim Reveal ist ein Uebergang, kein
   Glitch — aber eine dauerlaufende text-shadow-Animation ist das Gegenteil
   davon, was diese Seite verkraftet: der Schatten ist nicht compositor-faehig,
   jeder Frame malt die 195-px-Zeile neu, und ein dauerhafter Vollflaechen-
   Effekt hat den Pane hier schon einmal zum Stillstand gebracht (s. Grain).
   Vier Vorkehrungen dagegen, alle vier noetig:

   - `step-end` statt einer Kurve. Es wird nichts interpoliert, also gibt es nur
     an den Keyframe-Grenzen einen Repaint statt 60 pro Sekunde. Ein Glitch
     springt ohnehin, er blendet nicht.
   - Kurze Bursts in einem langen Zyklus. Vier Aussetzer von 45–120 ms auf
     ~9 Sekunden; dazwischen steht der Schatten auf 0 und kostet nichts.
   - Die Bursts sitzen auf unregelmaessigen Abstaenden (12,3 → 43,6 → 58,0 →
     89,2 %), sonst zaehlt das Auge den Takt mit.
   - Jeder Buchstabe hat eine eigene Dauer, nicht nur einen eigenen Versatz.
     Nur mit dem Versatz liefe ein Wanderwelle-Muster im Gleichtakt durch das
     Wort; unterschiedliche Dauern driften auseinander und richten sich nie
     wieder aus — das ist die eigentliche Quelle der Unregelmaessigkeit.

   Die Amplitude bleibt bei <= .055em, und das ist eine Grenze, keine Vorliebe:
   `.ch-wrap` hat `overflow: hidden` fuer den Vorhang, und die Wraps sind als
   Flex-Items ohne `flex: none` um ~3 px gestaucht — ein weiterer Schatten wird
   an der rechten Wrap-Kante abgeschnitten (bei .2em deutlich sichtbar). Wer
   mehr Weg will, muss an die Wrap-Geometrie, und daran haengt die gerechnete
   Randbuendigkeit der Zeile. Dass dabei pro Buchstabe unterschiedlich viel
   wegfaellt, ist kein Schaden: genau das laesst den Versatz uneinheitlich
   aussehen.

   Der Delay von 1,6 s haelt die erste Runde vom Reveal frei — der laeuft
   spaetestens nach 1,31 s aus (8 x .045s + .15s + .8s). */
@keyframes chGlitch {
  0%,    11.9% { text-shadow: 0 0 0 rgba(197,50,56,0), 0 0 0 rgba(127,212,205,0); }
  12.3%        { text-shadow: -.05em 0 0 var(--red), .038em 0 0 var(--teal); }
  13.0%        { text-shadow: .022em -.008em 0 var(--red), -.03em .008em 0 var(--teal); }
  13.6%, 43.2% { text-shadow: 0 0 0 rgba(197,50,56,0), 0 0 0 rgba(127,212,205,0); }
  /* nur ein Kanal — ein Glitch trifft nicht jedes Mal beide */
  43.6%        { text-shadow: .034em 0 0 var(--red), 0 0 0 rgba(127,212,205,0); }
  44.1%, 57.6% { text-shadow: 0 0 0 rgba(197,50,56,0), 0 0 0 rgba(127,212,205,0); }
  58.0%        { text-shadow: -.026em .01em 0 var(--red), .042em -.006em 0 var(--teal); }
  58.5%        { text-shadow: -.055em 0 0 var(--red), .012em 0 0 var(--teal); }
  59.3%, 88.8% { text-shadow: 0 0 0 rgba(197,50,56,0), 0 0 0 rgba(127,212,205,0); }
  89.2%        { text-shadow: .015em .008em 0 var(--red), -.04em 0 0 var(--teal); }
  89.9%, 100%  { text-shadow: 0 0 0 rgba(197,50,56,0), 0 0 0 rgba(127,212,205,0); }
}
.hero__title.is-in .ch {
  animation: chGlitch calc(8.6s + var(--i) * .37s) step-end
             calc(1.6s + var(--i) * 1.09s) infinite;
}
/* Ausserhalb des Viewports angehalten, wie das Hero-Video (Punkt 9 in main.js).
   Die Klasse setzt das JS und sie bedeutet "aus" statt "an": faellt das JS aus,
   laeuft der Glitch weiter statt fuer immer zu stehen. */
.hero.is-offscreen .hero__title .ch { animation-play-state: paused; }

/* wrapper keeps the slide-up clipped */
.hero__title .ch-wrap { display: inline-block; overflow: hidden; padding-block: .04em; }

/* the tagline is a review quote, not band copy — so it gets quote styling */
.hero__quote {
  margin: 0 0 2.2rem;
  max-width: 40ch;
  border-left: 2px solid var(--red);
  padding-left: clamp(1rem, 2vw, 1.5rem);
}
.hero__quote blockquote {
  margin: 0;
  font-size: clamp(1.15rem, 1rem + .8vw, 1.7rem);
  line-height: 1.35;
  color: #e8e7e6;
  font-weight: 300;
}
.hero__quote blockquote::before { content: "“"; }
.hero__quote blockquote::after  { content: "”"; }
.hero__quote figcaption {
  margin-top: .7rem;
  font-family: var(--display);
  font-size: .7rem; font-weight: 600;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--teal);
}

.hero__actions { display: flex; flex-wrap: wrap; gap: .8rem; }

.hero__scroll {
  position: absolute; right: var(--gut); bottom: clamp(4rem,10vh,7rem);
  z-index: 1;
  display: inline-flex; flex-direction: column; align-items: center; gap: .8rem;
  text-decoration: none;
  font-size: .62rem; letter-spacing: .28em; text-transform: uppercase;
  color: var(--muted);
  transition: color .3s var(--ease);
}
.hero__scroll:hover { color: var(--text); }
.hero__scroll i {
  width: 1px; height: 54px;
  background: linear-gradient(to bottom, var(--teal), transparent);
  position: relative; overflow: hidden;
}
.hero__scroll i::after {
  content: ""; position: absolute; inset: 0;
  background: var(--text);
  animation: trickle 2.2s var(--ease) infinite;
}
@keyframes trickle {
  0%   { transform: translateY(-100%); }
  60%,100% { transform: translateY(100%); }
}
@media (max-width: 720px) { .hero__scroll { display: none; } }

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

.section { padding-block: clamp(5rem, 12vh, 9rem); position: relative; }
.section__head { margin-bottom: clamp(2.5rem, 6vw, 4rem); }
.section__head--row {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 2rem; flex-wrap: wrap;
}
.section__head .display { margin-bottom: 0; }

/* ---------- album ---------- */

.album { background: linear-gradient(180deg, var(--bg), var(--bg-2) 60%, var(--bg)); }
.album__grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(2rem, 6vw, 5rem);
  align-items: start;
}
@media (max-width: 900px) { .album__grid { grid-template-columns: 1fr; } }

.album__art { position: sticky; top: calc(var(--header-h) + 3rem); }
@media (max-width: 900px) { .album__art { position: static; } }

.tilt {
  position: relative;
  overflow: hidden;
  transform-style: preserve-3d;
  transition: transform .6s var(--ease);
  box-shadow: 0 30px 80px -30px rgba(0,0,0,.9), 0 0 0 1px var(--line-soft);
  will-change: transform;
}
.tilt img { width: 100%; }
.tilt__shine {
  position: absolute; inset: 0;
  background: radial-gradient(50% 50% at var(--mx,50%) var(--my,50%), rgba(255,255,255,.16), transparent 60%);
  opacity: 0; transition: opacity .4s var(--ease);
  pointer-events: none;
}
.tilt:hover .tilt__shine { opacity: 1; }

.album__credit {
  margin-top: 1rem;
  font-size: .72rem; letter-spacing: .16em; text-transform: uppercase;
  color: #55555c;
}

.album__actions { margin-top: 2rem; }

.album__stat {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 1rem;
  margin: 0;
  font-family: var(--display);
  font-size: .78rem; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--muted);
}
.album__stat i { width: 4px; height: 4px; border-radius: 50%; background: var(--red); }

.feats {
  list-style: none; margin: 2rem 0 0; padding: 0;
  border-top: 1px solid var(--line-soft);
}
.feats li {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  padding: .85rem 0;
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--display);
  font-weight: 600; font-stretch: 108%;
  font-size: clamp(1rem, .9rem + .5vw, 1.3rem);
  text-transform: uppercase; letter-spacing: .01em;
  transition: color .3s var(--ease), padding-left .4s var(--ease);
}
.feats li:hover { color: var(--teal); padding-left: .6rem; }
.feats span {
  font-family: var(--body);
  font-size: .78rem; font-weight: 400;
  letter-spacing: .12em;
  color: var(--muted);
  text-transform: uppercase;
}

/* tracklist */
.tracklist { margin-top: 2.5rem; border-top: 1px solid var(--line); }
.tracklist summary {
  list-style: none;
  cursor: pointer;
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.1rem 0;
  font-family: var(--display);
  font-size: .8rem; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted);
  transition: color .3s var(--ease);
}
.tracklist summary::-webkit-details-marker { display: none; }
.tracklist summary:hover { color: var(--text); }
.tracklist summary i {
  width: 12px; height: 12px; position: relative; flex: none;
}
.tracklist summary i::before,
.tracklist summary i::after {
  content: ""; position: absolute; inset: 50% 0 auto 0; height: 1.5px;
  background: currentColor;
  transition: transform .4s var(--ease);
}
.tracklist summary i::after { transform: rotate(90deg); }
.tracklist[open] summary i::after { transform: rotate(0deg); }
.tracklist[open] summary { color: var(--text); }

.tracklist ol {
  list-style: none; margin: 0; padding: 0 0 1.5rem;
  animation: fadeDown .55s var(--ease) both;
}
@keyframes fadeDown {
  from { opacity: 0; transform: translateY(-8px); }
}
.tracklist li {
  display: flex; gap: 1rem; align-items: baseline;
  padding: .6rem 0;
  border-bottom: 1px solid var(--line-soft);
  transition: color .3s var(--ease);
}
.tracklist li:hover { color: var(--teal); }
.t__no {
  font-family: var(--display); font-size: .72rem; font-weight: 700;
  letter-spacing: .1em; color: var(--red); flex: none; width: 2ch;
}
.t__name { font-weight: 400; }
.t__name em {
  display: block;
  font-style: normal;
  font-size: .78rem; letter-spacing: .06em;
  color: var(--muted);
}

/* ---------- news ---------- */

/* Der Abschnitt steht zwischen dem auf --bg auslaufenden .album und der
   --bg-2-Flaeche von .press — nach oben liegt also gleiche Farbe auf gleicher
   Farbe, und zwischen der letzten Albumzeile und der Eyebrow stehen 240 px
   Leere, in der nichts sagt, dass hier ein neuer Abschnitt anfaengt. Die Regel
   verlangt fuer einen abgesetzten Abschnitt eins von beidem: weiche Kante oder
   Haarlinie. Hier ist es die Haarlinie, ueber die volle Fensterbreite wie bei
   .press, plus ein rotes Stueck an ihrem Anfang — dieselbe Farbe, die unten den
   neuesten Eintrag markiert, und die einzige Stelle, an der die Linie
   ueberhaupt gesehen wird: sie sitzt buendig unter der Eyebrow-Spalte. */
.news { border-top: 1px solid var(--line); }
.news::before {
  content: ""; position: absolute; top: -1px; height: 1px;
  /* buendig mit der Textspalte, nicht am Fensterrand: die Spalte wird auf
     dieser Seite genau einmal verlassen, beim Duo-Foto. Das ist die linke
     Innenkante des .wrap, ohne dass hier ein Element im .wrap noetig waere. */
  left: max(var(--gut), (100% - var(--wrap)) / 2 + var(--gut));
  width: clamp(72px, 12vw, 132px);
  background: var(--red);
}

.news__list { list-style: none; margin: 0; padding: 0; }

.news__item {
  display: grid;
  grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
  gap: clamp(.6rem, 4vw, 3rem);
  padding-block: clamp(1.8rem, 4vw, 2.6rem);
  border-top: 1px solid var(--line);
}
.news__item:first-child { padding-top: 0; border-top: 0; }

/* Der einzige Schmuck des Abschnitts, und er sagt etwas: rot markiert den
   neuesten Eintrag. Bei nur einem Eintrag traegt der Punkt die Zeile, damit
   die Liste nicht wie ein leerer Rest aussieht. */
.news__date {
  display: flex; align-items: baseline; gap: .7rem;
  margin: 0;
  font-family: var(--display);
  font-size: .78rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted);
}
.news__date::before {
  content: ""; flex: none;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--line);
  transform: translateY(-1px);
}
.news__item:first-child .news__date { color: var(--text); }
.news__item:first-child .news__date::before { background: var(--red); }

.news__title {
  margin: 0 0 .6rem;
  font-family: var(--display);
  font-weight: 700; font-stretch: 106%;
  font-size: clamp(1.25rem, 1.05rem + .9vw, 1.9rem);
  line-height: 1.15;
  letter-spacing: -.005em;
}
.news__body p { max-width: 54ch; color: #c9c8cc; }

@media (max-width: 720px) {
  .news__item { grid-template-columns: 1fr; }
  .news__date { margin-bottom: .9rem; }
}

/* ---------- press ---------- */

.press { background: var(--bg-2); border-block: 1px solid var(--line-soft); }

.press__list {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: clamp(1.8rem, 4vw, 3.2rem);
}

.pq { position: relative; padding-top: 1.6rem; border-top: 1px solid var(--line); }
/* the opening quote mark doubles as the section's only ornament */
.pq::before {
  content: "“";
  position: absolute; top: -.35em; left: -.06em;
  font-family: var(--display); font-weight: 800;
  font-size: 4rem; line-height: 1;
  color: var(--red);
  opacity: .35;
  pointer-events: none;
}
/* Die erste echte Farbflaeche der Seite. Rot kam bis hierhin nur als Button,
   Punkt und Haarlinie vor — auf einer Seite, deren Palette aus einem roten
   Cover stammt. Weiss auf --red liegt bei 7,8:1, der Text bleibt also auch in
   .72rem lesbar; die Haarlinie und das ::before-Ornament weichen der Flaeche. */
.pq--lead {
  grid-column: 1 / -1;
  border-top: 0;
  background: linear-gradient(152deg, var(--red), #94232a);
  /* Die untere Grenze oben traegt das 4rem-Anfuehrungszeichen: bei 2.4rem sass
     es auf der ersten Zeile auf, sichtbar erst auf dem Handy, wo 5vw den
     Abstand nicht mehr aufzieht. */
  padding: clamp(3.2rem, 5vw, 3.8rem) clamp(1.6rem, 4vw, 3.2rem) clamp(1.8rem, 4vw, 2.8rem);
  box-shadow: 0 30px 70px -40px rgba(197,50,56,.8);
}
.pq--lead::before {
  top: .12em; left: clamp(1.4rem, 3.6vw, 2.9rem);
  color: #fff;
  opacity: .28;
}
/* Kein <mark>-Gelb: nur die Farbe, kein Kasten */
.pq--lead mark { background: none; color: var(--teal); }
.pq--lead .pq__src a { color: #fff; }
.pq--lead .pq__src a:hover { border-bottom-color: #fff; }
.pq--lead .pq__src span { color: rgba(255,255,255,.78); }

/* Die dritte Farbflaeche der Seite und die erste in Tuerkis. Rot hatte mit
   .pq--lead und .video zwei, Tuerkis war ausschliesslich Text, Eyebrow und
   Haarlinie — auf einer Palette, die zur Haelfte aus Tuerkis besteht.
   Sie steht bewusst in derselben Sektion wie die rote Karte: Tuerkis neben Rot
   ist die Kombination vom 0A-Cover, also belegt und nicht erfunden. Und sie
   steht in der Mitte der Dreierreihe, nicht am Anfang — links unter der roten
   Karte laesse sie sich als deren Fortsetzung lesen.
   Kontrast (Gegenstueck zur --red-Tabelle in CLAUDE.md):
     #08080a auf --teal   11,6 : 1   alles, auch die .72rem-Quellenzeile
     #fff    auf --teal    1,5 : 1   nichts
   Das Vorzeichen dreht sich hier also: auf Rot ist Weiss die einzige sichere
   Farbe, auf Tuerkis ist es Schwarz. */
.pq--teal {
  border-top: 0;
  background: var(--teal);
  color: #08080a;
  padding: clamp(2.8rem, 4vw, 3.2rem) clamp(1.4rem, 3vw, 2rem) clamp(1.6rem, 3vw, 2rem);
}
/* Das Anfuehrungszeichen haengt bei .pq ausserhalb des Kastens (top: -.35em) —
   auf einer Flaeche muss es hinein, wie bei .pq--lead. */
.pq--teal::before {
  top: .1em; left: clamp(1.2rem, 2.6vw, 1.7rem);
  color: #08080a;
  opacity: .32;
}
.pq--teal .pq__src a { color: #08080a; }
.pq--teal .pq__src a:hover { border-bottom-color: #08080a; }
/* 6,25 : 1 auf --teal — traegt die 11,5-px-Zeile noch. */
.pq--teal .pq__src span { color: rgba(8,8,10,.72); }

.pq blockquote {
  margin: 0 0 1.2rem;
  font-family: var(--display);
  font-weight: 500; font-stretch: 106%;
  font-size: clamp(1.1rem, 1rem + .55vw, 1.6rem);
  line-height: 1.28;
  letter-spacing: -.005em;
  color: var(--text);
}
.pq--lead blockquote {
  font-weight: 700;
  font-size: clamp(1.5rem, 1.1rem + 2.4vw, 3.1rem);
  line-height: 1.1;
  max-width: 22ch;
}
/* Muss hier unten stehen, nicht oben beim Rest von .pq--teal: `.pq blockquote`
   setzt color auf --text und hat mit (0,1,1) dieselbe Spezifitaet wie
   `.pq--teal blockquote` — bei Gleichstand entscheidet die Reihenfolge, und
   oben stehend verliert die Regel. Genau deshalb steht .pq--lead blockquote
   ebenfalls hier. */
.pq--teal blockquote { color: #08080a; }
.pq blockquote em { font-style: italic; }

.pq__src { display: flex; flex-direction: column; gap: .2rem; margin: 0; }
.pq__src a {
  align-self: flex-start;
  font-family: var(--display);
  font-size: .78rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  text-decoration: none;
  color: var(--teal);
  border-bottom: 1px solid transparent;
  transition: border-color .35s var(--ease);
}
.pq__src a:hover { border-bottom-color: var(--teal); }
.pq__src span {
  font-size: .72rem; letter-spacing: .14em; text-transform: uppercase;
  color: #55555c;
}

.section__quote {
  margin: 1.2rem 0 0;
  display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap;
  font-size: clamp(1rem, .95rem + .35vw, 1.2rem);
  color: #c9c8cc;
}
.section__quote a {
  font-family: var(--display);
  font-size: .72rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  text-decoration: none;
  color: var(--teal);
  border-bottom: 1px solid transparent;
  transition: border-color .35s var(--ease);
}
.section__quote a:hover { border-bottom-color: var(--teal); }

/* ---------- video ---------- */

/* Die zweite Farbflaeche. Zwischen der Press-Karte und dem Footer lief die
   Seite vorher drei Bildschirmhoehen ohne Farbe durch. Derselbe Verlauf wie
   .pq--lead, damit die beiden roten Flaechen als dieselbe Geste lesen.
   Ein harter Farbwechsel an den Kanten ist hier richtig — die Regel gegen
   nackte Kanten gilt fuer den Sechs-Stufen-Sprung von --bg-2 auf --bg, der
   sich als Rechteck im Hintergrund liest. Rot auf Schwarz ist unmissverstaend-
   lich Absicht.
   Kontrast auf --red (Tabelle in CLAUDE.md): Weiss 7,8:1, Tuerkis nur 4,55:1 —
   also fuer Fliesstext ab 16 px und Headlines brauchbar, aber nicht fuer die
   .72rem-Eyebrow und die .72rem-Quelle darunter. Beide stehen deshalb hier
   weiss, nicht tuerkis. */
.video {
  background: linear-gradient(152deg, var(--red), #94232a);
}
.video .eyebrow { color: #fff; }
.video .eyebrow i { background: #fff; }
.video .section__quote { color: rgba(255,255,255,.92); }
.video .section__quote a { color: #fff; }
.video .section__quote a:hover { border-bottom-color: #fff; }
/* #55555c waere auf Rot nicht mehr zu sehen */
.video .player__note { color: rgba(255,255,255,.82); }
/* Der rote Knopf verschwand auf dem rosa Artwork fast — und ein rotes Element
   auf roter Flaeche ist ohnehin kein Signal mehr. Weiss mit rotem Dreieck. */
.video .player__play { background: #fff; }
.video .player__play svg { fill: var(--red); }
.video .player__facade:hover .player__play { background: var(--teal); }
/* Der schwarze Rahmen des Clips (s. .player__facade) liest sich auf Rot als
   gesetzter Rahmen — auf Schwarz sah er aus wie ein kaputtes Thumbnail. */
.video .player {
  box-shadow: 0 40px 90px -40px rgba(0,0,0,.65), 0 0 0 1px rgba(0,0,0,.35);
}

.player {
  position: relative;
  overflow: hidden;
  box-shadow: 0 40px 90px -40px rgba(0,0,0,.95), 0 0 0 1px var(--line-soft);
}
.player__facade {
  display: block; width: 100%; position: relative;
  padding: 0; border: 0; background: #000; cursor: pointer;
  aspect-ratio: 16 / 9;
}
.player__facade img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .8s var(--ease), filter .6s var(--ease);
}
.player__facade:hover img { transform: scale(1.04); filter: brightness(.82); }
/* Verlauf statt brightness-Filter auf dem ganzen Bild: das Artwork bleibt
   ungedaempft, abgedunkelt wird nur die untere Kante, wo .player__meta steht.
   ::after ist die letzte Kindbox und laege sonst ueber Button und Text — beide
   bekommen deshalb z-index: 1. */
.player__facade::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(to top, rgba(8,8,10,.88), rgba(8,8,10,.2) 40%, transparent 68%);
  pointer-events: none;
}

.player__play {
  z-index: 1;
  position: absolute; inset: 0; margin: auto;
  width: clamp(64px, 8vw, 96px); height: clamp(64px, 8vw, 96px);
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 0 0 rgba(197,50,56,.5);
  transition: transform .45s var(--ease), background .35s var(--ease);
}
.player__play svg { width: 42%; height: 42%; fill: #fff; margin-left: 8%; }
.player__facade:hover .player__play { transform: scale(1.12); background: var(--red-hot); }

.player__meta {
  position: absolute; z-index: 1;
  left: clamp(1rem,3vw,2rem); bottom: clamp(1rem,3vw,2rem);
  display: flex; flex-direction: column; align-items: flex-start; gap: .2rem;
  text-align: left;
}
.player__meta b {
  font-family: var(--display); font-weight: 700; font-stretch: 112%;
  font-size: clamp(1rem, .9rem + .6vw, 1.5rem);
  text-transform: uppercase; letter-spacing: .01em; color: var(--text);
}
.player__meta i {
  font-style: normal; font-size: .72rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--teal);
}
.player__note {
  margin-top: 1rem; font-size: .78rem; color: #55555c; letter-spacing: .04em;
}
.player iframe { width: 100%; aspect-ratio: 16/9; border: 0; display: block; }

/* ---------- quotes ---------- */

/* Kein harter Sprung an den Kanten. Von #0e0e11 auf #08080a sind es nur sechs
   Stufen Grau, aber die Kante läuft über die volle Breite: beim Scrollen liest
   sie sich als Rechteck, das plötzlich aufpoppt — sichtbar wurde das direkt
   unter dem Duo-Foto, wo diese Kante 116 px tiefer sitzt. .album blendet dafür
   schon immer auf --bg aus, .press rahmt seine Kanten mit Haarlinien; .quotes
   hatte weder das eine noch das andere. */
.quotes {
  background: linear-gradient(180deg,
    var(--bg), var(--bg-2) 12%, var(--bg-2) 88%, var(--bg));
}
/* Die Karten greifen in die Unterkante des Fotos, statt darunter zu stehen: die
   beiden verzahnen sich, und die Zuordnung ist ohne Avatar lesbar — Diana links
   im Bild (gespiegelt, s. .portrait img) wie in der Karte, Boog rechts. z-index,
   weil .portrait als spaeterer Nachbar sonst obenauf liegt. */
.quotes__grid {
  display: grid; gap: clamp(1.5rem, 3vw, 2.5rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  position: relative;
  z-index: 1;
  margin-top: calc(-1 * clamp(1.5rem, 4vw, 3.5rem));
}
.quote {
  margin: 0;
  padding: clamp(1.6rem, 3vw, 2.6rem);
  background: var(--surface);
  border: 1px solid var(--line-soft);
  position: relative;
  overflow: hidden;
  transition: border-color .45s var(--ease), transform .45s var(--ease);
}
.quote::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--red);
  transform: scaleY(0); transform-origin: 0 0;
  transition: transform .7s var(--ease);
}
.quote--alt::before { background: var(--teal); }
.quote.is-in::before { transform: scaleY(1); }
.quote:hover { border-color: rgba(255,255,255,.16); transform: translateY(-4px); }

/* the statements run long — smaller and airier than a pull quote, or the two
   cards read as a wall of text */
.quote blockquote {
  margin: 0 0 1.6rem;
  font-size: clamp(.96rem, .92rem + .28vw, 1.08rem);
  line-height: 1.72;
  color: #e2e1e0;
}
.quote blockquote::before { content: "“"; color: var(--red); font-size: 2em; line-height: 0; vertical-align: -.35em; margin-right: .1em; }
.quote--alt blockquote::before { color: var(--teal); }

.quote figcaption { display: flex; flex-direction: column; gap: .1rem; }
.quote figcaption b {
  font-family: var(--display); font-weight: 700; font-stretch: 112%;
  text-transform: uppercase; letter-spacing: .1em; font-size: .95rem;
}
.quote figcaption span {
  font-size: .72rem; letter-spacing: .18em; text-transform: uppercase; color: var(--muted);
}

/* Volle Fensterbreite, ausserhalb des .wrap. Das Foto ist die einzige Stelle,
   an der die Seite ihre 1180-px-Spalte verlaesst — genau deshalb sitzt es hier
   und nicht im Rahmen. Kein border-radius: eine gerundete Ecke am Fensterrand
   sieht nach vergessener Kachel aus. */
.portrait {
  margin-top: clamp(2.5rem, 6vw, 4.5rem);
  overflow: hidden;
  max-height: 62vh;
}
/* Das Foto füllt seinen Rahmen exakt aus — ohne den Zoom liefe es beim Scrollen
   an der Unterkante heraus und legte einen schwarzen Streifen frei. 8 % nach
   jeder Seite decken den Weg von speed/2 = 6 % ab.
   Negatives scaleX spiegelt das Bild: Diana steht damit links, wie in der ersten
   Karte darunter. Nur hier gespiegelt, nicht im Hero — dort traegt dasselbe Foto
   keine Zuordnung. */
.portrait img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(.15) contrast(1.05);
  transform: translate3d(0, var(--parallax-y, 0px), 0) scale(-1.16, 1.16);
  will-change: transform;
}

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

/* Vier gleich grosse Quadrate im Raster waren der generischste Block der Seite.
   Die erste Spalte ist breiter: 0A ist das aktuelle Album, um das die ganze
   Seite gebaut ist, die drei anderen sind Singles und eine EP. Weil
   .release__art aspect-ratio: 1 hat, wird die breitere Spalte automatisch zur
   groesseren Flaeche, und die Bildunterschrift der 0A-Kachel sitzt tiefer als
   die der anderen drei — die Reihe bekommt damit eine Kante, ohne dass ein
   zweiter Effekt noetig waere.
   Kein auto-fill mehr: das liess bei 1180 px eine fuenfte Spur entstehen, die
   leer blieb. Die Spaltenzahl ist jetzt an jedem Breakpoint gesetzt. */
.releases {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(1rem, 2.2vw, 1.8rem);
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  align-items: start;
}
@media (max-width: 900px) { .releases { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .releases { grid-template-columns: 1fr; } }
.release a {
  display: block; text-decoration: none;
  transition: transform .5s var(--ease);
}
.release a:hover { transform: translateY(-6px); }
.release__art {
  display: block; position: relative;
  overflow: hidden;
  aspect-ratio: 1;
  box-shadow: 0 18px 44px -22px rgba(0,0,0,.9), 0 0 0 1px var(--line-soft);
}
.release__art::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 40%, rgba(197,50,56,.35));
  opacity: 0; transition: opacity .45s var(--ease);
}
.release a:hover .release__art::after { opacity: 1; }
/* Kein saturate/brightness im Ruhezustand. Die vier Cover sind die einzige
   Stelle, an der die Band selbst laut wird — pinkes Oxytocin, blauer
   Mittelfinger —, und ein Filter drehte sie auf einer ohnehin grauen Seite
   leiser. Das Hover-Signal traegt jetzt allein scale + .release__art::after. */
.release__art img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .7s var(--ease);
}
.release a:hover .release__art img { transform: scale(1.06); }

.release__meta { display: flex; flex-direction: column; gap: .15rem; margin-top: .9rem; }
.release__meta b {
  font-family: var(--display); font-weight: 700; font-stretch: 108%;
  font-size: .95rem; text-transform: uppercase; letter-spacing: .03em;
}
/* Derselbe chromatische Versatz wie beim Hero-Reveal (s. .hero__title .ch),
   hier aber in px und ohne Transition. Drei Abweichungen mit Grund:
   - px statt em, weil .055em bei .95rem ein Achtel Pixel waere.
   - Er schnappt, statt einzublenden. Ein Glitch, der ueber .35s weich
     aufzieht, ist kein Glitch mehr.
   - 1 px, nicht 2. Der Versatz muss im Verhaeltnis zur Schrifthoehe stehen,
     nicht absolut: 2 px auf 15,2 px sind 13 %, im Hero sind es 5,5 % — bei
     einem zweizeichigen Titel wie "0A" las sich das als Schmier statt als
     Schnitt. 1 px liegt bei 6,6 % und bleibt trotzdem eine harte Kante.
   Er ersetzt den Farbwechsel auf Tuerkis, der hier vorher stand: ein tuerkiser
   Schatten neben tuerkiser Schrift ist unsichtbar, und der Titel bleibt weiss,
   damit beide Kanten sichtbar sind. Das Hover-Signal traegt ohnehin schon
   scale + .release__art::after. */
.release a:hover .release__meta b {
  text-shadow: -1px 0 0 var(--red), 1px 0 0 var(--teal);
}
.release__meta i {
  font-style: normal; font-size: .72rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted);
}

/* volle Discography unter den Kacheln
   Jahr und Label sind beide Metadaten und stehen deshalb auf demselben Grau;
   getrennt werden sie durch Position, Groesse und Laufweite, nicht durch Farbe.
   Ein drittes, dunkleres Grau waere hier naheliegend und faellt durch: #6e6e77
   auf --bg sind 3,96:1, und die Label-Zeile laeuft auf 10,9 px. */
.discog {
  margin-top: clamp(3.5rem, 8vw, 6rem);
  display: grid;
  gap: clamp(2.5rem, 5vw, 3.5rem);
}
.discog__title {
  margin: 0;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--display);
  /* 62 % wie .display — bei 12,8 px sieht man den Unterschied zu 74 % kaum,
     aber die Seite soll nicht zwei verschiedene schmale Werte kennen. */
  font-size: .8rem; font-weight: 700; font-stretch: 62%;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted);
}
.discog__list { list-style: none; margin: 0; padding: 0; }
.discog__list li {
  display: grid;
  grid-template-columns: 4.25rem minmax(0, 1fr) auto;
  gap: 1rem; align-items: baseline;
  padding: .8rem 0;
  border-bottom: 1px solid var(--line-soft);
}
.d__year {
  font-family: var(--display); font-weight: 700;
  font-size: .78rem; letter-spacing: .1em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.d__title { font-size: 1.12rem; }
.d__title em {
  font-style: normal; font-size: .8rem; color: var(--muted);
  white-space: nowrap;
}
.d__label {
  font-family: var(--display);
  font-size: .68rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted);
  text-align: right;
}

/* Die 17 EPs und Singles laufen zweispaltig — einspaltig waeren sie eine
   Kolonne, die laenger ist als der ganze Rest des Abschnitts. Multicol statt
   Grid, weil die Spalten sich dann selbst fuellen und die Reihenfolge dabei
   senkrecht bleibt: erst die Spalte zu Ende, dann die naechste. */
.discog__list--dense { columns: 2; column-gap: clamp(2rem, 5vw, 4rem); }
.discog__list--dense li { break-inside: avoid; padding: .62rem 0; }
.discog__list--dense .d__title { font-size: .98rem; }
@media (max-width: 900px) { .discog__list--dense { columns: 1; } }
@media (max-width: 520px) {
  .discog__list li { grid-template-columns: 3.5rem minmax(0, 1fr); row-gap: .2rem; }
  .d__label { grid-column: 2; text-align: left; }
}

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

/* Eigene Fläche statt derselben Schwärze wie die Discography darüber — sonst
   läuft der Kontaktblock als Anhängsel der Releases mit. bg-2 wie bei Press und
   Quotes, dazu der rote Schimmer von oben, der vorher die Newsletter-Bande vom
   Rest abgesetzt hat. */
.footer {
  background:
    radial-gradient(72% 130% at 50% 0%, rgba(197,50,56,.13), transparent 62%),
    linear-gradient(180deg, var(--bg), var(--bg-2) 16%);
  border-top: 1px solid var(--line);
  padding-block: clamp(4.5rem, 10vh, 7.5rem) 2rem;
}
.footer__top {
  display: flex; justify-content: space-between; gap: clamp(2rem,6vw,5rem);
  flex-wrap: wrap;
  padding-bottom: clamp(2.5rem, 6vw, 4rem);
  border-bottom: 1px solid var(--line-soft);
}
.footer__mail {
  display: inline-block;
  font-family: var(--display); font-weight: 800; font-stretch: 108%;
  font-size: clamp(1.4rem, 1rem + 2vw, 2.6rem);
  letter-spacing: -.01em;
  text-decoration: none;
  background-image: linear-gradient(var(--red), var(--red));
  background-size: 0% 1px; background-position: 0 100%; background-repeat: no-repeat;
  transition: background-size .5s var(--ease);
}
.footer__mail:hover { background-size: 100% 1px; }
.footer__note { margin-top: 1rem; max-width: 42ch; color: var(--muted); font-size: .92rem; }

.socials { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .1rem; }
.socials a {
  display: inline-flex; align-items: center; gap: .8rem;
  text-decoration: none;
  padding: .5rem 0;
  font-family: var(--display); font-weight: 600; font-stretch: 106%;
  font-size: .85rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted);
  transition: color .3s var(--ease), transform .4s var(--ease);
}
.socials a:hover { color: var(--text); transform: translateX(5px); }
.socials svg {
  width: 18px; height: 18px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.6;
}
.socials li:first-child svg { fill: currentColor; stroke: none; }
.socials li:nth-child(4) svg, .socials li:nth-child(5) svg { fill: currentColor; stroke: none; }
.socials li:nth-child(3) svg path { fill: currentColor; stroke: none; }

/* Die Schriftgroesse ist auf die viewBox gerechnet, nicht geraten: "JUNKSISTA"
   misst in Archivo 800 / wdth 125 bei 100 px genau 739,3 px, fuer 1000 Einheiten
   also 100 x 1000/739,3 = 135,3. textLength korrigiert den Rest ueber die
   Laufweite (lengthAdjust="spacing" — keine verzerrten Glyphen). */
.footer__mark {
  margin-top: clamp(2.5rem, 7vw, 5rem);
  margin-bottom: clamp(1.2rem, 3vw, 2.2rem);
}
.footer__mark svg { display: block; width: 100%; height: auto; overflow: visible; }
.footer__mark text {
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 125%;
  font-size: 135.3px;
  fill: var(--text);
}

.footer__bottom {
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  padding-top: 1.8rem;
  font-size: .74rem; letter-spacing: .1em; text-transform: uppercase;
  color: #4e4e56;
}
.footer__bottom p { margin: 0; }
.footer__bottom a { text-decoration: none; transition: color .3s var(--ease); }
.footer__bottom a:hover { color: var(--text); }

/* Impressum und Datenschutz laufen heller als der Rest der Zeile. #4e4e56 auf
   --bg sind 2,43:1 — fuer den Copyright-Vermerk egal, aber der Impressum-Link
   muss leicht erkennbar sein, und das ist bei 11,8 px die Farbe, nicht die
   Position. --muted liegt bei 7,34:1. */
.footer__legal a { color: var(--muted); }

/* =========================================================
   REVEAL ANIMATIONS
   ========================================================= */

[data-reveal] {
  opacity: 0;
  transform: translate3d(var(--rv-x, 0), var(--rv-y, 28px), 0);
  transition:
    opacity var(--dur) var(--ease),
    transform var(--dur) var(--ease),
    filter var(--dur) var(--ease);
  transition-delay: var(--rv-d, 0s);
}
[data-reveal="up"]    { --rv-y: 30px; }
[data-reveal="left"]  { --rv-x: -36px; --rv-y: 0; }
[data-reveal="right"] { --rv-x: 36px;  --rv-y: 0; }
[data-reveal="zoom"]  { --rv-y: 0; transform: scale(.94); }
[data-reveal].is-in {
  opacity: 1;
  transform: none;
  filter: none;
}

/* The mask curtain sits on the child, never on the observed element itself:
   an element clipped to zero height has an empty intersection rect, the
   observer never fires for it and the reveal never arrives. */
[data-reveal="mask"] { --rv-y: 40px; }
[data-reveal="mask"] > :first-child {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.1s var(--ease);
  transition-delay: var(--rv-d, 0s);
}
[data-reveal="mask"].is-in > :first-child { clip-path: inset(0 0 0 0); }

/* staggered children */
[data-reveal="stagger"] { opacity: 1; transform: none; }
[data-reveal="stagger"] > * {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity .75s var(--ease), transform .75s var(--ease);
  transition-delay: calc(var(--i, 0) * 90ms);
}
[data-reveal="stagger"].is-in > * { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  [data-reveal], [data-reveal="stagger"] > *, [data-reveal="mask"] > :first-child,
  .hero__title .ch {
    opacity: 1 !important; transform: none !important; clip-path: none !important;
  }
  .grain { display: none; }
  .hero__video { display: none; }
}

/* =========================================================
   RESPONSIVE
   ========================================================= */

@media (max-width: 860px) {
  .burger { display: block; }
  .nav {
    position: fixed; inset: 0;
    flex-direction: column; justify-content: center;
    gap: 2.5rem;
    background: rgba(8,8,10,.97);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    opacity: 0; visibility: hidden;
    transition: opacity .45s var(--ease), visibility .45s;
  }
  .nav.is-open { opacity: 1; visibility: visible; }
  .brand { position: relative; z-index: 2; }
  .brand__logo { width: 104px; }
  .nav ul { flex-direction: column; gap: 1.4rem; text-align: center; }
  .nav__link { font-size: 1.35rem; letter-spacing: .18em; }
  .nav__link::after { display: none; }
  .burger { z-index: 2; }
  body.nav-open { overflow: hidden; }
}

@media (max-width: 900px) {
  .hero__img { object-position: 50% 34%; filter: grayscale(0) contrast(1.05) brightness(.9); }
  .hero__video { display: none; }
  .hero__scrim {
    background:
      radial-gradient(120% 70% at 50% 0%, rgba(197,50,56,.18), transparent 55%),
      linear-gradient(to top, var(--bg) 2%, rgba(8,8,10,.86) 34%, rgba(8,8,10,.30) 70%, rgba(8,8,10,.55) 100%);
  }
}

@media (max-width: 560px) {
  .hero__actions .btn { width: 100%; justify-content: center; }
  .footer__bottom { flex-direction: column; gap: .5rem; }
}
