/* ==========================================================
   bewegung.css · Bewegungsschicht

   Gebaut nach Kanon 12 (scrollgebundene Bewegung) und 13
   (Renderkosten, First Paint, Bewegungstechnik).

   DREI SCHICHTEN, bewusst getrennt:

     1  BUEHNE - reines CSS, laeuft ohne JavaScript.
        Kanon 13.5.1: oberhalb des Folds animiert ausschliesslich
        CSS. 13.5.4: der Effekt muss auch bei blockiertem Skript
        noch funktionieren.

     2  AUFTRITT - unterhalb des Folds, ausgeloest vom
        IntersectionObserver in beeit.js. Nur transform und
        opacity, also Kostenklasse 1 (13.2).

     3  SCROLLGEBUNDEN - eine einzige rAF-Schleife in beeit.js
        mit Tiefpass. Schreibt nur CSS-Variablen, die hier
        verrechnet werden.

   AUS-ZUSTAND, gleichzeitig gebaut (13.4), drei Wege:

     a) prefers-reduced-motion: reduce
     b) html.a11y-motion - der projektinterne Schalter (12.7,
        Kanon 5.4). Ein Bedienpanel dafuer gibt es im Entwurf
        noch nicht, die Klasse wirkt aber bereits.
     c) kein JavaScript - dann fehlt html.bewegung-an

   DAS WICHTIGSTE AN DER KONSTRUKTION: Jeder Startzustand haengt
   an html.bewegung-an. Diese Klasse setzt beeit.js NUR, wenn
   Bewegung erlaubt ist. Faellt das Skript aus, greift der
   Ausschalter oder hat jemand reduced-motion gesetzt, ist
   NICHTS verborgen - die Seite steht einfach da. Der umgekehrte
   Weg (verbergen per CSS, zeigen per JS) haette bei jedem
   Skriptfehler eine leere Seite ergeben.

   13.6.1: keine Einzel-Properties translate/rotate/scale.
   Ueberall transform. Gegenprobe:
     grep -nE '^\s*(translate|rotate|scale):' public/assets/css/*.css

   Stand: p.015 · 23.09.2026
   ========================================================== */


/* ==========================================================
   0 · Gemeinsame Werte
   ========================================================== */
:root {
  --ey-weg:        28px;   /* Bewegungsweg. 13.3.5: hoechstens
                              100px und kleiner als der kleinste
                              Nachbarabstand (hier py-16 = 64px). */
  --ey-weg-klein:  18px;
  --ey-dauer:      .72s;
  --ey-dauer-kurz: .5s;
  --ey-takt:       0;      /* Staffelindex, von beeit.js gesetzt */
  --ey-takt-ms:    70ms;
  --ey-kurve:      cubic-bezier(.22, .61, .36, 1);
  --ey-p:          0;      /* Scrollfortschritt 0..1, geglaettet */
}


/* ==========================================================
   1 · BUEHNE · reines CSS, ohne JavaScript

   Kanon 13.5.2 ist hier die bindende Regel: H1 und Lead sind ab
   First Paint DECKEND sichtbar. Animiert wird nur die
   Transformachse, nie die Opazitaet. Eine einblendende
   Hauptueberschrift wertet Lighthouse als spaeten LCP - gemessen
   wurden 12,7 s auf gedrosseltem Mobil.

   Deshalb: h1 und Lead fahren nur hoch. Beiwerk - Vertrauenszeile,
   Knoepfe - darf blenden, es ist nicht das LCP-Element.
   ========================================================== */
@media (prefers-reduced-motion: no-preference) {
  html:not(.a11y-motion) .buehne h1 {
    animation: ey-heben var(--ey-dauer) var(--ey-kurve) both;
  }

  html:not(.a11y-motion) .buehne h1 + p,
  html:not(.a11y-motion) .buehne p.mx-auto {
    animation: ey-heben var(--ey-dauer) var(--ey-kurve) .08s both;
  }

  /* Beiwerk: Opazitaet erlaubt, kein LCP-Kandidat */
  html:not(.a11y-motion) .buehne .rounded-full {
    animation: ey-auf var(--ey-dauer) var(--ey-kurve) both;
  }

  html:not(.a11y-motion) .buehne a[class*="rounded-md"]:nth-of-type(1) {
    animation: ey-auf var(--ey-dauer) var(--ey-kurve) .22s both;
  }
  html:not(.a11y-motion) .buehne a[class*="rounded-md"]:nth-of-type(2) {
    animation: ey-auf var(--ey-dauer) var(--ey-kurve) .3s both;
  }

  html:not(.a11y-motion) .buehne .ki-hinweis {
    animation: ey-auf .9s ease-out .6s both;
  }

  /* Ambient-Loop auf dem Buehnenbild.

     13.3.2: Budget ist EIN dauerhaft laufender Loop je Viewport
     - das hier ist er, und es gibt keinen zweiten.
     Kostenklasse 1: reine transform-Animation am aeusseren
     Element, compositbar, laeuft auch bei blockiertem
     Haupt-Thread (13.5.3).

     Kein scrollgebundenes Dekor - das waere oberhalb des Folds
     nach 12.13 unzulaessig. Ein CSS-Loop ist keins. */
  html:not(.a11y-motion) .buehne > img {
    animation: ey-atem 26s ease-in-out infinite alternate;
    transform-origin: 52% 46%;
    will-change: transform;
  }
}

