/* ==================================================================
   motion.css — die Bewegungsschicht aller Seiten
   Losi Bau · angelegt 25.09.2026

   Ein Motiv, konsequent (blueprint.md §8):
   DIE ZEICHNUNG BAUT SICH AUF, WIE EINE ZEICHNUNG ENTSTEHT.

   Daraus folgen vier Bewegungen und keine fuenfte:
     1. Text und Flaechen steigen aus 20 px Tiefe auf   (Gruppe A)
     2. Reihen bauen sich Glied fuer Glied, 60 ms Takt  (Gruppe B)
     3. Bilder wischen von unten auf und setzen sich    (Gruppe C)
     4. Seitenwechsel als 180-ms-Kreuzblende            (View Transitions)

   REGELN, die hier nicht gebrochen werden:
   · Kein Inhalt haengt an einer Animation. Ohne JavaScript, ohne
     IntersectionObserver und bei prefers-reduced-motion steht jede
     Seite vollstaendig da.
   · Bewegt werden nur opacity, translate, scale und clip-path —
     nichts, was ein Layout neu rechnet.
   · Karten werden nicht gehoben und nicht skaliert (blueprint.md §8).
     Bewegt wird das Bild IN der Karte, nie die Karte.

   DER NOTAUSSCHALTER: Jeder Startzustand haengt an
   html.js:not(.rv-off). Die Klasse .rv-off schaltet damit die ganze
   Schicht ab, ohne dass irgendwo eine zweite Liste gepflegt werden
   muesste — und eine zweite Liste war es, die beim Bau zuerst
   unvollstaendig blieb. Gesetzt wird .rv-off von zwei Stellen:

     · von motion.js, wenn der Browser keinen IntersectionObserver hat
     · vom Zweizeiler im <head> jeder Seite, wenn motion.js nach zwei
       Sekunden nicht uebernommen hat (Datei fehlt, Netz weg, Fehler)

   Faellt also irgendetwas aus, steht die Seite spaetestens nach zwei
   Sekunden vollstaendig und unbewegt da.

   ⚠ DIE SELEKTORLISTE STEHT ZWEIMAL: einmal als --rv-sel, die
   motion.js ausliest, und einmal als echter Selektor — CSS kann
   keinen Selektor aus einer Variablen bilden. Beide stehen hier
   direkt untereinander. Wer eine aendert, aendert die andere mit.
   ================================================================== */

:root{
  --rv-dur:      .62s;   /* Text und Flaechen */
  --rv-dur-img:  .95s;   /* Bildwischer, bewusst traeger */
  --rv-step:     60ms;   /* Takt der Reihen — blueprint.md §8 */
  --rv-rise:     1.25rem;
  --rv-welle:    90ms;   /* Takt der ersten Welle, von motion.js gelesen */
  --ease-rv:     cubic-bezier(.22, .68, .24, 1);

  /* ---- Die Liste, die motion.js beobachtet (A + B + C) ---- */
  --rv-sel: ".band > .wrap > *:not(.chain, .steps, .qa, .rel, .proj, .duo, .schnitt, .fig, .doc-grid, .damage, .split, .lk__grid, .proof-grid, .rv__grid, .qual, .qual__marks, .qv-clients, .flow__track, .betrieb, .owner, .area, .ba, .tl, .svc3, .qc), .damage > *:not(.fig, .schnitt), .betrieb > *:not(.fig, .schnitt), .owner > *, .area > *, .split > .split__col, .doc > h2, .lk__head > *, .lk__foot, .rv__head, .rv__foot, .fq__panel, .card-shell, .notch-dia, .qv__top > *, .qual__head, .head-block > *, .ft-cta__main, .ft-steps, .chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc, .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients, .fq__list, .phead__facts, .foot-map, .spec, .meas, .fig, .schnitt";
}

/* ==================================================================
   DIE STARTSEITE LAEUFT LANGSAMER (Kay, 25.09.2026)
   Sie ist die einzige Seite, auf der die Bewegung selbst etwas
   bedeuten darf: Wer hier ankommt, sieht zuerst die Machart. Auf den
   Leistungsseiten will jemand etwas nachlesen, da waere dieselbe
   Dehnung Zeitverlust.

   Erkannt wird sie an ihrem Hero — die einzige Seite mit .hero, alle
   anderen tragen .phead. So bleibt die geteilte Datei ohne
   Sonderklasse im Markup.

   ⚠ Der 60-ms-Takt aus blueprint.md §8 gilt damit auf der Startseite
   nicht mehr; dort sind es 95 ms. Bewusste Abweichung, siehe notes.md.
   ================================================================== */
