/*
================================================================
  MANIFEST – css/components.css
  ┌──────────────────────┬─────────────────────────────────────────────────────────┐
  │ Block-ID             │ Beschreibung                                            │
  ├──────────────────────┼─────────────────────────────────────────────────────────┤
  │ header-nav           │ Sticky Header, Logo, Nav, Burger                        │
  │ mode-toggle          │ Hell/Dunkel-Umschalter im Header                        │
  │ page-head            │ Kompakter Seitenkopf für Unterseiten                    │
  │ media-gallery        │ Masonry-ähnliches Galerie-Grid                          │
  │ form-kontakt         │ Zweispaltiges Kontaktformular                           │
  │ form-field-status    │ Feld-Feedback grün/rot mit SVG-Icons                    │
  │ legal-page           │ Lesefreundliche Typo für Impressum/Datenschutz          │
  │ lightbox             │ Bild-Großansicht via <dialog>, inkl. Scroll-Lock        │
  │ back-to-top          │ Zurück-nach-oben-Button ab 400px                        │
  │ first-section-spacing│ Abstand Header → erster Abschnitt, gestaffelt           │
  │ legal-footer         │ Footer                                                  │
  │ enter-screen         │ Ganzseitiger JÆCK-Einstieg (Startseite) + Rechtslinks   │
  │ pinboard             │ Pinnwand: schwebende Karten, Handy gestapelt/Desktop verstreut │
  │ pinboard-transition  │ Reinzoom-Übergang JÆCK ↔ Pinnwand (reduced-motion-safe) │
  │ quote-overlay        │ Wiederverw. Vollbild-Overlay: Bild, Scrim, Text, Player │
  │ ebenen-einheitlich   │ Einheitliches Einblenden aller Vollbild-Ebenen          │
  │ scroll-lock          │ Ein Scroll-Lock fuer alle Vollbild-Ebenen               │
  │ ui-close             │ EIN Schliess-x fuer alle Ebenen und Seiten              │
  │ quote-cover-btn      │ Cover als fokussierbarer Button (Tastatur)              │
  │ event-past           │ Kompakte Karten fuer "Bisher gespielt"                  │
  │ events-more          │ Einklappen aelterer Auftritte ("Bisher gespielt")       │
  │ ebenen-scrollleisten │ Leiste der pausierten Ebene ausblenden                  │
  │ type-choice          │ Anfrage-Typ als drei sichtbare Auswahlkarten            │
  │ quote-events         │ Kommende Events in der Ticket-Karte                     │
  │ section-cta          │ Einheitlicher Verweis am Ende eines Blocks              │
  │ nav-mobil            │ Kebab-Menü: gleiche Einträge, Kontakt als Rahmen        │
  └──────────────────────┴─────────────────────────────────────────────────────────┘
================================================================
*/

/* ### BLOCK: header-nav ### */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  height: var(--nav-height);
  /* Aus bg-base abgeleitet statt hartcodiert – folgt jedem Theme/Modus */
  background: color-mix(in srgb, var(--c-bg-base) 85%, transparent);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--c-border-subtle);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
  gap: var(--space-4);
}

.site-header__logo {
  font-family: var(--font-heading);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  /* Champagner statt normaler Textfarbe: Das Logo ist die Marke,
     kein Menuepunkt. Token wechselt in beiden Modi automatisch. */
  color: var(--c-accent-text);
  text-decoration: none;
  letter-spacing: var(--tracking-tight);
  flex-shrink: 0;
}

/* Burger */
.nav-toggle {
  touch-action: manipulation; /* iOS: Doppeltipp-Zoom aus */
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  padding: var(--space-3);
  background: none;
  border: none;
  cursor: pointer;
  color: var(--c-text-primary);
  border-radius: var(--radius-md);
}

.nav-toggle span {
  display: block;
  width: 22px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
  transform-origin: center;
  transition:
    transform var(--dur-normal) var(--ease),
    opacity var(--dur-fast) var(--ease),
    width var(--dur-normal) var(--ease);
}

.nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
  width: 0;
}
.nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* Nav Mobile: kompaktes Dropdown-Panel oben rechts (nicht mehr Vollbild) */
.primary-nav {
  position: fixed;
  top: calc(var(--nav-height) - var(--space-2));
  right: var(--space-4);
  left: auto;
  bottom: auto;
  width: min(220px, calc(100vw - 2 * var(--space-4)));
  background: var(--c-bg-elevated);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
  display: flex;
  align-items: flex-start;
  padding: var(--space-2);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transform-origin: top right;
  transition:
    opacity var(--dur-normal) var(--ease),
    transform var(--dur-normal) var(--ease),
    visibility 0s var(--dur-normal);
  z-index: 101;   /* über dem abdunkelnden Layer (100) */
}

/* .nav-backdrop entfernt (Runde 6): kein Markup dafuer, in keiner
   HTML-Datei und in keinem Skript. */
.primary-nav.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition:
    opacity var(--dur-normal) var(--ease),
    transform var(--dur-normal) var(--ease),
    visibility 0s 0s;
}

/* Header deckend, solange das Mobil-Menü offen ist (kein Durchscheinen) */
.site-header:has(.nav-toggle[aria-expanded="true"]) {
  background: var(--c-bg-base);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.primary-nav__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
}

.primary-nav__link {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: var(--text-lg);
  font-weight: var(--weight-medium);
  color: var(--c-text-secondary);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

/* Gilt NUR fuer normale Menuepunkte. Der Kontakt-CTA ist ausgenommen:
   sonst stiehlt diese Regel seine Akzentflaeche, waehrend das !important
   in base.css die Textfarbe auf "Text-auf-Akzent" festhaelt -> 1,07:1
   im Dunkelmodus, 1,18:1 im Hellmodus (Bug 30: zwei Regeln, die einzeln
   stimmen und nur zusammen kippen).
   bg-inset statt bg-elevated: Der Mobil-Menue-Panel IST bg-elevated -
   die Markierung der aktuellen Seite war dort unsichtbar. */
/* Ueberfahren: dezente Flaeche. Gilt NUR fuer den Hover - die
   Markierung der aktuellen Seite ist etwas anderes und steht unten. */
.primary-nav__link:not(.btn-nav):hover {
  color: var(--c-text-primary);
  background: var(--c-bg-inset);
}

/* Aktuelle Seite: Rahmen in Akzentfarbe - dieselbe Sprache wie im
   Kebab-Menue am Handy. Vorher war es hier eine eingefaerbte Flaeche,
   dort eine Umrandung: zwei Muster fuer dieselbe Aussage.
   Der durchsichtige Rahmen im Grundzustand haelt die Breite stabil,
   sonst springt die Leiste beim Seitenwechsel um zwei Pixel. */
.primary-nav__link {
  border: 1px solid transparent;
}

.primary-nav__link[aria-current="page"] {
  border-color: var(--c-accent);
  color: var(--c-accent-text);
}

/* --- Kontakt-CTA: behaelt in JEDEM Zustand seine Akzentflaeche --- */

/* Grundzustand. Spezifitaet (0,2,0) schlaegt .primary-nav__link (0,1,0) -
   deshalb kann das !important in base.css jetzt entfallen. */
.primary-nav__link.btn-nav {
  background: var(--c-accent);
  color: var(--c-text-on-accent);
}

/* Aktuelle Seite: Flaeche bleibt, ein Ring markiert "du bist hier".
   KEIN Farbwechsel - genau der hat den Kontrast zerstoert. */
/* Sonderfall Kontakt auf der Kontaktseite: Ein Akzentrahmen auf
   Akzentflaeche waere unsichtbar. Deshalb hier weiterhin der Ring.
   ⚠️ MUSS in der Media-Query stehen: Ungekapselt gilt sie auch am
   Handy, und dort hat der Eintrag KEINE Akzentflaeche - Rahmen
   durchsichtig plus Grund durchsichtig ergab gar keine Markierung.
   Spezifitaet (0,3,0) schlaegt dabei die Kebab-Regel (0,2,0),
   Reihenfolge hilft also nicht. */
@media (min-width: 768px) {
  .primary-nav__link.btn-nav[aria-current="page"] {
    border-color: transparent;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-accent) 35%, transparent);
  }
}

/* Hover/Fokus steht ZULETZT, damit es auch auf der Kontaktseite greift.
   accent-hover ist im Dunkelmodus heller, im Hellmodus dunkler - der
   Kontrast wandert in beiden Modi in die sichere Richtung. */
.primary-nav__link.btn-nav:hover,
.primary-nav__link.btn-nav:focus-visible {
  background: var(--c-accent-hover);
  color: var(--c-text-on-accent);
}

/* Nav Desktop */
@media (min-width: 768px) {
  .nav-toggle { display: none; }

  .primary-nav {
    position: static;
    inset: auto;
    background: transparent;
    border: none;
    padding: 0;
    opacity: 1;
    visibility: visible;
    transform: none;
    display: flex;
    align-items: center;
  }

  .primary-nav__list {
    flex-direction: row;
    align-items: center;
    gap: var(--space-1);
    width: auto;
  }

  .primary-nav__link {
    font-size: var(--font-size-small);
    padding: var(--space-2) var(--space-3);
    min-height: 36px;
  }
}
/* ### /BLOCK: header-nav ### */

/* ### BLOCK: page-head ### */
/*
  Seitenkopf für Unterseiten (media, data, kontakt).
  Kompakter als der Hero, trägt H1 + kurze Einleitung.
*/
.page-head {
  position: relative;
  background:
    radial-gradient(ellipse 70% 60% at 50% -20%, rgb(201 186 142 / 0.10) 0%, transparent 70%),
    var(--c-bg-base);
  border-bottom: 1px solid var(--c-border-subtle);
  padding-block: clamp(var(--space-12), 8vw, var(--space-20));
}

.page-head h1 {
  margin-block: var(--space-3) var(--space-4);
  max-width: 20ch;
}

.page-head p {
  max-width: 60ch;
  font-size: clamp(var(--text-base), 1.5vw, var(--text-lg));
}
/* ### /BLOCK: page-head ### */

/* Block media-gallery entfernt (Runde 5): Boilerplate-Erbe fuer eine
   Galerie mit Bildunterschriften-Karten, die es nicht mehr gibt.
   Die Grid-Regeln waren komplett tot (media-page ueberschreibt sie),
   img-wrap und caption erzeugt medien.js gar nicht. Aktiv war nur
   ".gallery-item:first-child { grid-column: span 2 }" - dadurch war das
   erste Bild ab 1024px doppelt breit, obwohl das Raster laengst gleich
   grosse Kacheln vorsieht (Bug 30).
   Hover-Hebung und transition sind in den Block media-page gewandert. */


/* ### BLOCK: form-kontakt ### */
.form-section {
  background: var(--c-bg-base);
}

.contact-form {
  background: var(--c-bg-surface);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-card);
  padding: clamp(var(--space-6), 5vw, var(--space-10));
  margin-block-start: var(--space-8);
}

.form-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
}

@media (min-width: 560px) {
  .form-row {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-5);
  }
}
/* ### /BLOCK: form-kontakt ### */

/* ### BLOCK: form-field-status ### */
/*
  Visuelles Feedback für Formularfelder.
  Status wird per aria-invalid gesteuert (true = Fehler, false = ok).
  Icon rechts im Feld via background-image (SVG data-URI), kein extra Markup.
*/

/* Grundzustand: Platz rechts für ein mögliches Status-Icon */
.form-group input,
.form-group textarea,
.form-group select {
  background-repeat: no-repeat;
  background-position: right var(--space-4) center;
  background-size: 20px 20px;
}

/* Fehler: roter Rahmen + rotes Kreuz-Icon */
.form-group input[aria-invalid="true"],
.form-group textarea[aria-invalid="true"],
.form-group select[aria-invalid="true"] {
  border-color: var(--c-error);
  padding-right: var(--space-10);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23f87171' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");
}

/* Erfolg: grüner Rahmen + grüner Haken – nur wenn explizit als gültig markiert */
.form-group input[aria-invalid="false"],
.form-group textarea[aria-invalid="false"],
.form-group select[aria-invalid="false"] {
  border-color: var(--c-success);
  padding-right: var(--space-10);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%234ade80' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

/* textarea: Icon nach oben rücken, da mehrzeilig */
.form-group textarea[aria-invalid="true"],
.form-group textarea[aria-invalid="false"] {
  background-position: right var(--space-4) top var(--space-3);
}

/* Erfolgs-Meldung nach Absenden – mit Haken-Icon davor */
.form-feedback {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.form-feedback::before {
  content: "";
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  background: no-repeat center / contain
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%234ade80' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

/* Fehlerfall: Ausrufezeichen im Kreis statt Haken. */
.form-feedback--error::before {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23f87171' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 8v4'/%3E%3Cpath d='M12 16h.01'/%3E%3C/svg%3E");
}
/* ### /BLOCK: form-field-status ### */

/* ### BLOCK: legal-page ### */
/*
  Lesefreundliche Typo für Impressum & Datenschutz.
  Fließtext-Seiten ohne Karten – klarer vertikaler Rhythmus.
*/
.legal-page h1 {
  margin-block: var(--space-3) var(--space-8);
  /* Langer Titel „Datenschutzerklärung": etwas kleiner deckeln,
     damit er nicht umbricht; Silbentrennung als Sicherheitsnetz. */
  font-size: clamp(var(--text-3xl), 5vw, var(--text-5xl));
  hyphens: auto;
}

.legal-page h2 {
  font-size: var(--font-size-h4);
  margin-block: var(--space-8) var(--space-3);
}

.legal-page p {
  margin-block-end: var(--space-4);
  line-height: var(--leading-loose);
}
/* ### /BLOCK: legal-page ### */


/* ### BLOCK: back-to-top ### */
/*
  Platzhalter-Struktur – Ein-/Ausblend-Logik folgt in Runde 3.
  Standardmäßig via [hidden] ausgeblendet.
*/
.back-to-top {
  position: fixed;
  right: var(--space-5);
  bottom: var(--space-5);
  z-index: 90;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border: 1px solid var(--c-border-default);
  border-radius: var(--radius-full);
  background: var(--c-bg-elevated);
  color: var(--c-text-primary);
  cursor: pointer;
  box-shadow: var(--shadow-md);
}

.back-to-top[hidden] { display: none; }

.back-to-top:hover {
  border-color: var(--c-accent);
  box-shadow: var(--shadow-glow);
}
/* ### /BLOCK: back-to-top ### */

/* ### BLOCK: first-section-spacing ### */
/*
  Der klebende Header (var(--nav-height)) erzeugt zusammen mit dem
  vollen oberen Innenabstand des ersten Abschnitts zu viel Leerraum.
  Handy: knapper. Ab 768px: etwas mehr Luft.
*/
main > .section:first-child,
main > .hero:first-child {
  padding-block-start: var(--space-4);
}

main > .hero:first-child .hero__content {
  padding-block-start: var(--space-2);
}

@media (min-width: 768px) {
  main > .section:first-child,
  main > .hero:first-child {
    padding-block-start: var(--space-8);
  }
  main > .hero:first-child .hero__content {
    padding-block-start: var(--space-6);
  }
}
/* ### /BLOCK: first-section-spacing ### */

/* ### BLOCK: legal-footer ### */
.site-footer {
  background: var(--c-bg-surface);
  border-top: 1px solid var(--c-border-subtle);
  padding-block: var(--space-8);
}

.site-footer__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  text-align: center;
}

.site-footer__copy {
  font-size: var(--font-size-small);
  color: var(--c-text-muted);
  max-width: none;
}

.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-1) var(--space-5);
}

