/* ===== Champion - die Vorstellung des Titelverteidigers =================
   Eine feste Bühne von genau einem Bildschirm: kein Scrollen, ein Ablauf.
   Was die Kulisse angeht, erbt diese Seite alles von der Einladung
   (../iv/css/welt.css, level1.css, dialog.css) - hier steht nur, was
   darüber hinaus geht.

   Masse durchgehend in Buehneneinheiten (--uw/--uh, siehe
   ../iv/js/buehne.js). */

/* ----- Die Bühne -------------------------------------------------------
   #welt-track ist wie in der Einladung 200uh hoch, weil die ganze Kulisse
   mit diesen Massen rechnet - Mond, Farbverlauf und Friedhofsebenen messen
   alle ab seiner Oberkante. Statt dorthin zu scrollen, wird er hier
   festgesetzt: seine UNTERKANTE liegt auf der des Fensters, der Rest waechst
   nach oben aus dem Bild heraus.

   Warum an der Unterkante und nicht mit festem Abstand von oben: die Kulisse
   hoert unten auf (die Oberwelt ist per --oberwelt-kuerzung beschnitten,
   siehe ../iv/css/level1.css). Haenge ich sie oben fest, bleibt auf jedem
   Fenster, das hoeher als 16:9 ist, unten ein schwarzer Streifen - genau
   das, was 'sieht nicht aus wie die Einladungsseite' heisst. Unten
   verankert steht der Boden immer am Bildschirmrand, egal wie hoch das
   Fenster ist. Die Galerie nebenan (../gl/) zeigt dieselbe Kulisse und
   landet durch ihr Scrollen ans Dokumentende auf genau dieser Position.

   Bei 16:9 (Fenster = 100uh hoch, Track = 183uh) liegt die Oberkante des
   Fensters damit bei 83uh im Track. Die Einladung steht mit ihren 80uh
   (../iv/js/kamerafahrt.js: berechneOberweltZielY, Oberkante der Oberwelt
   bei 100uh minus 20uh Vorlauf) drei Einheiten darueber - der Unterschied
   ist kleiner als eine Grabsteinspitze und der Preis dafuer, dass unten
   nichts fehlt. */
.ch-buehne {
    /* 100svh statt 100vh, und zwar aus genau dem Grund, den
       ../iv/js/buehne.js oben ausfuehrlich beschreibt: --uh wird dort ueber
       ein Hilfselement mit 100svh gemessen. Auf iOS ist 100vh die Hoehe OHNE
       Safari-Leiste und damit GROESSER als der sichtbare Bereich - die Buehne
       war dadurch um die Leistenhoehe zu hoch. Weil #welt-track (unten) und
       die Dialogbox (../iv/css/dialog.css: bottom) beide an ihrer UNTERKANTE
       haengen, rutschten Seele und Dialogbox genau um diesen Betrag unter
       den Bildschirmrand - unerreichbar, weil html/body hier overflow:hidden
       haben. Wer aufs Display doppeltippte, blendete die Leiste aus und sah
       es dadurch kurzzeitig richtig.
       Am Desktop sind vh und svh identisch, dort aendert sich nichts. */
    height: 100vh; /* Rueckfall fuer Browser ohne svh */
    height: 100svh;
    overflow: hidden;
}

.ch-buehne #welt-track {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
}

