/* ============================================================
   BEWEGUNG — die gemeinsame Schicht

   Ein Grundsatz, an dem sich jede Regel hier messen lassen muss:

       Bewegung erklaert eine Veraenderung. Kein Effekt ohne Aussage.

   Was sich bewegt, sagt damit etwas — wo etwas herkommt, wohin es geht,
   was zusammengehoert, was gerade neu ist. Was nichts zu sagen hat, steht
   still. Das ist der Unterschied zwischen einer Oberflaeche, die
   professionell wirkt, und einer, die zappelt.

   Zwei technische Regeln folgen daraus:

   1. Animiert werden `transform` und `opacity`. Beide laufen auf der
      Grafikkarte, ohne dass der Browser das Layout neu rechnet. Wer
      `height`, `top` oder `margin` animiert, zwingt ihn bei jedem Bild
      dazu — bei einer langen Etappenliste sieht man das.

   2. Keine Sekunde Bewegung ohne Not. Die Dauern stehen als Tokens in
      stil.css und sind dort begruendet; hier werden sie nur benutzt.

   Und eine Regel darueber:

   3. Kein Inhalt existiert nur als Animation. Wer `prefers-reduced-
      motion` gesetzt hat, sieht am Ende dasselbe — nur sofort. Der
      Abschalter dafuer steht ganz unten und ist absichtlich brutal.
   ============================================================ */


/* ============================================================
   1 · EINTRITTE

   Elemente mit `data-eintritt` starten unsichtbar und leicht versetzt.
   `logik/bewegung.js` setzt `.ist-da`, sobald sie in den Blick kommen.

   Die Richtung steckt im Wert: `data-eintritt="unten"` kommt von unten
   herauf, `links` von links herein. Voreinstellung ist `unten` — das
   liest sich beim Scrollen als „das war schon da, ich komme nur gerade
   erst hin", nicht als „das ist eben erschienen".

   Die Staffelung laeuft ueber `--verzug`, das die Bewegungsschicht je
   Element setzt. Sie ist der Grund, warum eine Liste als Liste
   erscheint und nicht als Block: Das Auge folgt der Reihenfolge.
   ============================================================ */
[data-eintritt]{
  opacity:0;
  transition:
    opacity   var(--d-mittel) var(--k-ein) var(--verzug, 0ms),
    transform var(--d-mittel) var(--k-ein) var(--verzug, 0ms);
  will-change:opacity, transform;
}
[data-eintritt="unten"],[data-eintritt=""]{transform:translateY(14px)}
[data-eintritt="oben"]                    {transform:translateY(-14px)}
[data-eintritt="links"]                   {transform:translateX(-18px)}
[data-eintritt="rechts"]                  {transform:translateX(18px)}
/* Fuer Dinge, die ankommen statt hereinzurutschen: eine Plakette, ein
   Ergebnis, ein Siegel. Der Massstab bleibt knapp unter eins — alles
   darunter sieht nach Zaubertrick aus. */
[data-eintritt="wachsen"]                 {transform:scale(.94)}

[data-eintritt].ist-da{opacity:1;transform:none;will-change:auto}


/* ============================================================
   2 · SCHRITTWECHSEL

   Zwei Ansichten tauschen den Platz, und die Richtung sagt, wohin die
   Reise geht: vorwaerts zieht das Alte nach links und bringt das Neue
   von rechts, rueckwaerts umgekehrt.

   Das ist mehr als Zierrat. In einem Assistenten mit neun Schritten ist
   die Richtung die einzige Rueckmeldung darauf, ob „Zurueck" gedrueckt
   wurde oder „Weiter" — die Ueberschrift wechselt in beiden Faellen.

   Hinaus geht es schneller als herein (`--d-kurz` gegen `--d-mittel`,
   `--k-aus` gegen `--k-ein`): Dem Auge ist gleichgueltig, wie etwas
   verschwindet, es wartet auf das, was kommt.
   ============================================================ */
.wechsel-raus-vor,.wechsel-raus-zurueck{
  transition:opacity var(--d-kurz) var(--k-aus), transform var(--d-kurz) var(--k-aus);
  opacity:0;
}
.wechsel-raus-vor    {transform:translateX(-26px)}
.wechsel-raus-zurueck{transform:translateX( 26px)}

.wechsel-rein-vor,.wechsel-rein-zurueck{
  animation-duration:var(--d-mittel);
  animation-timing-function:var(--k-ein);
  animation-fill-mode:both;
}
.wechsel-rein-vor    {animation-name:wechselVonRechts}
.wechsel-rein-zurueck{animation-name:wechselVonLinks}