.site-footer__legal a {
  font-size: var(--font-size-small);
  color: var(--c-text-muted);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

.site-footer__legal a:hover { color: var(--c-text-secondary); }

@media (min-width: 640px) {
  .site-footer__inner {
    flex-direction: row;
    justify-content: space-between;
    text-align: left;
  }

  .site-footer__legal { justify-content: flex-end; }
}
/* ### /BLOCK: legal-footer ### */

/* ### BLOCK: enter-screen ### */
/*
  Ganzseitiger JÆCK-Einstieg (nur Startseite, body.page-start).
  Name = großer Champagner-Button, Klick öffnet später die Pinnwand.
  100svh mit 100vh-Fallback (Lesson: kein Springen durch Handy-Leisten).
  Header schwebt transparent über dem Einstieg → wirkt wirklich ganzseitig.
*/

/* Header auf der Startseite transparent über den Einstieg legen */
.page-start .site-header {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  /* transparent statt none: box-sizing:border-box zieht den Rahmen vom
     Innenraum ab. Mit "none" ist der Innenraum hier 68px, auf Unterseiten
     67px - die Header-Inhalte sitzen dadurch 0,5px versetzt. Unsichtbarer
     Rahmen haelt die Hoehe gleich, ohne eine Linie zu zeigen. */
  border-bottom: 1px solid transparent;
  position: fixed;
  inset-inline: 0;
}
/* Auch bei offenem Menü transparent bleiben. Überstimmt die allgemeine
   Menü-offen-Regel (.site-header:has(...)): Auf Start/Pinnwand hat der
   Header keinen Hintergrund – der flache Unterseiten-Zustand stünde
   als Farbstreifen auf dem warmen Pinnwand-Verlauf. */
.page-start .site-header:has(.nav-toggle[aria-expanded="true"]) {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}


/* Der Einstieg füllt den ganzen Viewport */
.enter-screen {
  min-height: 100vh;          /* Fallback für alte Browser */
  min-height: 100svh;         /* moderne: berücksichtigt Browserleisten */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  padding: var(--space-8);
  text-align: center;
}

/* Der große Name als Button – ohne Button-Optik */
.enter-screen__name {
  background: none;
  border: none;
  cursor: pointer;
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-card);
  font-family: var(--font-heading);
  font-weight: var(--weight-bold);
  /* Riesig, skaliert mit Viewport von Handy bis Desktop */
  font-size: clamp(4rem, 22vw, 16rem);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  color: var(--c-accent-text);      /* Champagner-Silber (Text-Variante) */
  transition:
    text-shadow var(--dur-slow) var(--ease),
    transform var(--dur-slow) var(--ease);
}

/* Hover/Fokus: sanftes Aufleuchten + minimal größer – Einladung zum Klick */
.enter-screen__name:hover,
.enter-screen__name:focus-visible {
  text-shadow: 0 0 40px color-mix(in srgb, var(--c-accent) 45%, transparent);
  transform: scale(1.02);
}

/* Fokusring nach innen (Lesson: negativer offset, sonst abgeschnitten) */
.enter-screen__name:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: -2px;
}

/* "eintreten" – klein, leise, pulsierend */
.enter-screen__hint {
  font-family: var(--font-hand);    /* Caveat-Handschrift */
  font-size: clamp(var(--text-lg), 4vw, var(--text-2xl));
  color: var(--c-text-muted);
  letter-spacing: var(--tracking-wide);
  animation: enter-pulse 2.6s var(--ease) infinite;
}

@keyframes enter-pulse {
  0%, 100% { opacity: 0.45; transform: translateY(0); }
  50%      { opacity: 1;    transform: translateY(3px); }
}

@media (prefers-reduced-motion: reduce) {
  .enter-screen__name { transition: none; }
  .enter-screen__hint { animation: none; opacity: 0.7; }
}

/* Rechtslinks dezent am unteren Rand */
.start-legal {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  padding: var(--space-4);
  display: flex;
  justify-content: center;
}

.start-legal__list {
  display: flex;
  gap: var(--space-1) var(--space-5);
  flex-wrap: wrap;
  justify-content: center;
}

.start-legal__list a {
  font-size: var(--font-size-small);
  color: var(--c-text-muted);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

.start-legal__list a:hover { color: var(--c-text-secondary); }
/* ### /BLOCK: enter-screen ### */

/* ### BLOCK: pinboard ### */
/*
  Pinnwand (Zustand 2). Frei verstreute Karten – auch am Handy (auf
  Wunsch). Positionen in % → skalieren mit der Bildschirmbreite, ragen
  nicht hinaus. Feature-Karte zentriert & hervorgehoben. Karten klein,
  da der volle Inhalt sich per Klick öffnet.
  ACHTUNG Handy: Touch-Ziele klein – bewusst getestet. Sichtbarkeit
  steuert js/pinboard.js über [hidden] + .is-visible.
*/

.pinboard {
  min-height: 100vh;
  min-height: 100svh;
  padding: calc(var(--nav-height) + var(--space-6)) var(--space-4) var(--space-12);
  display: flex;
  align-items: center;
  background:
    radial-gradient(ellipse 70% 50% at 50% 0%,
      color-mix(in srgb, var(--c-accent) 10%, transparent) 0%, transparent 70%),
    var(--c-bg-base);
}

.pinboard[hidden] { display: none; }

.pinboard__inner {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
}

/* Verstreute Fläche – Basis (Handy) */
.pinboard__grid {
  position: relative;
  width: 100%;
  max-width: 560px;
  min-height: 540px;
  margin-inline: auto;
}

.pin-card {
  --pin-rot: 0deg;
  position: absolute;
  width: 42%;            /* zwei Karten passen nebeneinander */
  max-width: 200px;
  transform: rotate(var(--pin-rot));
  transition: transform var(--dur-normal) var(--ease);
}

/* Handy-Positionen (in %, skalieren mit Breite) */
.pin-card:nth-child(2) { top: 0%;   left: 2%;    --pin-rot: -3deg; }
.pin-card:nth-child(3) { top: 17%;  right: 2%; left: auto; --pin-rot: 3deg; }
.pin-card:nth-child(4) { top: 66%;  left: 4%;    --pin-rot: 2.5deg; }
.pin-card:nth-child(5) { top: 87%;  right: 3%; left: auto; --pin-rot: -2.5deg; }

/* Feature-Karte: zentriert, mittig, hervorgehoben */
.pin-card--feature {
  width: 56%;
  max-width: 260px;
  top: 41%;
  left: 50%;
  transform: translateX(-50%) rotate(0deg);
  z-index: 3;
}

.pin-card__btn {
  width: 100%;
  text-align: left;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  /* Dezenter: halbtransparenter Grund, kaum Rand, kein tragender Schatten.
     Der Text soll wirken, nicht der Kasten. Feature-Karte überschreibt
     das weiter unten und behält ihre Hervorhebung. */
  background: color-mix(in srgb, var(--c-bg-surface) 55%, transparent);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-card);
  box-shadow: none;
  transition:
    transform var(--dur-normal) var(--ease),
    border-color var(--dur-normal) var(--ease),
    box-shadow var(--dur-normal) var(--ease),
    background var(--dur-normal) var(--ease);
}

.pin-card__btn:hover,
.pin-card__btn:focus-visible {
  transform: translateY(-4px);
  border-color: var(--c-border-default);
  box-shadow: var(--shadow-lg);
}

.pin-card__btn:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: -2px;
}

/* Feature hervorgehoben */
.pin-card--feature .pin-card__btn {
  background: var(--c-bg-elevated);
  border-color: color-mix(in srgb, var(--c-accent) 45%, var(--c-border-default));
  box-shadow: var(--shadow-glow);
}

.pin-card--feature .pin-card__btn:hover,
.pin-card--feature .pin-card__btn:focus-visible {
  border-color: var(--c-accent);
}

.pin-card--feature .pin-card__preview {
  color: var(--c-text-primary);
}

.pin-card__kind {
  font-family: var(--font-sans);   /* bleibt normale Schrift, nicht Caveat */
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--c-accent-text);
}

.pin-card__preview {
  /* Songzeile in Handschrift – zieht den Caveat-Look ins Overlay durch */
  font-family: var(--font-hand);
  /* Skaliert leicht mit der Kartenbreite: nie zu klein, nie zu wuchtig */
  font-size: clamp(var(--text-xl), 2.4vw, var(--text-3xl));
  color: var(--c-text-primary);
  line-height: var(--leading-snug);
  white-space: pre-line;          /* \n aus verse werden zu echten Umbrüchen */
  display: -webkit-box;
  -webkit-line-clamp: 3;          /* max. 3 Zeilen, dann … */
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Handy: nur das Etikett (SINGLE/MERCH/…) – die Handschrift-Zeile gibt's
   ab Tablet/Desktop. quotes.json bleibt vollständig, das Overlay zeigt alles. */
.pin-card__preview { display: none; }

/* Desktop: breiter streuen, größere Karten */
@media (min-width: 768px) {
  .pinboard {
    display: flex;
    align-items: center;
    padding-block: var(--nav-height) var(--space-8);
  }
  /* Deckel lösen: inner war durch container-max (1200px) begrenzt und
     als Flex-Kind auf Inhaltsbreite geschrumpft (absolute Karten zählen
     nicht als Inhalt). Explizit volle Breite geben. */
  .pinboard__inner {
    width: 100%;
    max-width: none;
  }
  .pinboard__grid {
    position: relative;
    max-width: 1300px;
    min-height: 660px;
    width: 100%;
    margin-inline: auto;
  }
  .pin-card {
    width: 280px;
    max-width: 280px;
  }

  /* Vier Karten weit in die Ecken – linke/rechte Kante am Flächenrand.
     Große vertikale Spreizung: oben nahe 0, unten nahe Flächen-Ende. */
  .pin-card:nth-child(2) { top: 0;     left: 0;              --pin-rot: -2deg; }
  .pin-card:nth-child(3) { top: 40px;  left: auto; right: 0; --pin-rot: 2deg; }
  .pin-card:nth-child(4) { top: 440px; left: 0;              --pin-rot: 1.5deg; }
  .pin-card:nth-child(5) { top: 480px; left: auto; right: 0; --pin-rot: -1.5deg; }

  /* Feature zentriert im Zentrum der Fläche */
  .pin-card--feature {
    width: 360px;
    max-width: 360px;
    top: 230px;
    left: 50%;
    transform: translateX(-50%) rotate(0deg);
  }

  .pin-card__btn { padding: var(--space-5) var(--space-6); }
  /* Desktop: Handschrift größer. Feature kräftiger als die normalen Karten.
     Skaliert mit clamp mit, damit es auf breiten Schirmen wirkt. */
  .pin-card--feature .pin-card__preview { font-size: clamp(var(--text-2xl), 3vw, var(--text-4xl)); }
  .pin-card__preview {
    display: -webkit-box;           /* ab hier wieder sichtbar */
    -webkit-line-clamp: 4;          /* Platz für längere Auszüge */
    font-size: clamp(var(--text-xl), 2.4vw, var(--text-3xl));
  }

  .pin-card:hover,
  .pin-card:focus-within {
    transform: rotate(0deg) translateY(-4px);
    z-index: 4;
  }
  .pin-card--feature:hover,
  .pin-card--feature:focus-within {
    transform: translateX(-50%) translateY(-4px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .pin-card, .pin-card__btn { transition: none; }
  .pin-card--feature { transform: translateX(-50%); }
}
/* ### /BLOCK: pinboard ### */

/* ### BLOCK: pinboard-transition ### */
/*
  Der Reinzoom-Übergang JÆCK → Pinnwand.
  JS (pinboard.js) setzt am <body>:
    .show-pinboard → Pinnwand sichtbar
    .is-entering   → nur beim Klick-Übergang (löst Zoom aus; fehlt beim
                     Direktaufruf via #pinnwand, dann kein Zoom)
  Enter-Screen und Pinnwand werden während des Übergangs überlagert.
*/

/* Solange die Pinnwand offen ist: kein Seiten-Scroll (geschlossenes Vollbild) */
body.show-pinboard {
  overflow: hidden;
}

/* Enter-Screen: sanft skalier-/blendbar machen */
.page-start .enter-screen {
  transition:
    opacity 700ms var(--ease),
    transform 700ms var(--ease);
}

/* Beim Eintreten: JÆCK-Ebene wächst und verblasst (Reinzoomen) */
.page-start.show-pinboard .enter-screen {
  opacity: 0;
  transform: scale(1.6);
  pointer-events: none;   /* nicht mehr klickbar, sobald weg */
  /* Aus dem Layout nehmen, damit die Pinnwand oben beginnt (kein Scrollen) */
  position: fixed;
  inset: 0;
}

/* Pinnwand: Grundzustand für die Einblend-Transition */
.page-start .pinboard {
  opacity: 0;
  transform: scale(0.98);
  transition:
    opacity 700ms var(--ease),
    transform 700ms var(--ease);
}

/* Pinnwand sichtbar: einblenden + auf Normalgröße heranzoomen */
.page-start.show-pinboard .pinboard {
  opacity: 1;
  transform: scale(1);
}

/* Rechtslinks am Einstieg beim Eintreten ausblenden */
.page-start.show-pinboard .start-legal {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-normal) var(--ease);
}

/* reduced-motion: kein Zoom, nur schlichtes Blenden */
@media (prefers-reduced-motion: reduce) {
  .page-start.show-pinboard .enter-screen { transform: none; }
  .page-start .pinboard { transform: none; }
  .page-start.show-pinboard .pinboard { transform: none; }
}
/* ### /BLOCK: pinboard-transition ### */

/* ### BLOCK: quote-overlay ### */
/*
  Vollbild-Overlay für Song-/Quote-Karten. Grundzustand hidden.
  Sichtbarkeit + Inhalt steuert js/pinboard.js (Teil 3).
  Aufbau (gestapelt per z-index):
    __bg    – Hintergrundbild (<picture>), object-fit:cover
    __scrim – abdunkelnder Verlauf, damit Text lesbar bleibt (Lesson)
    __inner – der eigentliche Textinhalt, zentriert
  Ohne Bild: __bg zeigt nichts, __scrim liefert allein den Theme-Gradient.
*/
.quote-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;               /* über Header (100) und Pinnwand */
  display: flex;
  /* NICHT align-items:center - das Overlay scrollt (overflow-y:auto).
     Bei zu hohem Inhalt ragt zentrierter Flex-Inhalt oben UND unten
     hinaus, und der obere Teil liegt ausserhalb der scrollbaren Flaeche:
     unerreichbar. Genau so verschwand das Etikett "SINGLE" bei langen
     Songs am Handy.
     Die Zentrierung uebernimmt margin:auto am __inner - automatische
     Raender werden nie negativ: passt der Inhalt, steht er mittig;
     passt er nicht, beginnt er oben und alles bleibt scrollbar. */
  align-items: flex-start;
  justify-content: center;
  /* Oben extra Luft fürs Schließen-X (v.a. am Handy), sonst klebt das Label */
  padding: clamp(var(--space-12), 5vw, var(--space-16)) clamp(var(--space-6), 5vw, var(--space-16)) clamp(var(--space-8), 5vw, var(--space-16));
  overflow-y: auto;           /* lange Strophen: scrollbar innerhalb des Overlays */
  /* Deckendes dunkles Fundament: bei bildlosen Karten (Merch/Tickets) der
     Hintergrund, bei Bild-Karten liegt das Foto darüber und verdeckt es. */
  background: var(--c-bg-base);
}

.quote-overlay[hidden] { display: none; }

/* Hintergrundbild – füllt das ganze Overlay */
/* fixed statt absolute: .quote-overlay scrollt (overflow-y:auto), und
   absolute Kinder scrollen darin mit. Das Hintergrundfoto fuhr bei langen
   Overlays nach oben weg - darunter kam der nackte Grund zum Vorschein.
   .quote-overlay ist selbst fixed/inset:0, die Position bleibt identisch. */
.quote-overlay__bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: block;          /* <picture> ist sonst inline ohne Ausdehnung */
}