/* Die Kamerafahrt, eingefroren an derselben Stelle.
   Der Zug nach oben allein reicht nicht: in der Einladung liegen Mond und
   hintere Friedhofsebene an dieser Stelle noch NICHT auf ihren CSS-
   Endwerten. ../iv/js/parallax.js schiebt sie beim Scrollen durch den
   Himmel von einem Startversatz (-28uh bzw. -40uh) auf 0 herunter, und bei
   80 von 100uh ist dieser Weg noch nicht zu Ende - der Mond steht dort
   also ein gutes Stueck hoeher als im blanken CSS. Ohne diese Korrektur
   haette diese Seite zwar denselben Ausschnitt, aber einen sichtbar
   tieferen Mond als die Einladung.
   parallax.js selbst einzubinden geht nicht: es haengt am Scrollen, und
   hier wird nicht gescrollt - es wuerde den Startversatz stehen lassen.
   Die Zahlen sind deshalb einmal ausgerechnet, mit genau dessen Formel
   (Anteil = 80/100 mal Geschwindigkeit, geglaettet ueber 3t^2-2t^3,
   Versatz = Startversatz * (1 - Anteil)):
     Mond              0.8 * 0.85 = 0.68 -> 0.7583 -> -28 * 0.2417 = -6.77uh
     Ebene hinten      0.8 * 1.00 = 0.80 -> 0.8960 -> -40 * 0.1040 = -4.16uh
     Kamera-Ebene      0.8 * 1.30 = 1.04 -> gekappt auf 1 -> 0
   Nachgemessen in der Einladung an derselben Scrollposition: -6.85uh und
   -4.21uh (die Abweichung ist deren Rundung auf ganze Pixel). Aendert sich
   in parallax.js die Tabelle EBENEN, muss das hier mit. */
.ch-buehne #level1-mond {
    transform: translateY(calc(-6.77 * var(--uh)));
}

.ch-buehne #level1-ebene-hinten {
    transform: translateY(calc(-4.16 * var(--uh)));
}

/* Und damit die Seite selbst nirgends mehr Platz hergibt. */
html, body {
    overflow: hidden;
}

/* ----- Die drei Bilder -------------------------------------------------
   z-index 1: dieselbe Ebene, auf der auch die aufsteigenden Seelen
   unterwegs sind - hinter der vorderen Friedhofsebene (die liegt auf 2,
   siehe ../iv/css/level1.css) und vor Mond und hinterer Ebene. Sie fahren
   also wirklich hinter Boden01.svg hervor. */
.ch-buehnenbild {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
}

/* Aufbau wie in der Galerie: Foto hinten, gezeichneter Rahmen darueber,
   Binnenformen in Theme-Farbe zuoberst. Die Breite kommt als
   Inline-Variable aus index.php und ist die des RAHMENS - sie wurde dort
   aus der gewuenschten Fotobreite zurueckgerechnet. */
.ch-figur {
    position: absolute;
    bottom: calc(var(--ch-ruhe-unten, 6) * var(--uh));
    width: calc(var(--ch-breite) * var(--uw));
}

.ch-rahmen {
    position: relative;
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: var(--ch-verh, 1);
    /* Derselbe weisse Schein wie in der Galerie: schwarze Tusche auf
       nachtschwarzem Himmel ist ohne ihn schlicht unsichtbar. */
    filter:
        drop-shadow(0 0 calc(0.7 * var(--uh)) rgba(255, 255, 255, 0.75))
        drop-shadow(0 0 calc(2.6 * var(--uh)) rgba(255, 255, 255, 0.45));
}

.ch-foto {
    position: absolute;
    left: calc(var(--ch-ol) * 1%);
    top: calc(var(--ch-ot) * 1%);
    width: calc(var(--ch-ow) * 1%);
    height: calc(var(--ch-oh) * 1%);
    object-fit: cover;
}

.ch-figur--oval .ch-foto,
.ch-figur--oval .ch-binnen {
    clip-path: ellipse(50% 50% at 50% 50%);
}

/* Binnenformen in Theme-Farbe - die Maske steht am Element selbst, weil
   eine url() in einer CSS-Variablen gegen das Stylesheet aufgeloest wuerde
   und nicht gegen das Dokument. */
.ch-binnen {
    position: absolute;
    inset: 0;
    background: var(--accent-bright);
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    transition: background var(--theme-transition);
}

/* ----- Die Auffahrt ----------------------------------------------------
   Die beiden Pokale fahren gleichzeitig los, wenn die Musik beginnt, und
   sind auf einem Schlag im Lied am Ziel: der rechte nach 6, der linke nach
   12 Sekunden. Sie fahren also unterschiedlich schnell, nicht
   unterschiedlich frueh - deshalb steht auf keinem von beiden mehr eine
   Verzoegerung. Die Dauern setzt js/champion.js als Inline-Wert (dort
   stehen die Schlaege als einzige Quelle), hier steht nur die Kurve und
   ein brauchbarer Ausgangswert.

   Die Kurve ist bewusst fast linear mit weichem Auslauf (kein
   Ueberschwinger): etwas, das gleichmaessig und unaufhaltsam nach oben
   faehrt, wirkt feierlich - etwas, das hochschnellt und nachfedert, wirkt
   wie ein Pilz aus dem Boden. */