@keyframes wechselVonRechts{
  from{opacity:0;transform:translateX( 30px)}
  to  {opacity:1;transform:none}
}
@keyframes wechselVonLinks{
  from{opacity:0;transform:translateX(-30px)}
  to  {opacity:1;transform:none}
}


/* ============================================================
   3 · SKELETT

   Der Ladezustand hat eine Aufgabe, die oft uebersehen wird: Er haelt
   den Platz frei. Ein Spinner an der Stelle, wo spaeter drei Zeilen
   stehen, laesst die Seite beim Eintreffen der Daten springen — und wer
   in dem Moment auf etwas darunter zielt, trifft daneben.

   Deshalb hat das Skelett die Form dessen, was kommt, nicht die Form
   eines Wartesymbols.

   Der Schimmer lief vorher im Planer unter dem Namen `schimmern` und
   war dort eingebaut. Er steht jetzt hier, weil ihn jede Seite braucht.
   ============================================================ */
.skelett{
  position:relative;overflow:hidden;
  background:var(--flaeche);border-radius:var(--r-klein);
  color:transparent!important;user-select:none;pointer-events:none;
}
.skelett::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(
    100deg,
    transparent 20%,
    rgba(237,241,245,.055) 42%,
    rgba(237,241,245,.085) 50%,
    rgba(237,241,245,.055) 58%,
    transparent 80%
  );
  background-size:240% 100%;
  animation:schimmern 1.5s linear infinite;
}
@keyframes schimmern{from{background-position:120% 0} to{background-position:-120% 0}}

/* Zeilenskelette: mehrere untereinander, die letzte kuerzer — so sieht
   Fliesstext aus, und genau daran erkennt das Auge, dass Text kommt. */
.skelett-zeile{height:1em;margin-bottom:var(--a2);border-radius:var(--r-fein)}
.skelett-zeile:last-child{width:62%;margin-bottom:0}


/* ============================================================
   4 · MELDUNGEN (Toast)

   Sie legen sich ueber die Seite, statt sie zu verschieben. Eine
   Fehlermeldung, die oben eingefuegt wird, schiebt das Formular nach
   unten — und der Knopf, auf den der Finger schon unterwegs ist, ist
   nicht mehr dort, wo er war.

   Unten rechts, weil oben die klebende Kopfleiste sitzt.
   ============================================================ */
.meldungen{
  position:fixed;right:var(--a5);bottom:var(--a5);z-index:var(--z-meldung);
  display:flex;flex-direction:column;gap:var(--a2);
  max-width:min(380px, calc(100vw - 2 * var(--a5)));
  pointer-events:none;
}
.toast{
  display:flex;align-items:flex-start;gap:var(--a3);
  background:var(--flaeche-2);border:1px solid var(--linie-hell);
  border-left-width:3px;
  border-radius:var(--r-klein);padding:var(--a3) var(--a4);
  box-shadow:var(--schatten-2), var(--glanz);
  font-size:14.5px;line-height:1.5;color:var(--text);
  pointer-events:auto;
  animation:toastRein var(--d-mittel) var(--k-ein) both;
}
.toast.gut     {border-left-color:var(--frei)}
.toast.schlecht{border-left-color:var(--warn)}
.toast.hinweis {border-left-color:var(--peage)}
.toast.geht{animation:toastRaus var(--d-kurz) var(--k-aus) both}

@keyframes toastRein{
  from{opacity:0;transform:translateY(12px) scale(.97)}
  to  {opacity:1;transform:none}
}
@keyframes toastRaus{
  from{opacity:1;transform:none}
  to  {opacity:0;transform:translateX(24px)}
}

@media (max-width:640px){
  /* Am Telefon ueber die volle Breite und unten — dort, wo der Daumen
     ohnehin ist, und ausserhalb dessen, was gerade gelesen wird. */
  .meldungen{left:var(--a4);right:var(--a4);bottom:var(--a4);max-width:none}
}


/* ============================================================
   5 · LISTENBEWEGUNG (FLIP)

   Beim Umsortieren und beim Entfernen springen Zeilen sonst an ihren
   neuen Platz. Im Planer bedeutet das: Man drueckt „nach vorn", die
   Liste sieht danach anders aus, und niemand weiss, welche Zeile sich
   bewegt hat.

   `logik/bewegung.js` misst vorher und nachher (First–Last–Invert–Play)
   und setzt den Unterschied als `transform`. Diese Klasse spielt ihn ab.
   ============================================================ */