.quote-overlay__bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  /* contain statt cover: quadratische Cover bleiben VOLLSTÄNDIG sichtbar
     (kein Beschnitt), skalieren mittig und lassen je nach Format oben/unten
     bzw. links/rechts Rand frei. Bei querformatigen Fotos wirkt das ebenso. */
  object-fit: contain;
  object-position: center;
}

/* Abdunkelnder Verlauf über dem Bild – IMMER da (auch ohne Bild = Gradient) */
/* fixed: muss deckungsgleich mit __bg bleiben, sonst wandert der
   Abdunkler getrennt vom Foto. */
.quote-overlay__scrim {
  position: fixed;
  inset: 0;
  z-index: 1;
  /* Halbtransparente Abdunklung – Bild scheint durch, Text bleibt lesbar.
     KEINE deckende Grundfarbe mehr (die verdeckte das Foto komplett). */
  background:
    linear-gradient(to bottom,
      color-mix(in srgb, var(--c-bg-base) 45%, transparent) 0%,
      color-mix(in srgb, var(--c-bg-base) 70%, transparent) 100%),
    radial-gradient(ellipse 80% 60% at 50% 30%,
      color-mix(in srgb, var(--c-accent) 14%, transparent) 0%, transparent 70%);
}

/* Schliessen-X oben rechts.
   fixed statt absolute - DAS war die Ursache des "x sitzt am Handy
   woanders": Beim Scrollen fuhr dieses x nach oben aus dem Bild, waehrend
   Lyrics-x und Zoom-x (beide fixed) stehenblieben. Am PC fiel es nicht auf,
   weil dort nichts zu scrollen war. Jetzt sind alle drei Ebenen-x
   deckungsgleich - auch waehrend gescrollt wird. */
/* Schliessen-x: siehe Block ui-close am Dateiende.
   WICHTIG fuer die Nachwelt - dort steht position:fixed, und das muss so
   bleiben: .quote-overlay scrollt (overflow-y:auto), ein absolut
   positioniertes x wuerde beim Scrollen nach oben aus dem Bild fahren.
   Genau das war die Ursache des "x sitzt am Handy woanders".
   Rahmen, Weichzeichner und 90-Grad-Drehung sind in Runde 3c entfallen. */

/* Der Textinhalt, zentriert und lesbar begrenzt */
.quote-overlay__inner {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  max-width: 44rem;
  width: 100%;
  text-align: center;
  /* Uebernimmt die senkrechte Zentrierung von align-items:center - siehe
     Kommentar oben. Wird nie negativ, schneidet also nie oben ab. */
  margin-block: auto;
}

.quote-overlay__kind {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--c-accent-text);
}

/* --- TITEL: clean, nicht Caveat. Schrift per Umschalt-Variable unten. --- */
.quote-overlay__title {
  /* Nutzt --font-heading (Lora). Fraunces wurde am 06.08. entfernt -
     sie wurde geladen, aber nie verwendet. */
  font-family: var(--font-heading);
  font-weight: var(--weight-bold);
  font-size: clamp(var(--text-3xl), 6vw, var(--text-5xl));
  line-height: 1.05;
  letter-spacing: var(--tracking-tight);
  color: var(--c-text-primary);
  margin: 0;
}

/* Handgezogener Trennstrich in Champagner */
.quote-overlay__rule {
  display: block;
  width: 3rem;
  height: 2px;
  border-radius: var(--radius-full);
  background: var(--c-accent-text);
  opacity: 0.8;
}

/* Die versteckte Aushänge-Zeile (nur noch a11y-Anker) */
.quote-overlay__line--hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* Der Songtext – Caveat, präsent, der Star des Overlays */
.quote-overlay__verse {
  font-family: var(--font-hand);
  font-size: clamp(var(--text-xl), 3.4vw, var(--text-3xl));
  line-height: var(--leading-snug);
  color: var(--c-text-primary);
  white-space: pre-wrap;
  margin: 0;
  text-align: center;
}

.quote-overlay__verse:empty { display: none; }

/* Story: unter den Lyrics, aber klar präsent (größer, kontrastreicher) */
.quote-overlay__story {
  font-family: var(--font-sans);
  font-size: clamp(var(--text-base), 1.6vw, var(--text-lg));
  line-height: var(--leading-loose);
  color: var(--c-text-secondary);
  max-width: 52ch;
  margin-block-start: var(--space-4);
}

.quote-overlay__story:empty { display: none; }


/* ### BLOCK: quote-cover ### */
/*
  Cover-Artwork im Prerelease-Overlay (Weg A): quadratisch, mittig,
  begrenzte Breite. Kein Hintergrundbild – das Overlay bleibt hell/dunkel
  je nach Modus. Lesson: <picture> ist inline und kollabiert → display:block.
*/
.quote-overlay__cover {
  display: block;
  width: min(280px, 70%);
  margin-inline: auto;
  margin-block-start: var(--space-2);
  aspect-ratio: 1 / 1;
  /* --radius-small existiert in tokens.css nicht - es griff immer nur der
     Rueckfallwert 4px. Der Block cover-hover setzt weiter unten ohnehin
     12px. Auf den echten Token gesetzt (Runde 5). */
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: 0 12px 32px rgb(0 0 0 / 0.45);
}

.quote-overlay__cover[hidden] { display: none; }

.quote-overlay__cover img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;
  user-select: none;
  pointer-events: none;   /* verhindert Rechtsklick → "Bild speichern" */
}
/* ### /BLOCK: quote-cover ### */

/* --- Audio-Player --- */
.quote-overlay__audio {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  max-width: 30rem;
  margin-block-start: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: color-mix(in srgb, var(--c-bg-elevated) 70%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-full);
}

.quote-overlay__audio[hidden] { display: none; }

.quote-overlay__play {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--c-accent);
  color: var(--c-bg-base);
  cursor: pointer;
  transition: transform 0.15s ease, background 0.15s ease;
}

.quote-overlay__play:hover {
  transform: scale(1.06);
}

.quote-overlay__play:active {
  transform: scale(0.96);
}

/* Play-Dreieck wirkt optisch links versetzt – minimal ausgleichen.
   Beim Pause-Symbol nicht nötig, fällt aber nicht auf. */
.quote-overlay__icon {
  margin-inline-start: 2px;
}

.quote-overlay__play:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

/* Icon-Umschaltung: das hidden-Attribut muss gewinnen. Eine display-Regel
   auf SVGs überschreibt es sonst still (gleiche Lesson wie bei
   .quote-overlay__audio[hidden]) → beide Symbole gleichzeitig sichtbar. */
.quote-overlay__play svg[hidden] { display: none; }

.quote-overlay__progress {
  position: relative;
  flex: 1;
  height: 4px;
  background: var(--c-bg-inset);
  border-radius: var(--radius-full);
  cursor: pointer;
}

/* Unsichtbare Trefferfläche nach oben/unten: 4px sind mit der Maus
   schwer zu treffen, am Handy fast unmöglich. Als Pseudo-Element,
   damit der sichtbare Balken davon unberührt bleibt. */
.quote-overlay__progress::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: -10px;
  bottom: -10px;
}

.quote-overlay__progress-fill {
  position: relative;   /* Bezug fuer den Griff (::after) */
  height: 100%;         /* war 4px, wurde weiter unten ohnehin ueberschrieben */
  width: 0%;
  background: var(--c-accent);
  border-radius: var(--radius-full);
}

/* Griff am Ende des gefüllten Bereichs – erscheint bei Hover und beim Ziehen */
.quote-overlay__progress-fill::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--c-accent);
  transform: translate(50%, -50%) scale(0);
  transition: transform 0.15s ease;
}

.quote-overlay__progress:hover .quote-overlay__progress-fill::after,
.quote-overlay__progress.is-dragging .quote-overlay__progress-fill::after {
  transform: translate(50%, -50%) scale(1);
}

.quote-overlay__progress:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 4px;
}

/* Zweite Definition von .quote-overlay__progress-fill entfernt (Runde 5,
   Bug 30) - sie wiederholte alles bis auf height und hatte position:relative
   verloren. Die Werte stehen jetzt vollstaendig in der ersten Definition. */
.quote-overlay__time {
  flex-shrink: 0;
  font-size: var(--font-size-small);
  color: var(--c-text-muted);
  font-variant-numeric: tabular-nums;
  min-width: 3.5ch;
}

/* Optionaler Link-Button (statt/neben Audio) */
.quote-overlay__link[hidden] { display: none; }

/* Sanftes Einblenden beim Öffnen (JS setzt .is-open) */
.quote-overlay {
  opacity: 0;
  transition: opacity var(--dur-normal) var(--ease);
}
.quote-overlay.is-open { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .quote-overlay { transition: none; }
  /* Die Zeile gegen die 90-Grad-Drehung ist entfallen (Runde 3c) -
     es gibt keine Drehung mehr, die abzuschalten waere. */
}

/* Scroll-Lock: solange ein Overlay offen ist, Hintergrund einfrieren */
body.overlay-open { overflow: hidden; }

/* --- Formular im Overlay (Merch-/Tickets-Voranmeldung) --- */
.quote-overlay__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  width: 100%;
  max-width: 30rem;
  margin-block-start: var(--space-2);
  text-align: left;
}

.quote-overlay__form[hidden] { display: none; }

.quote-form__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.quote-form__input {
  flex: 1 1 12rem;
  min-width: 0;
  padding: var(--space-3) var(--space-4);
  font-size: var(--font-size-body);
  color: var(--c-text-primary);
  background: color-mix(in srgb, var(--c-bg-elevated) 85%, transparent);
  border: 1px solid var(--c-border-default);
  border-radius: var(--radius-md);
}

.quote-form__input::placeholder { color: var(--c-text-muted); }

.quote-form__input:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: -1px;
  border-color: var(--c-accent);
}

.quote-form__submit {
  flex: 0 0 auto;
  cursor: pointer;
  /* iOS/Safari stylt Buttons sonst mit System-Blau → explizit überschreiben */
  -webkit-appearance: none;
  appearance: none;
  color: var(--c-bg-base);           /* dunkle Schrift auf hellem Akzent */
  background: var(--c-accent);
  border: none;
}

/* Einwilligung: kleine Checkbox + Text nebeneinander */
.quote-form__consent {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--font-size-small);
  line-height: var(--leading-normal);
  color: var(--c-text-secondary);
  cursor: pointer;
}