.ch-figur {
    transform: translateY(0);
    transition: transform 6s cubic-bezier(0.25, 0.6, 0.3, 1);
}

/* Ruheposition VOR der Auffahrt: unterhalb der Buehne. 130% der eigenen
   Hoehe statt 100%, damit auch der Schein aus dem Bild ist. */
.ch-figur.ch-unten {
    transform: translateY(130%);
}

.ch-figur--links  { left: calc(2.5 * var(--uw)); }
.ch-figur--rechts { right: calc(2.5 * var(--uw)); }

/* Jede Figur hat ihre eigene Standhoehe, weil der Boden vor ihnen ein
   Huegel ist und keine Gerade - die Werte und die Messung dazu stehen in
   index.php (CH_RUHE_*). */
.ch-figur--links  { bottom: calc(var(--ch-ruhe-links, 23) * var(--uh)); }
.ch-figur--rechts { bottom: calc(var(--ch-ruhe-rechts, 16) * var(--uh)); }

/* Der Champion faehrt NICHT - er ist beim Schlag einfach da.
   Zwei gleiche Bewegungen und dann eine dritte derselben Art waere die
   Aufloesung, die man schon kennt; der Auftritt lebt davon, dass er anders
   kommt als die Pokale. Der kurze Sprung von 92% auf 100% ist kein
   Auffahren, sondern das, was ein harter Schnitt allein nicht kann: er
   gibt dem Erscheinen eine Richtung. 0.35s, damit es auf dem Schlag sitzt
   und nicht darueber hinaus laeuft. */
.ch-figur--champion {
    left: 50%;
    /* Die Verschiebung um die halbe eigene Breite muss in DIESELBE
       transform-Angabe wie das Erscheinen - transform ist eine einzige
       Eigenschaft. */
    transform: translate(-50%, 0) scale(1);
    opacity: 1;
    transition: transform 0.35s cubic-bezier(0.2, 0.9, 0.3, 1),
                opacity 0.35s ease-out;
}

.ch-figur--champion.ch-unten {
    transform: translate(-50%, 0) scale(0.92);
    opacity: 0;
}

/* ----- Die Seele -------------------------------------------------------
   Steht mit ihrer Fussmitte auf der linken oberen Ecke der Dialogbox -
   dieselbe Verankerung, die ../iv/js/verlorene-seele.js dort nachrechnet
   (positioniereSeeleAnBoxEcke: left = boxRect.left, top = boxRect.top,
   dazu translate(-50%,-100%)). left/top setzt js/champion.js
   (setzeSeeleAnBox); hier stehen nur die Startwerte.

   Sie liegt ausdruecklich NEBEN der Box im Markup und nicht darin: nach der
   letzten Textseite geht die Box ab, die Seele soll aber noch stehen und
   auf den Ton-Knopf zeigen. In der Box waere sie mit ihr verschwunden.

   Die Hoehe ist die von dort: SEELE_ZIEL_HOEHE = 125px, skaliert mit
   Buehnenbreite/1357. In Buehneneinheiten sind das 125/1357*100 = 9.21uw -
   dieselbe Groesse wie in der Einladung, ohne eine Zeile JavaScript. (Die
   165px von .seele-dauerhaft gelten fuer die Seele im TITEL, die ist
   absichtlich groesser.) */
