/* ============================================================
   loader.css — Startbildschirm „Ansage“

   Von Paul aus zehn Vorschlägen gewählt (siehe _loader-designs.html,
   Nr. 3) und in mehreren Runden verfeinert. Der Ablauf:

     0,24 s  „Fordern.“    rammt als Block herein – harter Ruck
     0,98 s  Schnitt auf Off-White: „Spielen.“ hüpft Buchstabe
             für Buchstabe (grüne Schrift), kleiner Ruck
     1,62 s  Schnitt auf Grün: „Aufsteigen.“ steigt von unten
             durch eine Maske auf, Ruck nach oben
     2,39 s  „Rangliste.“ stürzt aus der Vergrößerung herab
     2,52 s  AUFPRALL – das dichteste Ereignis: Blitz-Schnitt
             (ein Off-White-Bild), härtester Ruck, Druckwellen-
             Ring, Tempo-Streifen; die Druckwelle malt die
             Bühne (Fando-Verlauf) kreisförmig vom Wort aus;
             das Wort staucht sich wie ein Gummiball und wippt
             in drei abklingenden Wellen aus; eine Setz-Welle
             läuft durch die Buchstaben; die Palme wird in
             ihre Ecke geweht
     2,9–3,2 s  Overline, Balken und Punkt docken UM das Wort
             an – das Wort selbst bewegt sich nie wieder
     danach  Abgang: die Fläche teilt sich in der Mitte

   Endbild ZENTRIERT (Pauls Vorgabe): Overline über dem Wort,
   Balken darunter, füllt sich aus der Mitte. Die Palme sitzt
   wie auf jeder grünen Fläche der App angeschnitten unten
   rechts. Alles flach – kein 3D, keine Reflexe, kein Glow;
   der „Blitz“ ist ein Farbschnitt.

   SCHICHTEN (von unten nach oben):
     1 .ldr-haelfte  zwei Hälften mit dem fertigen Bild – nur
                     für den Abgang; bis dahin deckt der Aufbau
     3 .ldr-aufbau   die Show (Worte, Schnitte, Druckwelle);
                     ihr Endbild ist deckungsgleich mit den
                     Hälften, darum ist das Umblenden unsichtbar
     4 .ldr-inhalt   Masthead + Fortschritt; ruckt synchron
                     zum Aufbau (gleiche Keyframes, gleicher
                     Start – zwei Elemente, ein Takt)

   Die Wort-Achse hängt an --achse: Worte und Titel teilen sie
   sich, damit „Rangliste.“ GENAU dort einschlägt, wo die drei
   Worte davor standen.

   Das Zeitgerüst gehört zu MIN_SHOW_MS/ABGANG_MS in
   js/lib/loader.js – wer hier Zeiten ändert, muss dort nachziehen.
   ============================================================ */

