/* ===========================================================================
   PHYSIOPARK — Bewegungsschicht (06.10.2026)

   Anlass, Julian: „zu statisch, nicht interaktiv, es macht keinen Spaß durch
   die Seite zu scrollen“, vor allem am Handy. Das Branding bleibt, wie es
   ist. Diese Datei bringt KEINE neue Form, nur Bewegung und Rückmeldung an
   den Bauteilen, die das Mockup (Brandkit 15.png) schon zeigt:
   Strich über der Überschrift, zweizeilige Überschrift, Pille mit Punkt,
   Kartenreihen, versetztes Fotopaar, Faktenkarte, Slider, Team-Umschalter.

   Lädt als letzte Datei. Ausbauen: eine Zeile in bauen.py (kopf_bauen).

     B0  Takt und Wege (Tokens)
     B1  Einblenden nach Rolle (data-bw, vergeben von bauen.py)
     B2  Bühne: Auftritt beim Laden, Tiefe beim Scrollen
     B3  Rückmeldung: Knöpfe, Karten, Pfeilkreise, Links
     B4  Kein klebendes Hover am Handy, Tippflächen
     B5  Team-Stimmen: Überblenden ohne Höhensprung
     B6  Handyleiste
     B7  Ziehen mit der Maus, Seitenwechsel, reduzierte Bewegung, Druck

   Grundsätze
   - Bewegt werden nur opacity, translate, scale und clip-path. Die
     Einzeleigenschaften translate/scale setzen sich mit vorhandenen
     transform-Regeln zusammen (Kantenpillen, Hover-Hebung) statt sie zu
     überschreiben.
   - Versteckt wird nur unter html.js.bw. Die Klasse setzt frueh.js im Kopf
     und nimmt sie nach 3 s selbst wieder weg, falls bewegung.js nicht lädt.
     Ohne Skript, bei reduzierter Bewegung und im Druck ist alles sichtbar.
   - Jede Bewegung läuft einmal und steht dann. Keine Schleifen.
   =========================================================================== */

/* ---------- B0  Takt und Wege ---------- */
:root {
  --bw-auftritt: 640ms;
  --bw-bild: 900ms;
  --bw-strich: 560ms;
  --bw-staffel: 70ms;
  --bw-weg: 22px;
  --bw-weg-quer: 28px;
  --kurve-feder: cubic-bezier(.34, 1.4, .64, 1);   /* nur Pille, Punkt, Pfeil */
  --kurve-rein: cubic-bezier(.4, 0, 1, 1);
  --bw-rand: 56px;                                   /* Schattenreserve der Bildmaske */
}
@media (max-width: 760px) {
  :root { --bw-auftritt: 520ms; --bw-bild: 720ms; --bw-strich: 460ms;
          --bw-staffel: 55ms; --bw-weg: 16px; --bw-weg-quer: 40px; }
}

/* Zeilen der Überschriften (bauen.py zeilen_teilen). Gilt immer, auch ohne
   Skript: Blockzeilen brechen genau wie die <br>, die sie ersetzen. */
.bw-z { display: block; }
.sub-strich { transform-origin: left center; }
.cta-band .sub-strich,
.schritte-kopf .sub-strich { transform-origin: center; }
@media (min-width: 761px) { .galerie-kopf .abschnitt-kopf .sub-strich { transform-origin: right center; } }
/* Zählende Zahl: feste Breite (bewegung.js W1b), Ziffern gleich breit.
   Linksbündig, damit ein Rest an Breite hinter der Zahl steht, nicht davor. */
span[data-zaehl]:not(.zahl) { font-variant-numeric: tabular-nums; display: inline-block; text-align: left; }

/* ---------- B1  Einblenden nach Rolle ----------
   Ausgangslage gilt nur bis .bw-da. Was ein Kind einer Gruppe ist und keine
   eigene Rolle hat, kommt als ganzer Block. Eine Gruppe, deren einziges
   Kind ein div ist (cta-band > .container), reicht die Rolle eine Ebene
   weiter, genau wie bewegung.js. */