.flip-laeuft{
  transition:transform var(--d-mittel) var(--k-beides);
  will-change:transform;
}
/* Neu hinzugekommene Zeilen kommen nicht aus dem Nichts, sondern von
   dort, wo sie herkommen: von der Seite, an der der Knopf steht. */
.flip-neu{animation:flipNeu var(--d-mittel) var(--k-ein) both}
@keyframes flipNeu{
  from{opacity:0;transform:translateX(-16px)}
  to  {opacity:1;transform:none}
}
.flip-weg{animation:flipWeg var(--d-kurz) var(--k-aus) both}
@keyframes flipWeg{
  from{opacity:1;transform:none}
  to  {opacity:0;transform:translateX(20px)}
}


/* ============================================================
   6 · AUF UND ZU

   Die Hoehe eines Panels ist beim Oeffnen unbekannt — `height:auto`
   laesst sich nicht animieren. `hoeheWechseln()` misst sie und setzt sie
   als eigene Zahl; diese Klasse faehrt darauf zu.

   `height` steht hier ausdruecklich, obwohl Regel 2 oben etwas anderes
   sagt. Der Grund: Ein `scaleY` verzerrt den Inhalt beim Fahren — Text
   wird gestaucht und schnappt am Ende zurueck. Bei einem Panel mit
   Beschriftungen sieht man das sofort. Die teurere Loesung ist hier die
   richtige, und sie laeuft nur waehrend der Bewegung.
   ============================================================ */
.hoehe-laeuft{
  overflow:hidden;
  transition:height var(--d-mittel) var(--k-beides),
             opacity var(--d-kurz)  var(--k-beides);
}


/* ============================================================
   7 · AUFMERKSAMKEIT

   Sparsam einzusetzen. Ein Wert, der sich gerade geaendert hat, darf
   einmal aufleuchten — das beantwortet die Frage „hat mein Klick etwas
   bewirkt?", die sonst offenbleibt, wenn sich eine Zahl weit weg vom
   Knopf aendert.

   Einmal. Nicht dauernd. Was dauernd blinkt, wird uebersehen.
   ============================================================ */
.frisch{animation:frisch var(--d-lang) var(--k-ein)}
@keyframes frisch{
  0%  {background-color:rgba(107,166,222,.20)}
  100%{background-color:transparent}
}

/* Der Pulsring markiert genau eine Stelle: die, an der man gerade steht.
   Er laeuft weiter, weil er einen Zustand anzeigt und kein Ereignis. */
.puls{position:relative}
.puls::after{
  content:"";position:absolute;inset:-4px;border-radius:inherit;
  border:1px solid currentColor;opacity:0;
  animation:pulsRing 2.2s var(--k-ein) infinite;
  pointer-events:none;
}
@keyframes pulsRing{
  0%  {opacity:.55;transform:scale(.86)}
  70% {opacity:0;   transform:scale(1.35)}
  100%{opacity:0;   transform:scale(1.35)}
}


/* ============================================================
   8 · DER ABSCHALTER

   `prefers-reduced-motion` ist keine Geschmacksfrage. Wer es setzt,
   bekommt von Bewegung Uebelkeit oder Kopfschmerzen — bei manchen
   Menschen loesen bewegte Oberflaechen Migraene aus.

   Deshalb steht hier keine hoefliche Abschwaechung, sondern ein Stopp.
   Und deshalb ist die Regel oben, dass kein Inhalt nur als Animation
   existiert, keine Stilfrage: Ab hier ist jeder Eintritt sofort da,
   jeder Zaehler steht auf seinem Endwert, jeder Schimmer haelt an.
   Wer das hier liest und eine neue Animation schreibt, muss pruefen,
   ob ihr Endzustand fuer sich allein steht.

   `animation-iteration-count:1` gehoert dazu: Ohne sie laufen endlose
   Animationen mit 1 ms Dauer weiter — hunderte Bilder pro Sekunde, der
   Luefter dreht auf, und der Nutzer hat genau das Gegenteil von dem
   bekommen, worum er gebeten hat.
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:1ms!important;
    animation-iteration-count:1!important;
    animation-delay:0ms!important;
    transition-duration:1ms!important;
    transition-delay:0ms!important;
    scroll-behavior:auto!important;
  }
  [data-eintritt]{opacity:1!important;transform:none!important}
  .skelett::after{animation:none;background:none}
  .puls::after{display:none}
}
