/* ===== Einladung - "Ruf von oben" (Phase 1) ==============================
   Kompletter Neubau, ersetzt die fruehere Story-Choreografie dieser Datei.
   Nutzt weiterhin die 1:1 aus SoulKeeper uebernommene Dialogbox (css/dialog.
   css, js/dialog.js, dort unveraendert) fuer die Optik - die Positionierung
   und der Ablauf kommen aber komplett aus js/verlorene-seele.js.

   Ablauf: Sobald der User beim Herunterscrollen "Wann & Wo" erreicht, taucht
   die Box am oberen Bildschirmrand auf (.ruf-oben, position:fixed - das
   Gegenstueck zu bottom in css/dialog.css). Scrollt der User wieder nach
   oben, bleibt sie dort kleben (echtes position:fixed, kein JS noetig), bis
   js/verlorene-seele.js eine vorher berechnete Ziel-Dokumentposition
   innerhalb der Oberwelt erreicht - ab dann uebernimmt .ruf-oben--dokument
   (position:absolute, top wird per Inline-Style gesetzt) und die Box wird
   endgueltig Teil des Dokuments, verlaesst dafuer die fixe Buehne
   (#seelen-dialog-buehne) und wird direkt nach <body> gehaengt (body ist
   position:relative laut css/base.css - siehe js/verlorene-seele.js). */

.ruf-oben {
    position: fixed;
    top: clamp(1.5rem, calc(4 * var(--uh)), 3rem);
    bottom: auto;
    left: 50%;
    transform: translateX(-50%);
    pointer-events: none;
    /* Muss ausnahmslos ueber ALLEM liegen (Vorgabe) - hoechster bisheriger
       Wert im Projekt ist 1000 (css/fluchwarnung.css: .fluch-hinweis-
       overlay), deutlich drueber. #seelen-dialog-buehne (die Buehne, in der
       die Box VOR dem Umhaengen nach <body> steckt) traegt bewusst KEIN
       eigenes z-index mehr - die Box setzt es hier direkt selbst, damit es
       auch nach dem Umhaengen (siehe .ruf-oben--dokument) unveraendert
       gilt. */
    z-index: 9999;
}

.ruf-oben--eintritt {
    animation: ruf-oben-eintritt 0.7s ease;
}

@keyframes ruf-oben-eintritt {
    from { transform: translate(-50%, -60px); opacity: 0; }
    to   { transform: translate(-50%, 0); opacity: 1; }
}

/* Endzustand: Box ist kein Fixum mehr, sondern steht an einer festen
   Stelle IM Dokument (siehe js/verlorene-seele.js: settlePosition). top wird
   dort als Inline-Style in px gesetzt, deshalb hier nur bottom/left/
   transform - kein eigener top-Wert, der die Inline-Angabe ueberschreiben
   wuerde. */
.ruf-oben--dokument {
    position: absolute;
    bottom: auto;
    left: 50%;
    transform: translateX(-50%);
    /* Ab hier (Phase 2) klickbar - erst wenn die Box an ihrer festen
       Dokumentposition angekommen ist, uebernimmt SoulKeeperDialog
       (js/dialog.js) die Story mit echter Klick-/Mehrseiten-Logik (siehe
       js/verlorene-seele.js: aufDokumentPositionUmhaengen). Waehrend .ruf-
       oben (Bildschirm-fixiert, siehe oben) bleibt es bei pointer-
       events:none - "Pssst..." ist reiner Text, nicht klickbar. */
    pointer-events: auto;
    cursor: pointer;
    z-index: 9999;
}