.quote-form__consent input {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  margin-block-start: 2px;
  accent-color: var(--c-accent);
  cursor: pointer;
}

.quote-form__consent a {
  color: var(--c-accent-text);
  text-decoration: underline;
}

/* Honeypot: aus dem Sichtfeld, aber NICHT display:none (Lesson) */
.quote-form__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Rückmeldung nach Absenden */
.quote-form__feedback {
  font-size: var(--font-size-small);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  margin: 0;
}

.quote-form__feedback[hidden] { display: none; }

.quote-form__feedback[data-state="success"] {
  color: var(--c-success);
  background: color-mix(in srgb, var(--c-success) 12%, transparent);
}

.quote-form__feedback[data-state="error"] {
  color: var(--c-error);
  background: color-mix(in srgb, var(--c-error) 12%, transparent);
}

/* --- Social-/Linktree-Buttons (Kontakt-Karte) --- */
.quote-overlay__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: 100%;
  max-width: 26rem;
  margin-block-start: var(--space-2);
}

.quote-overlay__links[hidden] { display: none; }

.quote-link {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  background: color-mix(in srgb, var(--c-bg-elevated) 78%, transparent);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-md);
  color: var(--c-text-primary);
  text-decoration: none;
  font-weight: var(--weight-medium);
  transition:
    transform var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}

.quote-link:hover,
.quote-link:focus-visible {
  transform: translateY(-2px);
  border-color: var(--c-accent);
  background: color-mix(in srgb, var(--c-bg-elevated) 92%, transparent);
}

.quote-link:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

.quote-link__icon {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  color: var(--c-accent-text);   /* Icons in Akzentfarbe */
}

.quote-link__name {
  font-size: var(--font-size-body);
}


/* === Overlay-Farblogik: Bild-Overlays immer dunkel, bildlose folgen dem Modus === */

/* Bildlose Overlays: Hintergrund folgt dem Modus. Die Deklaration steht
   bereits oben in der Haupt-Regel von .quote-overlay - Dopplung hier
   entfernt (Runde 5, Bug 30). */

/* Bild-Overlays: immer dunkles Fundament, egal welcher Modus */
.quote-overlay.has-image {
  background: rgb(5, 5, 5);
}

/* Textfarben in BILD-Overlays: immer hell (beide Modi) */
.quote-overlay.has-image .quote-overlay__kind  { color: var(--c-accent-text); }
.quote-overlay.has-image .quote-overlay__title { color: #f5f0e8; }
.quote-overlay.has-image .quote-overlay__story { color: rgba(245, 240, 232, 0.85); }

/* Light-Mode, aber NUR Bild-Overlays: dunkler Scrim (heller Text lesbar) */
[data-mode="light"] .quote-overlay.has-image .quote-overlay__scrim {
  background:
    linear-gradient(to bottom,
      rgba(5, 5, 5, 0.55) 0%,
      rgba(5, 5, 5, 0.78) 100%),
    radial-gradient(ellipse 80% 60% at 50% 30%,
      color-mix(in srgb, var(--c-accent) 14%, transparent) 0%, transparent 70%);
}

/* ### /BLOCK: quote-overlay ### */

/* ### BLOCK: mode-toggle ### */
/*
  Hell/Dunkel-Umschalter im Header. Zwei SVGs (Mond + Sonne) liegen im
  Button; je nach data-mode ist nur EINES sichtbar. Rechte Header-Gruppe
  hält Toggle + Burger + Nav zusammen (Position rechts statt Mitte).
*/

/* Rechte Header-Gruppe: Toggle, Burger, Nav nebeneinander rechts */
.site-header__right {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.mode-toggle {
  touch-action: manipulation; /* iOS: Doppeltipp-Zoom aus (Easter Egg = 4 schnelle Tipps) */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: var(--radius-md);
  color: var(--c-text-secondary);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
  /* touch-action steht bereits oben in dieser Regel - Dopplung entfernt
     (Runde 5). Zweck unveraendert: schnelles Tippen zaehlt als Klicks,
     noetig fuers Easter Egg. */
  -webkit-user-select: none;
  user-select: none;
}

.mode-toggle:hover {
  color: var(--c-text-primary);
  background: var(--c-bg-elevated);
}

.mode-toggle:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: -2px;
}

/* Symbol-Logik: standardmäßig BEIDE verstecken … */
.mode-toggle__moon,
.mode-toggle__sun {
  display: none;
}

/* … im Dunkel-Modus die SONNE zeigen (Klick → wird hell) … */
[data-mode="dark"] .mode-toggle__sun {
  display: block;
}

/* … im Hell-Modus den MOND zeigen (Klick → wird dunkel). */
[data-mode="light"] .mode-toggle__moon {
  display: block;
}
/* ### /BLOCK: mode-toggle ### */

/* ### BLOCK: easter-egg ### */
/*
  Belohnungs-Animation, ausgelöst durch das versteckte Mode-Wechsel-Egg.
  JS (pinboard.js) setzt .egg-active am body und blendet die Fund-Botschaft ein.
  reduced-motion: kein Zittern/Schweben, nur sanftes Ein-/Ausblenden.
*/

/* Karten zittern kurz, dann schweben sie weg */
@keyframes egg-shake {
  0%, 100% { transform: translate(0, 0) rotate(var(--pin-rot, 0deg)); }
  20% { transform: translate(-3px, 1px) rotate(calc(var(--pin-rot, 0deg) - 1deg)); }
  40% { transform: translate(3px, -1px) rotate(calc(var(--pin-rot, 0deg) + 1deg)); }
  60% { transform: translate(-2px, 2px) rotate(calc(var(--pin-rot, 0deg) - 1deg)); }
  80% { transform: translate(2px, -2px) rotate(calc(var(--pin-rot, 0deg) + 1deg)); }
}

@keyframes egg-float-away {
  to { opacity: 0; transform: translateY(-40px) scale(0.9) rotate(var(--pin-rot, 0deg)); }
}

/* Phase 1: Zittern */
body.egg-shaking .pin-card {
  animation: egg-shake 0.4s var(--ease);
}

/* Phase 2: Wegschweben */
body.egg-active .pin-card {
  animation: egg-float-away 0.7s var(--ease) forwards;
  pointer-events: none;
}

/* Feature-Karte: NUR verblassen, keine Bewegung (verhindert den Sprung).
   !important, um konkurrierende transform-Animationen sicher zu überschreiben. */
body.egg-active .pin-card--feature {
  animation: egg-fade-only 0.6s var(--ease) forwards !important;
}

@keyframes egg-fade-only {
  to { opacity: 0; }
}

/* Feature-Karte nicht mitzittern lassen (würde ebenfalls springen) */
body.egg-shaking .pin-card--feature {
  animation: none;
}

/* Goldenes Aufleuchten vom Zentrum */
.egg-glow {
  position: fixed;
  inset: 0;
  z-index: 150;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(circle at 50% 45%,
    color-mix(in srgb, var(--c-accent) 70%, transparent) 0%,
    transparent 55%);
}

body.egg-active .egg-glow {
  animation: egg-glow-pulse 1.4s var(--ease) forwards;
}

@keyframes egg-glow-pulse {
  0%   { opacity: 0; transform: scale(0.6); }
  35%  { opacity: 0.9; }
  100% { opacity: 0; transform: scale(1.4); }
}

/* .egg-reveal entfernt (Runde 6, Bug 9): Das Easter Egg oeffnet seit
   laengerem das Geheim-Overlay statt einer eigenen Botschaft. Das HTML
   wurde damals entfernt, die Regeln blieben liegen.
   .egg-glow bleibt - den gibt es. */
@media (prefers-reduced-motion: reduce) {
  body.egg-shaking .pin-card,
  body.egg-active .pin-card { animation: none; opacity: 0; }
  body.egg-active .egg-glow { animation: none; }
}

/* Header während des Easter Eggs ausblenden (wie bei Overlays) */
body.egg-active .site-header,
body.egg-shaking .site-header {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-normal) var(--ease);
}

/* ### /BLOCK: easter-egg ### */

/* ### BLOCK: form-kontakt-branch ### */
/*
  Kontaktformular mit Typ-Weiche (kontakt.html).
  Der Formular-Körper und die beiden Zweige werden per hidden + disabled
  geschaltet (js/kontakt.js). Hier nur die Optik.

  WICHTIG: Fieldsets brauchen explizite Resets – Browser geben ihnen
  standardmäßig Rahmen, Padding und ein eigenes Box-Modell.
*/

/* Hinweis im Grundzustand, solange nichts gewählt ist */
.form-hint {
  color: var(--c-text-muted);
  font-size: var(--font-size-small);
  margin-block: var(--space-4) 0;
}

/* Formular-Körper: erscheint erst nach der Typ-Auswahl */
.form-body {
  border: 0;
  padding: 0;
  margin: 0;
  min-inline-size: 0;        /* verhindert Overflow in Grid/Flex */
}

/* Zweig-Fieldsets: leicht abgesetzt, damit erkennbar ist,
   dass sie zur gewählten Anfrageart gehören */
.form-branch {
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-card);
  padding: var(--space-5);
  margin-block: var(--space-5);
  min-inline-size: 0;
  background: var(--c-bg-inset);
}
/* Legend aus dem Rahmen holen und als eigene Zeile darüber setzen.
   float+Reset ist nötig, weil legend im Fieldset ein Sonderelement ist
   und sich sonst nicht normal positionieren lässt. */
.form-branch > legend {
  float: left;
  inline-size: 100%;
  padding: 0;
  margin-block-end: var(--space-4);
  color: var(--c-accent-text);
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.01em;
}
/* Nach float muss der Inhalt wieder darunter beginnen */
.form-branch > legend + * {
  clear: both;
}

/* Datumsfeld erbt die Feld-Optik aus base.css (Liste dort ergänzt).
   color-scheme steuert nur noch das Kalender-Icon des Browsers,
   damit es im Dunkelmodus nicht schwarz auf dunkel steht. */
.form-group input[type="date"] {
  color-scheme: dark;
}
[data-mode="light"] .form-group input[type="date"] {
  color-scheme: light;
}

/* Zusatz zu einem Label, z. B. "(optional)" – tritt zurück */
.form-label__hint {
  color: var(--c-text-muted);
  font-weight: 400;
}

/* Checkbox-Zeile: Kästchen links, Text rechts daneben.
   Grid statt Flex, damit lange Texte sauber unter sich bleiben
   und nicht unter das Kästchen rutschen. */
.form-group--check {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--space-3);
  align-items: start;
  margin-block: var(--space-5);
}
.form-group--check .form-check {
  inline-size: 1.15rem;
  block-size: 1.15rem;
  margin-block-start: 0.15rem;   /* auf erste Textzeile ausrichten */
  accent-color: var(--c-accent);
  flex-shrink: 0;
}
.form-label--check {
  font-weight: 400;
  line-height: 1.5;
}
/* Fehlermeldung überspannt beide Spalten */
.form-group--check .form-error {
  grid-column: 1 / -1;
}

/* ── HONEYPOT ──────────────────────────────────────────────────────
   Muss für Menschen unsichtbar sein, für Bots aber im DOM erreichbar.
   Deshalb KEIN display:none und KEIN hidden – manche Bots überspringen
   genau das gezielt. Stattdessen aus dem Sichtfeld schieben. */
.form-hp {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* ### /BLOCK: form-kontakt-branch ### */

/* ### BLOCK: form-field-align ### */
/*
  input[type=date] hat eine intrinsische Breite und ignoriert die
  Grid-Spalte – ohne min-inline-size:0 bleibt es schmaler als die
  Nachbarfelder. Betrifft auch number/time.
*/
.form-group input[type="date"],
.form-group input[type="time"],
.form-group input[type="number"] {
  inline-size: 100%;
  min-inline-size: 0;
  /* iOS-Safari rendert Datums-/Zeitfelder als natives Bedienelement.
     Ist nichts gewaehlt, gibt es keinen Inhalt - die Box faellt auf eine
     duenne Zeile zusammen. Am PC faellt das nicht auf, weil dort von
     sich aus "TT.MM.JJJJ" steht. Deshalb die Hoehe FESTLEGEN, statt sie
     sich aus dem Inhalt ergeben zu lassen.
     Der Wert ist aus denselben Tokens gerechnet wie die uebrigen Felder
     (base-forms): Zeilenhoehe + Innenabstand oben/unten + Rahmen.
     Aendert jemand dort das Padding, wandert das Datumsfeld mit. */
  min-block-size: calc(
    var(--leading-normal) * 1em + 2 * var(--space-3) + 2px
  );
}

/* Der Wert im iOS-Datumsfeld sitzt sonst mittig und tanzt aus der Reihe
   der linksbuendigen Nachbarfelder. */
.form-group input[type="date"]::-webkit-date-and-time-value,
.form-group input[type="time"]::-webkit-date-and-time-value {
  text-align: start;
}

/*
  Felder einer Zeile auf gleicher Höhe halten, auch wenn ein Label
  umbricht und das andere nicht. Ohne das rutscht das kürzere Feld
  nach oben und die Zeile wirkt schief.
*/
@media (min-width: 560px) {
  .form-row > .form-group {
    display: flex;
    flex-direction: column;
    align-self: start;
  }
  /*
    Statt die Felder nach unten zu druecken (margin-block-start:auto),
    bekommt die Label-Zeile eine Mindesthoehe. Grund: Sobald unter einem
    Feld eine Fehlermeldung erscheint, waechst dessen Zelle – bei
    auto-Margin wandert das Feld dann mit nach unten und die Zeile
    verspringt. Ueber das Label bleibt die Feldposition stabil.
  */
  .form-row > .form-group > .form-label {
    min-block-size: 2.4em;
    display: flex;
    align-items: flex-end;
  }
}

/*
  Zaehl-Pfeile bei number-Feldern entfernen (Runde 5).
  Sie kamen im Browser-Grau und passten nicht ins Champagner-Design.
  base.css setzt zwar appearance:none auf die Feld-Liste - das entfernt
  in Chrome/Safari die Pfeile aber NICHT, dafuer braucht es das
  Pseudo-Element. Firefox wiederum kennt das Pseudo-Element nicht und
  braucht appearance:textfield. Deshalb beide Wege.

  Achtung fuer spaeter: Ohne Pfeile ist die Tastatur der einzige Weg,
  das Feld zu fuellen. inputmode="numeric" im HTML sorgt dafuer, dass am
  Handy die Ziffern-Tastatur erscheint - NICHT entfernen.
*/
.form-group input[type="number"] {
  appearance: textfield;
  -moz-appearance: textfield;
}
.form-group input[type="number"]::-webkit-outer-spin-button,
.form-group input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
}