.js.bw :is([data-bw="gruppe"] > :not([data-bw]):not(div:only-child), [data-bw="gruppe"] > div:only-child:not([data-bw]) > :not([data-bw])):not(.bw-da),
.js.bw [data-bw="karte"]:not(.bw-da),
.js.bw [data-bw="reihe"]:not(.bw-da) > *,
.js.bw [data-bw="fakten"]:not(.bw-da) > :not(.badge),
.js.bw [data-bw="kopf"]:not(.bw-da) > .bw-z,
.js.bw [data-bw="kopf"]:not(.bw-da):not(:has(> .bw-z)) {
  opacity: 0;
  translate: 0 var(--bw-weg);
}
.js.bw [data-bw="karte"]:not(.bw-da) { scale: .985; }
.js.bw [data-bw="strich"]:not(.bw-da) { scale: 0 1; }
.js.bw [data-bw="fakten"]:not(.bw-da) > .badge { opacity: 0; scale: .9; }
/* Wischreihe am Handy: die Karten blenden nur ein. Ein waagerechter
   Versatz der Karten liess das Einrasten (scroll-snap) springen, ein
   Versatz der ganzen Reihe machte die Seite breiter. Dass man wischen
   kann, zeigt stattdessen ein einmaliges Anrollen (bewegung.js W1c). */
@media (max-width: 760px) {
  .js.bw .wisch[data-bw="reihe"]:not(.bw-da) > * { translate: none; }
}
.bw-wink { scroll-snap-type: none !important; }

/* Fotopaar: Bild 1 wird von oben aufgedeckt, Bild 2 schiebt sich schräg nach.
   Bildenthüllung: negativer Rand der Maske, damit der Schatten stehen bleibt. */
.erwartet-bilder .bild-1 { --bw-bogen: 14px; }
.split-bild img { --bw-bogen: 16px; }
.js.bw [data-bw="paar"]:not(.bw-da) .bild-2 { opacity: 0; translate: var(--bw-weg-quer) var(--bw-weg-quer); }
/* Am Handy liegt Bild 2 am rechten Rand: ein Versatz nach rechts machte die
   Seite bis zum Einblenden 406 statt 390 px breit (Gegenprüfung 06.10.2026). */
@media (max-width: 760px) {
  .js.bw [data-bw="paar"]:not(.bw-da) .bild-2 { translate: 0 var(--bw-weg); }
}
.js.bw [data-bw="paar"]:not(.bw-da) .bild-1,
.js.bw [data-bw="bild"]:not(.bw-da) img {
  clip-path: inset(calc(-1 * var(--bw-rand)) calc(-1 * var(--bw-rand)) 100% calc(-1 * var(--bw-rand))
                   round calc(var(--bw-bogen, 16px) + var(--bw-rand)));
}
/* Endlage ausdrücklich: inset() lässt sich nicht nach none überblenden */
.js.bw [data-bw="paar"].bw-da:not(.bw-fertig) .bild-1,
.js.bw [data-bw="bild"].bw-da:not(.bw-fertig) img {
  clip-path: inset(calc(-1 * var(--bw-rand)) round calc(var(--bw-bogen, 16px) + var(--bw-rand)));
}