@keyframes ey-heben {
  from { transform: translate3d(0, var(--ey-weg), 0); }
  to   { transform: translate3d(0, 0, 0); }
}

@keyframes ey-auf {
  from { opacity: 0; transform: translate3d(0, var(--ey-weg-klein), 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes ey-atem {
  from { transform: scale(1.005); }
  to   { transform: scale(1.075); }
}


/* ==========================================================
   2 · AUFTRITT · unterhalb des Folds

   Startzustand haengt an html.bewegung-an (siehe Kopf).
   beeit.js vergibt die Klassen, setzt den Startzustand mit
   erzwungenem Reflow (13.6.2) und nimmt das Ziel nach dem
   Auftritt wieder aus der Beobachtung.
   ========================================================== */
html.bewegung-an .ey-auftritt {
  opacity: 0;
  transform: translate3d(0, var(--ey-weg), 0);
}

html.bewegung-an .ey-auftritt--klein {
  transform: translate3d(0, var(--ey-weg-klein), 0);
}

/* Karten kommen zusaetzlich aus der Tiefe. Kein scale: - die
   Einzel-Property waere nach 13.6.1 verboten (und wuerde sich
   mit dem translate aus derselben Regel gegenseitig loeschen). */
html.bewegung-an .ey-auftritt--karte {
  transform: translate3d(0, var(--ey-weg), 0) scale(.972);
}

/* Bilder setzen sich, statt zu fahren. */
html.bewegung-an .ey-auftritt--bild {
  opacity: 0;
  transform: scale(1.06);
}

/* Seitlich fuer zweispaltige Sektionen. Der Weg ist absichtlich
   kurz - 13.3.5 verlangt ihn kleiner als den kleinsten
   Nachbarabstand, und mobil wird daraus ohnehin ein vertikaler
   Kurzweg (siehe Medienabfrage unten). */
html.bewegung-an .ey-auftritt--links  { transform: translate3d(calc(var(--ey-weg) * -1), 0, 0); }
html.bewegung-an .ey-auftritt--rechts { transform: translate3d(var(--ey-weg), 0, 0); }

html.bewegung-an .ey-auftritt.ist-da {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

@media (prefers-reduced-motion: no-preference) {
  html.bewegung-an:not(.a11y-motion) .ey-auftritt {
    transition:
      opacity   var(--ey-dauer) var(--ey-kurve),
      transform var(--ey-dauer) var(--ey-kurve);
    transition-delay: calc(var(--ey-takt) * var(--ey-takt-ms));
  }

  html.bewegung-an:not(.a11y-motion) .ey-auftritt--bild {
    transition:
      opacity   var(--ey-dauer-kurz) ease-out,
      transform 1.1s var(--ey-kurve);
  }
}

/* 13.3.5: mobil wird der Weg begrenzt, nicht gestrichen - und
   aus dem seitlichen Slide wird ein vertikaler Kurzweg. */
@media (max-width: 760px) {
  :root { --ey-weg: 20px; --ey-weg-klein: 14px; }

  html.bewegung-an .ey-auftritt--links,
  html.bewegung-an .ey-auftritt--rechts {
    transform: translate3d(0, var(--ey-weg), 0);
  }
}


/* ==========================================================
   3 · SCROLLGEBUNDEN

   beeit.js schreibt nur zwei Dinge: die Variable --ey-p am
   Dokument (Gesamtfortschritt, geglaettet nach 12.1) und je
   Bild eine eigene --ey-versatz. Gerechnet wird hier.
   ========================================================== */

/* Fortschrittsband oben. Die Breite folgt dem GEGLAETTETEN
   Wert - 12.1 verbietet die 1:1-Kopplung ausdruecklich, und der
   Nachlauf ist genau das, was den Unterschied zwischen "klebt
   am Scrollrad" und "hat Gewicht" ausmacht. */
.ey-fortschritt {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 60;
  height: 3px;
  pointer-events: none;
  transform-origin: 0 50%;
  transform: scaleX(var(--ey-p));
  background: linear-gradient(90deg, #E8A837, #F2BE5C);
}

html.a11y-motion .ey-fortschritt { display: none; }

/* Kopf verdichtet sich. Zustandswechsel per Klasse, nicht per
   Scrollwert - eine Schwelle mit Hysterese in beeit.js. Das ist
   keine scrollgebundene Bewegung im Sinn von 12, sondern ein
   Zustand, und darf deshalb hart schalten. */
@media (prefers-reduced-motion: no-preference) {
  html:not(.a11y-motion) body > header {
    transition: box-shadow .3s ease, background-color .3s ease;
  }
}

html.gescrollt body > header {
  box-shadow: 0 1px 0 rgb(20 24 29 / .08), 0 8px 24px -16px rgb(20 24 29 / .5);
}

html.nacht.gescrollt body > header {
  box-shadow: 0 1px 0 rgb(237 241 245 / .10), 0 10px 30px -18px #000;
}

/* Parallaxe an Bildern UNTERHALB des Folds.

   12.13: oberhalb des Folds ist scrollgebundenes Dekor
   unzulaessig - die Buehne ist deshalb ausdruecklich
   ausgenommen, sie hat ihren CSS-Loop.

   Das Bild wird ueberformatig gehalten (scale 1.10), damit der
   Versatz nie eine Kante freilegt. Der Rahmen schneidet.

   GEMESSEN, nicht angenommen: 1,10 gibt 5 % Ueberstand je Seite.
   Der Versatz in beeit.js betraegt 3,5 % der Rahmenhoehe - damit
   bleibt bei JEDER Bildhoehe Reserve.

   Ein fester Deckel waere hier falsch gewesen: vier der sechs
   Rahmen sind nur 167 px hoch und haben 8,3 px Ueberstand. Der
   erste Entwurf deckelte auf 14 px und haette dort die Bildkante
   freigelegt. Aufgefallen beim Nachmessen aller sechs Rahmen,
   nicht beim Ansehen des ersten. */
.ey-parallaxe { overflow: hidden; }

@media (prefers-reduced-motion: no-preference) {
  html.bewegung-an:not(.a11y-motion) .ey-parallaxe > img {
    transform: translate3d(0, var(--ey-versatz, 0px), 0) scale(1.10);
    will-change: transform;
  }
}


/* ==========================================================
   4 · Zahlen, die hochlaufen

   beeit.js schreibt den Zwischenwert in den Text. Hier steht
   nur, dass die Ziffern nicht springen duerfen - ohne
   Tabellenziffern wackelt die Zeile bei jedem Schritt.
   ========================================================== */
.ey-zaehlt {
  font-variant-numeric: tabular-nums;
}


/* ==========================================================
   5 · Feinbewegung an Bedienelementen

   5.20: jede :hover-Regel in @media (hover: hover), sonst
   kostet sie auf Touch einen zusaetzlichen Tipp.
   ========================================================== */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  html:not(.a11y-motion) a[class*="rounded-md"],
  html:not(.a11y-motion) button[class*="rounded-md"],
  html:not(.a11y-motion) a[class*="rounded-lg"] {
    transition: transform .22s var(--ey-kurve), background-color .22s ease;
  }

  html:not(.a11y-motion) a[class*="rounded-md"]:hover,
  html:not(.a11y-motion) button[class*="rounded-md"]:hover {
    transform: translate3d(0, -2px, 0);
  }

  /* Die Kacheln der drei Tueren heben sich mit dem Text. */
  html:not(.a11y-motion) .group .size-11,
  html:not(.a11y-motion) .group .size-10 {
    transition: transform .3s var(--ey-kurve), background-color .3s ease;
  }
  html:not(.a11y-motion) .group:hover .size-11,
  html:not(.a11y-motion) .group:hover .size-10 {
    transform: translate3d(0, -3px, 0) scale(1.06);
  }
}


/* ==========================================================
   6 · Druck

   Papier bewegt sich nicht. Alles in Ruhelage, nichts verborgen.
   ========================================================== */
@media print {
  .ey-fortschritt { display: none !important; }

  html.bewegung-an .ey-auftritt,
  html.bewegung-an .ey-auftritt--karte,
  html.bewegung-an .ey-auftritt--bild,
  html.bewegung-an .ey-auftritt--links,
  html.bewegung-an .ey-auftritt--rechts {
    opacity: 1 !important;
    transform: none !important;
  }

  .ey-parallaxe > img { transform: none !important; }
  .buehne > img { animation: none !important; }
}