.ch-seele {
    position: fixed;
    left: 0;
    top: 0;
    height: calc(9.21 * var(--uw));
    width: auto;
    aspect-ratio: 577 / 852;
    /* Ueber der Kulisse: #buehne bildet keinen eigenen Stapelkontext, also
       konkurriert #scene-level1 (z-index 1, siehe ../iv/css/level1.css)
       direkt mit allem anderen im Dokument - ohne eigenen z-index waere die
       Seele schlicht dahinter verschwunden. Eins ueber der Dialogbox (60),
       damit ihr Fuss auf deren Rand steht statt dahinter. */
    z-index: 61;
    pointer-events: none;
    filter: drop-shadow(0 0 calc(1.2 * var(--uh)) rgba(255, 255, 255, 0.7));
    opacity: 0;
    transform: translate(-50%, -100%) scale(0.6);
    transform-origin: bottom center;
    transition: opacity 1.1s ease-out, transform 1.1s ease-out;
}

.ch-seele--da {
    opacity: 1;
    transform: translate(-50%, -100%) scale(1);
}

/* Schweben laeuft ueber margin und nicht ueber transform: dort sitzt schon
   die Verankerung samt Erscheinen-Skalierung, und transform ist eine
   einzige Eigenschaft. */
.ch-seele--da {
    animation: ch-seele-schweben 3.6s ease-in-out infinite;
}

@keyframes ch-seele-schweben {
    0%, 100% { margin-top: 0; }
    50%      { margin-top: calc(-1.4 * var(--uh)); }
}

/* ----- Die Dialogbox ---------------------------------------------------
   Feste Hoehe statt der Mindesthoehe aus ../iv/css/dialog.css: die Texte
   hier sind unterschiedlich lang. Mit einer blossen Mindesthoehe waechst
   die Box beim Seitenwechsel mit - und weil sie am unteren Rand verankert
   ist, springt dabei ihre Oberkante. Genau das ist das Ruckeln.

   Der Wert ist GENAU die Mindesthoehe aus ../iv/css/dialog.css: die Box
   ist damit so hoch wie ueberall sonst, nur eben immer. Das geht auf, weil
   dort .dialog-text ohnehin 5.1em reserviert - fuer bis zu drei Zeilen
   aendert sich also sowieso nichts, und laenger ist hier kein Text. Wer
   einen laengeren einbaut, sieht ihn unten anstossen; dann gehoert er
   geteilt, nicht die Box vergroessert. */
.dialog-box {
    z-index: 60;
    height: calc(18.0556 * var(--uh));
}

/* ----- Der Zeigepfeil ueber dem Ton-Knopf ------------------------------
   Erscheint erst, wenn die Seele darauf verweist. Pendelt auf und ab und
   sagt damit, was Worte an dieser Stelle nur umstaendlich sagen koennten.

   Die Huelle um den Knopf ist nur dafuer da, dem Pfeil einen Bezugspunkt zu
   geben: sie ist genau so breit wie der Knopf, also trifft left:50% dessen
   Mitte. display:inline-flex haelt sie im Flexfluss der Leiste, ohne deren
   Ausrichtung zu stoeren. */