/* js/dialog.js (unveraendert!) haengt bei JEDEM starte()-Aufruf zusaetzlich
   .dialog-box--betritt an (siehe css/dialog.css: eigene Slide-/Fade-
   Keyframe-Animation von unten) - fuer die urspruengliche SoulKeeper-Szene
   passend (Box war vorher unsichtbar), hier aber unerwuenscht: unsere Box
   steht zu diesem Zeitpunkt (Story-Start in aufDokumentPositionUmhaengen)
   schon sichtbar und fertig positioniert an ihrer Dokumentstelle - die
   Animation wuerde sie sichtbar nochmal "huepfen" lassen. Selektor mit
   hoeherer Spezifitaet als die einfache .dialog-box--betritt-Regel in
   dialog.css neutralisiert das gezielt NUR fuer unseren Fall. */
.ruf-oben--dokument.dialog-box--betritt {
    animation: none;
}

/* Behebt das Wackeln/Anwachsen der Box, sobald der Weiter-Pfeil (▼)
   erscheint: css/dialog.css reserviert dessen Platz eigentlich dauerhaft
   (.dialog-hinweis.hidden { visibility: hidden; } statt display:none - der
   Pfeil-Zeile bleibt Raum, nur unsichtbar). Diese Seite hat aber SEITENWEIT
   eine eigene, generische .hidden-Klasse (css/welt.css: .hidden {
   display: none !important; }), die exakt denselben Klassennamen benutzt
   und wegen !important IMMER gewinnt - der Pfeil hatte dadurch gar keinen
   reservierten Platz mehr, sein Erscheinen liess die Box sichtbar in der
   Hoehe springen. js/dialog.js ist 1:1 uebernommen und darf nicht
   angepasst werden, also hier die urspruengliche SoulKeeper-Regel mit
   hoeherer Spezifitaet + !important zurueckerobert - NUR fuer den Weiter-
   Pfeil, nicht fuer die Box selbst (die soll bei "hidden" weiterhin
   vollstaendig verschwinden). #dialog-hinweis ist als eindeutige ID-Regel
   spezifisch genug, funktioniert dadurch auch nach dem Umhaengen nach
   <body>. */
#dialog-hinweis.hidden {
    display: block !important;
    visibility: hidden;
}

/* Bugfix (Mobile-Breite auf z.B. dem iPhone): css/dialog.css setzt im
   @media(max-width:560px)-Block width:92% - 1:1 aus SoulKeeper uebernommen,
   wo sich das IMMER auf #story bezieht (== exakt 100 * var(--uw), siehe
   css/base.css). Diese Seite hat aber kein #story-Sichtfenster: die Box
   haengt (siehe js/verlorene-seele.js: aufDokumentPositionUmhaengen)
   entweder als position:fixed (Phase 1) oder direkt an <body> (Phase 2) -
   beide ohne Breitenbegrenzung auf die Buehne, 92% bezieht sich dadurch auf
   den ECHTEN Viewport statt auf die Buehnenbreite. Auf Viewports, die nicht
   exakt 16:9 sind (z.B. iPhone-Hochformat, siehe --letterbox-x), ist der
   echte Viewport breiter als die Buehne - die Box wurde dadurch sichtbar
   groesser dargestellt als im Spiel. calc(92 * var(--uw)) erzwingt exakt
   denselben Realwert wie in SoulKeeper, unabhaengig vom tatsaechlichen
   Containing Block. #dialog-box ist als ID-Regel spezifisch genug, um die
   Klassenregel aus dialog.css zuverlaessig zu schlagen (unabhaengig von der
   Ladereihenfolge), funktioniert dadurch auch nach dem Umhaengen nach
   <body>. */
@media (max-width: 560px) {
    #dialog-box {
        width: calc(92 * var(--uw));
    }
}

@media (prefers-reduced-motion: reduce) {
    .ruf-oben--eintritt {
        animation: none;
    }
}

/* ===== Ja/Nein-Entscheidung (Phase 3) =====================================
   SoulKeeper/js/dialog.js kennt keine Verzweigung (reine Seiten-Abfolge) -
   eigenstaendiges, aber im selben Pixel-Look gehaltenes UI-Element statt
   eines Umbaus des uebernommenen Dialogsystems. Erscheint an derselben
   Dokumentposition, an der die Dialogbox stand (top wird wie bei
   .ruf-oben--dokument per Inline-Style von js/verlorene-seele.js gesetzt),
   sobald deren erste Story-Sequenz fertig getippt und die Box selbst wieder
   geschlossen ist (js/dialog.js schliesst IMMER nach der letzten Seite). */
