:root{
/* Bewegung antwortet auf den Zeiger — und einmal je Block auf den Eintritt ins Bild.
   Der Auftritt ist eine Einblendung mit kurzem Anstieg: eine Achse, kein Zoom, keine Drehung,
   kein Weichzeichner, kein zweites Mal. Der Block kommt herauf und bleibt oben.
   --reveal-step staffelt Geschwister über --i (siehe .ds-delay-* in base.css).
   Unter prefers-reduced-motion fallen alle Dauern auf 0 ms und --reveal-shift auf 0 —
   der Auftritt steht dann sofort, statt zu verschwinden. */
--dur-instant:90ms; /* @kind other */
--dur-fast:140ms; /* @kind other */
--dur:200ms; /* @kind other */
--dur-reveal:620ms; /* @kind other */
--dur-page:260ms; /* @kind other */
--ease-snap:cubic-bezier(.2,.85,.25,1); /* @kind other */
--ease-out:cubic-bezier(.16,1,.3,1); /* @kind other */
--reveal-shift:18px; /* @kind spacing */
--reveal-step:80ms; /* @kind other */
/* --hover-lift wird von nichts gelesen: nicht von einer Komponente im Bundle, nicht von einer
   der sieben Seiten. Der Wert bleibt trotzdem stehen, weil ihn zu streichen aus einer Token-Datei,
   die neu erzeugt werden kann, nur beim nächsten Lauf wieder auftaucht. Wer ihn braucht, soll ihn
   nehmen; wer die Datei liest, soll nicht glauben, er sei irgendwo in Gebrauch. */
--hover-lift:-1px; /* @kind spacing */
--press-sink:1px; /* @kind spacing */
--press-scale:.985; /* @kind other */
--tilt-a:-4deg; /* @kind other */
--tilt-b:3.5deg; /* @kind other */
--tilt-c:-7deg; /* @kind other */
}
@media (prefers-reduced-motion:reduce){:root{
--dur-instant:0ms; /* @kind other */
--dur-fast:0ms; /* @kind other */
--dur:0ms; /* @kind other */
--dur-reveal:0ms; /* @kind other */
--dur-page:0ms; /* @kind other */
--reveal-shift:0px; /* @kind spacing */
--reveal-step:0ms; /* @kind other */
--hover-lift:0px; /* @kind spacing */
--press-sink:0px; /* @kind spacing */
--press-scale:1; /* @kind other */
}}

/* ---- Seitenwechsel ----
   Onepager, Arbeiten und Formular sind drei echte Dokumente, kein SPA. Der Wechsel läuft über
   die View-Transitions-API: der Browser hält einen Schnappschuss der alten Seite und blendet die
   neue darüber. Kein Skript, kein Vorabladen, keine abgefangenen Klicks.

   navigation:auto greift nur bei Navigation innerhalb derselben Herkunft. Beide Seiten müssen
   die Regel tragen — weil alle Seiten dieselben Token-Dateien laden, gilt das automatisch.
   Browser ohne Unterstützung springen wie bisher hart um. Die Regel kann also nichts kaputt
   machen, sie kann nur ausbleiben.

   Nur die Dauer wird gesetzt, die Überblendung selbst ist die eingebaute. Absichtlich kein
   Schieben, kein Zoom: die Seite wechselt, sie fährt nicht vorbei.

   Kein view-transition-name auf Navigation oder Fuß. Ein Name darf im Dokument nur einmal
   vorkommen, der Onepager trägt aber zwei NavBars (die klebende und die im Hero), und das
   Formular trägt gar keine. Ein doppelter Name bricht den gesamten Übergang ab. */
@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){
  animation-duration:var(--dur-page);
  animation-timing-function:var(--ease-snap);
}
/* Unter prefers-reduced-motion fällt --dur-page ohnehin auf 0. Die Animation zusätzlich ganz
   abzuschalten ist der dokumentierte Weg und verhindert, dass ein 0-ms-Lauf mit fill:both
   in einem halben Zustand stehen bleibt. */
@media (prefers-reduced-motion:reduce){
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none !important}
}

/* ---- Der Eintritt, für alles andere ----
   Gemessen am 15.08.2026: die Regel oben wird geladen und geparst, und trotzdem meldeten
   pageswap auf der abgehenden und pagereveal auf der ankommenden Seite beide viewTransition:null.
   Zwei minimale Testseiten mit derselben Regel und drei Sekunden Dauer schnitten ebenso hart um.
   Chrome 148, gleiche Herkunft, kein Rahmen, reduced-motion aus — die Voraussetzungen stimmten
   alle. Dokumentübergreifende Übergänge laufen in der Electron-Vorschau schlicht nicht, und das
   ist die Ansicht, in der die Seite tatsächlich angeschaut wird. Eine Regel, die nur in einem
   Browser greift, den niemand benutzt, ist keine Umsetzung.

   Deshalb zusätzlich ein Eintritt, der ohne die API auskommt: die neue Seite blendet auf.
   Der Grund bleibt dabei stehen — er liegt auf html und ist auf jeder Seite derselbe Creme —,
   es wechselt nur der Inhalt darüber. Genau die Aussage, die der Übergang tragen soll.

   Warum body und nicht ein Vorhang: ein deckender Vorhang, der weggeblendet wird, verdeckt die
   Seite so lange, bis die Animation läuft. Bleibt sie einmal aus, steht eine leere Verkaufsseite
   da — dieselbe Falle, gegen die .ds-reveal an .js-reveal hängt. Beim Aufblenden ist der
   schlimmste Fall, dass der Inhalt sofort sichtbar ist.

   backwards, nicht both: der Anfangswert gilt vor dem Start, danach wird gar keine Deckkraft mehr
   gesetzt. Ein liegengebliebenes opacity würde auf body einen Stapelkontext aufmachen und die
   klebende Rücklaufleiste aus position:fixed lösen.

   Läuft in einem Browser doch die eingebaute Überblendung, überlagern sich beide. Das ist keine
   doppelte Bewegung, sondern eine etwas weichere: 260 ms Überblendung, während der ankommende
   Inhalt selbst noch aufzieht. Beide Werte hängen an --dur-page, sie können nicht auseinanderlaufen.

   linear, nicht --ease-snap: gemessen steht die Blende mit der Schnappkurve nach einem Viertel der
   Zeit schon auf 76 % Deckkraft und nach der Hälfte auf 95 — die restlichen 130 ms passiert
   sichtbar nichts mehr. Bei einer Bewegung im Raum ist dieses Vorauseilen richtig, weil echte
   Masse so anhält. Deckkraft hat keine Masse; sie wird gleichmäßig wahrgenommen, und eine
   Kurve darauf verkürzt die Blende gefühlt auf die Hälfte. Das ist genau das Gegenteil von
   dem, was hier verlangt war. */
@keyframes ds-seite-ein{from{opacity:0}}
@media (prefers-reduced-motion:no-preference){
  body{animation:ds-seite-ein var(--dur-page) linear backwards}
}