/* ### /BLOCK: form-field-align ### */

/* ### BLOCK: page-head-spacing ### */
/*
  .page-head und die folgende .section bringen beide eigenes
  padding-block mit – zusammen entsteht eine Lücke von ~200px.
  Nachbar-Selektor trifft nur diese Kombination, .section bleibt
  überall sonst unverändert.
*/
.page-head + .section {
  padding-block-start: var(--space-10);
}
/* ### /BLOCK: page-head-spacing ### */

/* ### BLOCK: header-logo-enter ### */
/*
  Startseite, Enter-Screen: Header-Logo ausblenden, weil dort bereits
  das große JÆCK steht. Sobald die Pinnwand offen ist (.show-pinboard),
  erscheint es – dann ist kein anderes JÆCK mehr sichtbar.

  visibility statt display: Der Platz bleibt reserviert, sonst springt
  der Header-Inhalt beim Eintreten. Fokus wird zusätzlich entzogen,
  damit die Tab-Reihenfolge im Enter-Screen sauber bleibt.
*/
.page-start:not(.show-pinboard) .site-header__logo {
  visibility: hidden;
  pointer-events: none;
}
/* Sanftes Erscheinen beim Eintreten */
.site-header__logo {
  transition: opacity var(--dur-slow) var(--ease);
}
.page-start:not(.show-pinboard) .site-header__logo {
  opacity: 0;
}
/* ### /BLOCK: header-logo-enter ### */





/* ### BLOCK: enter-hint-clickable ### */
/*
  "eintreten" ist per JS klickbar (enter-hint-click).
  Zeiger und Hover-Reaktion machen das sichtbar.
*/
.enter-screen__hint {
  cursor: pointer;
  /* --dur-base existiert nicht -> Deklaration war ungueltig, der
     Farbwechsel sprang (Runde 5). */
  transition: color var(--dur-normal) var(--ease);
}
.enter-screen__hint:hover {
  color: var(--c-accent-text);
}
/* ### /BLOCK: enter-hint-clickable ### */

/* ### BLOCK: media-page ### */
/*
  Medienseite: Videos, Galerie, Auftritte, Lightbox.
  Datenquellen: videos.json, gallery.json, events.json (js/media.js)
*/

/* ── Sprungmarken im Seitenkopf ──────────────────────────────── */
.jump-nav {
  margin-block-start: var(--space-6);
}
.jump-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  list-style: none;
  padding: 0;
  margin: 0;
}
.jump-nav a {
  display: inline-block;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-btn);
  color: var(--c-text-muted);
  text-decoration: none;
  font-size: var(--font-size-small);
  transition: color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
.jump-nav a:hover {
  color: var(--c-accent-text);
  border-color: var(--c-accent);
}

/* ── Ladehinweise ────────────────────────────────────────────── */
.loading-note {
  color: var(--c-text-muted);
  font-size: var(--font-size-small);
}
.loading-note--error {
  color: var(--c-error);
}
.privacy-note {
  /* Bewusst kleingedruckt: Der Hinweis muss da sein, soll aber
     nicht mit den Video-Texten darueber konkurrieren. */
  margin-block-start: var(--space-8);
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--c-border-subtle);
  color: var(--c-text-muted);
  font-size: 0.78rem;
  line-height: 1.5;
  opacity: 0.75;
  max-inline-size: 55ch;
  /* Rechtsbuendig: der Hinweis soll sich vom Inhalt absetzen,
     nicht als weiterer Absatz gelesen werden. */
  margin-inline-start: auto;
  text-align: right;
}

/* ── Videos ──────────────────────────────────────────────────── */
.video-list {
  display: grid;
  gap: var(--space-8);
}
/* Ab Tablet nebeneinander. Darunter bleiben sie untereinander,
   sonst werden die 16:9-Flaechen zu schmal zum Antippen. */
@media (min-width: 720px) {
  .video-list {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-6);
    align-items: start;
  }
}
/* Alte Videokarten-Regeln entfernt (Runde 5, Bug 30): Der Block
   media-video-cards weiter unten ueberschreibt sie vollstaendig - und
   setzt auf dem ::before content:none, das Pseudo-Element entstand also
   gar nicht. Sie stammten aus der Zeit mit YouTube-Vorschaubildern, die
   aus Datenschutzgruenden bewusst entfallen sind.
   .video-card__frame, __title und __text bleiben - die gibt es nur hier. */
.video-card__frame {
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-card);
  overflow: hidden;
}
.video-card__frame iframe {
  inline-size: 100%;
  block-size: 100%;
  border: 0;
  display: block;
}
.video-card__title {
  margin-block: var(--space-4) var(--space-2);
  font-size: var(--text-lg);
}
.video-card__text {
  color: var(--c-text-muted);
  margin: 0;
}

/* ── Galerie ─────────────────────────────────────────────────── */
.gallery-grid {
  display: grid;
  /* auto-fill statt fester Spaltenzahl: passt sich jeder Breite an */
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-3);
}
@media (min-width: 768px) {
  .gallery-grid {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: var(--space-4);
  }
}
.gallery-item {
  display: block;
  inline-size: 100%;
  aspect-ratio: 1;         /* gleichmaessige Kacheln trotz Hoch-/Querformat */
  padding: 0;
  border: 0;
  border-radius: var(--radius-card);
  overflow: hidden;
  cursor: pointer;
  background: var(--c-bg-inset);
  -webkit-appearance: none;
  appearance: none;
  /* Aus dem geloeschten Block media-gallery uebernommen (Runde 5).
     border-color ist aus der Liste raus: border ist 0, da gab es nichts
     zu animieren. */
  transition: transform var(--dur-normal) var(--ease),
              box-shadow var(--dur-normal) var(--ease);
}

/* Karte hebt sich beim Hover. Diese Hebung ist die Vorlage fuer den
   Cover-Hover im Song-Overlay - beide muessen gleich bleiben.
   :focus-visible ist NEU: Vorher hob sich die Karte nur bei der Maus,
   per Tastatur passierte nichts. */
.gallery-item:hover,
.gallery-item:focus-visible {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}
.gallery-item img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;          /* Lesson: sonst kollabiert es als inline */
  /* --dur-base existiert in tokens.css NICHT. Eine ungueltige var() macht
     die GANZE Deklaration ungueltig - es gab hier also nie einen Uebergang,
     der Zoom sprang. Ersetzt durch --dur-normal (Runde 5). */
  transition: transform var(--dur-slow) var(--ease),
              opacity var(--dur-normal) var(--ease);
}
.gallery-item:hover img,
.gallery-item:focus-visible img {
  transform: scale(1.05);
  opacity: 0.9;
}
.gallery-item:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

/* ── Lightbox ────────────────────────────────────────────────── */
/* ### Lightbox – neu aufgebaut 28.07. ###
   Bild wird von Flexbox mittig gesetzt. Die drei Buttons liegen absolut
   darueber, damit sie das Bild NICHT aus der Mitte druecken koennen.
   Ein Layout fuer alle Geraete, keine Grid-Spalten-Tricks. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  block-size: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  /* 98 % statt 95 %: Bei 5 % Durchlaessigkeit schien die Seite dahinter
     deutlich durch und konkurrierte mit dem Foto - besonders bei dunklen
     Buehnenbildern, die stellenweise dunkler sind als die Seite darunter.
     Ein Hauch Durchblick bleibt bewusst (Runde 5). */
  background: rgb(0 0 0 / 0.98);
  touch-action: none;            /* kein Wisch-Scroll, kein Doppeltipp-Zoom */
  overscroll-behavior: contain;  /* Scroll-Impuls nicht an die Seite weiterreichen */
}
.lightbox[hidden] {
  display: none;   /* Lesson Bug 12: hidden verliert sonst gegen display */
}
.lightbox__figure {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  max-inline-size: 100%;
  max-block-size: 100%;
}
.lightbox__img {
  max-inline-size: 100%;
  max-block-size: 85dvh;
  inline-size: auto;
  block-size: auto;
  object-fit: contain;
  border-radius: var(--radius-card);
  display: block;
}
.lightbox__caption {
  display: none;   /* bewusst keine Bildunterschrift, siehe medien.js */
}
.lightbox__close,
.lightbox__nav {
  position: absolute;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgb(255 255 255 / 0.12);
  color: #fff;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  touch-action: manipulation;
  transition: background var(--dur-fast) var(--ease);
}
.lightbox__close:hover,
.lightbox__nav:hover {
  background: rgb(255 255 255 / 0.25);
}
.lightbox__close {
  inset-block-start: var(--space-4);
  inset-inline-end: var(--space-4);
  inline-size: 2.75rem;
  block-size: 2.75rem;
  z-index: 2;
}
.lightbox__nav {
  inset-block-start: 50%;
  transform: translateY(-50%);
  inline-size: 3rem;
  block-size: 3rem;
  z-index: 2;
}
.lightbox__nav--prev { inset-inline-start: var(--space-3); }
.lightbox__nav--next { inset-inline-end: var(--space-3); }

@media (max-width: 560px) {
  /* Pfeile bleiben seitlich, aber kleiner und naeher am Rand.
     Das Bild bleibt durch Flexbox automatisch mittig. */
  .lightbox { padding: var(--space-2); }
  .lightbox__nav {
    inline-size: 2.75rem;
    block-size: 2.75rem;
  }
  .lightbox__nav--prev { inset-inline-start: var(--space-1); }
  .lightbox__nav--next { inset-inline-end: var(--space-1); }
  .lightbox__img { max-block-size: 80dvh; }
}

/* ── Auftritte ───────────────────────────────────────────────── */
.event-block + .event-block {
  margin-block-start: var(--space-10);
}
.event-block__title {
  font-size: var(--text-lg);
  color: var(--c-accent-text);
  margin-block-end: var(--space-5);
}
.event-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-4);
}
.event-item {
  display: grid;
  gap: var(--space-1) var(--space-5);
  align-items: start;
  padding: var(--space-5);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-card);
  background: var(--c-bg-inset);
}
@media (min-width: 640px) {
  .event-item {
    grid-template-columns: 10rem 1fr auto;
    align-items: center;
  }
}
.event-item__date {
  color: var(--c-accent-text);
  font-weight: var(--weight-semibold);
  font-size: var(--font-size-small);
}
.event-item__title {
  margin: 0;
  font-weight: var(--weight-semibold);
}
.event-item__place,
.event-item__text {
  margin: 0;
  color: var(--c-text-muted);
  font-size: var(--font-size-small);
}
.event-item__btn {
  justify-self: start;
}
.events-cta {
  margin-block-start: var(--space-8);
  color: var(--c-text-muted);
}
/* ### /BLOCK: media-page ### */

/* ### BLOCK: media-video-cards ### */
/*
  Videokarten ohne Vorschaubild.

  Warum keine Thumbnails: YouTube-Vorschaubilder liegen auf
  img.youtube.com – ein Google-Server. Sie zu laden waere ein
  Kontakt zu Google noch vor dem Klick und wuerde den Zweck der
  Klick-Aktivierung aufheben. Eigene Screenshots aus fremdem
  Material (The Voice) sind urheberrechtlich nicht zulaessig.

  Deshalb: gestaltete Flaeche, Play-Symbol, klarer Hinweistext.
  Ueberschreibt die aeltere .video-card__preview-Regel im
  media-page-Block.
*/
.video-card__preview,
.video-card__preview--extern {
  display: grid;
  place-content: center;
  gap: var(--space-3);
  justify-items: center;
  inline-size: 100%;
  aspect-ratio: 16 / 9;
  padding: var(--space-6);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-card);
  background: var(--c-bg-inset);
  background-image: none;
  color: var(--c-text-muted);
  text-decoration: none;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  -webkit-appearance: none;
  appearance: none;
  /* --dur-base existiert nicht (Runde 5) */
  transition: border-color var(--dur-normal) var(--ease),
              background var(--dur-normal) var(--ease);
}
/* Die Gegenregel "content: none" ist entfallen (Runde 5) - sie hat nur
   den Abdunkler aus dem geloeschten Block neutralisiert. Ohne den Block
   gibt es nichts mehr zu neutralisieren. */
.video-card__preview:hover,
.video-card__preview:focus-visible {
  border-color: var(--c-accent);
  background: var(--c-bg-elevated);
}
.video-card__preview:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

/* Play-Symbol: statisch positioniert statt absolut zentriert,
   damit der Hinweistext darunter Platz hat */
.video-card__play {
  position: static;
  translate: none;
  inline-size: 4rem;
  block-size: 4rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--c-accent);
  color: var(--c-text-on-accent);
  transition: transform var(--dur-normal) var(--ease);   /* war --dur-base */
}
.video-card__preview:hover .video-card__play {
  transform: scale(1.08);
}

.video-card__hint {
  font-size: var(--font-size-small);
  text-align: center;
  max-inline-size: 28ch;
  line-height: 1.4;
}

/* Hinweis unter der Karte, z. B. gesperrte Einbettung */
.video-card__note {
  margin-block-start: var(--space-2);
  color: var(--c-text-muted);
  font-size: var(--font-size-small);
  font-style: italic;
}
/* ### /BLOCK: media-video-cards ### */