.app-loader {
  position: absolute; inset: 0; z-index: 200; overflow: hidden;
  --achse: 42%;                 /* Oberkante der großen Worte */
  --gruen-t: #2f6118;
  --grad: linear-gradient(158deg, #5aa233 0%, #43811f 54%, #2f6118 100%);
}

/* ------------------------------------------------------------
   Schicht 1 · Bühne (zwei Hälften für den Abgang)
   ------------------------------------------------------------ */
.ldr-haelfte { position: absolute; left: 0; right: 0; height: 50.4%; overflow: hidden; z-index: 1; }
.ldr-oben  { top: 0; }
.ldr-unten { bottom: 0; }
.ldr-bild { position: absolute; left: 0; right: 0; height: 198.4%; background: var(--grad); }
.ldr-oben  .ldr-bild { top: 0; }
.ldr-unten .ldr-bild { bottom: 0; }

/* Palmen-Wasserzeichen: Pflicht auf jeder grünen Fläche, groß und
   ANGESCHNITTEN. Grundstellung 8°; das Wiegen beginnt, wenn das
   Endbild steht. Die Kopie im Aufbau läuft synchron (gleicher Start). */
.ldr-wm {
  position: absolute; right: -66px; bottom: -58px; width: 296px; height: 340px;
  background: url("../img/logo-white.svg") no-repeat center / contain;
  opacity: 0.13; pointer-events: none;
  transform: rotate(8deg); transform-origin: 62% 26%;
  animation: ldr-sway 7s var(--ease-in-out) 3.4s infinite;
}
@keyframes ldr-sway { 0%, 100% { transform: rotate(8deg); } 50% { transform: rotate(11.5deg); } }

/* ------------------------------------------------------------
   Schicht 3 · Aufbau — die Show
   ------------------------------------------------------------ */
.ldr-aufbau { position: absolute; inset: 0; z-index: 3;
  animation: ldr-ruck 2700ms linear both; }
/* Vier Rucke, jeder anders: hart (Fordern) · verspielt (Spielen) ·
   aufwärts (Aufsteigen) · Einschlag (Rangliste, der härteste). */
@keyframes ldr-ruck {
  0%, 13.2% { transform: translate(0, 0); }
  14.2% { transform: translate(-5px, 4px); }
  15.7% { transform: translate(4px, -3px); }
  17.2% { transform: translate(-2px, 1px); }
  18.7%, 47.6% { transform: translate(0, 0); }
  48.6% { transform: translate(-2px, 2px); }
  50.1% { transform: translate(1px, -1px); }
  51.6%, 67.6% { transform: translate(0, 0); }
  68.6% { transform: translate(0, -3px); }
  70.1% { transform: translate(0, 1px); }
  71.6%, 92.6% { transform: translate(0, 0); }
  93.6% { transform: translate(-7px, 6px); }
  95.1% { transform: translate(6px, -4px); }
  96.6% { transform: translate(-3px, 2px); }
  98.1% { transform: translate(1px, -1px); }
  99%, 100% { transform: translate(0, 0); }
}

/* Grundfläche: tiefes Grün – die Farbe der Schnitte 1 und 3 */
.ldr-grund { position: absolute; inset: 0; background: var(--gruen-t); }

/* Schnitt auf Off-White – nur während „Spielen.“ */
.ldr-flach { position: absolute; inset: 0; background: var(--bg); opacity: 0;
  animation: ldr-flach 640ms linear 980ms both; }
@keyframes ldr-flach { 0% { opacity: 0; } 1%, 99% { opacity: 1; } 100% { opacity: 0; } }

/* Die Bühne: kein Aufblenden – die DRUCKWELLE des Einschlags malt
   den Verlauf kreisförmig vom Wort aus über den Schirm. */
.ldr-buhne { position: absolute; inset: 0; background: var(--grad);
  clip-path: circle(0px at 50% 47%);
  animation: ldr-buhne 680ms var(--ease-emphasis) 2520ms both; }
@keyframes ldr-buhne { to { clip-path: circle(130% at 50% 47%); } }

/* Die Palme wird vom Einschlag in ihre Ecke geweht */
.ldr-buhne .ldr-wm { opacity: 0;
  animation: ldr-wm-ein 620ms var(--ease-out) 2600ms both,
             ldr-sway 7s var(--ease-in-out) 3.4s infinite; }
@keyframes ldr-wm-ein {
  from { opacity: 0; transform: rotate(14deg) translate(-60px, -30px); }
  60%  { opacity: 0.13; }
  to   { opacity: 0.13; transform: rotate(8deg) translate(0, 0); }
}

/* ---------------- Die drei Worte ---------------- */
.ldr-wort {
  position: absolute; left: 0; right: 0; top: var(--achse); text-align: center;
  font-size: clamp(40px, 13.9vw, 54px); font-weight: var(--fw-black);
  letter-spacing: var(--tracking-tight); line-height: 1.05; color: #fff;
}

/* 1 · „Fordern.“ – ein Block, rammt aus der Breite, hält LESBAR
   mit kaum sichtbarem Drift, harter Schnitt am Ende. */
.ldr-aw1 { opacity: 0; animation: ldr-ramme 740ms linear 240ms both; }
@keyframes ldr-ramme {
  0%   { opacity: 0; transform: scale(1.55); letter-spacing: 0.12em; }
  1%   { opacity: 1; }
  15%  { transform: scale(1); letter-spacing: var(--tracking-tight); }
  98%  { opacity: 1; transform: scale(1.04); letter-spacing: var(--tracking-tight); }
  100% { opacity: 0; transform: scale(1.04); }
}

/* 2 · „Spielen.“ – Buchstaben hüpfen einzeln (grün auf Off-White) */
.ldr-aw2 { color: var(--gruen-t); opacity: 0; animation: ldr-halt2 640ms linear 980ms both; }
.ldr-aw2 b { display: inline-block; font-weight: inherit; opacity: 0; transform-origin: 50% 90%;
  animation: ldr-huepf 240ms cubic-bezier(0.2, 1.6, 0.4, 1) calc(980ms + var(--n) * 24ms) both; }
@keyframes ldr-huepf {
  0%   { opacity: 0; transform: translateY(-0.55em) scale(1.4); }
  30%  { opacity: 1; }
  62%  { transform: translateY(0.04em) scaleY(0.9) scaleX(1.06); }
  100% { opacity: 1; transform: none; }
}
@keyframes ldr-halt2 { 0% { opacity: 0; } 1%, 98% { opacity: 1; transform: scale(1); }
  99.5% { transform: scale(1.035); } 100% { opacity: 0; transform: scale(1.035); } }

/* 3 · „Aufsteigen.“ – steigt durch die Maske, Überschwung, Drift */
.ldr-aw3 { overflow: hidden; padding: 0.1em 0; opacity: 0;
  animation: ldr-halt3 660ms linear 1620ms both; }
@keyframes ldr-halt3 { 0% { opacity: 0; } 1%, 99% { opacity: 1; } 100% { opacity: 0; } }
.ldr-aw3w { display: inline-block; transform: translateY(115%);
  animation: ldr-steigen 340ms var(--ease-emphasis) 1640ms both,
             ldr-drift 560ms linear 2000ms both; }
@keyframes ldr-steigen { 0% { transform: translateY(115%); }
  74% { transform: translateY(-5%); } 100% { transform: translateY(0); } }
@keyframes ldr-drift { to { transform: scale(1.04); } }

/* ---------------- Der Aufprall ---------------- */
/* Blitz-Schnitt: EIN kurzes Off-White-Bild genau im Aufprallmoment */
.ldr-blitz { position: absolute; inset: 0; background: var(--bg); opacity: 0;
  animation: ldr-blitz 130ms linear 2500ms both; }
@keyframes ldr-blitz { 0% { opacity: 0; } 4%, 38% { opacity: 0.95; } 46%, 100% { opacity: 0; } }

/* Flache Druckwellen-Kontur vom Wort aus */
.ldr-dring { position: absolute; left: 50%; top: calc(var(--achse) + 26px);
  width: 90px; height: 90px; margin: -45px 0 0 -45px;
  border-radius: 50%; border: 3px solid rgba(255, 255, 255, 0.9); opacity: 0;
  animation: ldr-dring 560ms var(--ease-out) 2520ms both; }
@keyframes ldr-dring { 0% { opacity: 0; transform: scale(0.3); } 4% { opacity: 0.9; }
  100% { opacity: 0; transform: scale(5.2); } }

/* Tempo-Streifen: flache Striche schießen seitlich aus dem Wort.
   0 % UNSICHTBAR – mit „both“ gilt das Startbild schon während der
   Wartezeit, sonst kleben die Striche von Anfang an im Bild. */
.ldr-striche { position: absolute; left: 50%; top: calc(var(--achse) + 26px); }
.ldr-striche i { position: absolute; left: 0; top: -1.5px; width: 46px; height: 3px;
  border-radius: 2px; background: rgba(255, 255, 255, 0.9);
  transform-origin: 0 50%; opacity: 0;
  animation: ldr-strich 420ms var(--ease-out) 2515ms both; }
@keyframes ldr-strich {
  0%   { opacity: 0; transform: rotate(var(--a)) translateX(64px) scaleX(0.25); }
  4%   { opacity: 0.95; }
  100% { opacity: 0; transform: rotate(var(--a)) translateX(168px) scaleX(1.25); }
}

/* ------------------------------------------------------------
   Schicht 4 · Masthead + Fortschritt (zentriertes Endbild)
   ------------------------------------------------------------ */
.ldr-inhalt {
  position: absolute; left: 24px; right: 24px; top: calc(var(--achse) - 25px); z-index: 4;
  display: flex; flex-direction: column; align-items: center; text-align: center; color: #fff;
  /* Ruckt synchron zum Aufbau: gleiche Keyframes, gleicher Start. */
  animation: ldr-ruck 2700ms linear both;
}

.ldr-overline {
  display: flex; align-items: center; gap: 9px;
  font-size: var(--fs-xs); font-weight: var(--fw-bold);
  letter-spacing: 0.16em; text-transform: uppercase; color: rgba(255, 255, 255, 0.82);
  animation: ldr-overline 700ms var(--ease-out) 2860ms both;
}
.ldr-overline::before {
  content: ""; width: 4px; height: 13px; border-radius: 2px; background: #fff; flex: none;
}
@keyframes ldr-overline {
  from { opacity: 0; letter-spacing: 0.42em; transform: translateY(10px); }
  to   { opacity: 1; letter-spacing: 0.16em; transform: translateY(0); }
}

/* „Rangliste.“ – stürzt herab und landet wie ein Gummiball: breit
   gestaucht, hoch gestreckt, drei abklingende Wellen. Ab dem
   Aufprall steht das Wort fest und bewegt sich nie wieder. */
.ldr-titel {
  margin-top: 10px;
  font-size: clamp(40px, 13.9vw, 54px); font-weight: var(--fw-black);
  letter-spacing: var(--tracking-tight); line-height: 1.05;
  opacity: 0; transform: scale(2.6); transform-origin: 50% 60%;
  animation: ldr-finale 860ms 2390ms both;
}
@keyframes ldr-finale {
  0%   { opacity: 0; transform: scale(2.6); animation-timing-function: cubic-bezier(0.55, 0, 0.8, 0.4); }
  4%   { opacity: 1; }
  15%  { transform: scale(1); animation-timing-function: cubic-bezier(0.2, 0.7, 0.35, 1); }
  21%  { transform: scaleX(1.16) scaleY(0.8); animation-timing-function: cubic-bezier(0.3, 0.6, 0.35, 1); }
  31%  { transform: scaleX(0.93) scaleY(1.13); animation-timing-function: cubic-bezier(0.35, 0.5, 0.4, 1); }
  41%  { transform: scaleX(1.06) scaleY(0.93); animation-timing-function: cubic-bezier(0.35, 0.5, 0.4, 1); }
  52%  { transform: scaleX(0.975) scaleY(1.04); animation-timing-function: cubic-bezier(0.35, 0.5, 0.4, 1); }
  64%  { transform: scaleX(1.015) scaleY(0.985); animation-timing-function: cubic-bezier(0.35, 0.5, 0.4, 1); }
  76%  { transform: scale(1); }
  100% { opacity: 1; transform: scale(1); }
}
.ldr-maske { display: block; overflow: hidden; padding-bottom: 0.06em; margin-bottom: -0.06em; }
.ldr-zeile { display: inline-block; }
/* Setz-Welle: die Buchstaben schlucken den Treffer nacheinander */
.ldr-zeile b:not(.ldr-dot) { display: inline-block; font-weight: inherit; transform-origin: 50% 100%;
  animation: ldr-setzen 220ms cubic-bezier(0.3, 0.7, 0.4, 1) calc(2545ms + var(--n) * 17ms) both; }
@keyframes ldr-setzen { 0% { transform: none; } 42% { transform: translateY(0.05em) scaleY(0.88); }
  100% { transform: none; } }
/* Der Punkt fällt als letzter Taktschlag hinter der Maskenkante herein */
.ldr-dot { display: inline-block; font-weight: inherit; transform-origin: 50% 100%;
  animation: ldr-dot 520ms linear 3160ms both; }
@keyframes ldr-dot {
  0%   { opacity: 0; transform: translateY(-1.15em); animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.4); }
  6%   { opacity: 1; }
  44%  { transform: translateY(0) scaleY(0.74) scaleX(1.22); animation-timing-function: cubic-bezier(0.1, 0.8, 0.3, 1); }
  64%  { transform: translateY(-0.26em) scale(1); animation-timing-function: cubic-bezier(0.5, 0, 0.8, 0.5); }
  80%  { transform: translateY(0) scaleY(0.92) scaleX(1.06); animation-timing-function: cubic-bezier(0.2, 0.8, 0.4, 1); }
  91%  { transform: translateY(-0.07em); }
  100% { opacity: 1; transform: none; }
}

