/* ===== SoulKeeper - Level 3 Gameplay: "Das letzte Gefecht" ================
   Der Soulkeeper-Endgegner (js/level3-gameplay.js) - einzelnes, dauerhaftes
   Boss-Element (kein Spawn/Despawn wie bei normalen Gegnern, siehe
   css/level1-gameplay.css/css/level2-gameplay.css), das sich über die ganze
   Bühne (#level3-boss-buehne) bewegt. Nutzt für Punkte/Timer/Waffenwahl/
   Boss-HP-Leiste bewusst dieselben HUD-Klassen wie Level 1/2 (siehe
   #level1-spiel-hud in index.html) - keine zweite HUD-Struktur nötig.

   Bühne selbst hat bewusst KEINEN eigenen z-index (nur position:absolute) -
   sonst würde sie einen eigenen Stacking-Context aufmachen und
   .l3-soulkeeper/-seele könnten sich nie einzelnen Ebenen der Kulisse
   ein-/unterordnen. Stattdessen setzen Wrapper+Seele ihren z-index direkt
   selbst (siehe js/level3-gameplay.js: setzeTiefeUndBlickrichtung/
   FLUG_TIEFEN), dadurch reihen sie sich in denselben globalen Stapel wie
   Tor/Zäune/Brücke ein (siehe css/level3.css: bg-corner=1 ... e5-top=50) -
   in der Boden-Phase bleibt das fest bei 40 (vor allem außer dem
   dekorativen Vordergrund-Rahmen e5-top=50), in der Flug-/Aggro-Phase kann
   es je nach gewählter Tiefen-Stufe auch darunter liegen (Vorgabe: dort
   darf er auch hinter dem Tor/den Zäunen erscheinen). */

#level3-boss-buehne {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

/* ===== Bonus: fallender Totenkopf (js/bonus-skull.js, levelunabhängig) =====
   Aussehen kommt unverändert aus css/level2-gameplay.css (.bonus-skull ist
   dort levelunabhängig definiert und wird global geladen) - hier steht nur
   die Level-3-Einordnung in die Kulisse, die es in Level 1/2 nicht braucht:
   Dort liegt der Bonus in einem eigenen "nah"-Container mit eigenem z-index
   (siehe .level2-spielbereich--nah), hier hat die Bühne bewusst keinen
   (Kommentar oben), sodass der Totenkopf ohne diese Regel hinter Tor,
   Zäunen und Brücke verschwinden würde. 45 liegt vor dem Soulkeeper in der
   Bodenphase (z-index 40, siehe .l3-soulkeeper) und vor der gesamten
   Kulisse, aber hinter dem dekorativen Vordergrund-Rahmen (50, siehe
   css/level3.css) - dieselbe Staffelung wie in Level 2. */
#level3-boss-buehne .bonus-skull {
    z-index: 45;
}

/* ===== Boss-Grundgerüst (Position via left/top, JS-Transition) =====
   Dreistufig verschachtelt wie beim Hexen-Tod in Level 1 (siehe dortiger
   Kommentar): äußerer Wrapper übernimmt NUR die Position (left/top,
   CSS-Transition), .l3-soulkeeper-scale NUR Blickrichtung+Tiefen-Skalierung
   (JS-Inline-Transform, ändert sich sprunghaft bei Phasenwechsel/Etappen),
   .l3-soulkeeper-wackel NUR das kontinuierliche Idle-Schweben/-Wackeln
   (CSS-Keyframe) - drei getrennte Transform-Ebenen, damit sich keine davon
   gegenseitig überschreibt. z-index:40 ist nur der Startwert (Boden-Phase-
   Vorgabe) - js/level3-gameplay.js überschreibt ihn inline, sobald geflogen
   werden darf. */