/* Übergänge nur bis .bw-fertig, danach gelten wieder die eigenen (Hover) */
.js.bw [data-bw]:not(.bw-fertig),
.js.bw :is([data-bw="gruppe"] > :not(div:only-child), [data-bw="gruppe"] > div:only-child:not([data-bw]) > *):not(.bw-fertig),
.js.bw :is([data-bw="reihe"], [data-bw="fakten"], [data-bw="kopf"]):not(.bw-fertig) > *,
.js.bw [data-bw]:not(.bw-fertig) :is(.bento-bild img, img.bild-1, img.bild-2, .split-bild img, img) {
  transition-property: opacity, translate, scale, clip-path;
  transition-duration: var(--bw-auftritt);
  transition-timing-function: var(--kurve-aus);
  transition-delay: calc(var(--bw-d, 0ms) + var(--bw-i, 0) * var(--bw-staffel));
}
.js.bw [data-bw="strich"]:not(.bw-fertig) { transition-duration: var(--bw-strich); }
.js.bw [data-bw="kopf"]:not(.bw-fertig) > .bw-z:nth-child(2) { --bw-i: 1; }
.js.bw [data-bw="kopf"]:not(.bw-fertig) > .bw-z:nth-child(3) { --bw-i: 2; }
.js.bw [data-bw="kopf"]:not(.bw-fertig) > .bw-z:nth-child(n+4) { --bw-i: 3; }
.js.bw :is([data-bw="bild"], [data-bw="paar"]):not(.bw-fertig) img { transition-duration: var(--bw-bild); }
.js.bw [data-bw="paar"]:not(.bw-fertig) .bild-2 { transition-delay: calc(var(--bw-d, 0ms) + 200ms); }
.js.bw [data-bw="fakten"]:not(.bw-fertig) > .badge { transition-timing-function: var(--kurve-feder); }
/* Oberhalb des Fensters (Sprung, Neuladen): sofort da, ohne Bewegung */
.js.bw .bw-sofort,
.js.bw .bw-sofort * { transition: none !important; }

/* Tafeln, die ein Umschalter oder Finder zeigt, blenden ein statt zu springen */
@media (prefers-reduced-motion: no-preference) {
  .tafel:not([hidden]),
  .fall:not([hidden]),
  .schritt.aktiv { animation: bw-tafel 380ms var(--kurve-aus) both; }
}
@keyframes bw-tafel { from { opacity: 0; translate: 0 10px; } }

/* ---------- B2  Bühne ----------
   Ersetzt die Keyframes aus schwung.css S1 (dort entfernt). Text kommt
   Zeile für Zeile, in höchstens 900 ms. Das Foto ist sofort da (es ist das
   größte Element beim Laden, eine Einblendung über opacity verzögert den
   Ladewert) und bewegt sich nicht (Bildzoom ist laut Mockup-Vokabular V7 tabu). */
@media (prefers-reduced-motion: no-preference) {
  .buehne-text > * { animation: bw-steigt 520ms var(--kurve-aus) both; }
  .buehne-text > .krume { animation-name: bw-blendet; animation-duration: 320ms; }
  .buehne-text > .badge { animation: bw-pille 460ms var(--kurve-feder) 40ms both; }
  .buehne-text > h1 { animation: none; }
  .buehne-text > h1 > .bw-z { animation: bw-steigt 520ms var(--kurve-aus) both; }
  .buehne-text > h1 > .bw-z:nth-child(1) { animation-delay: 80ms; }
  .buehne-text > h1 > .bw-z:nth-child(2) { animation-delay: 160ms; }
  .buehne-text > h1 > .bw-z:nth-child(3) { animation-delay: 240ms; }
  .buehne-text > h1 > .bw-z:nth-child(n+4) { animation-delay: 320ms; }
  .buehne-text > h1:not(:has(> .bw-z)) { animation: bw-steigt 520ms var(--kurve-aus) 80ms both; }
  .buehne-text > .subline { animation-delay: 330ms; }
  .buehne-text > .btn-reihe { animation-delay: 410ms; }
  .buehne-rahmen { animation: none; }
}
@keyframes bw-steigt  { from { opacity: 0; translate: 0 18px; } }
@keyframes bw-blendet { from { opacity: 0; } }
@keyframes bw-pille   { from { opacity: 0; scale: .9; } }

/* Tiefe: das Bühnenfoto bleibt beim Scrollen leicht zurück. Nur am Rechner,
   nur wo der Browser scrollgetriebene Animation kann, höchstens 20 px
   (Vokabular E5: 16 bis 24 px). overflow: clip statt hidden: hidden machte
   die Bühne zum Scrollcontainer, an den sich view() hängte, die Tiefe
   wirkte nirgends (Gegenprüfung 06.10.2026). */