/* ### BLOCK: media-section-spacing ### */
/*
  Aufeinanderfolgende .section-Bloecke addieren ihr padding-block –
  zwischen zwei Abschnitten entsteht so der doppelte Abstand.
  Auf der Medienseite mit drei Abschnitten faellt das stark auf.

  Loesung: Ab dem zweiten Abschnitt oben weniger Padding.
  Gilt nur innerhalb von main auf dieser Seite, damit andere
  Seiten unberuehrt bleiben.
*/
#main-content .section + .section {
  padding-block-start: var(--space-5);
}
/* ### /BLOCK: media-section-spacing ### */


/* ### BLOCK: media-section-divider ### */
/*
  Trennlinien zwischen den Abschnitten der Medienseite.

  Wichtig: .section bringt padding-block oben UND unten mit.
  Wird nur padding-block-start angepasst, bleibt unten der volle
  Wert stehen – die Linie sitzt dann naeher am folgenden als am
  vorangehenden Abschnitt. Deshalb beide Seiten setzen.
*/
#main-content .section + .section {
  border-block-start: 1px solid var(--c-border-subtle);
  padding-block: var(--space-8);
}
/* Der erste Abschnitt braucht unten denselben Abstand,
   sonst sitzt die erste Linie zu tief. */
#main-content .section:first-of-type {
  padding-block-end: var(--space-8);
}
/* Letzter Abschnitt: unten wieder mehr Luft zum Footer */
#main-content .section:last-of-type {
  padding-block-end: var(--space-12);
}
/* ### /BLOCK: media-section-divider ### */

/* ### BLOCK: media-event-ticket ### */
/*
  Uhrzeit unter dem Datum, Ticket-Hinweise statt Button.
  Gesteuert ueber ticket_typ in events.json (js/media.js).
*/

/* Datumsspalte wird zweizeilig: Datum, darunter Uhrzeit */
.event-item__date {
  display: block;
}
.event-item__time {
  display: block;
  margin-block-start: 0.15rem;
  color: var(--c-text-muted);
  font-size: var(--font-size-small);
}

/*
  Hinweis statt Button. Bewusst KEIN Button-Aussehen: Es gibt
  nichts zu klicken, und ein Button-artiges Element, das nicht
  reagiert, irritiert mehr als es hilft.
*/
.event-item__ticket-note {
  justify-self: start;
  padding: var(--space-2) var(--space-4);
  border: 1px dashed var(--c-border-subtle);
  border-radius: var(--radius-btn);
  color: var(--c-text-muted);
  font-size: var(--font-size-small);
  white-space: nowrap;
}
.event-item__ticket-note--out {
  border-style: solid;
  border-color: var(--c-error);
  color: var(--c-error);
}
/* ### /BLOCK: media-event-ticket ### */

/* ### BLOCK: media-event-when ### */
/*
  Datum und Uhrzeit als eine Grid-Zelle. Ohne Wrapper wuerde die
  Uhrzeit eine eigene Spalte beanspruchen und die Karte
  auseinanderziehen.
*/
.event-item__when {
  display: grid;
  gap: 0.15rem;
}
.event-item__time {
  margin-block-start: 0;
}
/* ### /BLOCK: media-event-when ### */

/* ### BLOCK: media-event-btn-mobile ### */
/*
  Auf schmalen Schirmen steht der Event-Button unter dem Text.
  Linksbuendig wirkt er dort verloren – rechts sitzt er am Ende
  der Karte und liest sich als Abschluss.
*/
@media (max-width: 639px) {
  .event-item__btn,
  .event-item__ticket-note {
    justify-self: end;
    font-size: 0.8rem;
    padding-block: var(--space-2);
    padding-inline: var(--space-4);
  }
}
/* ### /BLOCK: media-event-btn-mobile ### */

/* ### BLOCK: media-event-address ### */
/*
  Adressblock als <address>: semantisch korrekt und optisch
  vom Beschreibungstext abgesetzt, damit Titel/Beschreibung und
  Ortsangaben als zwei Gruppen lesbar sind.
*/
.event-item__address {
  margin-block-start: var(--space-3);
  margin-block-end: 0;
  color: var(--c-text-muted);
  font-size: var(--font-size-small);
  font-style: normal;    /* Browser kursiviert address sonst */
  line-height: 1.5;
}
.event-item__text {
  margin-block-start: 0.15rem;
}
/* ### /BLOCK: media-event-address ### */

/* ### BLOCK: legal-stand ### */
/*
  Stand-Datum auf Rechtsseiten. Bewusst fest eingetragen und nicht
  automatisch erzeugt: Es dokumentiert, wann der Text zuletzt
  geaendert wurde – ein taeglich wechselndes Datum waere falsch.
  Bei jeder inhaltlichen Aenderung von Hand nachziehen.
*/
.legal-page__stand {
  margin-block-start: var(--space-10);
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--c-border-subtle);
  color: var(--c-text-muted);
  font-size: var(--font-size-small);
}
.legal-page__back {
  margin-block-start: var(--space-6);
}
/* ### /BLOCK: legal-stand ### */