.l3-soulkeeper {
    position: absolute;
    width: 16%;
    /* Seitenverhältnis der SVG-Fassung (viewBox 0 0 655 980, siehe
       js/level3-gameplay.js: SOULKEEPER_BILD) - muss zum Bild passen, sonst
       verzerrt .l3-soulkeeper-bild (width/height je 100%) die Silhouette.
       width bleibt bewusst 16% (SOULKEEPER_HALBE_BREITE_PROZENT = 8 im JS
       ist die fest hinterlegte Hälfte davon); die Box wird dadurch flacher
       als zuvor, die DARGESTELLTE Höhe bleibt aber unverändert, weil
       passeSoulkeeperInsTorlochEin() die Referenzskalierung rein aus der
       Lochhöhe gegen genau diese Box-Höhe bildet. */
    aspect-ratio: 655 / 980;
    pointer-events: auto;
    /* Kein eigenes cursor:crosshair mehr - erbt das waffenabhängige
       Fadenkreuz von .level3-gameplay-aktiv, siehe css/level1-gameplay.css:
       .l1-feind für die ausführliche Begründung. */
    z-index: 40;
}

.l3-soulkeeper-scale {
    display: block;
    width: 100%;
    height: 100%;
    transform-origin: center;
}

.l3-soulkeeper-wackel {
    display: block;
    width: 100%;
    height: 100%;
    transform-origin: center;
    animation: l3-soulkeeper-boden-wackel 2.4s ease-in-out infinite;
}

/* Auftritt (Einblenden/theatralischer Stillstand/Runterlaufen): kein
   Wackeln, steht still (Vorgabe) - erst js/level3-gameplay.js: beginneKampf()
   entfernt diese Klasse wieder. */
.l3-soulkeeper--kein-wackel .l3-soulkeeper-wackel {
    animation: none;
    transform: none;
}

@keyframes l3-soulkeeper-boden-wackel {
    0%, 100% { transform: rotate(-1.5deg); }
    50%      { transform: rotate(1.5deg); }
}

/* Phase 2/3 (Flug): sanftes/aggressives Schweben statt des Boden-Wackelns -
   ersetzt dieselbe Animation auf demselben Element (keine zweite Ebene nötig). */
.l3-soulkeeper--flug .l3-soulkeeper-wackel {
    animation: l3-soulkeeper-schweben 1.8s ease-in-out infinite;
}

@keyframes l3-soulkeeper-schweben {
    0%, 100% { transform: translateY(0) rotate(-2deg); }
    50%      { transform: translateY(-4%) rotate(2deg); }
}

.l3-soulkeeper--aggro .l3-soulkeeper-wackel {
    animation: l3-soulkeeper-schweben-aggro 0.9s ease-in-out infinite;
}

@keyframes l3-soulkeeper-schweben-aggro {
    0%, 100% { transform: translateY(0) rotate(-4deg); }
    50%      { transform: translateY(-6%) rotate(4deg); }
}

.l3-soulkeeper-bild {
    display: block;
    width: 100%;
    height: 100%;
    pointer-events: none;
    filter: drop-shadow(0 0 14px rgba(0, 0, 0, 0.6));
}

/* Phase 3: zunehmend rot/aggressiv eingefärbt (Vorgabe Punkt 6) - reines
   CSS-Filter auf dem bestehenden Einzelbild, kein zweites Asset nötig. */