.seelen-wahl {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9999;
    display: flex;
    gap: 1rem;
}

.seelen-wahl.hidden {
    display: none;
}

.seelen-wahl-btn {
    font-family: 'Press Start 2P', monospace;
    font-size: clamp(0.6rem, calc(1.6 * var(--uw)), 0.8rem);
    padding: 0.9rem 1.4rem;
    border-radius: 6px;
    border: 2px solid var(--accent-bright);
    background: rgba(8, 6, 14, 0.85);
    color: var(--text);
    cursor: pointer;
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.6);
    transition: background 0.2s ease, transform 0.15s ease, border-color var(--theme-transition);
    animation: seelen-wahl-eintritt 0.5s ease;
}

.seelen-wahl-btn:hover {
    background: var(--accent);
    transform: translateY(-2px);
}

.seelen-wahl-btn:active {
    transform: translateY(0);
}

@keyframes seelen-wahl-eintritt {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Kompaktes "Mobile"-Layout (Spalten statt Reihe) per @media(max-width)
   entfernt (2026-08-24, Bühnen-Nachbesserung) - siehe css/dialog.css fuer
   die Begruendung. .seelen-wahl-btn skaliert bereits ueber rem proportional
   mit der Buehne mit, die Reihe passt dadurch bei jeder Buehnengroesse. */

@media (prefers-reduced-motion: reduce) {
    .seelen-wahl-btn {
        animation: none;
    }
}

/* ===== Seelen-Animation (Phase 2 - Enthuellung) ===========================
   Kompletter Neubau (2. Anlauf - die vorherige, baum-/grabstein-bezogene
   3-Etappen-Positionierung ist komplett raus, siehe Git-Historie falls
   jemals wieder gebraucht). img/Soul/SadSoul.png erscheint exakt bei
   Story-Seite 2 ("schoen, dass du mich gehoert hast..."), siehe
   js/verlorene-seele.js: starteSeelenAnimation - fest verankert an der
   linken oberen Ecke der Dialogbox (NICHT mehr an Baeumen/Grabsteinen).

   Liegt als <body>-Kind (wie die Dialogbox selbst) mit top/left als
   Inline-Style (Dokumentkoordinaten der Box-Oberkante, einmalig von
   js/verlorene-seele.js berechnet) - transform verankert sie an ihrer
   FUSS-Mitte genau auf diesem Punkt (translate(-50%,-100%): -50% zentriert
   horizontal auf "left", -100% zieht die eigene Hoehe nach oben, die Box-
   Oberkante ist dadurch ihre Fusslinie). z-index simpel > Dialogbox
   (9999), da sie hier (anders als vorher bei den Baeumen) von Anfang an
   sichtbar vor allem stehen soll, kein Hinter-/Davor-Wechsel noetig.

   Ablauf: 1. Erscheinen (opacity 0->1 + scale 0->1 gleichzeitig, "aus dem
   Nichts", feste Zielgroesse in der Hoehe bereits gesetzt - skaliert wird
   rein optisch per transform, keine Layout-/Hoehen-Aenderung noetig).
   2. Danach dauerhafte Schwebe-Animation (leichtes Auf-und-Ab, siehe
   .verlorene-seele--schwebt) - beide nutzen bewusst denselben
   transform-Ursprung (translate(-50%,-100%) scale(...) translateY(...)),
   damit sich Verankerung, Erscheinen-Skalierung und Schweben nicht
   gegenseitig ueberschreiben (transform ist eine einzige Eigenschaft). */
.verlorene-seele {
    position: absolute;
    aspect-ratio: 577 / 852;
    width: auto;
    pointer-events: none;
    z-index: 10000;
    transform-origin: bottom center;
    transform: translate(-50%, -100%) scale(0);
    opacity: 0;
    transition: opacity 1.2s ease-out, transform 1.2s ease-out;
}

.verlorene-seele--sichtbar {
    opacity: 1;
    transform: translate(-50%, -100%) scale(1);
}

.verlorene-seele--schwebt {
    animation: seele-schweben 3.2s ease-in-out infinite;
}

@keyframes seele-schweben {
    0%, 100% { transform: translate(-50%, -100%) scale(1) translateY(0); }
    50%      { transform: translate(-50%, -100%) scale(1) translateY(-10px); }
}

/* Macht top/left zu animierten Eigenschaften - fuers Hin-/Herfliegen
   zwischen Dialogbox-Ecke und Warte-Ecke unten links (siehe
   js/verlorene-seele.js: seeleFliegtInEcke/seeleFliegtZurBox). Bewusst
   NICHT von Anfang an auf .verlorene-seele selbst, sondern erst als eigene
   Klasse ab Ende der Erscheinen-Phase aktiviert (siehe js/verlorene-
   seele.js: starteSeelenAnimation) - sonst wuerde bereits das ALLERERSTE
   Setzen von top/left (ihre Startposition an der Box-Ecke) faelschlich
   als "Hinfliegen von 0,0" transitioniert statt direkt an Ort und Stelle
   zu erscheinen. Enthaelt die komplette transition-Liste (nicht nur top/
   left) - .verlorene-seele/--schwebt weiter oben setzen ebenfalls
   "transition", nur eine Deklaration pro Element kann gewinnen, diese hier
   (spaeter im Stylesheet, gleiche Spezifitaet) uebernimmt komplett. */
.verlorene-seele--fliegt {
    transition: opacity 1.2s ease-out, transform 1.2s ease-out, top 1.5s ease-in-out, left 1.5s ease-in-out;
}

/* Letzter Schritt der Erloesungssequenz: die Seele wandert sichtbar von der
   Dialogbox zur Titel-Seele (js/verlorene-seele.js: bewegeSeeleInTitel).
   Zusaetzlich zu top/left wird hier auch die HOEHE animiert - die Titel-
   Seele (.seele-dauerhaft, siehe unten) ist groesser als die fliegende, ein
   harter Groessensprung am Ziel waere sichtbar. opacity/filter gleichen sich
   waehrend der Bewegung schon an den Endzustand an (0.8 + gedaempfter
   Filter, exakt die Werte von .seele-dauerhaft/--sichtbar), damit der
   abschliessende Crossfade zwischen beiden Elementen optisch nicht auffaellt.
   Steht bewusst NACH .verlorene-seele--sichtbar/--fliegt (gleiche
   Spezifitaet, spaeter im Stylesheet gewinnt) und ueberschreibt deren
   opacity/transition komplett. */
.verlorene-seele--zum-titel {
    opacity: 0.8;
    filter: brightness(0.85) saturate(0.85);
    transition: opacity 1.5s ease-in-out, transform 1.2s ease-out,
                top 1.5s ease-in-out, left 1.5s ease-in-out,
                height 1.5s ease-in-out, filter 1.5s ease-in-out;
}

/* Crossfade am Ziel: diese Seele blendet aus, waehrend .seele-dauerhaft
   deckungsgleich darunter einblendet (.seele-dauerhaft--ueberblendung,
   siehe unten) - beide Dauern MUESSEN uebereinstimmen, sonst entsteht in
   der Mitte des Wechsels eine sichtbare Delle. Muss nach --zum-titel
   stehen, um dessen laengere opacity-transition zu ersetzen. */
.verlorene-seele--verblasst {
    opacity: 0;
    transition: opacity 0.7s ease-out;
}

/* Reaktion auf "Natuerlich helfe ich dir!" (js/verlorene-seele.js:
   freueDich - tauscht dabei auch das Bild SadSoul -> HappySoul). Reiner
   Groessen-Puls + Gluehen/"Bling" statt eines abrupten Bildwechsels.
   Ersetzt .verlorene-seele--schwebt zeitweise (siehe dort - beide
   animieren "transform", koennen nicht gleichzeitig aktiv sein),
   js/verlorene-seele.js nimmt das Schweben danach automatisch wieder auf.

   transform-origin: center (statt des geerbten "bottom center" von
   .verlorene-seele, dort fuers Wachsen-aus-den-Fuessen beim ERSTEN
   Erscheinen gebraucht) - dadurch skaliert scale() um die eigene Bildmitte
   statt um die Fuss-Verankerung: das (per translate(-50%,-100%) fest
   verankerte) Zentrum bleibt an derselben Stelle stehen, sie wird nur
   groesser/kleiner, ohne dabei nach oben zu wandern. */
.verlorene-seele--freude {
    transform-origin: center;
    animation: seele-freude 0.9s ease-out;
}

@keyframes seele-freude {
    0%   { transform: translate(-50%, -100%) scale(1); filter: brightness(1) drop-shadow(0 0 0 transparent); }
    40%  { transform: translate(-50%, -100%) scale(1.22); filter: brightness(1.35) drop-shadow(0 0 28px var(--accent-bright)); }
    100% { transform: translate(-50%, -100%) scale(1); filter: brightness(1) drop-shadow(0 0 0 transparent); }
}

@media (prefers-reduced-motion: reduce) {
    .verlorene-seele {
        transition: opacity 0.4s ease;
    }
    .verlorene-seele--sichtbar {
        transform: translate(-50%, -100%) scale(1);
    }
    .verlorene-seele--schwebt,
    .verlorene-seele--freude {
        animation: none;
    }
    .verlorene-seele--fliegt {
        transition: opacity 0.4s ease;
    }
}

/* ===== Dauerhafte Seele bei "Halloween Party" (Endzustand) ===============
   Letzter Schritt der Erlösungssequenz (js/verlorene-seele.js:
   erloesungAbgeschlossen -> zeigeDauerhafteSeele): sobald die fliegende
   Seele an der Dialogbox-Ecke verschwunden ist, blendet stattdessen DIESES
   Element bei der "Halloween Party 2026"-Überschrift ein - und bleibt ab
   dann (worldFree-Cookie, siehe js/fluch-status.js) bei jedem weiteren
   Seitenaufruf sofort so stehen (istWeltFrei-Zweig in init(), ohne Fade,
   siehe .seele-dauerhaft--ohne-fade).

   Liegt als Kind von .oberwelt-titel-text (css/welt.css, inline-block +
   position:relative extra dafuer gesetzt) - dieser Wrapper schmiegt sich
   per shrink-to-fit GENAU an die tatsaechlich gerenderte Breite/Hoehe des
   Zweizeilers "Halloween/Party 2026" an, nicht an die volle 640px-Breite
   von h1/.oberwelt-inhalt. top:50% + left:0 + translate(-50%,-50%)
   zentriert die Seele dadurch GENAU auf die Mitte dieses Zweizeilers
   (Vorgabe: "mittig, was den Zweizeiler angeht") und haengt sie
   gleichzeitig mit ihrer rechten Haelfte GENAU an der echten linken
   Buchstabenkante ein (halb hinter dem "H" von "Halloween", halb links
   davon sichtbar) - bei jeder Bildschirmbreite automatisch exakt richtig,
   keine geschaetzte vw-/Pixelberechnung noetig (siehe Git-Historie: eine
   frueherer Versuch mit festem vw-Offset lag bei mittleren Breiten
   deutlich daneben, weil er nicht an den echten Text gekoppelt war).
   left:-100px verschiebt diesen Ankerpunkt (auf Wunsch) zusaetzlich fest
   um 100px nach links von der Buchstabenkante weg - bewusst ein simpler
   fixer Pixelwert (nicht vw-skaliert), da es hier nur noch um einen
   gewuenschten Korrektur-Versatz geht, nicht mehr um die grundsaetzliche
   Ausrichtung an der Textkante.
   z-index:-1 wirkt hier NICHT innerhalb des Wrappers selbst (der erzeugt
   bewusst keinen eigenen Stacking-Context, siehe dortiger Kommentar),
   sondern haengt sich in den naechsten echten Stacking-Context hoch
   (.oberwelt-inhalt, z-index:2 in welt.css) - die Seele liegt dadurch
   trotzdem hinter dem Titeltext ("im Hintergrund"), ohne hinter die
   Szenengrafik von #scene-level1 selbst zu rutschen (die liegt AUSSERHALB
   dieses Stacking-Contexts, unveraendert unter .oberwelt-inhalt). */
/* height: die fruehere Form clamp(80px, calc(15 * var(--uw)), 165px)
   griff am Referenzgeraet (--stage-scale:1) bereits mit der OBEREN
   Grenze: der Mittelwert (15 * var(--uw) = 192px bei scale:1) lag schon
   ueber dem Deckel (165px), wodurch die Seele real IMMER auf 165px
   capte - unabhaengig von --stage-scale (TODO-20260901-039, dasselbe
   Muster wie bei .level1-hand in css/level1.css). calc(165px *
   var(--stage-scale)) liefert bei scale:1 denselben Wert wie bisher
   (165px, Referenzoptik unveraendert), skaliert von dort aber rein
   proportional zu --stage-scale. */
.seele-dauerhaft {
    position: absolute;
    top: 25%;
    left: -75px;
    height: calc(165px * var(--stage-scale));
    width: auto;
    aspect-ratio: 577 / 852;
    transform: translate(-50%, -50%) scale(0.9);
    transform-origin: center;
    z-index: -1;
    pointer-events: none;
    opacity: 0;
    filter: brightness(0.85) saturate(0.85);
    transition: opacity 1.4s ease-out;
    animation: seele-dauerhaft-schweben 4s ease-in-out infinite;
    animation-play-state: paused;
}

.seele-dauerhaft--sichtbar {
    opacity: 0.8;
    animation-play-state: running;
}

/* Fuer den istWeltFrei-Zweig (Seite laedt bereits mit gesetztem Cookie,
   siehe js/verlorene-seele.js: init()) - kein Ein-/Ausblenden noetig, die
   Seele soll dort einfach von Anfang an so stehen. */
.seele-dauerhaft--ohne-fade {
    transition: none;
}

/* Gegenstueck zu .verlorene-seele--verblasst (siehe oben): am Ende der
   Erloesungssequenz steht die fliegende Seele deckungsgleich ueber dieser
   hier und blendet gleichzeitig aus - dafuer der kurze, exakt gleich lange
   Crossfade statt der langen Standard-Einblendung von 1.4s. */
.seele-dauerhaft--ueberblendung {
    transition: opacity 0.7s ease-out;
}

@keyframes seele-dauerhaft-schweben {
    0%, 100% { transform: translate(-50%, -50%) scale(0.9) translateY(0); }
    50%      { transform: translate(-50%, -50%) scale(0.9) translateY(-8px); }
}

/* Kompakte Kompensations-Groesse per @media(max-width) entfernt (2026-08-24,
   Bühnen-Nachbesserung) - der urspruengliche Grund ("h1.oberwelt-titel ist
   am echten schmalen Viewport fast bildschirmbreit") gilt unter der neuen
   Buehnen-Logik nicht mehr: .oberwelt-titel skaliert ueber
   clamp(2.2rem, calc(7*var(--uw)), 4.6rem) (siehe css/welt.css) IMMER
   proportional zur Buehne, wird also nie ueberproportional breit. Die
   Basisgroesse von .seele-dauerhaft (calc(165px * var(--stage-scale)),
   siehe oben) skaliert dadurch bereits korrekt mit. */

@media (prefers-reduced-motion: reduce) {
    .seele-dauerhaft {
        transition: opacity 0.4s ease;
        animation: none;
    }
}