html:has(.hero){
  --rv-dur:      .95s;
  --rv-dur-img:  1.45s;
  --rv-step:     95ms;
  --rv-rise:     2.25rem;
  --rv-welle:    140ms;
}

@media (prefers-reduced-motion: no-preference){

/* ==================================================================
   A · EINZELNE AUFSTEIGEN
   Alles, was als geschlossener Block gelesen wird: Abschnittskopf,
   Fliesstextspalte, Hinweisfeld, Datentafel, Fusszeile.
   ================================================================== */
html.js:not(.rv-off) :is(
  .band > .wrap > *:not(.chain, .steps, .qa, .rel, .proj, .duo, .schnitt, .fig, .doc-grid, .damage, .split, .lk__grid, .proof-grid, .rv__grid, .qual, .qual__marks, .qv-clients, .flow__track, .betrieb, .owner, .area, .ba, .tl, .svc3, .qc),
  .damage > *:not(.fig, .schnitt),
  .betrieb > *:not(.fig, .schnitt),
  .owner > *, .area > *,
  .split > .split__col,
  .doc > h2,
  .lk__head > *, .lk__foot,
  .rv__head, .rv__foot,
  .fq__panel,
  .card-shell, .notch-dia,
  .qv__top > *, .qual__head,
  .head-block > *,
  .ft-cta__main, .ft-steps
){
  transition: opacity var(--rv-dur) var(--ease-rv), translate var(--rv-dur) var(--ease-rv);
}
/* Der Startzustand haengt an :not(.rv-on) und nicht an einer eigenen
   Sichtbar-Regel. Sonst muesste die gegen die Spezifitaet dieser Liste
   ankommen — sie verliert, und der Abschnitt bliebe durchsichtig
   stehen, obwohl er die Klasse traegt. */
html.js:not(.rv-off) :is(
  .band > .wrap > *:not(.chain, .steps, .qa, .rel, .proj, .duo, .schnitt, .fig, .doc-grid, .damage, .split, .lk__grid, .proof-grid, .rv__grid, .qual, .qual__marks, .qv-clients, .flow__track, .betrieb, .owner, .area, .ba, .tl, .svc3, .qc),
  .damage > *:not(.fig, .schnitt),
  .betrieb > *:not(.fig, .schnitt),
  .owner > *, .area > *,
  .split > .split__col,
  .doc > h2,
  .lk__head > *, .lk__foot,
  .rv__head, .rv__foot,
  .fq__panel,
  .card-shell, .notch-dia,
  .qv__top > *, .qual__head,
  .head-block > *,
  .ft-cta__main, .ft-steps
):not(.rv-on){
  opacity: 0;
  translate: 0 var(--rv-rise);
}

/* ==================================================================
   B · REIHEN BAUEN SICH GLIED FUER GLIED
   Beobachtet wird der Behaelter, gestaffelt werden seine Kinder.
   Deshalb steht der Behaelter selbst nicht in Gruppe A.
   ================================================================== */
html.js:not(.rv-off) :is(
  .chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc,
  .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients,
  .fq__list, .phead__facts, .foot-map
) > *{
  transition: opacity var(--rv-dur) var(--ease-rv), translate var(--rv-dur) var(--ease-rv);
}
html.js:not(.rv-off) :is(
  .chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc,
  .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients,
  .fq__list, .phead__facts, .foot-map
):not(.rv-on) > *{
  opacity: 0;
  translate: 0 var(--rv-rise);
}

/* Die Datentafel staffelt Zeilen, nicht Kinder */
html.js:not(.rv-off) .spec tr{ transition: opacity .45s var(--ease-rv), translate .45s var(--ease-rv); }
html.js:not(.rv-off) .spec:not(.rv-on) tr{ opacity: 0; translate: 0 .5rem; }

/* Der Takt. Sieben Stufen, danach laeuft alles gemeinsam — eine Reihe
   mit zwoelf Gliedern soll nicht zwoelf mal 60 ms brauchen.

   Die Verzoegerung steht als --rv-d am Glied und nicht direkt als
   transition-delay. Grund: eigene Eigenschaften werden vererbt, ein
   transition-delay nicht. So laeuft die wachsende Linie im ::before
   im selben Takt wie das Glied, ohne dass diese Liste ein zweites
   Mal getippt werden muss. */
html.js:not(.rv-off) :is(.chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc, .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients, .fq__list, .phead__facts, .foot-map, .duo, .meas) > *,
html.js:not(.rv-off) .spec tr{ transition-delay: var(--rv-d, 0ms); }

html.js:not(.rv-off) :is(.chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc, .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients, .fq__list, .phead__facts, .foot-map, .duo, .meas) > *:nth-child(2),
html.js:not(.rv-off) .spec tr:nth-child(2){ --rv-d: calc(var(--rv-step) * 1); }
html.js:not(.rv-off) :is(.chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc, .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients, .fq__list, .phead__facts, .foot-map, .duo, .meas) > *:nth-child(3),
html.js:not(.rv-off) .spec tr:nth-child(3){ --rv-d: calc(var(--rv-step) * 2); }
html.js:not(.rv-off) :is(.chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc, .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients, .fq__list, .phead__facts, .foot-map, .duo, .meas) > *:nth-child(4),
html.js:not(.rv-off) .spec tr:nth-child(4){ --rv-d: calc(var(--rv-step) * 3); }
html.js:not(.rv-off) :is(.chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc, .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients, .fq__list, .phead__facts, .foot-map, .duo, .meas) > *:nth-child(5),
html.js:not(.rv-off) .spec tr:nth-child(5){ --rv-d: calc(var(--rv-step) * 4); }
html.js:not(.rv-off) :is(.chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc, .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients, .fq__list, .phead__facts, .foot-map, .duo, .meas) > *:nth-child(6),
html.js:not(.rv-off) .spec tr:nth-child(6){ --rv-d: calc(var(--rv-step) * 5); }
html.js:not(.rv-off) :is(.chain, .steps, .qa, .rel, .proj, .tl, .svc3, .qc, .lk__grid, .proof-grid, .rv__grid, .qual__marks, .qv-clients, .fq__list, .phead__facts, .foot-map, .duo, .meas) > *:nth-child(n+7),
html.js:not(.rv-off) .spec tr:nth-child(n+7){ --rv-d: calc(var(--rv-step) * 6); }

/* ==================================================================
   B2 · DIE LINIE WAECHST
   blueprint.md §8: "Masskette: Endmarken zuerst, Linie waechst
   dazwischen." Zwei Stellen tragen so eine Linie — die Glieder der
   Ablaufkette und die Kennzahlen in den Belegkarten. In beiden Faellen
   uebernimmt ein Pseudo-Element die Linie, die echte Rahmenlinie wird
   dafuer durchsichtig gestellt. Faellt das Skript aus, sorgt das
   Sicherheitsnetz weiter unten dafuer, dass sie zurueckkommt.
   ================================================================== */
html.js:not(.rv-off) :is(.chain > li, .meas > div){ position: relative; border-top-color: transparent; }
html.js:not(.rv-off) :is(.chain > li, .meas > div)::before{
  content: ''; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: var(--blau); transform-origin: left center;
  transition: scale .55s var(--ease-rv) var(--rv-d, 0ms);
}
html.js:not(.rv-off) :is(.chain, .meas):not(.rv-on) > *::before{ scale: 0 1; }
html.js:not(.rv-off) .meas > div::before{ height: 1px; background: var(--line); }
html.js:not(.rv-off) .band--navy .chain > li::before{ background: var(--blau-soft); }

/* ==================================================================
   C · BILDER WISCHEN AUF
   Der Wischer laeuft von unten nach oben, das Bild setzt sich aus
   1.05 in seine Groesse. Bildunterschrift und Legende des Schnitts
   folgen danach — erst die Zeichnung, dann ihre Beschriftung.
   ================================================================== */
html.js:not(.rv-off) :is(.fig, .schnitt) img{
  transition: clip-path var(--rv-dur-img) var(--ease-rv),
              scale calc(var(--rv-dur-img) + .3s) var(--ease-rv);
}
html.js:not(.rv-off) :is(.fig, .schnitt):not(.rv-on) img{ clip-path: inset(0 0 100% 0); scale: 1.05; }

html.js:not(.rv-off) :is(.fig, .schnitt) :is(figcaption, .schnitt__liste, .fig--leer, .schnitt__leer){
  transition: opacity var(--rv-dur) var(--ease-rv) .22s, translate var(--rv-dur) var(--ease-rv) .22s;
}
html.js:not(.rv-off) :is(.fig, .schnitt):not(.rv-on) :is(figcaption, .schnitt__liste, .fig--leer, .schnitt__leer){
  opacity: 0;
  translate: 0 .625rem;
}

/* Eine Figur ohne Bild — der sichtbare Platzhalter, solange das Motiv
   fehlt (bildkonzept.md). Hier gibt es nichts zu wischen, also steigt
   die Figur selbst auf wie ein Textblock. */
html.js:not(.rv-off) :is(.fig, .schnitt):not(:has(img)){
  transition: opacity var(--rv-dur) var(--ease-rv), translate var(--rv-dur) var(--ease-rv);
}
html.js:not(.rv-off) :is(.fig, .schnitt):not(:has(img)):not(.rv-on){ opacity: 0; translate: 0 var(--rv-rise); }

/* Bildpaar und -trio unter den Arbeitsschritten: derselbe Takt wie
   eine Reihe, nur dass jedes Glied ein Bild ist. */
html.js:not(.rv-off) .duo > .fig:nth-child(2) img{ transition-delay: calc(var(--rv-step) * 2); }
html.js:not(.rv-off) .duo > .fig:nth-child(3) img{ transition-delay: calc(var(--rv-step) * 4); }

/* ==================================================================
   SEITENKOPF — das Hintergrundfoto setzt sich
   Der Text im Kopf steigt bereits (site.css, @keyframes rise). Hier
   kommt nur die Flaeche dahinter dazu, langsam genug, dass der Kopf
   nicht als Effekt gelesen wird.
   ================================================================== */
html.js:not(.rv-off) .phead--foto::before{ animation: phead-settle 1.6s var(--ease-rv) both; }
@keyframes phead-settle{ from{ scale: 1.06; } to{ scale: 1; } }

/* ==================================================================
   HOVER — Bild ja, Karte nein
   blueprint.md §8: "Karten-Hover: nur Rahmenfarbe, 120 ms — keine
   Hebung, keine Skalierung." Bewegt wird deshalb ausschliesslich das
   Bild im Ausschnitt der Karte.
   ================================================================== */
.proj-card img,
.card-fig img,
.ref-card__img img{ transition: scale .5s var(--ease-rv); }
a.proj-card:hover img,
a.proj-card:focus-visible img{ scale: 1.045; }

/* Pfeile in Verweisen laufen an — dieselbe Geste wie auf den Knoepfen */
.rel-card:hover .ic,
.rel-card:focus-visible .ic,
.lk-go:hover .ic,
.ref-go:hover .ic{ transform: translateX(.25rem); }

/* ==================================================================
   BILDKARTEN DER STARTSEITE (Kay, 25.09.2026: "bisschen mehr
   animation, bau was ein best practise bisschen fancy")

   Gemeint sind die beiden Kartenbaender, die ein Foto als Grund
   tragen und auf eine Seite fuehren: die drei Leistungskarten (.lk-card)
   und die vier Projektkarten (.ref-card). Beide sind gleich gebaut —
   Bild absolut auf z-index -2, Verlauf im ::before auf -1, Text und
   Kreispfeil darueber.

   Drei Dinge zugleich, und jedes hat einen Grund:

     1. ZOOM auf 1,065 in 850 ms. Langsam genug, dass man die Bewegung
        sieht und nicht nur ihr Ergebnis.
     2. SATTER UND EINE SPUR DUNKLER statt heller. Der naheliegende
        Weg waere, das Motiv beim Zeigen aufzuhellen — genau der wuerde
        hier Schaden anrichten (siehe Kontrast weiter unten).
     3. DER KREISPFEIL fuellt sich blau und waechst leicht.

   ⚠ KEIN LICHTSTREIF MEHR: Bis zum 25.09.2026 lief hier zusaetzlich
   ein Schimmer schraeg ueber die Projektkarten. Auf Wunsch von Kay
   wieder entfernt. Was davon bleibt, steht in notes.md — unter anderem
   der Grund, warum er auf den Leistungskarten ohnehin nie laufen
   durfte: Deren Titel steht auf 2,57 : 1, und der Streif drueckte ihn
   weiter.

   ⚠ Abweichung von blueprint.md §8, bewusst und auf die Startseite
   begrenzt: Dort steht "Karten-Hover: nur Rahmenfarbe". Die KARTE
   bewegt sich weiterhin nicht, sie hebt und skaliert nicht. Bewegt
   wird, was in ihr liegt.
   ================================================================== */
@media (hover: hover) and (pointer: fine){

  /* Das Praefix html:has(.hero) ist kein Schmuck: index.html traegt fuer
     den Kreispfeil eine Regel mit derselben Spezifitaet und steht als
     <style> NACH dieser Datei — ohne das Praefix gewinnt sie, und der
     Pfeil bliebe weiss statt blau. Gemessen, nicht vermutet. */
  html:has(.hero) :is(.ref-card, .lk-card) :is(img, .ref-card__img){
    transition: scale .85s var(--ease-rv), filter .5s var(--ease-rv);
  }
  html:has(.hero) :is(.ref-card, .lk-card):hover :is(img, .ref-card__img),
  html:has(.hero) :is(.ref-card, .lk-card):focus-within :is(img, .ref-card__img),
  html:has(.hero) .ref-card:focus-visible .ref-card__img{
    scale: 1.065;
    filter: saturate(1.12) brightness(.96);
  }

  /* Die Beschriftung rueckt nach, die Zeile darunter eine Spur spaeter.
     Kein Layoutwechsel: translate bewegt nichts, was neu gerechnet wird.

     ⚠ NUR auf der Projektkarte. Auf der Leistungskarte traegt
     `.lk-card__h a::after{ inset: 0 }` die Klickflaeche der ganzen
     Karte — ein Versatz von 3 px wuerde die Klickflaeche mitnehmen
     und unten einen toten Streifen lassen. Dort bleibt es deshalb bei
     Zoom, Licht und Pfeil. */
  html:has(.hero) :is(.ref-card__t, .ref-card__m){ transition: translate .5s var(--ease-rv); }
  html:has(.hero) .ref-card:hover .ref-card__t,
  html:has(.hero) .ref-card:focus-visible .ref-card__t{ translate: 0 -3px; }
  html:has(.hero) .ref-card:hover .ref-card__m,
  html:has(.hero) .ref-card:focus-visible .ref-card__m{ translate: 0 -3px; transition-delay: .05s; }

  /* Der Kreispfeil wird zum Ziel: gefuellt statt nur aufgehellt. */
  html:has(.hero) :is(.ref-go, .lk-go){ transition: background-color .3s var(--ease-rv), border-color .3s var(--ease-rv), scale .3s var(--ease-rv); }
  html:has(.hero) :is(.ref-card, .lk-card):hover :is(.ref-go, .lk-go),
  html:has(.hero) :is(.ref-card, .lk-card):focus-within :is(.ref-go, .lk-go){
    background: var(--blau); border-color: var(--blau); scale: 1.08;
  }

  /* Die Ueberlappungskarte traegt einen Knopf und ist selbst kein Link.
     Deshalb hier nur ein Anheben des Motivs, kein Licht, kein Pfeil. */
  html:has(.hero) .card-fig img{ transition: scale .85s var(--ease-rv); }
  html:has(.hero) .card:hover .card-fig img{ scale: 1.03; }
}

  14%{ opacity: 1; }
  80%{ opacity: 1; }
  to{ translate: -26% 0; opacity: 0; }
}

/* ==================================================================
   SEITENWECHSEL — 180-ms-Kreuzblende (blueprint.md §8)
   Nur dort, wo der Browser View Transitions kennt; sonst wechselt die
   Seite wie bisher.
   ================================================================== */
@view-transition{ navigation: auto; }
::view-transition-old(root),
::view-transition-new(root){ animation-duration: .18s; animation-timing-function: var(--ease-rv); }

} /* Ende prefers-reduced-motion: no-preference */

/* ==================================================================
   KEINE BEWEGUNG
   site.css setzt transition-duration und animation bereits global
   zurueck. Hier stehen die Eigenschaften, die dort nicht vorkommen —
   sonst bliebe ein Bild beschnitten stehen.
   ================================================================== */
@media (prefers-reduced-motion: reduce){
  :is(.fig, .schnitt) img{ clip-path: none; scale: 1; }
}