/* ### BLOCK: quote-lyrics-panel ### */
/*
  Lyrics-Vollbild-Panel + ruhiges Song-Overlay (Umbau 28.07.).
  Scroll nach Lightbox-Lehren (Bugs 31/32): eigener Scroll-Bereich,
  100dvh statt 100vh, overscroll-behavior gegen Durchscrollen.
*/
.quote-overlay__lyrics {
  position: fixed;
  inset: 0;
  z-index: 20;                        /* ueber allem im Overlay */
  display: flex;
  flex-direction: column;
  block-size: 100dvh;
  background: color-mix(in srgb, var(--c-bg-base) 96%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  padding: var(--space-6) var(--space-4) 0;
  overscroll-behavior: contain;
}
.quote-overlay__lyrics[hidden] { display: none; }   /* Bug 12 */

/* Lyrics-x: siehe Block ui-close am Dateiende. Hier stand die erste von
   fuenf Definitionen - noch als Flex-Kind gedacht (align-self/margin),
   laengst ueberholt. Entfernt in Runde 3c. */
.quote-overlay__lyrics-scroll {
  flex: 1;
  min-block-size: 0;                  /* Flex-Kind darf schrumpfen -> scrollt */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  touch-action: pan-y;
  padding-block-end: var(--space-10);
}
.quote-overlay__lyrics-scroll .quote-overlay__verse {
  margin-inline: auto;
  max-inline-size: 40ch;
}

/* Cover: kleines Artwork im Textfluss, zentriert. display:block (Bug 5) */
.quote-overlay__cover {
  display: block;
  inline-size: min(220px, 60vw);
  margin-inline: auto;
  margin-block: var(--space-5);
}
.quote-overlay__cover img {
  display: block;
  inline-size: 100%;
  border-radius: var(--radius-card);
}

/* Buttons: Spotify-Link + Lyrics-Button als Pill im Champagner-Look */
.quote-overlay__link.btn,
.quote-overlay__lyrics-btn.btn {
  display: block;
  inline-size: fit-content;
  margin-inline: auto;
  margin-block: var(--space-3);
  padding: var(--space-3) var(--space-6);
  border: 1px solid var(--c-accent);
  border-radius: var(--radius-btn);
  background: color-mix(in srgb, var(--c-accent) 12%, transparent);
  color: var(--c-accent-text, var(--c-accent));
  text-decoration: none;
  touch-action: manipulation;
  transition: background var(--dur-normal) var(--ease);
}
.quote-overlay__link.btn:hover,
.quote-overlay__link.btn:focus-visible,
.quote-overlay__lyrics-btn.btn:hover,
.quote-overlay__lyrics-btn.btn:focus-visible {
  background: color-mix(in srgb, var(--c-accent) 24%, transparent);
}
/* ### /BLOCK: quote-lyrics-panel ### */

/* ### BLOCK: quote-cover-zoom ### */
.quote-overlay__zoom {
  position: fixed;
  inset: 0;
  z-index: 30;                       /* ueber dem Lyrics-Panel */
  display: flex;
  align-items: center;
  justify-content: center;
  block-size: 100dvh;
  padding: var(--space-4);
  border: none;
  /* Grund und Bildmasse stehen im Block overlay-einheitlichkeit weiter
     unten - dort im Look der Galerie-Lightbox. Die frueheren Werte hier
     (modusabhaengiger Grund, blur(10px), 88dvh) wurden vollstaendig
     ueberschrieben und sind in Runde 5 entfallen (Bug 30).
     Der Weichzeichner lief seit dem Umbau am 28.07. nie mehr - bei 98 %
     Deckung gaebe es ohnehin nichts zu verwischen. */
  touch-action: manipulation;
  overscroll-behavior: contain;
}
.quote-overlay__zoom[hidden] { display: none; }
.quote-overlay__zoom img {
  display: block;
}
/* ### /BLOCK: quote-cover-zoom ### */

/* Bug-12-Absicherung (28.07.): display-Regeln duerfen hidden nie uebersteuern.
   Steht bewusst am Dateiende - gewinnt bei gleicher Spezifitaet. */
.quote-overlay__bg[hidden],
.quote-overlay__link[hidden],
.quote-overlay__lyrics-btn[hidden],
.quote-overlay__cover[hidden] { display: none; }

/* ### BLOCK: overlay-einheitlichkeit ### */
/* Lyrics-x + Cover-Zoom im Look der Galerie-Lightbox (28.07.).
   Steht am Dateiende und ueberstimmt so die aelteren Panel-Regeln. */
.quote-overlay__lyrics { padding-block-start: calc(var(--space-5) + 48px + var(--space-4)); } /* Scroll beginnt UNTER dem x */
/* Zweite von fuenf Lyrics-x-Definitionen (2.75rem, grauer Kreis).
   Entfernt in Runde 3c - siehe Block ui-close. */

.quote-overlay__zoom {
  background: rgb(0 0 0 / 0.98);      /* wie .lightbox */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.quote-overlay__zoom img {
  /* Werte wie .lightbox__img - eine Bildansicht-Sprache.
     max-inline-size aus dem geloeschten Block uebernommen (Runde 5). */
  max-inline-size: min(92vw, 720px);
  max-block-size: 85dvh;
  border-radius: var(--radius-card);
}
/* Zoom-x: siehe Block ui-close am Dateiende. Hier stand hartes #fff -
   ersetzt durch den Token --c-close-on-dark. Entfernt in Runde 3c. */
/* ### /BLOCK: overlay-einheitlichkeit ### */

/* ### BLOCK: lyrics-close-vereinheitlicht ### */
/* Ein x pro Ebene: Solange das Lyrics-Panel offen ist, pausiert das
   Overlay-x darunter. Position und Groesse kommen aus ui-close - die
   Wiederholungen hier sind in Runde 3c entfallen. */
.quote-overlay.lyrics-open .quote-overlay__close { visibility: hidden; }
/* ### /BLOCK: lyrics-close-vereinheitlicht ### */

/* ### BLOCK: zoom-ein-x ### */
/* Cover: normaler Zeiger statt Lupe. Zoom offen -> alle anderen x pausieren
   (Ein-x-pro-Ebene, wie beim Lyrics-Panel). Am Dateiende -> gewinnt. */
/* Die cursor-Regel aufs img ist entfallen (Runde 4): Das img hat
   pointer-events:none, war also nie Trefferziel - die Regel hat nie
   gewirkt. Der Zeiger kommt jetzt vom Cover-Button. */
.quote-overlay.zoom-open .quote-overlay__close,
.quote-overlay.zoom-open .quote-overlay__lyrics-close { visibility: hidden; }
/* ### /BLOCK: zoom-ein-x ### */

/* Block x-deckungsgleich entfernt (Runde 3c): Deckungsgleichheit ergibt
   sich jetzt daraus, dass alle x dieselbe Regel benutzen - nicht mehr
   aus wiederholten Positionsangaben, die auseinanderlaufen koennen. */

/* ### BLOCK: cover-hover ### */
/* Cover hovert wie ein Galeriebild (Werte aus media-gallery uebernommen):
   Karte hebt sich 2px + Schatten, Bild skaliert auf 1.04 (28.07.). */
.quote-overlay__cover {
  overflow: hidden;                 /* skalierendes Bild bleibt in der Rundung */
  border-radius: var(--radius-card);
  transition: transform var(--dur-normal) var(--ease),
              box-shadow var(--dur-normal) var(--ease);
}
.quote-overlay__cover img {
  /* Exakt die Werte von .gallery-item img - eine Geste, eine Sprache. */
  transition: transform var(--dur-slow) var(--ease),
              opacity var(--dur-normal) var(--ease);
}
.quote-overlay__cover:hover,
.quote-overlay__cover:focus-within {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}
.quote-overlay__cover:hover img,
.quote-overlay__cover:focus-within img {
  /* An die Galerie angeglichen: 1.05 mit leichter Abdunklung
     (war 1.04 ohne opacity). */
  transform: scale(1.05);
  opacity: 0.9;
}
/* ### /BLOCK: cover-hover ### */

/* ### BLOCK: lyrics-scrollbar ### */
/* Scrollleiste im Theme - Werte vom frueheren Zettel uebernommen (28.07.).
   Dazu: x liegt IMMER ueber dem scrollenden Text (Stapel-Fix Handy). */
/* Die z-index-Zeile fuers Lyrics-x ist entfallen (Runde 3c):
   ui-close bringt z-index:3 mit, liegt also weiterhin ueber dem Text. */
.quote-overlay__lyrics-scroll {
  scrollbar-width: thin;                          /* Firefox */
  scrollbar-color: var(--c-accent) transparent;
}
/* Die vier ::-webkit-scrollbar-Regeln sind entfallen (Runde 5):
   Sobald scrollbar-color gesetzt ist, ignorieren Chrome, Edge und Safari
   die Pseudo-Element-Regeln vollstaendig. Sie waren wirkungslos.
   Die Farbe kommt jetzt ohnehin global aus base.css (base-root) und
   vererbt sich hierher - scrollbar-color ist eine vererbte Eigenschaft. */
/* ### /BLOCK: lyrics-scrollbar ### */

/* ### BLOCK: ebenen-einheitlich ### */
/*
  Alle Ebenen blenden gleich ein (Runde 2). Vorher fadete nur das
  Overlay; Lyrics-Panel, Cover-Zoom und Galerie-Lightbox sprangen auf.
  Bewusst als ANIMATION, nicht als Transition: Die Ebenen werden ueber
  [hidden] geschaltet, und eine Animation startet von selbst neu, sobald
  ein Element aus display:none zurueckkommt - eine Transition kann das
  nicht. Deshalb ohne jede JS-Aenderung.
  reduced-motion greift automatisch ueber base-motion in base.css.
*/
@keyframes ebene-einblenden {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.quote-overlay__lyrics:not([hidden]),
.quote-overlay__zoom:not([hidden]),
.lightbox:not([hidden]) {
  animation: ebene-einblenden var(--dur-normal) var(--ease);
}
/* ### /BLOCK: ebenen-einheitlich ### */

/* ### BLOCK: scroll-lock ### */
/*
  EIN Scroll-Lock fuer alle Vollbild-Ebenen (Runde 2).
  Vorher: body.overlay-open{overflow:hidden} war wirkungslos, weil die
  Weitergabe an den Viewport nur greift, wenn <html> auf visible steht -
  base.css setzt dort overflow-x:hidden. medien.js sperrte stattdessen
  per Inline-Style. Jetzt reicht :has() die Klasse an <html> weiter,
  also an den echten Scroll-Container. :has() ist im Projekt bereits
  im Einsatz (site-header), keine neue Abhaengigkeit.
*/
html:has(body.overlay-open) {
  overflow: hidden;
}

/*
  Scroll-Impuls am Ende des Overlays NICHT an die Seite dahinter
  weiterreichen. Am Handy wandert sonst beim Weiterwischen am unteren
  Ende der Hintergrund mit. touch-action:none ist hier ausgeschlossen -
  das Overlay muss selbst scrollen koennen.
*/
.quote-overlay {
  overscroll-behavior: contain;
}
/* ### /BLOCK: scroll-lock ### */

/* ### BLOCK: ui-close ### */
/*
  EINE Bildsprache fuer alle Icon-Schaltflaechen auf Vollbild-Ebenen
  (Runde 3): Schliess-x und Blaetter-Pfeile.
  Vorher vier Bauarten mit drei Groessen, drei Farben, drei Hover-Verhalten.

  Prinzip: 48px sind die TREFFERFLAECHE, sichtbar ist nur der Strich.
  Wirkt feiner, ohne am Handy schwerer treffbar zu werden.

  Aufbau: erst das gemeinsame Aussehen, dann je Rolle die Position.
  So gibt es genau EINE Stelle fuer Farbe, Groesse und Hover.
*/
.ui-close,
.lightbox__nav {
  z-index: 3;
  display: grid;
  place-items: center;
  inline-size: 48px;
  block-size: 48px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: none;
  color: var(--c-accent-text);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  touch-action: manipulation;
  /* Aeltere Regeln bringen backdrop-filter mit - ohne Zuruecksetzen
     bliebe eine Weichzeichner-Scheibe ohne Hintergrund stehen. */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}

/* Position: das x immer oben rechts, auf jeder Ebene und Seite */
.ui-close {
  position: fixed;
  inset-block-start: var(--space-5);
  inset-inline-end: var(--space-5);
}

/* Position: Pfeile mittig an den Seitenraendern.
   translate statt transform - eine eigene Eigenschaft, die nicht mit
   transform-Regeln kollidiert (etwa dem transform:none im Hover unten).
   Die alte transform-Zentrierung MUSS dabei zurueckgesetzt werden,
   sonst addieren sich beide zu -100%. */
.lightbox__nav {
  position: absolute;
  inset-block-start: 50%;
  transform: none;
  translate: 0 -50%;
}
.lightbox__nav--prev { inset-inline-start: var(--space-5); }
.lightbox__nav--next { inset-inline-end: var(--space-5); }

/* Am Handy naeher an den Rand, sonst liegen die Pfeile auf dem Bild */
@media (max-width: 560px) {
  .lightbox__nav--prev { inset-inline-start: var(--space-2); }
  .lightbox__nav--next { inset-inline-end: var(--space-2); }
}

/* Feinerer Strich, kleineres Glyph. CSS schlaegt die width/height- und
   stroke-width-Attribute im HTML - kein Markup-Eingriff noetig. */
.ui-close svg,
.lightbox__nav svg {
  inline-size: 22px;
  block-size: 22px;
  stroke-width: 1.5;
}
/* Chevrons fuellen ihre Flaeche schlechter aus als ein x und wirken
   bei gleicher Groesse leichter - zwei Pixel gleichen das aus. */
.lightbox__nav svg {
  inline-size: 24px;
  block-size: 24px;
}

/* Keine Rotation mehr: ein sich drehendes x ist ein Software-Gestus
   und stand quer zum ruhigen Ton der Seite. */
.ui-close:hover,
.ui-close:focus-visible,
.lightbox__nav:hover,
.lightbox__nav:focus-visible {
  background: color-mix(in srgb, var(--c-accent) 12%, transparent);
  color: var(--c-accent);
  transform: none;
}
.ui-close:focus-visible,
.lightbox__nav:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: -2px;
}

/* Immer dunkler Grund: Cover-Zoom, Galerie-Lightbox, Bild-Overlays.
   Dort gilt der Hell/Dunkel-Modus nicht - die Flaeche ist in beiden
   Modi schwarz. Der Schatten haelt den Strich auch ueber hellen
   Bildpartien lesbar; dafuer war frueher der graue Kreis da. */
.ui-close--on-dark,
.lightbox__nav,
.quote-overlay.has-image .ui-close {
  color: var(--c-close-on-dark);
}
.ui-close--on-dark svg,
.lightbox__nav svg,
.quote-overlay.has-image .ui-close svg {
  filter: drop-shadow(0 1px 3px rgb(0 0 0 / 0.6));
}
.ui-close--on-dark:hover,
.ui-close--on-dark:focus-visible,
.lightbox__nav:hover,
.lightbox__nav:focus-visible,
.quote-overlay.has-image .ui-close:hover,
.quote-overlay.has-image .ui-close:focus-visible {
  background: color-mix(in srgb, var(--c-close-on-dark) 15%, transparent);
  color: #fff;
}
/* ### /BLOCK: ui-close ### */

/* ### BLOCK: quote-cover-btn ### */
/*
  Cover als echter Button (Runde 4). Der Button traegt selbst KEINE Optik -
  Groesse, Rundung und Schatten bleiben beim <picture> darin. So gelten
  alle bestehenden Cover-Regeln unveraendert weiter.
*/
.quote-overlay__cover-btn {
  display: block;
  inline-size: fit-content;
  /* Aussenabstand gehoert an den BUTTON, nicht ans <picture>: Ein Button
     bildet einen eigenen Formatierungskontext, die Abstaende des Kindes
     lagen also INNERHALB der Schaltflaeche - Fokusring und Klickflaeche
     waren dadurch 40px hoeher als das Bild. Im Layout aendert der Umzug
     nichts (Flex-Container, keine kollabierenden Abstaende). */
  margin-inline: auto;
  margin-block: var(--space-5);
  padding: 0;
  border: 0;
  background: none;
  /* cursor vererbt sich aufs <picture>. Das img hat pointer-events:none
     und ist nie Trefferziel - deshalb blieb die alte Regel
     ".quote-overlay__cover img { cursor:pointer }" wirkungslos. */
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  touch-action: manipulation;
  border-radius: var(--radius-card);
}
.quote-overlay__cover-btn .quote-overlay__cover {
  margin-block: 0;
}

/* Bug 12: display-Regel darf hidden nie uebersteuern */
.quote-overlay__cover-btn[hidden] { display: none; }

.quote-overlay__cover-btn:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
  /* Hebung am BUTTON statt am <picture>: So wandert der Fokusring mit,
     statt 2px versetzt stehenzubleiben. */
  transform: translateY(-2px);
}
.quote-overlay__cover-btn:focus-visible .quote-overlay__cover {
  box-shadow: var(--shadow-lg);
}
.quote-overlay__cover-btn:focus-visible .quote-overlay__cover img {
  transform: scale(1.05);
  opacity: 0.9;
}
/* ### /BLOCK: quote-cover-btn ### */

/* ### BLOCK: event-past ### */
/*
  "Bisher gespielt": gleiche Formsprache wie die kommenden Auftritte,
  aber flacher. Weniger Innenabstand, leichterer Titel, keine Ticket-
  Elemente - Vergangenes ist Nachweis, nicht Ankuendigung.
  Doppelte Klasse im Selektor: schlaegt die padding-Kurzschreibweise
  von .event-item unabhaengig von der Reihenfolge in der Datei.
*/
.event-item.event-item--past {
  padding-block: var(--space-4);
  /* Ausgebleicht ueber die TRANSPARENZ DER FLAECHE, nicht ueber opacity
     der ganzen Karte: opacity wuerde den Text mitziehen und den Kontrast
     unter die WCAG-Grenze druecken. So tritt nur der Kasten zurueck, die
     Schrift bleibt voll lesbar. Gleiche Sprache wie die Pinnwand-Karten
     (pin-card__btn). Doppelte Klasse schlaegt .event-item unabhaengig
     von der Reihenfolge in der Datei. */
  background: color-mix(in srgb, var(--c-bg-inset) 45%, transparent);
  border-color: color-mix(in srgb, var(--c-border-subtle) 60%, transparent);
}
.event-item--past .event-item__title {
  font-weight: var(--weight-medium);
}
/* Eine Zeile: STADT · Location. flex mit baseline, damit die kleine
   Versal-Stadt und die groessere Location auf derselben Schriftlinie
   sitzen. flex-wrap faengt lange Namen auf schmalen Schirmen ab. */
.event-item--past .event-item__place {
  margin-block-start: 0.15rem;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
}
.event-item__city {
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-semibold);
  font-size: var(--text-xs);
  color: var(--c-accent-text);
}
.event-item__sep {
  color: var(--c-text-muted);
  opacity: 0.6;
}
.event-item__venue {
  color: var(--c-text-muted);
}

/* Stadt mit Haken - dritte Spalte, rechts */
.event-item__done {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  justify-self: end;
  color: var(--c-accent-text);
  font-size: var(--font-size-small);
  white-space: nowrap;
}
.event-item__done-icon {
  flex-shrink: 0;
}

@media (min-width: 640px) {
  .event-item__done { align-self: center; }
}
/* ### /BLOCK: event-past ### */

/* ### BLOCK: events-more ### */
/*
  "Bisher gespielt" klappt ab dem dritten Eintrag ein (Runde 5).
*/

/* ⚠️ Bug 12: .event-item setzt display:grid und wuerde das
   hidden-Attribut sonst uebersteuern - die "versteckten" Karten blieben
   einfach sichtbar. Bei JEDER display-Regel auf hidden-gesteuerten
   Elementen sofort den Guard mitdenken. */
.event-item[hidden] { display: none; }

.events-more__btn {
  display: flex;                 /* .btn ist inline-flex - fuers Zentrieren */
  margin-inline: auto;
  margin-block-start: var(--space-5);
}

/* Der Header klebt oben (68px) und wuerde die angesprungene Ueberschrift
   verdecken. scroll-margin haelt beim Springen Abstand frei - wirkt nur
   auf Sprungziele, nicht aufs normale Layout. */
.event-block__title {
  scroll-margin-block-start: calc(var(--nav-height) + var(--space-4));
}

/* ### /BLOCK: events-more ### */

/* ### BLOCK: ebenen-scrollleisten ### */
/*
  Ueberlagernde Scrollleisten (Windows: "Bildlaufleisten automatisch
  ausblenden") zeichnet der Browser AUSSERHALB des DOM - ueber allem,
  auch ueber einer Vollbild-Ebene mit z-index. Die Leiste der pausierten
  Ebene schien deshalb durch Cover-Zoom und Galerie-Lightbox.

  GEMESSEN 30.07., nach zwei falschen Anlaeufen:
  - scrollbar-color:transparent wirkt NICHT - Chrome ignoriert die Farbe
    fuer diese Leisten.
  - scrollbar-width:none wirkt.
  Der Hebel ist also die Breite, nicht die Farbe. NICHT auf Farbe umbauen.

  Kein Sprung zu befuerchten: Bei KLASSISCHEN Leisten tritt der Fehler
  gar nicht auf (die Leiste liegt im Layout, der Zoom deckt sie mit ab),
  und ueberlagernde Leisten haben ohnehin null Breite.

  ::-webkit-scrollbar bewusst weggelassen: Sobald scrollbar-color gesetzt
  ist - und das steht global in base.css und vererbt sich - ignoriert
  Chrome die Pseudo-Element-Regeln. Sie waeren toter Code (Runde 5).
*/

/* Seiten-Leiste, solange eine Vollbild-Ebene offen ist */
html:has(body.overlay-open) {
  scrollbar-width: none;
}

/* Overlay-Leiste, solange Lyrics oder Zoom darueber liegen */
.quote-overlay.lyrics-open,
.quote-overlay.zoom-open {
  scrollbar-width: none;
}

/* Der Zoom liegt ueber dem Lyrics-Panel: dann pausiert auch dessen Leiste */
.quote-overlay.zoom-open .quote-overlay__lyrics-scroll {
  scrollbar-width: none;
}
/* ### /BLOCK: ebenen-scrollleisten ### */

