/* ==========================================================================
 * NEUES REDESIGN — Eventseite CRUISE, Scope `live80-v2`
 * --------------------------------------------------------------------------
 * 🔴 ERZEUGTE DATEI — NICHT VON HAND AENDERN.
 *    Quellen: _source/mockups/koeln-extra.css  (gemeinsames Eventkapitel)
 *             cruise-extra.css                 (Route, Trailer, Moderation)
 *    Bauweg : python3 exports/wp-builds/live80-v2/cruise/scope-css-cruise.py
 *
 * ERGAENZUNG zu live80-v2.css, kein Ersatz. Das Designsystem (Tokens, Typo,
 * Header, Footer, Act-Karten, Karussell, FAQ, Chatbot, Countdown-Leiste)
 * steht dort und wird auf jeder `_live80_v2`-Seite geladen — auch hier.
 *
 * Teil 1 = das gemeinsame Eventseiten-Kapitel, aus Koeln abgeleitet: die
 *          sechs Hero-Klassen tragen hier `cruise-` statt `koeln-`, alle
 *          WERTE sind unveraendert. Wer am Eventdesign etwas aendert, aendert
 *          `koeln-extra.css` — alle drei Eventseiten ziehen dann mit.
 * Teil 2 = die WordPress-Bruecke.
 * Teil 3 = was es NUR auf der Cruise gibt: Routenzeile im Hero, Trailer-
 *          Fassade, Moderation, Route-Sektion.
 * ========================================================================== */

/* ---------- Teil 1: gemeinsames Eventseiten-Kapitel ---------- */
/* ===========================================================================
   20. KÖLN – Eventseite
   ---------------------------------------------------------------------------
   Alles darueber ist das unveraenderte Designsystem der Startseite v3 – auch
   die Act-Karten mit Retro-Bildschirm, das Karussell und die Video-Lightbox.
   Dieses Kapitel ergaenzt NUR, was es dort nicht gibt: den Event-Hero mit
   Stadion-Kulisse und die Lichteffekte.

   ⚠️ Historie, damit es niemand zurueckdreht: Eine erste Fassung zeigte
   Fotokarten mit Spotify-Button, weil fuer die 13 Koelner Acts keine
   YouTube-IDs belegt waren (die Live-Seite verlinkt dort Spotify). Auf
   Kundenwunsch sind es jetzt Videos. Die IDs wurden einzeln recherchiert und
   zweistufig geprueft (Existenz per oEmbed, Einbettbarkeit per IFrame-API
   unter der echten Domain) – Details stehen ueber dem ACTS-Array in der
   koeln-app.js. Erfundene IDs waeren Fehlinformation gewesen, deshalb der
   Umweg.
   =========================================================================== */
/* --- Hero ---------------------------------------------------------------- */
/* Die Neon-Illustration des Stadions liegt als Kulisse hinter dem Hero.
   ⚠️ Zwei Dinge sind Absicht:
   (1) Sie sitzt am UNTEREN Rand und wird nach oben ausmaskiert – sonst
       schneidet sie als sichtbare Kastenkante durch die Headline (dieselbe
       Falle wie beim Horizontglühen, Checkliste 76).
   (2) object-fit:contain, nicht cover: die Illustration traegt Schriftzuege
       und ist randlos komponiert (siehe CLAUDE.md, "Event-Kacheln"). */
.live80-v2 .cruise-hero{min-height:min(96svh,940px);padding-block:var(--s8) var(--s9)}
/* ⚠️ Deckkraft ist gemessen, nicht geschaetzt: bei .5 stieg die Illustration
   hinter der Textsaeule so weit auf, dass die cyanfarbene Spielstaetten-Zeile
   im unguenstigsten Fall (hellste 5 % der Textflaeche) nur noch 2,39:1
   erreichte – unter der Norm von 3:1 fuer Grossschrift. */
/* ⚠️ transform-origin unten: die Scroll-Skalierung waechst dadurch nach OBEN,
   die Unterkante bleibt stehen. Ohne das entsteht beim Scrollen eine sichtbare
   Farbkante am Fuss des Hero (siehe Kommentar in koeln-app-a.js). */