.l3-soulkeeper--aggro .l3-soulkeeper-bild {
    filter:
        drop-shadow(0 0 18px #ff2200)
        drop-shadow(0 0 30px #b30000)
        sepia(1) saturate(6) hue-rotate(-30deg) brightness(0.95);
}

/* Kurzer Vorwarn-/Verschwinde-Effekt beim Teleport (Punkt: "dürfen nicht
   unfair werden") - eigene Übergangsdauer (kürzer als die normale
   Positions-Transition), auf der Scale-Ebene, damit sie sich nicht mit der
   Blickrichtungs-/Tiefen-Skalierung beißt. */
.l3-soulkeeper-scale {
    transition: opacity 0.22s ease, filter 0.22s ease;
}

.l3-soulkeeper--teleport-aus .l3-soulkeeper-scale {
    opacity: 0;
    filter: blur(6px);
}

/* ===== Trefferbox (identische Technik wie .l1-feind-hitbox/.l2-feind-hitbox) ===== */
.l3-soulkeeper-hitbox {
    position: absolute;
    inset: calc(-1 * max(18%, 20px));
    pointer-events: auto;
}

.l3-soulkeeper-hitbox--debug {
    background: rgba(255, 60, 60, 0.35);
    outline: 1px dashed rgba(255, 255, 255, 0.85);
}

/* LP-Debug-Anzeige (?hitbox=1): Grundstyling jetzt gemeinsam in
   css/base.css: .feind-lp-debug (siehe dort) - js/level3-gameplay.js weist
   diese Klasse zusätzlich zu .l3-soulkeeper-lp-debug zu. Level 3 behält
   hier bewusst eine kleinere Schrift und (anders als Level 1/2) keinen
   Text-Schatten - reiner Modifier, kein eigenständiges Layout mehr. */
.l3-soulkeeper-lp-debug {
    font-size: 0.75rem;
    text-shadow: none;
}

/* ===== Angeheftete Seele/Geist hinter dem Soulkeeper (Phase 2/3) =====
   Etwas größer als der Dämon selbst (Basisbreite ~1,5x .l3-soulkeeper-
   Breite von 16%, an einer Referenz-Bildvorlage abgestimmt - vorher mit 38%
   deutlich zu groß) - wirkt wie ein hinter ihm liegender Geist-/
   Flügelkörper, nicht wie ein simples, überdimensioniertes Hintergrundbild.
   Liegt per z-index HINTER dem Soulkeeper (siehe js/level3-gameplay.js:
   setzeTiefeUndBlickrichtung, setzt beide z-index-Werte gemeinsam).
   Position UND Skalierung UND eine leichte, aus der Bewegungsrichtung
   abgeleitete Rotation werden JEDEN Frame direkt per JS gesetzt (geglättete
   Interpolation Richtung Soulkeeper, zusätzlich hart auf max. ca. 25-50px
   Abstand begrenzt, siehe js/level3-gameplay.js: starteSeeleFolgen/
   GEIST_MAX_ABSTAND_PX), deshalb bewusst OHNE CSS-Transition auf left/top/
   transform - die träge, wabbelige Verzögerung entsteht schon durch die
   JS-Glättung selbst, eine zusätzliche CSS-Transition würde sie nur
   unkontrolliert verzerren/aufschaukeln. left/top markieren die Mitte,
   transform: translate(-50%,-50%) zentriert das (variabel große) Element
   exakt darauf, bevor Skalierung/Blickrichtung/Rotation angewendet werden.
   Die SVG selbst ist bereits schwarz/dunkelrot mit rotem Leucht-Filter
   gestaltet (siehe img/level3/Enemy/FinalBoss/SoulKeeperSoul.svg) - der
   äußere drop-shadow-Schein hier folgt konsequent derselben roten Farbe
   statt des ursprünglich bläulichen Seelen-Farbtons. */
.l3-soulkeeper-seele {
    position: absolute;
    width: 24%;
    aspect-ratio: 655 / 980;
    transform: translate(-50%, -50%);
    transform-origin: center;
    pointer-events: none;
    z-index: 39;
    opacity: 0;
    /* filter wird mit-transitioniert, damit der Leuchtschein am Ende des
       Einflugs weich zurueckkommt (siehe --einflug weiter unten). */
    transition: opacity 0.8s ease, filter 0.5s ease;
    filter: drop-shadow(0 0 12px #ff2200) drop-shadow(0 0 24px #8a0000);
    /* Der Einflug skaliert das Element kurzzeitig auf das Dreifache; ohne
       diesen Hinweis rastert der Browser die gefilterte Flaeche in jedem
       Frame neu (gemessene Frame-Zeit-Spitzen im dreistelligen Bereich). */
    will-change: transform, opacity;
}

/* Volle Deckkraft (Vorgabe TODO-20260908-005) - vorher 0.75. */
.l3-soulkeeper-seele--sichtbar {
    opacity: 1;
}

/* Nur waehrend des Einflugs, solange die Seele noch deutlich groesser als
   ihre Endgroesse ist (siehe SEELE_EINFLUG_FILTER_AB_FAKTOR in
   js/level3-gameplay.js): Der Leuchtschein wird auf Radius 0 gefahren.
   Bewusst dieselbe Filterkette mit denselben Funktionen statt "none" -
   nur so interpoliert die Transition sauber, wenn der Schein zum Ende des
   Einflugs wieder auf seinen vollen Wert laeuft. Grund ist die Rasterlast:
   bei dreifacher Groesse muessten beide drop-shadow-Filter ueber die
   neunfache Flaeche neu berechnet werden. */
.l3-soulkeeper-seele--einflug {
    filter: drop-shadow(0 0 0 rgba(255, 34, 0, 0)) drop-shadow(0 0 0 rgba(138, 0, 0, 0));
}

/* ===== Unverwundbarkeit waehrend Rueckweg und Seelenauftritt =====
   Der Spieler muss erkennen koennen, dass der Soulkeeper gerade nicht
   getroffen werden kann - sonst wirkt es wie ein Aussetzer der
   Treffererkennung. Durchgehender, kalt-blauer Schutzschein (deutlich
   abgesetzt vom sonst rot/dunklen Erscheinungsbild), dazu pro abgewehrtem
   Treffer ein kurzer Aufblitzer (siehe blitzeSchutzschildAuf in
   js/level3-gameplay.js) und ein "GESCHUETZT"-Popup statt der "-x LP". */
.l3-soulkeeper--unverwundbar {
    animation: l3-schutzschein 1.6s ease-in-out infinite;
}

@keyframes l3-schutzschein {
    0%, 100% { filter: drop-shadow(0 0 6px rgba(120, 200, 255, 0.55)); }
    50%      { filter: drop-shadow(0 0 18px rgba(150, 220, 255, 0.95)); }
}

.l3-soulkeeper--schutz-blitz {
    animation: l3-schutzschein 1.6s ease-in-out infinite, l3-schutz-blitz 0.28s ease-out;
}

@keyframes l3-schutz-blitz {
    0%   { filter: drop-shadow(0 0 26px rgba(220, 245, 255, 1)) brightness(1.6); }
    100% { filter: drop-shadow(0 0 6px rgba(120, 200, 255, 0.55)) brightness(1); }
}

.l3-soulkeeper-seele img,
.l3-soulkeeper-seele svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* ===== Todesfall in den Fluss =====
   Gleiche Technik wie .l1-feind--hexe-tod/.l2-feind--spinne-tod: Position
   wird vorher eingefroren (px statt %, siehe friereSoulkeeperPosition in
   js/level3-gameplay.js). left/top-Transition kommt (wie bei jeder anderen
   Bewegungsetappe) als Inline-Style aus js/level3-gameplay.js - hier nur
   das Ausblenden der Bild-Ebene gegen Ende des Falls, fest getimt auf die
   dort verwendete Falldauer (1.9s). */
.l3-soulkeeper--faellt .l3-soulkeeper-scale {
    transition: opacity 1.9s ease-in 0.9s;
}

/* ===== Punkte-Popup (analog .l1-punkte-popup/.l2-punkte-popup) =====
   Styling jetzt gemeinsam mit Level 1/2 in css/base.css definiert (siehe
   dort, .l3-punkte-popup + @keyframes punkte-auf). */

/* ===== Fadenkreuz während des aktiven Bosskampfs (analog .level1/.level2-gameplay-aktiv) =====
   Styling jetzt gemeinsam mit Level 1/2 in css/base.css definiert (siehe
   dort, .level3-gameplay-aktiv, sowie css/level1-gameplay.css für den
   vollständigen Herleitungs-Kommentar zu diesem Muster). */

/* =====================================================================
   OUTRO (js/level3-outro.js): langsamer Rückscroll + Abblendung
   ===================================================================== */

/* Überschreibt NUR die Übergangsdauer von #story-track (siehe
   css/base.css: transform 4s) - die eigentliche Zielposition kommt
   weiterhin aus .story-track--levelN (Klasse wird von js/level3-outro.js
   entfernt, um zurückzuscrollen). Bewusst als eigene Klasse statt eines
   Inline-Styles, damit sie sich sauber wieder entfernen lässt. */
#story-track.story-track--rueckblende {
    transition: transform 9s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Vollbild-Abblendung NACH Erreichen des Himmels (eigener, von der
   Kamerafahrt entkoppelter Beat - siehe js/level3-outro.js:
   beginneFadeToBlack/FADE_DAUER_MS) - liegt wie #countdown-overlay/
   #level1-gameover-overlay außerhalb von #story-track, deckt unabhängig
   von der Kamera-Position den kompletten Bildschirm ab. */
.level3-abblende {
    position: absolute;
    inset: 0;
    z-index: 30;
    pointer-events: none;
    background: #000;
    opacity: 0;
    transition: opacity 5s linear;
}

.level3-abblende--aktiv {
    opacity: 1;
}

/* Abschieds-Titel (siehe js/level3-outro.js: beiHimmelErreicht) - ersetzt
   optisch die eigentliche Himmel-Startszene, sobald die Kamera dort wieder
   ankommt (deren Press-Start/Menü/Theme-Switch werden währenddessen per
   .level3-himmel-versteckt unsichtbar geschaltet, siehe unten). Gleicher
   Overlay-Aufbau wie .level-einblendung (css/uebergang.css), eigene Klasse,
   damit sie sich unabhängig davon ein-/ausblenden lässt. */
.level3-abschied {
    position: absolute;
    inset: 0;
    /* Bewusst 26, nicht 25: eigener, bislang unbenutzter Wert - Zufalls-
       Übereinstimmung mit css/level1-gameplay.css: .level1-spinne-overlay
       (ebenfalls 25) behoben (siehe log/SK_SELF_CHECK_2026-08-23.md:
       SK-029). Beide Elemente sind nie gleichzeitig sichtbar, der alte Wert
       war also nie ein akuter Bug, nur ein Zufall statt einer bewussten
       Entscheidung. */
    z-index: 26;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 0 1rem;
    pointer-events: none;
    opacity: 0;
    transition: opacity 2.4s ease;
}

.level3-abschied--sichtbar {
    opacity: 1;
}

.level3-abschied-dank {
    margin: 1.6rem 0 0;
    font-size: clamp(0.65rem, calc(2 * var(--uw)), 1rem);
    letter-spacing: 0.08em;
    color: var(--text-dim);
    line-height: 1.6;
}

/* Blendet die interaktive Himmel-Startszene (Press Start/Menü/Theme-
   Switch/Copyright) während des Abschieds unsichtbar, OHNE ihren
   State/ihre Klassen anzufassen - #kehrtZumHauptmenueZurueck (js/main.js)
   bzw. der grafische Abspann-Naturschluss (js/abspann.js) entfernt diese
   Klasse wieder, sobald die Startszene tatsächlich wieder benutzbar sein
   soll. visibility statt display, damit #scene-himmel weiterhin normal
   Layout-Platz einnimmt. */
.level3-himmel-versteckt {
    visibility: hidden;
}

/* ===== Aufsteigende Seelen (Oberwelt-Szene, Outro) =====
   Bugfix: nutzte bislang noch die alte Tropfen-/Glow-Optik von VOR der
   Umstellung der fallenden Höllentor-/Unterwelt-Seelen auf das echte
   Pixel-Art-Bild (img/Soul/DeadSoul_pixel.png, siehe css/level3.css: .soul
   /css/level2.css: .level2-soul) - dieser Umstieg war hier beim Umbau
   übersehen worden (Vorgabe: dieselbe Seelen-Grafik überall, auch beim
   Aufstieg im Abspann). ::before zeigt jetzt dasselbe Bild wie dort, KEINE
   180°-Drehung (anders als beim Fallen: die Seele steigt Kopf-oben auf,
   das Bild ist bereits kopf-oben ausgerichtet). Bewegungsrichtung nach
   oben statt unten - kein zweites Partikel-System, nur eigene Keyframes
   (soul-aufstieg-kf unten). Vorgabe: die Seelen dürfen nicht vorzeitig
   ausblenden, sondern müssen sichtbar bis außerhalb des Bildschirms
   weiterfliegen (siehe soul-aufstieg-kf: bleibt bis zum Ende sichtbar, sehr
   große Steigdistanz, Entfernen aus dem DOM übernimmt animationend bzw. die
   Aufräumfunktion in js/level3-outro.js) - der Schweif (::after unten)
   bleibt unten/hinter der Seele (umgekehrte Ausrichtung ggü. der fallenden
   .soul, die gar keinen Schweif mehr hat - hier bewusst erhalten, da die
   Seele über eine deutlich größere Strecke sichtbar bleibt). Schweif
   (::after) wieder entfernt (Vorgabe) - nur noch das reine Bild (::before),
   analog zu den fallenden Seelen (css/level3.css: .soul, hat schon lange
   keinen Schweif mehr). */
.soul-aufstieg {
    /* top/left werden pro Instanz von js/level3-outro.js gesetzt (Position
       des jeweiligen Grabsteins, siehe spawneSeelenAufstieg) - hier nur die
       gemeinsame Basis. */
    position: absolute;
    width: 20px;
    height: 32px;
    margin-left: -10px;
    pointer-events: none;
    opacity: 0;
    z-index: 6;
    animation: soul-aufstieg-kf 20s linear forwards;
    will-change: transform, opacity;
}

.soul-aufstieg::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: url('../img/Soul/DeadSoul_pixel.png');
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    image-rendering: pixelated;
    image-rendering: -moz-crisp-edges;
}

/* Benannte Seelen (mit Sprechblase, siehe .soul-aufstieg-text unten) sind
   deutlich größer als die namenlosen - Vorgabe: "damit man den Text besser
   lesen kann". ::before (Glow-Körper) füllt via inset:0 automatisch die
   neue Größe aus, ::after (Schweif) braucht eigene, proportional größere
   Maße (skaliert nicht automatisch mit width/height des Elternelements). */
.soul-aufstieg--gross {
    width: 46px;
    height: 74px;
    margin-left: -23px;
}

/* Nur die beiden Abschieds-Seelen im Himmel (siehe js/level3-outro.js:
   beiHimmelErreicht) - zusätzlich zu --gross, nochmal größer ("quasi auch
   weiter vorn am Bild", Vorgabe) - muss NACH .soul-aufstieg--gross stehen,
   damit die größeren Maße hier gewinnen (beide Klassen liegen gemeinsam
   auf demselben Element, gleiche Selektor-Spezifität, spätere Regel siegt).
   z-index deutlich über #level3-abschied (26, siehe oben) - Vorgabe: "die
   letzten beiden Seelen müssen vollständig vorn sein, auch vor dem Titel". */
.soul-aufstieg--vorne {
    width: 64px;
    height: 103px;
    margin-left: -32px;
    z-index: 30;
}

/* ===== Tiefenebenen der Oberwelt-Seelen (Vorgabe: "ein paar mehr
   gleichzeitig", gestaffelt vorne/hinter dem schwarzen Berg/am grauen
   Berg/ganz hinten hinter allen Bergen, aber immer vor Mond+Himmel") -
   Größe UND Stapel-Reihenfolge (z-index, siehe js/level3-outro.js:
   TIEFENEBENEN) variieren gemeinsam, damit weiter hinten liegende Seelen
   auch kleiner/entfernter wirken. Nur für namenlose Seelen gedacht - die
   benannten (mit Sprechblase) bleiben bewusst immer in Tiefe 1 (vorne,
   volle Größe), damit ihr Text lesbar bleibt. */
.soul-aufstieg--tiefe-1 {
    width: 27px;
    height: 43px;
    margin-left: -13.5px;
}

.soul-aufstieg--tiefe-2 {
    width: 15px;
    height: 24px;
    margin-left: -7.5px;
}

.soul-aufstieg--tiefe-3 {
    width: 11px;
    height: 18px;
    margin-left: -5.5px;
}

.soul-aufstieg--tiefe-4 {
    width: 8px;
    height: 13px;
    margin-left: -4px;
}

/* Sprechblase an einzelnen, benannten Seelen (siehe js/level3-outro.js:
   spawneSeelenAufstieg, benannteNachrichten) - Kind-Element von
   .soul-aufstieg, macht die Aufstiegs-Animation des Elternelements dadurch
   automatisch mit (kein zweiter Bewegungsmechanismus nötig). */
.soul-aufstieg-text {
    position: absolute;
    left: 50%;
    bottom: 100%;
    margin-bottom: 0.8em;
    transform: translateX(-50%);
    width: max-content;
    max-width: clamp(12rem, calc(32 * var(--uw)), 19rem);
    padding: 0.75em 1em;
    /* Jede Sprechblase gehört zu einer .soul-aufstieg--gross (nur benannte
       Seelen bekommen überhaupt eine Sprechblase, siehe js/level3-outro.js)
       - entsprechend großzügig bemessen, damit der Text gut lesbar bleibt
       (Vorgabe). */
    font-size: clamp(0.75rem, calc(2.1 * var(--uw)), 1.05rem);
    line-height: 1.6;
    text-align: center;
    color: #eaf9ff;
    background: rgba(10, 22, 32, 0.78);
    border: 1px solid rgba(127, 208, 242, 0.55);
    border-radius: 8px;
    box-shadow: 0 0 16px rgba(127, 208, 242, 0.4);
    pointer-events: none;
}

.soul-aufstieg-text::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    width: 14px;
    height: 14px;
    margin-left: -7px;
    margin-top: -7px;
    background: rgba(10, 22, 32, 0.78);
    border-right: 1px solid rgba(127, 208, 242, 0.55);
    border-bottom: 1px solid rgba(127, 208, 242, 0.55);
    transform: rotate(45deg);
}

/* Die beiden Abschieds-Seelen im Himmel (.soul-aufstieg--vorne) bekommen
   eine nochmal etwas größere Sprechblase, passend zur größeren Seele. */
.soul-aufstieg--vorne .soul-aufstieg-text {
    max-width: clamp(14rem, calc(36 * var(--uw)), 22rem);
    padding: 0.9em 1.2em;
    font-size: clamp(0.9rem, calc(2.6 * var(--uw)), 1.3rem);
}

@keyframes soul-aufstieg-kf {
    0%   { transform: translateY(0) scale(0.6); opacity: 0; }
    8%   { opacity: 0.95; }
    100% { transform: translateY(calc(-170 * var(--uh))) scale(1.05); opacity: 0.95; }
}

@media (prefers-reduced-motion: reduce) {
    .l3-soulkeeper-wackel,
    .soul-aufstieg {
        animation: none;
    }
    #story-track.story-track--rueckblende,
    .level3-abblende {
        transition: none;
    }
}

/* =====================================================================
   ABSPANN (js/abspann.js): Filmabspann-Overlay
   ===================================================================== */

.abspann-overlay {
    position: absolute;
    inset: 0;
    z-index: 50;
    background: #000;
    overflow: hidden;
    pointer-events: auto;
    opacity: 1;
}

.abspann-scroll {
    position: absolute;
    left: 50%;
    top: 100%;
    z-index: 1;
    width: min(560px, calc(84 * var(--uw)));
    transform: translateX(-50%);
    text-align: center;
    /* Kryptische Terminal-/Matrix-Typografie statt des sonst überall
       genutzten Press-Start-2P-Pixel-Looks (siehe [[soulkeeper-pixelfont-
       standard]]) - VT323 ist bereits als Google Font in css/base.css
       eingebunden (Monospace-Terminal-Schrift), kein neuer Font-Import nötig. */
    font-family: 'VT323', monospace;
    /* Themeabhängig statt festem Terminal-Grün (Vorgabe): der Abspann lief
       vorher immer in #6dffb0, egal ob der Spieler Lila, Orange, Grauweiß
       oder Schneeweiß gewählt hatte - nach 20 Minuten Orange kam am Ende ein
       grüner Abspann. Die SCHRIFT (VT323) bleibt bewusst der Terminal-Look,
       nur die Farbe folgt jetzt dem Theme. Glow-Aufbau wie in
       css/himmel.css: innen --accent-bright, außen weicher --accent. */
    color: var(--accent-bright);
    text-shadow:
        0 0 6px var(--accent-bright),
        0 0 14px color-mix(in srgb, var(--accent) 60%, transparent);
    animation: abspann-scroll-kf linear forwards;
    animation-duration: var(--abspann-dauer, 60s);
}

@keyframes abspann-scroll-kf {
    from { transform: translate(-50%, 0); }
    to   { transform: translate(-50%, calc(-100% - calc(100 * var(--uh)))); }
}

/* Vorgabe: deutlich mehr Abstand VOR der Überschrift (Trennung zum
   vorherigen Block) als NACH ihr (zu ihren eigenen Namen) - vorher genau
   umgekehrt (margin nur unten), dadurch klebte die Überschrift am
   vorherigen Block und der große Abstand lag zwischen Überschrift und den
   eigenen Namen statt zwischen den Blöcken. */
.abspann-rolle {
    margin: 3.5em 0 1em;
    font-size: clamp(1.1rem, calc(3 * var(--uw)), 1.6rem);
    letter-spacing: 0.35em;
    text-transform: uppercase;
    /* Mit .abspann-name (siehe unten) hochgezogen, damit die Überschriften
       heller bleiben als ihre Namenszeilen - sonst hätte die Anhebung dort
       die Rangfolge umgedreht. */
    opacity: 0.9;
}

.abspann-titel {
    margin: 4em 0 0.6em;
    font-size: clamp(1.6rem, calc(5 * var(--uw)), 2.6rem);
    letter-spacing: 0.08em;
}

.abspann-zeile {
    margin: 0.4em 0;
    font-size: clamp(1rem, calc(2.6 * var(--uw)), 1.3rem);
    /* Hellste Stufe der drei Abspann-Textgrößen - deshalb --accent-bright
       ohne Abdunklung (siehe .abspann-scroll: Farbe folgt dem Theme). */
    color: var(--accent-bright);
    opacity: 0.85;
}

/* Namenszeilen aus abspann.md (siehe js/abspann.js: renderCredits) -
   kleiner/gedämpfter als die Rollen-Überschriften, damit die eigentlichen
   Kategorien (GAME/DESIGN/...) optisch klar als solche herausstechen. */
.abspann-name {
    margin: 0.15em 0;
    font-size: clamp(0.8rem, calc(2 * var(--uw)), 1rem);
    letter-spacing: 0.06em;
    /* Bewusst --accent-bright (nicht --accent) trotz der gedämpften Wirkung:
       mit opacity 0.7 wäre das dunklere --accent im Grau-Theme (#6b7280) auf
       Schwarz kaum noch lesbar, während die Zeilen vorbeiscrollen. Die
       Abstufung zu den Überschriften entsteht hier über Schriftgröße und
       opacity, nicht über einen zweiten Farbton. */
    color: var(--accent-bright);
    /* 0.86 statt vorher 0.7: die Themefarben sind dunkler als das alte feste
       Grün. Gemessener Kontrast auf Schwarz (Namenszeile, kleinste Stufe) -
       vorher #4fd696 bei 0.7 = 5.7:1; mit Themefarbe bei 0.7 fiel Purple
       (Standard-Theme) auf 3.8:1, bei 0.86 liegt es wieder bei 5.3:1.
       Orange 6.7:1, Grau 7.1:1, Weiß 15.2:1. Wichtig, weil der Text klein
       ist UND sich bewegt. */
    opacity: 0.86;
}

/* Deckende Fläche mit weichem Verlauf nach oben - verhindert, dass die
   hochlaufende Credit-Schrift sichtbar hinter/durch den Skip-Button läuft
   (Vorgabe) - liegt über dem Scroll-Text, unter dem Button. */
.abspann-blende {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: calc(34 * var(--uh));
    z-index: 5;
    pointer-events: none;
    background: linear-gradient(to top, #000 0%, #000 35%, rgba(0, 0, 0, 0) 100%);
}

/* Erscheint bewusst nicht sofort mit dem Overlay, sondern erst wenn die
   hochlaufende Schrift zur Hälfte im Bildschirm angekommen ist (siehe
   js/abspann.js: planeSkipEinblendung/.abspann-skip--sichtbar) - die
   schwarze Fläche/der Text dahinter sind aber schon da. Langsame Opacity-
   Transition (Vorgabe: "soll langsam eingeblendet werden", kein Zack-an).
   pointer-events: none, solange unsichtbar, damit ein Klick auf die
   spätere Button-Position währenddessen nicht versehentlich greift. */
.abspann-skip {
    position: absolute;
    left: 50%;
    bottom: clamp(1rem, calc(4 * var(--uh)), 2.2rem);
    transform: translateX(-50%);
    z-index: 10;
    padding: 0.6em 1.4em;
    font-family: 'VT323', monospace;
    font-size: clamp(1rem, calc(2.4 * var(--uw)), 1.3rem);
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--accent-bright);
    background: color-mix(in srgb, var(--accent) 22%, rgba(0, 0, 0, 0.66));
    border: 1px solid var(--accent);
    border-radius: 4px;
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    transition: opacity 2.4s ease;
}

.abspann-skip--sichtbar {
    opacity: 1;
    pointer-events: auto;
}

.abspann-skip:hover,
.abspann-skip:focus-visible {
    background: color-mix(in srgb, var(--accent) 34%, rgba(0, 0, 0, 0.78));
    border-color: var(--accent-bright);
}

@media (prefers-reduced-motion: reduce) {
    .abspann-scroll {
        animation: none;
        top: calc(8 * var(--uh));
        transform: translateX(-50%);
    }
}