/* ### BLOCK: error-page ### */
/* Rueckwege auf der 404-Seite: mehrere Buttons nebeneinander. */
.legal-page__back {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* Am Handy stapeln sich die Buttons - dann volle Breite statt drei
   verschieden breite Kaestchen untereinander. */
@media (max-width: 480px) {
  .legal-page__back .btn {
    inline-size: 100%;
  }
}
/* ### /BLOCK: error-page ### */

/* ### BLOCK: type-choice ### */
/*
  Anfrage-Typ als drei sichtbare Karten statt eines <select> (04.08.).
  Grund: Ein zugeklapptes Menue verbirgt, was ueberhaupt moeglich ist -
  gerade "Zusammenarbeit" wurde so nie gesehen.

  ⚠️ Die Radios sind NICHT display:none. Ein verstecktes Radio ist nicht
  fokussierbar - Tastatur, Pfeiltasten und Screenreader verlieren die
  Gruppe. Stattdessen liegt es unsichtbar UEBER der Karte (opacity:0),
  behaelt also Fokus und Trefferflaeche. Dieselbe Lehre wie beim
  Honeypot: aus dem Sichtfeld, nicht aus dem Baum.
*/
.type-choice {
  border: 0;
  padding: 0;
  margin: 0;
  min-inline-size: 0;
}

/* Luft zwischen der Auswahl und dem Formular darunter. Beide hatten
   margin:0 - der Koerper begann direkt unter der letzten Karte, was am
   Handy zu gedraengt wirkte. Am Hinweis im Grundzustand liegt es nicht:
   der verschwindet ja nach der Auswahl. */
/* ⚠️ NICHT als Nachbar-Selektor (.type-choice + .form-body): Zwischen
   beiden steht der Hinweisabsatz .form-hint. Der ist zwar hidden, sobald
   gewaehlt wurde - im DOM bleibt er aber stehen, und "+" verlangt einen
   DIREKTEN Nachbarn. Die Regel griff deshalb nie (gemessen: 0px).
   Stattdessen die Elemente selbst ansprechen. */
/* Ein Wert fuer alle Breiten: Am PC stehen die Felder nebeneinander und
   bringen von sich aus mehr Luft mit - eine zusaetzliche Erhoehung war
   dort zu viel. Am Handy stapelt sich alles, dort traegt dieser Abstand
   allein. */
.form-hint,
.form-body {
  margin-block-start: var(--space-8);
}

.type-choice > legend {
  /* legend ist im fieldset ein Sonderelement und laesst sich nicht
     normal positionieren - float loest es heraus (wie .form-branch). */
  float: left;
  inline-size: 100%;
  padding: 0;
  margin-block-end: var(--space-3);
}

.type-choice__list {
  clear: both;          /* nach dem float der legend wieder darunter */
  display: grid;
  gap: var(--space-3);
}

.type-card {
  position: relative;
  display: block;
  cursor: pointer;
  /* Sprungziel nach der Auswahl (js/kontakt.js). Ohne scroll-margin
     schoebe der klebende Header die Karte unter sich. */
  scroll-margin-block-start: calc(var(--nav-height) + var(--space-4));
}

/* Unsichtbar, aber vorhanden: deckt die ganze Karte ab. */
.type-card__input {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
  /* appearance:none verhindert, dass iOS dem gedehnten Radio eine
     eigene Mindestgroesse aufzwingt. */
  -webkit-appearance: none;
  appearance: none;
}

.type-card__body {
  display: flex;
  /* Icon mittig zur ganzen Karte statt an der Titelzeile: Bei zwei
     Textzeilen wirkt ein oben ausgerichtetes Icon nach oben gerutscht. */
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--c-border-subtle);
  border-radius: var(--radius-card);
  background: var(--c-bg-inset);
  transition: border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}

.type-card__icon {
  flex-shrink: 0;
  color: var(--c-accent-text);
}

.type-card__text {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.type-card__title {
  font-weight: var(--weight-semibold);
  color: var(--c-text-primary);
  line-height: var(--leading-snug);
}

.type-card__hint {
  font-size: var(--font-size-small);
  color: var(--c-text-muted);
  line-height: 1.4;
}

/* Hover: nur andeuten, nicht schon wie gewaehlt aussehen. */
.type-card:hover .type-card__body {
  border-color: var(--c-border-default);
  background: var(--c-bg-elevated);
}

/* Gewaehlt: Rahmen in Akzentfarbe. Zweiter Rahmen per box-shadow statt
   dickerem border - eine wachsende Rahmenbreite wuerde das Layout um
   1px verschieben und die Karten beim Klicken zucken lassen. */
.type-card__input:checked + .type-card__body {
  border-color: var(--c-accent);
  box-shadow: 0 0 0 1px var(--c-accent);
  background: var(--c-bg-elevated);
}

/* Fokusring an der sichtbaren Karte, nicht am unsichtbaren Radio.
   Negativer offset: Der Ring liegt innen und wird an keiner Kante
   abgeschnitten (Projektlehre aus dem Header). */
.type-card__input:focus-visible + .type-card__body {
  outline: 2px solid var(--c-focus);
  outline-offset: -2px;
}

/* Fehlerzustand: Der Fehler gilt der GRUPPE, nicht einer Karte.
   Deshalb faerbt aria-invalid am ersten Radio alle drei Rahmen. */
.type-choice:has(.type-card__input[aria-invalid="true"]) .type-card__body {
  border-color: var(--c-error);
}

/* Die Statusicons aus form-field-status gelten fuer Textfelder.
   Auf einem unsichtbaren Radio waere ein Haken sinnlos - abschalten. */
.type-card__input[aria-invalid] {
  background-image: none;
  padding-right: 0;
}

@media (prefers-reduced-motion: reduce) {
  .type-card__body { transition: none; }
}

/* ⚠️ Bug 12: .form-group traegt eine display-Regel und wuerde das
   hidden-Attribut uebersteuern - die bedingten Freitextfelder waeren
   dauerhaft sichtbar. Genau das ist am 04.08. passiert. */
.form-group[hidden] { display: none; }
/* ### /BLOCK: type-choice ### */

/* ### BLOCK: quote-events ### */
/*
  Kommende Termine in der Ticket-Karte (04.08.).
  Vorher behauptete die Karte "Aktuell stehen keine Konzerte an" - auch
  dann, wenn in events.json einer stand. Jetzt zeigt sie, was ansteht,
  und die Eintrage-Zeile bezieht sich auf die Termine DANACH.

  Bewusst schlichter als die Event-Karten auf der Kontaktseite: Hier
  geht es um einen schnellen Blick, nicht um Adresse und Anfahrt.
*/
.quote-events {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  /* 44rem statt 30rem: Die Event-Karte geht ab 640px in drei Spalten
     (Datum | Text | Ticket) - bei 30rem waere sie gequetscht. Gleiche
     Breite wie der uebrige Overlay-Text. */
  max-width: 44rem;
  margin-block-start: var(--space-4);
}

/* ⚠️ Bug 12: display-Regel wuerde hidden sonst uebersteuern. */
.quote-events[hidden] { display: none; }

.quote-events__title {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--c-accent-text);
  margin: 0;
}

.quote-events__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-2);
  width: 100%;
}

/* Die eigenen .quote-event-Regeln sind entfallen (04.08.): Die Liste
   kommt jetzt aus baueEventListe und traegt damit die .event-item-
   Klassen der Kontaktseite. Eigene Regeln haetten gegen jene gearbeitet.
   ⚠️ Aendert sich dort das Aussehen, aendert es sich hier mit - das ist
   der Zweck. */

/* Der Text im Overlay ist zentriert, die Event-Karte ist es nicht.
   Zuruecksetzen, sonst steht die Adresse mittig unter dem Titel. */
.quote-events .event-item {
  text-align: start;
}

/* .quote-events__all entfallen (04.08.): Der Verweis nutzt jetzt den
   gemeinsamen Stil aus dem Block section-cta - eine Bauart fuer alle
   drei Stellen statt drei verschiedene. */
/* ### /BLOCK: quote-events ### */

/* ### BLOCK: section-cta ### */
/*
  EIN Stil fuer die Verweise am Ende eines Blocks (04.08.).
  Vorher drei Bauarten fuer dieselbe Sache: linksbuendiger Fliesstext auf
  der Medienseite, zentrierter unterstrichener Link im Overlay, auf der
  Kontaktseite gar nichts.

  Prinzip: ein Satz, der erklaert WARUM man klickt - kein nackter Link.
  Der Satz tritt zurueck, der Link darin traegt die Akzentfarbe.

  ⚠️ .events-cta ist der bestehende Name auf der Medienseite und bleibt
  als Zweitklasse bestehen, damit dort nichts umgebaut werden muss.
*/
.section-cta,
.events-cta {
  margin-block-start: var(--space-8);
  color: var(--c-text-muted);
  font-size: var(--font-size-small);
}

.section-cta a,
.events-cta a {
  color: var(--c-accent-text);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.section-cta a:hover,
.events-cta a:hover {
  color: var(--c-accent);
}

.section-cta a:focus-visible,
.events-cta a:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Im Overlay ist ALLES zentriert - ein linksbuendiger Satz fiele dort
   aus der Reihe. Gleiche Machart, an den Ort angepasst. */
.quote-events .section-cta {
  margin-block-start: var(--space-2);
  text-align: center;
}

/* Sprungziele: Der klebende Header (68px) verdeckt sonst den Anfang des
   angesprungenen Abschnitts. scroll-margin wirkt nur beim Springen,
   nicht im normalen Layout - gleiche Loesung wie .event-block__title. */
#kontakt,
#auftritte {
  scroll-margin-block-start: calc(var(--nav-height) + var(--space-4));
}
/* ### /BLOCK: section-cta ### */

/* ### BLOCK: nav-mobil ### */
/*
  Kebab-Menue neu gefasst (05.08.). Vorher hatten die drei Eintraege drei
  verschiedene Optiken: aktuelle Seite eine dezente Flaeche, ein Eintrag
  gar keine, "Kontakt" eine kraeftige. Das wirkte unruhig, besonders im
  schmalen Panel, wo der Akzentblock fast ein Drittel fuellte.

  Jetzt: alle drei gleich, Unterschiede nur ueber Farbe und einen
  schmalen Strich. "Kontakt" bleibt der Handlungsaufruf, aber als Rahmen
  statt als Flaeche.

  ⚠️ MUSS in max-width stehen: Die Desktop-Regeln kommen in dieser Datei
  WEITER OBEN - ungekapselt wuerde dieser Block sie ueberschreiben und
  die waagrechte Leiste am PC zerlegen.
*/
@media (max-width: 767px) {

  .primary-nav {
    /* Naeher an den Header und etwas breiter: wirkt angedockt statt
       frei schwebend. */
    top: calc(var(--nav-height) - var(--space-3));
    width: min(260px, calc(100vw - 2 * var(--space-4)));
    padding: var(--space-2);
  }

  /* Haarlinien zwischen den Eintraegen statt Flaechen. Nur zwischen
     ihnen, nicht am Rand - deshalb ueber den Nachbar-Selektor.
     ⚠️ Nicht, wenn einer der beiden Nachbarn umrandet ist: Linie und
     Rahmen direkt uebereinander ergeben einen doppelten Strich. */
  .primary-nav__list > li + li {
    border-block-start: 1px solid var(--c-border-subtle);
  }
  .primary-nav__list > li:has([aria-current="page"]) + li,
  .primary-nav__list > li:has(+ li [aria-current="page"]) + li {
    border-block-start-color: transparent;
  }

  .primary-nav__link:not(.btn-nav) {
    /* Grundzustand fuer ALLE gleich: kein Hintergrund, ruhiger Text. */
    background: none;
    position: relative;
    padding-inline: var(--space-4);
  }

  /* Aktuelle Seite: Akzentfarbe und ein gerader Strich links.
     ⚠️ NICHT als border-inline-start: Der Rand folgt der 8px-Rundung
     des Eintrags und verbiegt sich zu einer angeschnittenen Klammer.
     Als absolut gesetztes Pseudo-Element bleibt er gerade. */
  .primary-nav__link:not(.btn-nav)[aria-current="page"] {
    background: none;
    color: var(--c-accent-text);
  }

  /* Markierung steht am ENDE dieses Blocks - siehe unten. */

  /* Tippen: kurze Rueckmeldung, mehr braucht es am Handy nicht. */
  .primary-nav__link:not(.btn-nav):active {
    background: var(--c-bg-inset);
  }

  /* ⚠️ Kontakt bekommt KEINEN Sonderabstand und KEINEN eigenen Rahmen
     mehr. Drei gleichwertige Eintraege, eine Markierung: der Rahmen
     zeigt, wo man ist - sonst nichts. */
  .primary-nav__link.btn-nav {
    justify-content: flex-start;
    background: none;
    color: var(--c-text-secondary);
    border: 1px solid transparent;
  }

  /* ⚠️ KEINE Sonderregel fuer Kontakt auf der Kontaktseite. Die
     gefuellte Flaeche machte den Eintrag zum Fremdkoerper zwischen
     zwei schlichten - genau die Unruhe, die dieser Block beseitigt.
     Die aktuelle Seite markiert der Strich, bei allen dreien gleich.
     Der Ring aus der allgemeinen btn-nav-Regel muss aber weg, sonst
     liegt er zusaetzlich um den Rahmen. */
  /* ⚠️ MUSS ganz am Ende stehen: Die btn-nav-Regel weiter oben setzt
     border:transparent und hat dieselbe Spezifitaet - bei Gleichstand
     gewinnt die spaetere Regel. Stand die Markierung davor, blieb der
     Kontakt-Eintrag auf seiner eigenen Seite unmarkiert (gemessen:
     borderColor rgba(0,0,0,0)).
     Die aktuelle Seite traegt den Rahmen - dieselbe Markierung fuer
     alle drei Eintraege. */
  .primary-nav__link[aria-current="page"] {
    border: 1px solid var(--c-accent);
    color: var(--c-accent-text);
  }

  .primary-nav__link.btn-nav[aria-current="page"] {
    /* Ring aus der allgemeinen btn-nav-Regel weg - der Rahmen genuegt. */
    box-shadow: none;
  }
}

/* iOS legt beim Antippen eine eigene Farbflaeche ueber Links
     (-webkit-tap-highlight-color), abgeleitet aus der Textfarbe -
     bei Champagner sieht das aus wie ein goldenes Nachleuchten.
     Wir haben mit :active bereits eine eigene Rueckmeldung. */
  .primary-nav__link {
    -webkit-tap-highlight-color: transparent;
  }
/* ### /BLOCK: nav-mobil ### */