.live80-v2 .cruise-skyline{position:absolute;left:50%;bottom:0;transform:translateX(-50%);
  transform-origin:50% 100%;
  width:min(150%,1900px);aspect-ratio:3/2;z-index:0;pointer-events:none;opacity:.34;
  -webkit-mask-image:linear-gradient(to top,#000 0 34%,rgba(0,0,0,.55) 62%,transparent 88%);
          mask-image:linear-gradient(to top,#000 0 34%,rgba(0,0,0,.55) 62%,transparent 88%)}
.live80-v2 .cruise-skyline img{width:100%;height:100%;object-fit:contain;object-position:50% 100%}
.live80-v2 .cruise-hero .hero-inner{position:relative;z-index:3;text-align:center;display:grid;
  justify-items:center;gap:var(--s5)}
.live80-v2 .cruise-logo{width:clamp(150px,17vw,250px);filter:drop-shadow(0 0 24px rgba(255,45,149,.6))}
/* Das Datum ist auf einer Eventseite die wichtigste Zahl der Seite – es steht
   deshalb als eigener Block ueber dem Stadtnamen, nicht als Nebenzeile. */
.live80-v2 .cruise-date{display:inline-flex;align-items:baseline;gap:var(--s3);flex-wrap:wrap;
  justify-content:center;
  font-family:'Core Sans M','Asap',sans-serif;font-weight:800;
  font-size:clamp(20px,1rem + 1.1vw,30px);letter-spacing:.06em;text-transform:uppercase;
  color:var(--c-gold);
  /* 🔴 DUNKLER Saum ZUERST, Glimmen danach. Auf einem Foto-/Illustrations-
     grund traegt ein reines Leuchten die Lesbarkeit nicht – es hellt die
     Umgebung zusaetzlich auf. Dieselbe Logik wie bei .chrome (siehe dort). */
  text-shadow:0 2px 0 rgba(var(--c-shadow),.85), 0 0 10px rgba(var(--c-shadow),.95),
              0 0 26px rgba(var(--c-shadow),.7), var(--glow-gold)}
.live80-v2 .cruise-date strong{font-size:.66em;letter-spacing:.2em;color:var(--c-ink-2);
  text-shadow:0 1px 0 rgba(var(--c-shadow),.9), 0 0 12px rgba(var(--c-shadow),.9)}
.live80-v2 .cruise-city{font-size:var(--fs-4xl);line-height:.86}
/* 🔴 Die Spielstaetten-Zeile sitzt auf einer dunklen Plakette, nicht frei auf
   dem Grund. Grund ist gemessen, nicht Geschmack: Cyan auf dem hellen Hero-
   Pink kam im unguenstigsten Fall auf 2,30:1 – unter der Norm von 3:1 fuer
   Grossschrift. Ein dunkler Textsaum allein loest das nicht (WCAG rechnet
   Schatten nicht an, und die Umgebung bleibt hell); erst eine eigene Flaeche
   unter der Schrift tut es. Optisch passt sie zu den Eckdaten-Pillen darunter.
   ⚠️ Wer die Plakette entfernt, muss die Schriftfarbe auf Weiss stellen. */
.live80-v2 .cruise-venue{display:inline-block;
  font-family:'Core Sans M','Asap',sans-serif;font-size:clamp(17px,.9rem + .8vw,27px);
  letter-spacing:.2em;text-transform:uppercase;color:var(--c-cyan);
  padding:.3em .95em;border-radius:var(--r-pill);
  border:1px solid rgba(38,224,255,.35);
  background:rgba(var(--c-shadow-deep),.72);backdrop-filter:blur(7px);
  text-shadow:0 0 14px rgba(38,224,255,.5)}
.live80-v2 .hero-actions{display:flex;gap:var(--s4);flex-wrap:wrap;justify-content:center}
/* --- Stadion-Sektion ------------------------------------------------------ */
/* Eigene Rampe, weil "RHEINENERGIESTADION" als ein Wort nicht umbrechen kann:
   die untere Grenze ist an ihm ausgemessen (327px Platz bei 375px Viewport),
   nach oben laeuft sie auf denselben Wert wie --fs-2xl.
   ⚠️ Der Selektor MUSS .sec-head enthalten: das Designsystem setzt
   `.sec-head h2{font-size:var(--fs-3xl)}` (Spezifitaet 0,1,1). Eine einzelne
   Klasse verliert dagegen – nachgemessen, die Ueberschrift blieb bei 40px
   statt 21px und ragte weiter aus dem Bild. */
.live80-v2 .sec-head .venue-h{font-size:clamp(21px,5.2vw,50px)}
.live80-v2 .venue-sec{position:relative;overflow:hidden}
.live80-v2 .venue-illu{position:absolute;inset:0;z-index:0;pointer-events:none;opacity:.42}
.live80-v2 .venue-illu img{width:100%;height:100%;object-fit:cover;object-position:50% 42%}
/* Schleier in Magenta, NICHT in Schwarz – Schwarz entsaettigt den Grund und
   erzeugt genau den "schmutzigen" Eindruck (Checkliste 89). */
.live80-v2 .venue-sec::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(180deg,rgba(122,11,99,.72),rgba(58,17,96,.86))}
.live80-v2 .venue-sec>.wrap{position:relative;z-index:2}
/* --- Der Textblock unter der Ueberschrift ---------------------------------
   🔴 Vorgeschichte in zwei Stufen, damit niemand zurueckdreht (beide am
   2026-08-05, beide vom Kunden abgelehnt):
     1. Ausgangsfassung: drei zentrierte Absaetze in Kleinschrift, frei im
        Raum -> "muss besser umbrochen werden und auch besser gestyled".
     2. Mein erster Umbau: jeder Act auf eigener Zeile, Songtitel cyan,
        Anrede als cyanfarbene Versalzeile, Satzbreite 64ch (518px)
        -> "richtig beschissen, mach es wieder breiter und nicht so chaotisch".
   Stufe 2 war zu viel: Sie hat einen FLIESSTEXT in eine Liste zerlegt und ihn
   mit drei Farben und zwei Schriftschnitten ueberzeichnet – und dabei die
   Zeilen auch noch schmaler gemacht, obwohl "zu eng" nie das Problem war.

   Jetzt: Fliesstext wie im Original, aber BREITER als vorher (860px gegen
   655px), ruhig gesetzt, und mit dem einzigen Eingriff, um den es
   ursprunglich ging – Kuenstlername und Songtitel brechen nicht mehr um.
   Farbe traegt nur noch der Name (weiss/fett), sonst nichts.
   ⚠️ .nobr NICHT durch white-space:nowrap auf dem Absatz ersetzen – dann
   laeuft die ganze Zeile aus dem Bild.
   ------------------------------------------------------------------------- */
.live80-v2 .nobr{white-space:nowrap}
.live80-v2 .venue-copy{position:relative;max-width:860px;margin:var(--s7) auto 0;
  padding:var(--s7) clamp(20px,4vw,56px);
  border-radius:var(--r-lg);
  border:1px solid rgba(38,224,255,.22);
  background:linear-gradient(180deg,rgba(58,17,96,.55),rgba(42,11,73,.66));
  box-shadow:0 26px 70px rgba(var(--c-shadow),.4)}
/* Eine einzige Zierlinie oben – kein zusaetzliches Element, kein Layout. */
.live80-v2 .venue-copy::before{content:"";position:absolute;left:clamp(20px,4vw,56px);right:clamp(20px,4vw,56px);top:-1px;
  height:2px;border-radius:2px;
  background:linear-gradient(90deg,transparent,var(--c-pink),var(--c-cyan),transparent);
  box-shadow:0 0 16px rgba(255,45,149,.5)}
.live80-v2 .venue-copy p{margin:0;font-size:var(--fs-md);line-height:1.75;color:var(--c-ink-2);
  text-align:center;text-wrap:pretty;hyphens:manual}
.live80-v2 .venue-copy p + p{margin-top:var(--s5)}
/* Nur der Name wird hervorgehoben. Songtitel bleiben im Fliesstext stehen –
   sie zusaetzlich einzufaerben war der "chaotisch"-Anteil. */
.live80-v2 .venue-copy b{font-family:'Core Sans M','Asap',sans-serif;font-weight:700;letter-spacing:.03em;color:#fff}
/* Die Ansage etwas groesser; balance gleicht die Restzeile aus, statt eine
   Waise ("in Köln.") stehenzulassen. */
.live80-v2 .vc-lead{font-size:var(--fs-lg);line-height:1.5;text-wrap:balance}
.live80-v2 .vc-brand{font-family:'Core Sans M','Asap',sans-serif;font-weight:800;letter-spacing:.05em;color:#fff}
/* --- Abschluss-CTA: das Ticket -------------------------------------------
   🔴 Vorgeschichte, damit es niemand zurueckdreht: Hier stand ein heller
   Kasten mit Ueberschrift und Knopf. Der Kunde hat ihn am 2026-08-05
   ausdruecklich verworfen ("LANGWEILIG!!!! ... nicht so einen blöden weißen
   kasten"). Die Flaeche ist jetzt selbst das Produkt – ein Ticket mit
   Abrissabschnitt.
   ⚠️ Kein heller Kasten mehr an dieser Stelle. */
.live80-v2 .ticket-sec{position:relative;overflow:hidden;padding-block:var(--s10)}
/* Zwei Scheinwerfer, die auf die Mitte zulaufen – dieselbe Bauart wie die
   Buehnenstrahler ueber dem Line-up, nur enger gestellt. */
.live80-v2 .tk-spots{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.live80-v2 .tk-spots i{position:absolute;top:-20%;width:34vw;height:140%;
  transform-origin:50% 0;mix-blend-mode:screen;opacity:.6;
  -webkit-mask-image:linear-gradient(to bottom,#000,transparent 80%);
          mask-image:linear-gradient(to bottom,#000,transparent 80%)}
.live80-v2 .tk-spots i:first-child{left:-6vw;
  background:linear-gradient(to bottom,rgba(255,217,61,.42),rgba(255,45,149,.12) 45%,transparent 76%);
  animation:sl-l 12s ease-in-out infinite alternate}
.live80-v2 .tk-spots i:last-child{right:-6vw;
  background:linear-gradient(to bottom,rgba(255,45,149,.45),rgba(38,224,255,.1) 45%,transparent 76%);
  animation:sl-r 15s ease-in-out infinite alternate}
.live80-v2 .ticket-sec>.wrap-narrow{position:relative;z-index:2}
/* Das Ticket selbst. Hauptteil + Abriss, dazwischen die Perforation. */
.live80-v2 .ticket{position:relative;display:grid;grid-template-columns:1fr 190px;
  border-radius:var(--r-lg);overflow:hidden;isolation:isolate;
  background:
    radial-gradient(120% 140% at 12% 0%,rgba(255,45,149,.34),transparent 62%),
    linear-gradient(150deg,#4b1a7c 0%,#2a0b49 52%,#3a1160 100%);
  border:2px solid rgba(255,45,149,.5);
  box-shadow:0 30px 80px rgba(var(--c-shadow),.65),
             0 0 40px rgba(255,45,149,.35),0 0 110px rgba(255,45,149,.16)}
/* 🔴 Die Perforation ist eine echte Aussparung, kein aufgemalter Strich:
   die beiden Halbkreise werden per mask aus dem Ticket HERAUSGESCHNITTEN.
   Ein aufgemalter Kreis muesste die Hintergrundfarbe treffen – die ist hier
   aber ein Verlauf und aendert sich mit der Scrollposition. */
.live80-v2 .ticket{
  --perf:190px;   /* Abstand der Perforation vom rechten Rand = Stubbreite */
  -webkit-mask-image:
    radial-gradient(20px 20px at calc(100% - var(--perf)) 0,transparent 19px,#000 20px),
    radial-gradient(20px 20px at calc(100% - var(--perf)) 100%,transparent 19px,#000 20px);
          mask-image:
    radial-gradient(20px 20px at calc(100% - var(--perf)) 0,transparent 19px,#000 20px),
    radial-gradient(20px 20px at calc(100% - var(--perf)) 100%,transparent 19px,#000 20px);
  -webkit-mask-composite:source-in; mask-composite:intersect}
.live80-v2 .tk-main{position:relative;z-index:2;display:grid;justify-items:center;gap:var(--s5);
  text-align:center;padding:var(--s9) var(--s6)}
.live80-v2 .tk-eyebrow{font-family:'Core Sans M','Asap',sans-serif;font-size:var(--fs-xs);
  letter-spacing:.42em;text-transform:uppercase;color:var(--c-gold);text-shadow:var(--glow-gold)}
.live80-v2 .tk-h{font-size:var(--fs-2xl);line-height:1.02}
.live80-v2 .tk-sub{color:var(--c-ink-2);max-width:46ch}
.live80-v2 .tk-btn{min-height:60px;font-size:var(--fs-sm)}
/* Abrissabschnitt, senkrecht gesetzt wie auf einem echten Ticket. */
.live80-v2 .tk-stub{position:relative;z-index:2;display:grid;align-content:center;justify-items:center;
  gap:var(--s3);padding:var(--s6) var(--s4);
  border-left:2px dashed rgba(255,255,255,.35);
  background:linear-gradient(180deg,rgba(255,255,255,.07),rgba(255,255,255,.02))}
/* 🔴 Diese Zeile ist an „KÖLN" ausgemessen — VIER Zeichen. Der Abriss bietet
   innen 158px; bei 26px und .12em Laufweite braucht „KÖLN" davon 81px.
   Jede abgeleitete Eventseite mit einem laengeren Ortsnamen braucht deshalb eine
   EIGENE Rampe in ihrem Kapitel-CSS, sonst wird der Name rechts abgeschnitten:
     KÖLN 81 · HAMBURG 148 · NORWEGEN 122* · OBERHAUSEN 124*   (*mit eigener Rampe)
   Belegt: „NORWEGEN" lief auf 175px (Checkliste 260), „OBERHAUSEN" auf 200px
   (Checkliste 274). ⚠️ Gefunden hat es beide Male der SCREENSHOT — die Sektion
   hatte jeweils die erwartete Hoehe. ⚠️ Und die Gegenrichtung merken: mehr
   Laufweite macht es breiter, nicht schmaler. */
.live80-v2 .tk-city{font-family:'Core Sans M','Asap',sans-serif;font-weight:800;font-size:26px;
  letter-spacing:.12em;color:#fff;text-shadow:0 0 16px rgba(255,45,149,.8)}
.live80-v2 .tk-date{font-family:'Core Sans M','Asap',sans-serif;font-weight:800;font-size:18px;
  letter-spacing:.04em;color:var(--c-gold);font-variant-numeric:tabular-nums}
/* Barcode aus einem Verlauf – kein Bild, keine Schrift, rein dekorativ. */
.live80-v2 .tk-code{width:118px;height:52px;
  background:repeating-linear-gradient(90deg,#fff 0 2px,transparent 2px 4px,
             #fff 4px 5px,transparent 5px 9px,#fff 9px 12px,transparent 12px 14px);
  opacity:.9}
.live80-v2 .tk-no{font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--c-ink-3)}
/* 🔴 Neonroehre statt Gluehbirnenkette. Die Kette wurde am 2026-08-05
   verworfen ("eher Zirkus style als 80er") – laufende Birnen sind Jahrmarkt-
   und Theaterfassade, die 80er sind Neonroehre, Chrom und Gitterboden.
   Die Roehre ist ein Rahmen mit doppeltem Schein nach innen UND aussen; das
   unregelmaessige Anlaufen ist derselbe Flacker-Gedanke wie bei den Neonnamen
   der Startseite, nur ruhiger. Nur opacity, kein Filter. */
.live80-v2 .tk-tube{position:absolute;inset:9px;z-index:3;pointer-events:none;
  border:2px solid rgba(255,45,149,.9);border-radius:calc(var(--r-lg) - 8px);
  box-shadow:0 0 10px rgba(255,45,149,.85),0 0 26px rgba(255,45,149,.45),
             inset 0 0 12px rgba(255,45,149,.5),inset 0 0 30px rgba(255,45,149,.2);
  animation:tube 7s ease-in-out infinite}
@keyframes tube{
  0%,100%{opacity:1}
  49%{opacity:1} 50%{opacity:.55} 51.5%{opacity:1} 52.5%{opacity:.7} 53.5%{opacity:1}
}
/* Gitterboden im Fuss des Tickets – Fluchtpunkt-Optik der Zeit.
   Statisch, also kostenlos: zwei Verlaeufe, keine Animation. */
.live80-v2 .tk-grid{position:absolute;left:0;right:0;bottom:0;height:44%;z-index:0;pointer-events:none;
  background:
    repeating-linear-gradient(90deg,transparent 0 38px,rgba(38,224,255,.22) 38px 39px),
    repeating-linear-gradient(0deg,transparent 0 26px,rgba(255,45,149,.18) 26px 27px);
  -webkit-mask-image:linear-gradient(to top,#000,transparent 92%);
          mask-image:linear-gradient(to top,#000,transparent 92%);
  opacity:.75}
/* Hologramm-Streifen, der ueber das Ticket wandert. Nur translateX. */
.live80-v2 .tk-holo{position:absolute;top:0;bottom:0;left:0;width:34%;z-index:1;pointer-events:none;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.16) 45%,
             rgba(38,224,255,.14) 60%,transparent);
  transform:skewX(-16deg) translateX(-60%);
  animation:tk-holo 6.5s ease-in-out infinite}
@keyframes tk-holo{0%{transform:skewX(-16deg) translateX(-60%)}
                   55%,100%{transform:skewX(-16deg) translateX(330%)}}
@media (prefers-reduced-motion:reduce){.live80-v2 .tk-tube,.live80-v2 .tk-holo,.live80-v2 .tk-spots i{animation:none}}
/* ===========================================================================
   21. MOBILER HERO
   ---------------------------------------------------------------------------
   🔴 Kundenmeldung 2026-08-05: "gerade der hero stimmt mobil noch nicht".
   Gemessen bei 375x812 war die Ursache nicht der Text, sondern die Kulisse:
   .cruise-skyline ist 150 % breit – am Desktop sind das 1900px, am Handy aber
   nur 563px, bei 3:2 also 375px hoch. Sie sass damit als schmaler Streifen
   ganz unten, zum groessten Teil unterhalb der Sichtkante. Vom Stadion war
   praktisch nichts zu sehen, der Hero wirkte leer.

   Am Handy muss die Kulisse deshalb DEUTLICH ueber die Bildschirmbreite
   hinausgehen: 300 % ergeben 1125px Breite und 750px Hoehe – der Ausschnitt
   zeigt die Stadionmitte gross statt das ganze Motiv winzig. Dass links und
   rechts etwas wegfaellt, ist hier richtig: es ist Kulisse, kein Inhalt.
   =========================================================================== */
@media (max-width:700px){/* Etwas flacher und oben knapper – 96svh plus 64px Polster liessen den
     Inhalt in der Mitte schwimmen, mit grosser Leere unter dem Header. */
.live80-v2 .cruise-hero{min-height:min(88svh,720px);padding-block:var(--s5) var(--s8)}
.live80-v2 .cruise-hero .hero-inner{gap:var(--s4)}
.live80-v2 .cruise-skyline{width:300%;opacity:.42;
    /* Weniger weit ausgeblendet als am Desktop: bei dieser Groesse liegt der
       obere Rand ohnehin ausserhalb des Hero. */
    -webkit-mask-image:linear-gradient(to top,#000 0 52%,rgba(0,0,0,.6) 76%,transparent 96%);
            mask-image:linear-gradient(to top,#000 0 52%,rgba(0,0,0,.6) 76%,transparent 96%)}
/* Das Logo ist die Marke – 150px waren auf dem Handy zu zaghaft. */
.live80-v2 .cruise-logo{width:min(58%,210px)}
/* 🔴 Der Chat-Knopf sitzt fest unten rechts und lag auf dem zweiten Button
     ("Line-up ansehen"). Die Knoepfe stehen deshalb nicht mehr ueber die volle
     Breite, sondern zentriert mit Abstand zum rechten Rand. */
.live80-v2 .cruise-hero .hero-actions{width:100%;flex-direction:column;align-items:center;gap:var(--s3)}
.live80-v2 .cruise-hero .hero-actions .btn{width:min(100%,300px)}
/* Luft nach unten, damit der Chat-Knopf nicht auf dem letzten Button liegt. */
.live80-v2 .cruise-hero .hero-inner{padding-bottom:var(--s8)}}
/* Unter 780px steht der Abriss UNTER dem Hauptteil – nebeneinander bliebe fuer
   die Ueberschrift keine Breite. Perforation und Aussparungen drehen mit. */
@media (max-width:780px){.live80-v2 .ticket{grid-template-columns:1fr;--perf:150px;
    -webkit-mask-image:
      radial-gradient(20px 20px at 0 calc(100% - var(--perf)),transparent 19px,#000 20px),
      radial-gradient(20px 20px at 100% calc(100% - var(--perf)),transparent 19px,#000 20px);
            mask-image:
      radial-gradient(20px 20px at 0 calc(100% - var(--perf)),transparent 19px,#000 20px),
      radial-gradient(20px 20px at 100% calc(100% - var(--perf)),transparent 19px,#000 20px)}
.live80-v2 .tk-main{padding:var(--s8) var(--s5)}
/* ⚠️ KEINE feste Hoehe. Sie stand hier auf 150px, der Inhalt brauchte aber
     199px – mit align-content:center lief er nach OBEN aus dem Abriss heraus,
     "Admit One" stand dadurch oberhalb der Trennlinie (gemessen: 22px zu hoch).
     Die Hoehe kommt jetzt aus dem Inhalt, und die Perforation richtet sich
     danach (siehe --perf-Abgleich im Skript). */
.live80-v2 .tk-stub{border-left:0;border-top:2px dashed rgba(255,255,255,.35);
    grid-auto-flow:row;padding:var(--s5) var(--s4)}
.live80-v2 .tk-code{width:150px;height:40px}}
/* --- Lichteffekte ---------------------------------------------------------
   🔴 Alle drei arbeiten AUSSCHLIESSLICH mit transform und opacity. Kein
   animiertes blur(), keine Filter, kein background-attachment:fixed – genau
   diese drei waren die belegten Bremsklötze dieses Projekts (iPhone 15,3 s,
   siehe CLAUDE.md "Mobil-Performance" und "Desktop wurde langsam").
   Der weiche Schein kommt aus Verlaeufen und box-shadow, nicht aus Filtern. */
/* Kamerablitze im Stadion. Die <i> erzeugt das Skript mit Zufallspositionen. */
.live80-v2 .flashes{position:absolute;inset:0;z-index:1;pointer-events:none;overflow:hidden}
.live80-v2 .flashes i{position:absolute;width:5px;height:5px;border-radius:50%;
  background:#fff;box-shadow:0 0 10px 3px rgba(255,255,255,.9),0 0 22px 8px rgba(255,220,255,.45);
  opacity:0;animation:flash 9s linear infinite}
/* Sehr kurzer Blitz, lange Pause – ein Dauerflackern saehe nach Defekt aus. */
@keyframes flash{
  0%,100%{opacity:0;transform:scale(.6)}
  1.2%{opacity:1;transform:scale(1)}
  3%{opacity:0;transform:scale(.7)}
  46%{opacity:0}
  47%{opacity:.85;transform:scale(.95)}
  49%{opacity:0}
}
/* Zwei Flutlichtkegel aus den oberen Ecken. */
.live80-v2 .floods{position:absolute;inset:0;z-index:1;pointer-events:none;overflow:hidden}
.live80-v2 .floods .fl{position:absolute;top:-18%;width:44vw;height:135%;
  background:linear-gradient(to bottom,rgba(255,255,255,.16),rgba(255,45,149,.09) 45%,transparent 78%);
  -webkit-mask-image:linear-gradient(to bottom,#000,transparent 82%);
          mask-image:linear-gradient(to bottom,#000,transparent 82%);
  transform-origin:50% 0;mix-blend-mode:screen;opacity:.8}
.live80-v2 .floods .fl-l{left:-8vw;animation:flood-l 13s ease-in-out infinite alternate}
.live80-v2 .floods .fl-r{right:-8vw;animation:flood-r 16s ease-in-out infinite alternate}
@keyframes flood-l{from{transform:rotate(9deg)}to{transform:rotate(20deg)}}
@keyframes flood-r{from{transform:rotate(-20deg)}to{transform:rotate(-9deg)}}
/* Buehnenstrahler ueber dem Line-up – von LINKS und RECHTS, nicht von einer
   Traverse (Kundenvorgabe 28.07. auf der Startseite). */
.live80-v2 .lineup-sec{position:relative;overflow:hidden}
.live80-v2 .stagelights{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.live80-v2 .stagelights .sl{position:absolute;top:-14%;width:38vw;height:128%;
  transform-origin:50% 0;mix-blend-mode:screen;opacity:.75;
  -webkit-mask-image:linear-gradient(to bottom,#000,transparent 76%);
          mask-image:linear-gradient(to bottom,#000,transparent 76%)}
.live80-v2 .stagelights .sl-l{left:-10vw;
  background:linear-gradient(to bottom,rgba(255,45,149,.5),rgba(255,45,149,.14) 42%,transparent 74%);
  animation:sl-l 11s ease-in-out infinite alternate}
.live80-v2 .stagelights .sl-r{right:-10vw;
  background:linear-gradient(to bottom,rgba(38,224,255,.42),rgba(38,224,255,.12) 42%,transparent 74%);
  animation:sl-r 14s ease-in-out infinite alternate}
@keyframes sl-l{from{transform:rotate(12deg)}to{transform:rotate(26deg)}}
@keyframes sl-r{from{transform:rotate(-26deg)}to{transform:rotate(-12deg)}}
.live80-v2 .lineup-sec>.wrap{position:relative;z-index:2}
/* Suchscheinwerfer, der einmal quer durch den Hero faehrt und dann pausiert.
   ⚠️ Bewusst ein schraeger Verlaufsstreifen, der per translateX wandert –
   nicht per left/width, das waere ein Layout-Durchlauf je Bild. */
.live80-v2 .sweep{position:absolute;top:-20%;left:0;width:26vw;height:150%;z-index:1;
  pointer-events:none;mix-blend-mode:screen;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.15) 42%,
             rgba(255,120,220,.22) 55%,transparent);
  transform:skewX(-14deg) translateX(-40vw);
  animation:sweep 9s cubic-bezier(.5,0,.5,1) infinite}
@keyframes sweep{
  0%{transform:skewX(-14deg) translateX(-40vw)}
  38%,100%{transform:skewX(-14deg) translateX(135vw)}
}
/* Nur Bewegung abschalten – die Kegel bleiben als ruhiges Licht stehen,
   sonst wirkt der Hero ploetzlich leer. */
@media (prefers-reduced-motion:reduce){.live80-v2 .flashes i,.live80-v2 .floods .fl,.live80-v2 .stagelights .sl,.live80-v2 .sweep{animation:none}
.live80-v2 .flashes i{opacity:.5}
.live80-v2 .sweep{opacity:0}}
/* Auf dem Handy weniger gleichzeitige Ebenen – der Hero ist dort schmal, die
   Kegel ueberlagern sich sonst zu einem hellen Schleier ueber der Headline. */
@media (max-width:700px){.live80-v2 .floods .fl{width:70vw;opacity:.55}
.live80-v2 .stagelights .sl{width:64vw;opacity:.6}
.live80-v2 .sweep{display:none}}
/* ⚠️ Die Countdown-Uhr hat vier Einheiten mit je min-width:76px – zusammen mit
   den Abstaenden 340 px. Auf einem 320-px-Geraet stehen aber nur 272 px zur
   Verfuegung, die Uhr wurde am rechten Rand abgeschnitten (gemessen).
   Unterhalb 400 px teilen sich die vier Kaesten deshalb die vorhandene Breite.
   ⚠️ Dieselbe Stelle betrifft auch die Startseite – dort steht die Uhr im
   gemeinsamen Designsystem und ist NICHT mitgeaendert (waere ein Eingriff in
   eine abgenommene Seite). Als offener Punkt in der CLAUDE.md vermerkt. */
@media (max-width:400px){.live80-v2 .clock{gap:var(--s2);width:100%}
.live80-v2 .clock-unit{min-width:0;flex:1;padding:var(--s3) 4px}
.live80-v2 .clock-lbl{letter-spacing:.12em}}
/* ===========================================================================
   22. TIPPZIELE
   ---------------------------------------------------------------------------
   🔴 Bedingung ist das EINGABEGERAET, nicht die Breite: ein Tablet mit 900px
   wird genauso mit dem Finger bedient wie ein Handy. Mit `max-width:700px`
   allein blieben dort 26 Ziele unter 44px (gemessen bei 768 und 1024).
   =========================================================================== */
@media (hover:none),(max-width:1000px){/* Tippziele. Gemessen bei 375px: die Namen im Band "LIVE MIT" waren 26px
     hoch, die Karussellpunkte 26x10, die Fusszeilen-Links 18px – alle unter
     den 44px, die eine Fingerkuppe braucht. Vergroessert wird die FLAECHE,
     nicht die Optik: Schriftgroesse und Punktgroesse bleiben unveraendert.
     ⚠️ Dieselben drei Stellen betreffen auch die Startseite; sie stehen dort
     im gemeinsamen Designsystem und sind hier NICHT mitgeaendert (das waere
     ein Eingriff in eine abgenommene Seite). Steht als offener Punkt. */
.live80-v2 .names button{padding:9px .2em;min-height:44px}
/* 🔴 2026-08-07 UMGEZOGEN: Die Karussellpunkte standen hier — und damit NUR
     hier. Sie erbten dadurch nur nach unten (Hamburg, Cruise, Oberhausen leiten
     aus dieser Datei ab), waehrend die Startseite bei 10x10 blieb. Genau die
     Mechanik von Checkliste 274. Die Regel steht jetzt im gemeinsamen
     Designsystem (`startseite-redesign-v3.html`, direkt hinter `.car-dots`)
     und gilt fuer alle fuenf Seiten. Hier bewusst NICHT wiederholt: eine
     zweite Fassung wuerde die erste ueberschreiben und den Fehler neu anlegen.
     ⚠️ Dabei kam ein `border:0` dazu, das hier gefehlt hat — ohne den Reset trug
     die 22x44 grosse Flaeche den 1px-Rahmen der Grundregel weiter, und die
     Punktreihe sah aus wie eine Kette leerer Kaestchen. */
.live80-v2 .foot-legal a{display:inline-block;padding:12px 4px}}
/* --- Moderation --------------------------------------------------------------
   🔴 2026-08-07 aus `cruise-extra.css` HIERHER geholt. Sie lag nur im
   Cruise-Kapitel und erbte damit nur nach unten — als Koeln laut Plakat des
   Kunden ebenfalls eine Moderation bekam (Peter Illmann), waere sie dort
   ungestylt erschienen. Genau die Mechanik von Checkliste 274.
   Hier im gemeinsamen Eventkapitel gilt sie fuer Koeln, Hamburg, Cruise und
   Oberhausen.
   🔴🔴 KEIN `.live80-v2` VOR DIESE SELEKTOREN SCHREIBEN. Diese Datei ist die
   UNGESCOPTE Quelle — `scope-css-*.py` haengt den Scope selbst davor. Genau
   dieser Praefix stand hier vom 2026-08-07 bis zum selben Tag und erzeugte
   `.live80-v2 .live80-v2 .moderation`: ein Selektor, der NIE greift, weil die
   Klasse nur einmal am <body> haengt. Peter Illmanns Portraet lief dadurch auf
   Koeln, Cruise und Oberhausen von 74x74 auf 920x500 px auf — im Frontend UND
   im Mockup. → Checkliste 305

   ✅ 2026-08-07 bereinigt: Cruise und Oberhausen trugen sie in ihrem eigenen
   Kapitel ein zweites Mal mit zeichengleichen Werten. Beide Dubletten sind
   entfernt — DIES HIER IST JETZT DIE EINZIGE STELLE. Wer die Moderation
   aendert, aendert sie hier, und alle vier Eventseiten ziehen mit. */
.live80-v2 .moderation{
  display: flex; align-items: center; gap: var(--s5);
  justify-content: center;
  margin: var(--s7) auto 0;
  width: fit-content;
  max-width: 100%;
  padding: var(--s4) var(--s6) var(--s4) var(--s4);
  border-radius: var(--r-pill);
  background: rgba(var(--c-shadow-deep), .72);
  border: 1px solid rgba(255, 217, 61, .34);
  box-shadow: 0 16px 40px rgba(var(--c-shadow), .45);
}
.live80-v2 .mod-portrait{
  flex: none;
  width: 74px; height: 74px;
  border-radius: 50%;
  overflow: hidden;
  border: 3px solid rgba(255, 217, 61, .85);
  background: #0a0420;
  box-shadow: var(--glow-gold);
}
.live80-v2 .mod-portrait img{
  width: 100%; height: auto; display: block;
  transform: scale(1.62) translateY(13%);
}
.live80-v2 .mod-text{ display: grid; gap: 2px; text-align: left; }
.live80-v2 .mod-label{
  font-family: 'Core Sans M', 'Asap', sans-serif;
  font-size: var(--fs-xs); letter-spacing: .3em; text-transform: uppercase;
  color: var(--c-gold); text-shadow: var(--glow-gold);
}
.live80-v2 .mod-name{
  font-family: 'Core Sans M', 'Asap', sans-serif;
  font-size: var(--fs-lg); font-weight: 800; letter-spacing: .03em;
  color: #fff; line-height: 1.1;
}
@media (max-width:700px){.live80-v2 .moderation{
    flex-direction: column; text-align: center;
    border-radius: var(--r-lg);
    padding: var(--s5) var(--s5) var(--s6);
  }
.live80-v2 .mod-text{ text-align: center; justify-items: center; }}

/* ---- WordPress-Bruecke CRUISE (nicht aus dem Mockup, siehe scope-css-cruise.py) ---- */

/* (a) `core/image` schiebt eine <figure> zwischen Container und <img>.
   Bei diesen zielen die Mockup-Regeln auf das <img> INNERHALB eines absolut
   positionierten Containers — die Figur dazwischen wuerde die Groessenkette
   unterbrechen. `display:contents` loest sie auf. */
.live80-v2 .cruise-skyline figure.wp-block-image,
.live80-v2 .venue-illu figure.wp-block-image{display:contents}

.live80-v2 .cruise-logo img{width:100%;height:auto;display:block}

/* Das Moderations-Portraet ist dagegen ein NORMALES Bild in einem runden
   Rahmen — hier darf die Figur NICHT aufgeloest werden, sonst greift
   `overflow:hidden` des Rahmens nicht mehr und die Namensplakette des Badges
   ragt heraus (Checkliste 54). Nur die Grundlinienluecke muss weg. */
.live80-v2 .mod-portrait figure.wp-block-image{margin:0;line-height:0;display:block}

/* (b) Die Knoepfe. `.wp-block-button.btn` ist in der Basis geregelt; hier nur
   die Container, die es im Mockup nicht gab. */
.live80-v2 .hero-actions.wp-block-buttons{
  display:flex;gap:var(--s4);flex-wrap:wrap;justify-content:center}
.live80-v2 .wp-block-buttons.tk-cta,
.live80-v2 .wp-block-buttons.outro-cta{display:contents}

/* (c) Inline-`style=""` aus dem Mockup, jetzt als Klasse.
   ⚠️ SPEZIFITAET: als blosse Klasse verlieren diese Groessen gegen
   `.sec-head .display` (0,2,0) — deshalb hier mit derselben Tiefe
   (Checkliste 185). */
.live80-v2 .lineup-sec .sec-head .display.lineup-h,
.live80-v2 .faq-sec .sec-head .display.faq-h,
.live80-v2 .route-sec .sec-head .display.route-h,
.live80-v2 .trailer-sec .sec-head .display.trailer-h{font-size:var(--fs-2xl)}
.live80-v2 .lineup-sec .sec-head .lead.lineup-lead,
.live80-v2 .faq-sec .sec-head .lead.faq-lead,
.live80-v2 .route-sec .sec-head .lead.route-lead,
.live80-v2 .trailer-sec .sec-head .lead.trailer-lead{font-size:var(--fs-md)}
.live80-v2 .names-sec .eyebrow{text-align:center}

/* 🔴 KEIN Abstands-Reset auf die Ueberschriften — siehe die ausfuehrliche
   Begruendung in scope-css-koeln.py (Checkliste 199). Ein Reset
   „vorsichtshalber" schlaegt mit 0,2,0 den gewollten negativen oberen Abstand
   aus `.chrome` (0,1,0); gemessen wurde die Ticket-Sektion dadurch 8 px hoeher. */

/* Der Hero ist ein Grid mit `gap`; die Absaetze duerfen keine eigenen
   Abstaende mitbringen. Im Mockup hatte `.hero-inner > *` das ueber den Reset. */
.live80-v2 .cruise-hero .hero-inner > p{margin:0}

/* ⚠️ Die Ueberschrift der Illustrations-Sektion: die Rampe aus dem
   Koeln-Kapitel (`.sec-head .venue-h`) ist an „RHEINENERGIESTADION"
   ausgemessen — 19 Zeichen, ein Wort, das nicht umbrechen kann. Die
   Cruise-Ueberschrift „DIE GROESSTE 80ER-PARTY AUF AIDA" ist laenger, besteht
   aber aus fuenf Woertern und bricht deshalb um. Nachgemessen bei 375 px: kein
   Ueberlauf. Keine eigene Regel noetig. */

/* ---------- Teil 3: nur Cruise (Route, Trailer, Moderation) ---------- */
/* ==========================================================================
   CRUISE — Kapitel-CSS des neuen Looks
   --------------------------------------------------------------------------
   ERGAENZT `live80-v2.css` (Designsystem) und wird NUR auf Seiten mit
   `_live80_v2_seite = cruise` geladen. Alles hier ist unter `.live80-v2`
   gescopt; die Tokens (--c-*, --fs-*, --s*, --r-*) kommen aus der Basis.

   🔴 DIESE DATEI IST HANDGESCHRIEBEN UND BEREITS GESCOPT.
   Sie laeuft NICHT durch den Scoper — `scope-css-cruise.py` prueft nur, dass
   jede Regel darin den Scope schon traegt. Das gemeinsame Eventseiten-Kapitel
   (Hero-Kulisse, Kamerablitze, Flutlicht, Suchscheinwerfer, Buehnenstrahler,
   Illustrations-Sektion, Textpanel, Ticket-Abschluss) wird dort aus
   `koeln-extra.css` abgeleitet — es steht NICHT hier.

   WAS HIER NEU IST — und warum es das in Koeln und Hamburg nicht gibt
   Hamburg war eine ERWEITERUNG der Vorlage (Checkliste 228: drei
   Sektionsformen mehr). Die Cruise ist der umgekehrte Fall: ihre Live-Seite
   ist ein Teaser von 4,9 KB gegen 30 KB in Hamburg. Sie traegt dafuer drei
   Dinge, fuer die es im Redesign keine Form gab:

     1. eine ROUTE (Hamburg – Kristiansand (Norwegen) – Hamburg) statt einer
        Spielstaette. Eine Reise hat keinen Ort, sie hat einen Verlauf.
     2. eine MODERATION (Peter Illmann) — kein anderer Termin hat eine.
     3. einen TRAILER im Hero (`[dwd_vimeo]`, Zwei-Klick-Fassade).

   🔴 KEIN ERFUNDENER INHALT. Die Route-Sektion arbeitet mit genau vier
   Angaben, die auf der Live-Seite stehen: den drei Ortsnamen und den
   Praepositionen des Kunden („von Hamburg ueber Kristiansand (Norwegen) und
   zurueck nach Hamburg"). Es gibt keine erfundenen Uhrzeiten, keine
   Liegezeiten, keine Ausflugsprogramme und keine Kabinenkategorien — nichts
   davon steht auf der Quellseite. Was fehlt, sagt der Bot ehrlich und
   verweist auf die Buchung (siehe build-js-cruise.py).

   🔴 ALLE BEWEGUNG NUR UEBER `transform` UND `opacity` (Checkliste 251).
   Kein animiertes `blur()`, keine Filter-Animation, kein
   `background-attachment:fixed` — die drei belegten Bremskloetze dieses
   Projekts (iPhone 15,3 s → 1,0 s). Weicher Schein kommt aus Verlaeufen und
   `box-shadow`. Am Ende steht ein `prefers-reduced-motion`-Block, der NUR die
   Bewegung abstellt und die Buehne stehen laesst.
   ========================================================================== */

/* --- Hero: die Routenzeile ------------------------------------------------
   Sie sitzt unter der AIDAprima-Plakette und ersetzt nichts — Koelns Hero
   endet nach der Spielstaette, hier folgt noch der Reiseverlauf. Bewusst
   ruhig gesetzt: die grosse Fassung der Route ist die eigene Sektion weiter
   unten, hier genuegt die Zeile.
   ⚠️ Die Gedankenstriche stehen im Text des Kunden (`–`, en dash) und werden
   NICHT durch Pseudoelemente ersetzt — sonst verschwinden sie, sobald der
   Kunde die Zeile im Editor umformuliert.
   ------------------------------------------------------------------------- */
.live80-v2 .cruise-route {
  font-family: 'Core Sans M', 'Asap', sans-serif;
  font-size: clamp(13px, .72rem + .35vw, 17px);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--c-ink-2);
  text-shadow: 0 1px 0 rgba(var(--c-shadow), .9), 0 0 12px rgba(var(--c-shadow), .9);
  text-wrap: balance;
}

/* --- Trailer -------------------------------------------------------------
   Die Live-Seite zeigt den Trailer direkt im Hero. Im Redesign ist der Hero
   ein enges Grid (Logo, Datum, Ueberschrift, Plakette, Route, Knoepfe) — ein
   16:9-Video darin wuerde ihn sprengen. Er bekommt deshalb die Sektion direkt
   danach, also dieselbe STELLE im Leseverlauf.

   🔴 Der Shortcode `[dwd_vimeo]` bleibt der Bauweg (Zwei-Klick-Fassade: erst
   nach dem Klick entsteht das Vimeo-iframe im DOM — Complianz). Seine CSS
   liegt aber in der globalen `dwd.css` und ist auf `.dwd27` gescopt; auf
   `live80-v2`-Seiten kommt davon NICHTS an. Deshalb steht die Fassade hier
   noch einmal, in der Bildsprache des Redesigns: Retro-Bildschirm mit
   Neonrahmen, Scanlines und Glasreflex — dieselbe Familie wie `.act-screen`.
   ⚠️ Auch `video.js` laeuft hier nicht (`dwd-fx.php` schaltet die globalen
   Effekt-Skripte auf diesem Scope ab, Checkliste 168) — der Klick-Handler
   steckt deshalb im Cruise-JS.
   ------------------------------------------------------------------------- */
.live80-v2 .trailer-sec { position: relative; }

.live80-v2 .trailer-frame {
  position: relative;
  max-width: 980px;
  margin: var(--s7) auto 0;
  border-radius: var(--r-lg);
  padding: clamp(10px, 1.2vw, 18px);
  background: linear-gradient(160deg, rgba(75, 26, 124, .92), rgba(24, 5, 54, .96));
  border: 1px solid rgba(38, 224, 255, .34);
  box-shadow: 0 26px 70px rgba(var(--c-shadow), .5), 0 0 48px rgba(38, 224, 255, .12);
}
/* Zierlinie oben wie bei `.venue-copy` und `.anreise-card` — damit der Rahmen
   sichtbar zur selben Familie gehoert. */
.live80-v2 .trailer-frame::before {
  content: "";
  position: absolute; left: clamp(18px, 3vw, 40px); right: clamp(18px, 3vw, 40px);
  top: -1px; height: 2px; border-radius: 2px; z-index: 4;
  background: linear-gradient(90deg, transparent, var(--c-pink), var(--c-cyan), transparent);
  box-shadow: 0 0 16px rgba(255, 45, 149, .5);
}

.live80-v2 .dwd27-vimeo {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-md);
  overflow: hidden;
  background: #07030f;
  isolation: isolate;
  cursor: pointer;
}
.live80-v2 .dwd27-vimeo-preview,
.live80-v2 .dwd27-vimeo-frame {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  border: 0;
  object-fit: cover;
}
/* Scanlines + Glasreflex. Beides statisch — im Gegensatz zu `.act-screen`
   flackert hier nichts, weil unter dem Overlay ein laufender Videoloop liegt
   und zwei Bewegungen uebereinander unruhig wirken. */
.live80-v2 .dwd27-vimeo::after {
  content: "";
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    linear-gradient(115deg, rgba(255, 255, 255, .10) 0 22%, transparent 46%),
    repeating-linear-gradient(180deg,
      rgba(0, 0, 0, .22) 0 1px, transparent 1px 3px),
    radial-gradient(120% 100% at 50% 50%, transparent 52%, rgba(var(--c-shadow), .62));
}
.live80-v2 .dwd27-vimeo.is-playing::after { display: none; }

/* Der Knopf ist die ganze Flaeche — so ist das Ziel gross genug, ohne dass
   ein zweites Tippziel daneben liegt. Die Pille darin ist nur die Optik. */
.live80-v2 .dwd27-vimeo-btn {
  position: absolute; inset: 0; z-index: 3;
  display: grid; place-items: center; gap: var(--s4);
  grid-auto-flow: row;
  border: 0; background: none; cursor: pointer;
  font: inherit; color: #fff;
}
.live80-v2 .dwd27-vimeo.is-playing .dwd27-vimeo-btn { display: none; }

.live80-v2 .dwd27-vimeo-play {
  display: grid; place-items: center;
  width: clamp(62px, 6vw, 88px); height: clamp(62px, 6vw, 88px);
  border-radius: 50%;
  color: #fff;
  background: rgba(255, 45, 149, .9);
  border: 2px solid rgba(255, 255, 255, .85);
  box-shadow: var(--glow-pink), 0 14px 34px rgba(var(--c-shadow), .6);
  transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.live80-v2 .dwd27-vimeo-play svg { width: 46%; height: 46%; display: block; }
.live80-v2 .dwd27-vimeo-btn:hover .dwd27-vimeo-play,
.live80-v2 .dwd27-vimeo-btn:focus-visible .dwd27-vimeo-play {
  transform: scale(1.08);
  box-shadow: var(--glow-pink-lg), 0 18px 40px rgba(var(--c-shadow), .65);
}
.live80-v2 .dwd27-vimeo-txt {
  font-family: 'Core Sans M', 'Asap', sans-serif;
  font-size: var(--fs-sm); font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase;
  text-shadow: 0 2px 0 rgba(var(--c-shadow), .9), 0 0 18px rgba(var(--c-shadow), .95);
}

/* --- Moderation ----------------------------------------------------------
   🔴 STEHT NICHT MEHR HIER. Die Regeln (`.moderation`, `.mod-portrait`,
   `.mod-text`, `.mod-label`, `.mod-name` samt Mobil-Block) liegen seit dem
   2026-08-07 im gemeinsamen Eventkapitel `_source/mockups/koeln-extra.css`
   und kommen ueber `scope-css-cruise.py` von dort. Hier standen sie bis
   dahin ein zweites Mal mit ZEICHENGLEICHEN Werten — folgenlos, aber genau
   die Doppelpflege, an der dieses Projekt schon dreimal einen falschen Wert
   ausgeliefert hat (Checkliste 274, 301).
   ⚠️ Wer die Moderation aendern will, aendert `koeln-extra.css` — dann ziehen
   Koeln, Hamburg, Cruise und Oberhausen gemeinsam mit.
   ------------------------------------------------------------------------- */

/* --- Route-Sektion -------------------------------------------------------
   Das Gegenstueck zur Anreise-Sektion in Hamburg: dort geht es darum, wie man
   ZUM Event kommt, hier ist der Weg das Event.

   🔴 Vier Angaben, alle von der Live-Seite: die drei Ortsnamen und die
   Praepositionen des Kunden. „von" / „ueber" / „zurueck nach" stehen wortwoertlich
   im Lead-Absatz („von Hamburg ueber Kristiansand (Norwegen) und zurueck nach
   Hamburg") — sie sind deshalb die Stationsbeschriftung. Erfunden ist nichts.
   ⚠️ Die Zahl „4 Tage" ist gerechnet (23., 24., 25., 26.), nicht geschaetzt.

   Die Buehne: ein Meeresband mit Horizont, drei Stationen auf einer Neonlinie,
   ein Schiff, das darauf faehrt. Bewegung ausschliesslich `transform`.
   ------------------------------------------------------------------------- */
.live80-v2 .route-sec { position: relative; overflow: hidden; }

/* Meer + Horizont. Beides Verlaeufe, kein Bild — die Sektion soll nichts
   nachladen, das Schiffsmotiv traegt schon die Illustration weiter oben. */
.live80-v2 .route-sea {
  position: absolute; inset: auto 0 0; height: 46%;
  z-index: 0; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(38, 224, 255, .00), rgba(38, 224, 255, .10)),
    repeating-linear-gradient(180deg,
      rgba(38, 224, 255, .16) 0 1px, transparent 1px 22px),
    linear-gradient(180deg, rgba(24, 5, 54, .0), rgba(24, 5, 54, .8));
  border-top: 2px solid rgba(38, 224, 255, .45);
  box-shadow: 0 -8px 34px rgba(38, 224, 255, .18);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}
.live80-v2 .route-sec > .wrap-narrow { position: relative; z-index: 2; }

/* ---- Die Stationen ------------------------------------------------------ */
.live80-v2 .route-map {
  position: relative;
  margin-top: var(--s8);
  /* Die Mitte der Stationspunkte — Linie und Schiff haengen daran. Als eigene
     Variable, damit die drei Stellen unten nicht auseinanderlaufen koennen. */
  --cr-dot: clamp(52px, 5vw, 68px);
}
/* Die Fahrtlinie verbindet die MITTEN der aeusseren Stationen.
   🔴 `1/6` und nicht `8 %`: die drei Stationen sind gleich breite Grid-Spalten,
   ihre Mitten liegen also bei 1/6, 3/6 und 5/6 der Breite. Eine Linie von 8 %
   bis 92 % ragt beidseitig darueber hinaus — gemessen 71 px, im Bild zwei
   Linienstummel neben den Ankern, und das Schiff startete links NEBEN Hamburg.
   Alle Geometriemessungen waren dabei „richtig"; gefunden hat es erst der
   Screenshot (Checkliste 171). */
.live80-v2 .route-track {
  position: absolute; left: calc(100% / 6); right: calc(100% / 6);
  top: calc(var(--cr-dot) / 2);
  height: 4px; border-radius: 4px;
  overflow: hidden;
  background: linear-gradient(90deg,
    rgba(38, 224, 255, .25), rgba(255, 217, 61, .55), rgba(38, 224, 255, .25));
  box-shadow: 0 0 16px rgba(38, 224, 255, .4);
}
/* Die laufende Strichelung — dieselbe Mechanik wie die Mittellinie des
   Partybus-Highways: `translateX` auf einem sich wiederholenden Verlauf,
   keine Positionsanimation. */
.live80-v2 .route-track::after {
  content: "";
  position: absolute; left: -30%; right: -30%; top: 0; bottom: 0;
  background: repeating-linear-gradient(90deg,
    rgba(255, 255, 255, .9) 0 22px, transparent 22px 62px);
  opacity: .7;
  animation: cr-dash 1.6s linear infinite;
}
@keyframes cr-dash { to { transform: translateX(-62px); } }

/* ---- Das Schiff --------------------------------------------------------
   🔴 Warum ZWEI verschachtelte Elemente und nicht eines mit einem Pixelwert:
   Die Strecke ist die Breite der Stationsreihe und damit vom Viewport
   abhaengig. `transform: translateX(%)` rechnet aber gegen die EIGENE Breite
   des Elements, nicht gegen die des Elternteils — ein einzelnes Schiff liesse
   sich also nur mit einer festen Pixelzahl bewegen, und die waere bei jeder
   anderen Fensterbreite falsch.

   Deshalb: `.route-lane` spannt genau die Strecke (dieselben 1/6 wie die
   Linie), `.route-sail` ist genauso breit wie die Lane und wandert um
   `translateX(100%)` — also um exakt eine Lane-Breite. Das Schiff sitzt an
   deren linkem Rand und kommt damit punktgenau von Station 1 zu Station 3,
   bei jeder Fensterbreite und ohne eine einzige Zahl im Skript.

   ⚠️ Die Spiegelung fuer die Rueckfahrt sitzt auf dem SCHIFF, nicht auf der
   Lane: ein `scaleX(-1)` auf dem wandernden Element wuerde auch die
   Fahrtrichtung umkehren. Zwei Animationen, beide gleich lang, beide nur
   `transform` (Checkliste 251).

   🔴 GEMESSENER FEHLER, den es hier zweimal gab und der jetzt behoben ist:
   (1) Das Schiff trug `animation` ZWEIMAL — einmal `cr-flip` und weiter unten
       nochmal `cr-sail`. Bei derselben Eigenschaft gewinnt die spaetere: das
       Schiff lief also dieselbe Strecke wie der Wanderer, beide addierten sich,
       und es stand bei `left:1827` weit AUSSERHALB der Sektion (Sektion endet
       bei 1420). Ein Element darf genau eine `animation`-Deklaration haben.
   (2) `cr-flip` war ueberhaupt nicht definiert — ein unbekannter
       Animationsname wird stillschweigend ignoriert, es gab also nie eine
       Fehlermeldung.
   ------------------------------------------------------------------------ */
.live80-v2 .route-lane {
  position: absolute; left: calc(100% / 6); right: calc(100% / 6);
  top: calc(var(--cr-dot) / 2);
  height: 0; z-index: 3; pointer-events: none;
}
/* Der Wanderer: genauso breit wie die Lane, wandert um seine eigene Breite. */
.live80-v2 .route-sail {
  position: absolute; left: 0; top: 0; width: 100%; height: 0;
  animation: cr-sail 13s cubic-bezier(.45, 0, .55, 1) infinite;
}
/* `translate` traegt die Zentrierung, die Keyframe fasst nur `transform` an —
   beide addieren sich (Checkliste 52), zusammen in einer Eigenschaft waere das
   Schiff um 100 % verschoben.
   ⚠️ GENAU EINE `animation`-Deklaration an diesem Element (siehe oben). */
.live80-v2 .route-ship {
  position: absolute; left: 0; top: 0;
  width: clamp(38px, 3.6vw, 54px); height: clamp(38px, 3.6vw, 54px);
  translate: -50% -50%;
  animation: cr-flip 13s steps(1, end) infinite;
  background: center / contain no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 10.189V14m0-12v3m7 8V7a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v6'/%3E%3Cpath d='M19.38 20A11.6 11.6 0 0 0 21 14l-8.188-3.639a2 2 0 0 0-1.624 0L3 14a11.6 11.6 0 0 0 2.81 7.76'/%3E%3Cpath d='M2 21c.6.5 1.2 1 2.5 1c2.5 0 2.5-2 5-2c1.3 0 1.9.5 2.5 1s1.2 1 2.5 1c2.5 0 2.5-2 5-2c1.3 0 1.9.5 2.5 1'/%3E%3C/svg%3E");
  /* ⚠️ Statischer `filter`, KEINE Filter-Animation — der Schein darf nicht
     Bild fuer Bild neu gerastert werden (Checkliste 59). */
  filter: drop-shadow(0 0 8px rgba(255, 255, 255, .8))
          drop-shadow(0 0 22px rgba(38, 224, 255, .6));
}
/* Hin und zurueck — die Route ist eine Rundreise, das Schiff kehrt also um.
   Ein reines Durchlaufen von links nach rechts waere die falsche Aussage.
   ⚠️ `100%` bezieht sich auf die eigene Breite des Wanderers, also genau eine
   Lane-Breite. Der frueher hier stehende Pixelwert (`var(--cr-span, 640px)`)
   war bei jeder anderen Fensterbreite falsch. */
@keyframes cr-sail {
  0%,  4%   { transform: translateX(0); }
  44%, 52%  { transform: translateX(100%); }
  96%, 100% { transform: translateX(0); }
}
/* Die Spiegelung: das Schiff dreht am Wendepunkt, nicht unterwegs. `steps(1)`
   macht daraus einen harten Wechsel statt eines Zusammenquetschens. */
@keyframes cr-flip {
  0%,  50%  { transform: scaleX(1); }
  54%, 100% { transform: scaleX(-1); }
}

.live80-v2 .route-stops {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s5);
  position: relative;
  z-index: 2;
}
.live80-v2 .route-stop {
  position: relative;
  display: grid; justify-items: center; gap: var(--s2);
  text-align: center;
  /* Platz fuer das Stationssymbol, das als `::before` darueber sitzt. */
  padding-top: calc(var(--cr-dot) + var(--s5));
}
/* 🟢 Die Stationssymbole sind PSEUDOELEMENTE mit einer data-URI, kein eigener
   Baustein im Seiteninhalt (Checkliste 249 und 173).

   Zwei Gruende, und beide sind hier belegt:
   (1) Ein leeres `core/group` bekommt im Editor einen ~280 px hohen
       „Block hinzufuegen"-Platzhalter (Checkliste 173/31) — drei davon mitten
       in der Route haetten den Bearbeitungsmodus zerrissen.
   (2) Als `core/html` waeren es drei weitere Code-Kaesten zwischen den
       Inhalten — genau die Kloetze, die aus den Act-Karten herausgehalten
       wurden (Checkliste 186/245).
   Damit traegt jede Station nur noch das, was Inhalt IST: Praeposition und
   Ortsname.

   ⚠️ Preis, den man kennen muss: die Symbole haengen ueber `:nth-child()` an
   der POSITION der Station. Wer die Reihenfolge im Editor aendert, aendert die
   Symbole mit. Bei reiner Deko vertretbar — die Aussage steht im Text.
   ⚠️ `currentColor` funktioniert im data-URI NICHT, die Farbe muss fest hinein
   (`%23` fuer `#`). */
.live80-v2 .route-stop::before {
  content: "";
  position: absolute; top: 0; left: 50%; translate: -50% 0;
  width: var(--cr-dot); height: var(--cr-dot);
  border-radius: 50%;
  background: rgba(var(--c-shadow-deep), .85) center / 46% 46% no-repeat;
  border: 2px solid rgba(38, 224, 255, .6);
  box-shadow: var(--glow-cyan), inset 0 0 18px rgba(38, 224, 255, .18);
}
/* Hamburg — Anker, cyan. Steht zweimal: Abfahrt und Rueckkehr. */
.live80-v2 .route-stop:nth-child(1)::before,
.live80-v2 .route-stop:nth-child(3)::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2326e0ff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 6v16m7-9l2-1a9 9 0 0 1-18 0l2 1m4-2h6'/%3E%3Ccircle cx='12' cy='4' r='2'/%3E%3C/svg%3E");
}
/* Kristiansand — Fjordberge, Gold. Das Ziel bekommt die Auszeichnungsfarbe. */
.live80-v2 .route-stop:nth-child(2)::before {
  border-color: rgba(255, 217, 61, .7);
  box-shadow: var(--glow-gold), inset 0 0 18px rgba(255, 217, 61, .2);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffd93d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m8 3l4 8l5-5l5 15H2z'/%3E%3Cpath d='M4.14 15.08q3.93-2.355 7.86.42c2.74 1.94 5.49 2 8.23.19'/%3E%3C/svg%3E");
}

.live80-v2 .route-pre {
  font-family: 'Core Sans M', 'Asap', sans-serif;
  font-size: var(--fs-xs); letter-spacing: .26em; text-transform: uppercase;
  color: var(--c-cyan); text-shadow: var(--glow-cyan);
}
.live80-v2 .route-stop:nth-child(2) .route-pre {
  color: var(--c-gold); text-shadow: var(--glow-gold);
}
.live80-v2 .route-place {
  font-family: 'Core Sans M', 'Asap', sans-serif;
  font-size: clamp(19px, .9rem + 1.1vw, 34px); font-weight: 800;
  letter-spacing: .04em; text-transform: uppercase;
  line-height: 1.06; color: #fff;
  text-shadow: 0 2px 0 rgba(var(--c-shadow), .85), 0 0 18px rgba(var(--c-shadow), .9);
  text-wrap: balance;
}
/* Das Land als Beisatz — es steht in der Klammer der Live-Seite. */
.live80-v2 .route-land {
  font-size: var(--fs-xs); letter-spacing: .18em; text-transform: uppercase;
  color: var(--c-ink-3);
}

/* Die Eckdaten unter der Karte. Zwei Pillen, dieselbe Optik wie die
   Spielstaetten-Plakette im Hero. */
.live80-v2 .route-meta {
  display: flex; flex-wrap: wrap; gap: var(--s3); justify-content: center;
  margin-top: var(--s8);
}
.live80-v2 .route-meta p {
  margin: 0;
  font-family: 'Core Sans M', 'Asap', sans-serif;
  font-size: var(--fs-sm); letter-spacing: .16em; text-transform: uppercase;
  color: var(--c-ink-2);
  padding: .42em 1.1em; border-radius: var(--r-pill);
  border: 1px solid rgba(38, 224, 255, .3);
  background: rgba(var(--c-shadow-deep), .66);
}
.live80-v2 .route-meta p b { color: #fff; font-weight: 800; }

/* --- Buchung statt Ticketkauf --------------------------------------------
   Der Ticket-Abschluss selbst kommt aus dem abgeleiteten Koeln-Kapitel (die
   Karte mit Abrissabschnitt, Neonroehre, Gitterboden, Barcode). Hier nur die
   Unterschiede: der Abriss traegt bei einer Reise keine Platznummer, sondern
   die Reisedaten, und der Knopf ist breiter, weil „Jetzt Cruise buchen" laenger
   ist als „Jetzt Tickets sichern".
   ⚠️ Die zweite Zeile im Abriss ist die ANFANGSzahl der Spanne — das JS setzt
   sie aus dem Hero (Checkliste 176: Spanne vor Einzeldatum parsen).
   ------------------------------------------------------------------------- */
/* 🔴 Eigene Rampe fuer den Ortsnamen im Abriss — GEMESSEN, nicht geschaetzt.
   Der Abriss ist 190 px breit; die Basisregel `.tk-city` ist an „KÖLN"
   ausgemessen (4 Zeichen, 26 px, .12em Laufweite). „NORWEGEN" hat 8 Zeichen und
   kam damit auf 175 px Textbreite — der Name war rechts ABGESCHNITTEN.
   ⚠️ Mein erster Anlauf hat es verschlimmert: `letter-spacing:.14em` machte ihn
   noch breiter. Richtig ist die Gegenrichtung.
   ⚠️ Gefunden hat das der Screenshot, nicht die Geometriemessung — die Sektion
   hatte die erwarteten 794 px Hoehe (Checkliste 171). */
.live80-v2 .ticket-sec .tk-stub .tk-city {
  font-size: clamp(17px, 1.05vw, 20px);
  letter-spacing: .06em;
}
.live80-v2 .ticket-sec .tk-btn { min-width: min(100%, 300px); }

/* --- Mobil ---------------------------------------------------------------- */
@media (max-width: 780px) {
  /* Die Route wird senkrecht: drei Stationen nebeneinander sind bei 375 px
     rund 100 px pro Ortsname — „KRISTIANSAND" braucht mehr.
     ⚠️ Gemessen, nicht geschaetzt: einspaltig steht der laengste Name auf einer
     Zeile, dreispaltig brach er auf drei. */
  .live80-v2 .route-stops { grid-template-columns: 1fr; gap: var(--s7); }
  /* Waagerechte Linie, Fahrtkorridor und Schiff haben senkrecht keine Aussage
     mehr — die Reihenfolge der Stationen traegt sie. Ausblenden statt drehen:
     eine senkrechte Fahrtlinie zwischen drei gestapelten Bloecken sieht wie ein
     Darstellungsfehler aus. */
  .live80-v2 .route-track,
  .live80-v2 .route-lane { display: none; }
  .live80-v2 .route-sea { height: 30%; }

  .live80-v2 .trailer-frame { padding: 8px; }
}

/* --- Ruhezustand ---------------------------------------------------------
   Nur die BEWEGUNG faellt weg, die Buehne bleibt stehen — dieselbe Linie wie
   bei den Lichteffekten des Hero und beim Partybus-Highway. Das Schiff bleibt
   sichtbar an der Abfahrtsstation.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .live80-v2 .route-track::after,
  .live80-v2 .route-ship { animation: none; }
  .live80-v2 .dwd27-vimeo-preview { display: none; }
  .live80-v2 .dwd27-vimeo-play { transition: none; }
}

/* --- Hero-Logo: Absetzung vom Grund -----------------------------------------
   🔴 Das Cruise-Logo stand als einziges Bild der Seite ohne jeden Saum auf dem
   hellen Magenta — und dahinter liegt zusaetzlich die Neon-Schiffskulisse.
   Gemessen am gerenderten Bild (Median der Leuchtdichte im 9-px-Ring um die
   Silhouette gegen das Logo selbst): **1,09 : 1**. Es loeste sich also optisch
   im Hintergrund auf. Nach dieser Regel: **1,31 : 1**.
   ⚠️ Der WRAPPER `.cruise-logo` traegt bereits einen weiten pinken Schein
   (`drop-shadow(0 0 24px rgba(255,45,149,.6))`) — der bleibt und liefert den
   warmen Aussenrand. Hier kommt nur der DUNKLE Nahsaum dazu, der die Kante
   traegt, plus ein kuehler Ring in der Cruise-Leitfarbe Cyan.
   ⚠️ Reihenfolge ist nicht beliebig: erst eng und hart (Kante), dann weit und
   weich (Tiefe). Umgekehrt frisst der weiche Schatten den harten auf.
   ⚠️ Kein `filter` auf ein ANIMIERTES Element legen, das per Keyframe selbst
   `filter` setzt — genau die `.chrome`-Falle mit `fill:both`. Geprueft: weder
   `.cruise-logo` noch das `img` tragen eine Animation (`animation-name:none`),
   die Einblendung laeuft ueber `opacity`/`transform` am Wrapper. */
.live80-v2 .cruise-logo img {
  filter:
    drop-shadow(0 0 4px rgba(14, 2, 34, .95))
    drop-shadow(0 2px 2px rgba(14, 2, 34, .9))
    drop-shadow(0 0 14px rgba(14, 2, 34, .85))
    drop-shadow(0 14px 30px rgba(8, 1, 22, .7))
    drop-shadow(0 0 38px rgba(38, 224, 255, .55));
}