.seite-hero, .seite-hero.buehne { overflow: clip; }
@supports (animation-timeline: view()) {
  @media (min-width: 961px) and (prefers-reduced-motion: no-preference) {
    .seite-hero.buehne .buehne-bild {
      animation: bw-tiefe linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 100%;
    }
    /* Das versetzte Fotopaar: Bild 2 läuft beim Scrollen gegen Bild 1 */
    .erwartet-bilder .bild-2 {
      animation: bw-gegen linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
  }
}
@keyframes bw-tiefe { to { transform: translateY(20px); } }
@keyframes bw-gegen { from { transform: translateY(10px); } to { transform: translateY(-10px); } }

/* ---------- B3  Rückmeldung ---------- */
:is(.btn, .pfeil-btn, .team-person, .weiche-karte, .weg-karte, .bento-karte,
    .aufklapp-knopf, .umschalter button, .wahl, .weiter, .preis-wahl label,
    .finder-kuerzel button, .footer-auftritte a, .bereichsleiste a) {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* Knöpfe: Hover hebt (style.css), Druck staucht spürbar */
.btn { transition: transform var(--zeit-tipp) var(--kurve-aus), scale 120ms var(--kurve-aus),
                   box-shadow var(--zeit-tipp) var(--kurve-aus), background var(--zeit-tipp) linear,
                   color var(--zeit-tipp) linear; }
.btn:active { scale: .965; transition-duration: 70ms; }
.btn svg { transition: translate var(--zeit-wechsel) var(--kurve-feder); }
@media (hover: hover) and (pointer: fine) {
  .btn:hover svg:last-child:not(:first-child) { translate: 3px 0; }
}

/* Karten, die selbst ein Link sind: heben sich wie die Stellenkarten der
   Karriereseite, Rahmen wird grün, beim Tippen geben sie nach. */
:is(a.weiche-karte, a.weg-karte, a.bento-karte) {
  transition: transform 220ms var(--kurve-aus), scale 120ms var(--kurve-aus),
              box-shadow 220ms var(--kurve-aus), border-color 220ms linear;
}
@media (hover: hover) and (pointer: fine) {
  :is(a.weiche-karte, a.weg-karte):hover { transform: translateY(-4px); box-shadow: var(--schatten-2); border-color: var(--gruen); }
  a.bento-karte:hover { border-color: var(--gruen); box-shadow: var(--schatten-2); }
}
:is(a.weiche-karte, a.weg-karte, a.bento-karte):active { scale: .98; border-color: var(--gruen); }

/* Nicht anklickbar: keine Hebung (sie behauptet einen Klick, den es nicht
   gibt), nur der Rahmen antwortet. */
:is(.leistung, .wert, .saeule, .standort-karte, .format):hover { transform: none; }
@media (hover: hover) and (pointer: fine) {
  :is(.leistung, .saeule, .standort-karte, .format, .zusage, .v-karte, .schritt-karte):hover { border-color: var(--gruen); }
}

/* Linie am Textlink wächst auch beim Tippen und mit der Tastatur */
.weiter::after { transition: width var(--zeit-wechsel) var(--kurve-aus); }
.weiter:is(:active, :focus-visible)::after,
.bento-karte:is(:active, :focus-visible) .mehr::after { width: 34px; }

/* Pfeilkreise: Druck staucht, Winkel stupst in die Richtung */
.pfeil-btn { transition: background var(--zeit-tipp) linear, transform var(--zeit-tipp) var(--kurve-aus),
                         scale 120ms var(--kurve-aus); }
.pfeil-btn:active:not([aria-disabled="true"]) { scale: .9; }
.pfeil-btn svg { transition: translate var(--zeit-wechsel) var(--kurve-feder); }
@media (hover: hover) and (pointer: fine) {
  .pfeil-btn[data-dir="1"]:hover:not([aria-disabled="true"]) svg  { translate: 3px 0; }
  .pfeil-btn[data-dir="-1"]:hover:not([aria-disabled="true"]) svg { translate: -3px 0; }
}

/* Team-Umschalter: Punkt springt mit Feder, Druck gibt nach */
.team-person .dot { transition: background var(--zeit-wechsel) linear, transform var(--zeit-wechsel) var(--kurve-feder); }
.team-person { transition: border-color var(--zeit-wechsel) linear, box-shadow var(--zeit-wechsel) var(--kurve-aus),
                           transform var(--zeit-wechsel) var(--kurve-aus), scale 120ms var(--kurve-aus); }
.team-person:active { scale: .98; }

/* Auswahl, Aufklapper, Umschalter: Druck gibt nach */
:is(.wahl, .umschalter button, .aufklapp-knopf, .preis-wahl label, .finder-kuerzel button):active { scale: .985; }
:is(.wahl, .umschalter button, .preis-wahl label, .finder-kuerzel button) { transition-property: border-color, box-shadow, transform, background, color, scale; }

/* Zahl, die sich ändert (Rechner, Auswahl-Zähler): kurzes Nicken */
.bw-nicken { animation: bw-nicken 260ms var(--kurve-feder); }
@keyframes bw-nicken { 40% { scale: 1.12; } }

/* ---------- B4  Handy: kein klebendes Hover, Tippflächen ----------
   iOS lässt :hover nach dem Tippen stehen. Eine Karte, die nach dem Tippen
   gehoben bleibt, sieht kaputt aus. */
@media (hover: none) {
  .umschalter.umschalter-liste button:hover { transform: none; }
  :is(.btn, .pfeil-btn, .team-person:not(.aktiv), .weiche-karte, .weg-karte, .bento-karte,
      .umschalter button, .wahl, .leistung, .wert, .saeule, .standort-karte, .format,
      .footer-nav a, .footer-auftritte a):hover { transform: none; }
  .btn-primary:hover { background: var(--orange-aktion); box-shadow: 0 6px 16px rgba(193, 80, 15, .28); }
  .btn-outline:hover { background: #fff; color: var(--orange-aktion); }
  .weiche-karte:hover .pfeil { transform: none; }
  .weiche-karte:active .pfeil { transform: translateX(5px); }
}
/* Tippflächen: Textlinks, Telefon und Mail waren 16 bis 21 px hoch. Die
   Fläche wächst unsichtbar über ein Pseudoelement, der Satz verschiebt sich
   nicht. ::after ist bei .weiter die wachsende Linie, deshalb ::before. */
.weiter { position: relative; }
.weiter::before { content: ""; position: absolute; inset: -12px -6px; }
:is(p, li, span, td, dd, address) > a:is([href^="tel:"], [href^="mailto:"]):not(.btn) { padding-block: 10px; }
:is(div > a:is([href^="tel:"], [href^="mailto:"]):not(.btn), .krume a, .finder-kuerzel button, .finder-leeren) { position: relative; }
:is(div > a:is([href^="tel:"], [href^="mailto:"]):not(.btn), .krume a)::before { content: ""; position: absolute; inset: -12px -4px; }
:is(.finder-kuerzel button, .finder-leeren)::before { content: ""; position: absolute; inset: -6px -3px; }
/* Fokusring im dunkelgrünen Schlussband: orange war dort kaum zu sehen */
.cta-band :focus-visible { outline-color: #fff; }
@media (max-width: 760px) {
  .btn { min-height: 44px; }
  /* Der aktive Eintrag rückte 6 px nach rechts und ragte in den Rand */
  .team-person.aktiv { transform: none; }
  .team-person { min-height: 44px; }
}

/* ---------- B5  Team-Stimmen ----------
   Alle Stimmen liegen in derselben Zelle, die Karte ist so hoch wie die
   längste. Der Wechsel blendet über; der Knopf darunter springt nicht mehr. */
html.js .zitat-karte { display: grid; grid-template-columns: minmax(0, 1fr); }
html.js .zitat-karte > :not(.stimme-inhalt) { grid-column: 1; }
html.js .zitat-karte > .stimme-inhalt { grid-row: 2; grid-column: 1; }
html.js .stimme-inhalt:not(.aktiv) { display: block; visibility: hidden; opacity: 0; translate: 0 8px; }
html.js .stimme-inhalt {
  transition: opacity 280ms var(--kurve-aus), translate 280ms var(--kurve-aus), visibility 0s linear 280ms;
}
html.js .stimme-inhalt.aktiv { transition-delay: 90ms, 90ms, 0s; }
.zitat-thema-wert { display: inline-block; }
.zitat-thema-wert.bw-nicken { animation-duration: 300ms; }

/* ---------- B6  Handyleiste ----------
   Wie .handy-leiste der Karriereseite (dort style.css, 07.09.2026): ein
   Knopf, fährt nach der Bühne herein, geht vor dem Schlussband und dem Fuß.
   Ohne Skript bleibt sie hidden. Am Rechner nie. */
.handy-leiste { display: none; }
@media (max-width: 760px) {
  .handy-leiste:not([hidden]) {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 90;
    display: flex;
    padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
    background: rgba(255, 255, 255, .97);
    border-top: 1px solid #E4EED2;
    box-shadow: 0 -6px 22px rgba(85, 86, 90, .14);
    translate: 0 120%;
    visibility: hidden;
    transition: translate 200ms var(--kurve-rein), visibility 0s linear 200ms;
  }
  .handy-leiste.ist-sichtbar { translate: 0 0; visibility: visible; transition: translate 320ms var(--kurve-aus), visibility 0s; }
  .handy-leiste-cta { flex: 1; justify-content: center; text-align: center; min-height: 46px; }
  .handy-leiste-cta svg { width: 17px; height: 17px; }
  /* Solange die Leiste steht, ist sie der untere Anker: der Knopf „nach
     oben“ tritt zurück. Über der Leiste schwebte er in der Textspalte und
     fing Taps ab (Gegenprüfung 06.10.2026). */
  html.hat-handy-leiste .nach-oben { opacity: 0; visibility: hidden; pointer-events: none; }
  /* Fokus und Ankersprünge halten über der Leiste */
  html.hat-handy-leiste { scroll-padding-bottom: 84px; }
}
@media print { .handy-leiste { display: none !important; } }

/* ---------- B7  Ziehen, Seitenwechsel, reduzierte Bewegung ---------- */
@media (hover: hover) and (pointer: fine) {
  .karten-slider, .galerie-slider { cursor: grab; }
}
.zieht { scroll-snap-type: none !important; scroll-behavior: auto !important; cursor: grabbing !important; user-select: none; }
.zieht img, .zieht a { pointer-events: none; }
/* Reihe passt ohne Blättern (bewegung.js W5b): Pfeile und Linie entfallen */
.ohne-blaettern .slider-pfeile, .ohne-blaettern .slider-balken { display: none; }

/* Weicher Wechsel zwischen den Seiten desselben Auftritts. Der Kopf bleibt
   stehen, der Inhalt blendet. Browser ohne die Fähigkeit wechseln wie bisher. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  .site-header { view-transition-name: kopf; }
  ::view-transition-old(root) { animation: bw-raus 160ms var(--kurve-rein) both; }
  ::view-transition-new(root) { animation: bw-rein 320ms var(--kurve-aus) both; }
}
@keyframes bw-raus { to { opacity: 0; } }
@keyframes bw-rein { from { opacity: 0; translate: 0 12px; } }

@media (prefers-reduced-motion: reduce) {
  .btn:active, .pfeil-btn:active, .team-person:active,
  :is(a.weiche-karte, a.weg-karte, a.bento-karte):active,
  :is(.wahl, .umschalter button, .aufklapp-knopf, .preis-wahl label, .finder-kuerzel button):active { scale: none; }
  html.js .stimme-inhalt { transition: none; }
  .handy-leiste:not([hidden]), .handy-leiste.ist-sichtbar { transition: none; }
  .bw-nicken { animation: none; }
}
@media print {
  [data-bw], [data-bw] *, .bw-z { opacity: 1 !important; translate: none !important;
    scale: none !important; clip-path: none !important; }
}