/* Fortschritt: hängt an den ECHTEN Startschritten (loader.js).
   Der Balken füllt sich aus der Mitte – zentriertes Endbild. */
.ldr-fuss { margin-top: 34px; width: 168px;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  animation: ldr-fuss 700ms var(--ease-out) 3060ms both; }
@keyframes ldr-fuss { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.ldr-bar { width: 100%; height: 3px; border-radius: 99px; background: rgba(255, 255, 255, 0.24); overflow: hidden; }
.ldr-fuell { display: block; height: 100%; width: 100%; border-radius: inherit;
  background: #fff; transform: scaleX(0); transform-origin: 50% 50%; }
.ldr-status {
  font-size: var(--fs-xs); font-weight: var(--fw-medium); color: rgba(255, 255, 255, 0.88);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ldr-status.wechsel { animation: fade-up 300ms var(--ease-out) both; }

/* ------------------------------------------------------------
   3D-Intro „Club“ (js/lib/intro3d.js) — liegt als eigene Bühne
   über dem Aufbau; solange es aktiv ist (Klasse ldr-3d am Loader),
   sind Wort-Ansage und Masthead ausgeblendet, nur der Fortschritt
   bleibt unten sichtbar. Der Abgang nutzt dieselben Hälften wie
   die Ansage – loader.js legt ihnen das letzte 3D-Bild unter.
   ------------------------------------------------------------ */
.ldr-3d-buehne { position: absolute; inset: 0; z-index: 3; background: var(--fando); overflow: hidden; }
.ldr-3d-buehne canvas { position: absolute; inset: 0; width: 100% !important; height: 100% !important; display: block; }
.ldr-3d-buehne::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse at 50% 50%, transparent 58%, rgba(10, 12, 8, 0.4) 100%); }
.ldr-3d-blende { position: absolute; inset: 0; background: var(--fando); pointer-events: none; }
.ldr-3d .ldr-aufbau, .ldr-3d .ldr-overline, .ldr-3d .ldr-titel { display: none; }
.ldr-3d .ldr-inhalt { top: auto; bottom: calc(28px + env(safe-area-inset-bottom, 0px)); animation: none; }
.ldr-3d .ldr-fuss { margin-top: 0; animation: none; }
.ldr-raus .ldr-3d-buehne { animation: ldr-blende 150ms linear 140ms both; }
/* Solange das Intro noch lädt (Bibliothek, Szenenaufbau), zeigt ein Warteschild
   eine pulsende Palme – erst nach 350 ms, damit es bei schnellem Start nicht
   aufblitzt. Steht das erste Bild, setzt loader.js ldr-3d-an: Bühne blendet
   weich ein, Schild blendet aus. */
.ldr-3d-warte { position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; padding-bottom: 60px; pointer-events: none; opacity: 0; animation: ldr-warte-ein 400ms var(--ease-out) 350ms both; transition: opacity 320ms linear; }
.ldr-3d-warte i { width: 72px; height: 84px; background: url("../img/logo-white.svg") no-repeat center / contain; animation: ldr-warte-puls 1.4s var(--ease-in-out) infinite; }
.ldr-3d-warte span { font-size: var(--fs-xs); font-weight: var(--fw-medium); color: rgba(255, 255, 255, 0.8); }
@keyframes ldr-warte-ein { to { opacity: 1; } }
@keyframes ldr-warte-puls { 0%, 100% { transform: scale(1); opacity: 0.7; } 50% { transform: scale(1.08); opacity: 1; } }
.ldr-3d-an .ldr-3d-warte { animation: none; opacity: 0; }
.ldr-3d-buehne { opacity: 0; transition: opacity 520ms linear; }
.ldr-3d-an .ldr-3d-buehne { opacity: 1; }

/* ------------------------------------------------------------
   Start-Fehler — die Bühne trägt die Erklärung (loader.js)
   ------------------------------------------------------------ */
.ldr-fehler { display: flex; flex-direction: column; align-items: center; gap: 12px;
  max-width: 320px; text-align: center; }
.ldr-fehler strong { font-size: var(--fs-xl); font-weight: var(--fw-black); letter-spacing: var(--tracking-tight); }
.ldr-fehler span { font-size: var(--fs-sm); color: rgba(255, 255, 255, 0.85); line-height: var(--lh-normal); }
.ldr-fehler button {
  margin-top: 6px; height: 44px; padding: 0 24px; border-radius: 99px;
  background: #fff; color: var(--fando-700); font-weight: var(--fw-bold);
}

/* ------------------------------------------------------------
   Überspringen — ein Tipp setzt alles auf den Endzustand
   (loader.js → ueberspringen). Die Worte sind dann vorbei, die
   Bühne steht, das Masthead ist da. Nur das Wiegen der Palme
   entfällt nach dem Tipp – Endzustand statt Schleife.
   ------------------------------------------------------------ */
.ldr-fix .ldr-aufbau, .ldr-fix .ldr-inhalt, .ldr-fix .ldr-flach, .ldr-fix .ldr-blitz,
.ldr-fix .ldr-buhne, .ldr-fix .ldr-buhne .ldr-wm, .ldr-fix .ldr-dring, .ldr-fix .ldr-striche i,
.ldr-fix .ldr-wort, .ldr-fix .ldr-aw2 b, .ldr-fix .ldr-aw3w,
.ldr-fix .ldr-overline, .ldr-fix .ldr-titel, .ldr-fix .ldr-zeile b, .ldr-fix .ldr-fuss {
  animation-duration: 0ms !important; animation-delay: 0ms !important;
}

/* ------------------------------------------------------------
   Abgang — Inhalt hebt ab, Aufbau blendet auf die deckungs-
   gleichen Hälften um (unsichtbar), dann teilt sich die Fläche.
   Die Dauern gehören zu ABGANG_MS in js/lib/loader.js.
   ------------------------------------------------------------ */
.ldr-raus .ldr-inhalt { animation: ldr-inhalt-weg 240ms var(--ease-standard) both; }
@keyframes ldr-inhalt-weg { to { opacity: 0; transform: translateY(-18px); } }
.ldr-raus .ldr-aufbau { animation: ldr-blende 150ms linear 140ms both; }
@keyframes ldr-blende { to { opacity: 0; } }
.ldr-raus .ldr-haelfte { will-change: transform; }
.ldr-raus .ldr-oben  { animation: ldr-auf-oben  640ms var(--ease-emphasis) 260ms both; }
.ldr-raus .ldr-unten { animation: ldr-auf-unten 640ms var(--ease-emphasis) 260ms both; }
@keyframes ldr-auf-oben  { to { transform: translateY(-100.4%); } }
@keyframes ldr-auf-unten { to { transform: translateY(100.4%); } }

/* ------------------------------------------------------------
   Weniger Bewegung: animations.css stellt jede Animation auf
   ~0 ms – alle „both“-Animationen springen in ihren Endzustand
   (Worte vorbei, Bühne voll, Masthead steht). Nur der Abgang
   braucht einen Ersatz.
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .ldr-raus { transition: opacity 200ms linear; opacity: 0; }
}