.ch-tonknopf {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.ch-pfeil {
    position: absolute;
    left: 50%;
    bottom: 100%;
    transform: translateX(-50%);
    margin-bottom: calc(0.6 * var(--uh));
    font-size: clamp(1rem, calc(2.6 * var(--uw)), 1.6rem);
    line-height: 1;
    color: var(--accent-bright);
    text-shadow: 0 0 calc(1.6 * var(--uh)) var(--accent);
    pointer-events: none;
    animation: ch-pfeil-tippen 1.1s ease-in-out infinite;
}

.ch-pfeil.hidden {
    display: none;
}

@keyframes ch-pfeil-tippen {
    0%, 100% { transform: translate(-50%, 0); }
    50%      { transform: translate(-50%, calc(-1.6 * var(--uh))); }
}

/* Solange der Pfeil zeigt, zieht der Knopf zusaetzlich Aufmerksamkeit -
   ein Pfeil allein ueber einem unauffaelligen Symbol wird leicht
   uebersehen. */
.ch-ton--gefragt {
    animation: ch-knopf-pochen 1.1s ease-in-out infinite;
}

@keyframes ch-knopf-pochen {
    0%, 100% { transform: scale(1);    filter: none; }
    50%      { transform: scale(1.18); filter: drop-shadow(0 0 calc(1.6 * var(--uh)) var(--accent-bright)); }
}

/* ----- Feuerwerk -------------------------------------------------------
   Ueber allem, aber ohne jede Bedienbarkeit - reines Bild. */
.ch-feuerwerk {
    position: fixed;
    inset: 0;
    /* width/height ausdruecklich, obwohl inset:0 schon alle vier Seiten
       festnagelt: ein <canvas> ist ein ERSETZTES Element und nimmt ohne
       Groessenangabe seine Eigengroesse an - also die des Zeichenpuffers
       (hier ein Viertel des Bildschirms). Das Feuerwerk sass dadurch in der
       linken oberen Ecke statt ueber dem ganzen Bild. */
    width: 100%;
    height: 100%;
    z-index: 80;
    pointer-events: none;
    /* Hier entsteht die Pixel-Optik: js/champion.js zeichnet auf einer
       Flaeche von einem Viertel der Darstellungsgroesse, und das hier
       skaliert sie hart hoch statt weich zu interpolieren. Jeder Funke wird
       dadurch ein grober Block - und das Zeichnen kostet ein Sechzehntel
       der Flaeche. */
    image-rendering: pixelated;
}

/* ----- Der Knopf zurueck ------------------------------------------------
   Sieht aus wie der Knopf im Himmelsportal der Einladung (.portal-wahl-btn
   aus ../iv/css/himmel-portal-wahl.css, dort mitgeladen) - hier steht nur,
   wo er sitzt und wie er auftaucht.

   Die drei transform-Zeilen sind kein Zierrat: die Vorlage animiert und
   verschiebt beim Ueberfahren ueber transform, und transform ist EINE
   Eigenschaft - ohne das translate(-50%) in jeder dieser Angaben spraenge
   der Knopf beim Ueberfahren aus der Mitte. Aus demselben Grund ist die
   Eintritts-Animation der Vorlage hier abgeschaltet und durch einen eigenen
   Uebergang ersetzt. */
.ch-heim {
    position: fixed;
    left: 50%;
    bottom: calc(10 * var(--uh));
    z-index: 90;
    animation: none;
    opacity: 0;
    transform: translate(-50%, calc(1.6 * var(--uh)));
    transition: opacity 0.9s ease-out, transform 0.9s ease-out,
                background 0.2s ease, border-color var(--theme-transition);
}

.ch-heim--da {
    opacity: 1;
    transform: translate(-50%, 0);
}

.ch-heim--da:hover  { transform: translate(-50%, -2px); }
.ch-heim--da:active { transform: translate(-50%, 0); }

.ch-heim.hidden {
    display: none;
}

/* ----- Schwarzblende ---------------------------------------------------
   Der Abgang: liegt ueber allem und ist bis zum Schluss durchsichtig. */
.ch-blende {
    position: fixed;
    inset: 0;
    z-index: 95;
    background: #000;
    opacity: 0;
    pointer-events: none;
    transition: opacity 2.4s ease-in;
}

.ch-blende--zu {
    opacity: 1;
}

/* ----- Bewegungsreduktion ---------------------------------------------
   Gleiche Haltung wie im Rest des Projekts: sichtbar und verstaendlich
   bleibt alles, nur die Bewegung faellt weg. Die Bilder fahren dann nicht
   hoch, sie sind da - der Ablauf bleibt derselbe. */
@media (prefers-reduced-motion: reduce) {
    /* Die Dauern der Pokale setzt js/champion.js inline (TAKTE_REDUZIERT
       greift dort denselben Fall ab) - hier steht nur, was das Stylesheet
       allein beisteuern kann. */
    .ch-figur {
        transition-duration: 0.6s;
        transition-delay: 0s;
    }

    .ch-figur--champion {
        transition-duration: 0.2s;
        transition-delay: 0s;
    }

    .ch-seele,
    .ch-seele--da {
        animation: none;
        transition-duration: 0.3s;
    }

    .ch-pfeil,
    .ch-ton--gefragt {
        animation: none;
    }

    .ch-heim {
        transform: translate(-50%, 0);
        transition-duration: 0.3s;
    }

    .ch-blende {
        transition-duration: 0.8s;
    }
}
