/* ==========================================================================
   okapio – Motion (Besitzer: Agent MOTION)
   Regeln:
   - Bewegung über transform/opacity. Ausnahmen: stroke-dashoffset („Linie zeichnet sich"),
     kurze Farbübergänge bei Zuständen (color/background-color/border-color, ≤ 320 ms)
     und die Höhe der FAQ-Antworten (nur wo der Browser interpolate-size kann).
   - Alle Ausgangszustände stehen unter .js UND prefers-reduced-motion: no-preference.
     → Ohne JS oder bei reduzierter Bewegung sind alle Endzustände sofort sichtbar.
   - Jede Animation hat einen dokumentierten Zweck.
   - MOTION greift nur auf data-Attribute und SVG-IDs zu (siehe /docs/svg-manifest.md).
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

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

  /* ---------- Keyframes ---------- */
  @keyframes mo-fade   { from { opacity: 0; } to { opacity: 1; } }
  @keyframes mo-rise   { from { opacity: 0; transform: translateY(var(--reveal-distance)); } to { opacity: 1; transform: none; } }
  @keyframes mo-rise-s { from { opacity: 0; transform: translateY(var(--space-2)); } to { opacity: 1; transform: none; } }
  @keyframes mo-slide  { from { opacity: 0; transform: translateX(var(--space-3)); } to { opacity: 1; transform: none; } }
  @keyframes mo-pop    { from { opacity: 0; transform: scale(0.6); } to { opacity: 1; transform: none; } }
  @keyframes mo-grow   { from { transform: scaleY(0); } to { transform: scaleY(1); } }
  @keyframes mo-draw   { to { stroke-dashoffset: 0; } }
  @keyframes mo-gauge  { from { stroke-dashoffset: 0.4; } to { stroke-dashoffset: 0; } }
  @keyframes mo-dim    { to { opacity: 0.6; } }
  @keyframes mo-ring   { from { opacity: 0.9; transform: scale(1); } to { opacity: 0; transform: scale(1.8); } }
  @keyframes mo-pulse  { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.25; transform: scale(1.12); } }
  @keyframes mo-hero-in { from { opacity: 0; transform: translateY(0.45em); } to { opacity: 1; transform: none; } }
  @keyframes mo-hero-fade { from { opacity: 0; transform: translateY(var(--space-4)); } to { opacity: 1; transform: none; } }
  @keyframes mo-scan   { 0% { opacity: 0; transform: translateY(-100%); } 8% { opacity: 1; } 55% { opacity: 1; transform: translateY(260%); } 62%, 100% { opacity: 0; transform: translateY(260%); } }
  @keyframes mo-menu   { from { opacity: 0; transform: translateY(calc(var(--space-2) * -1)); } to { opacity: 1; transform: none; } }
  @keyframes mo-mark   { from { opacity: 0; transform: translateY(35%); } to { opacity: 1; transform: none; } }

  /* ---------- 1. Scroll-Reveal ----------
     Zweck: Inhalte erscheinen in Lesereihenfolge und geben dem Auge einen Einstiegspunkt.
     Lange, weiche Auslaufkurve: wirkt ruhig statt verspielt. */
  .js [data-reveal] {
    opacity: 0;
    transform: translateY(var(--reveal-distance));
    transition: opacity var(--dur-reveal) var(--ease-expo), transform var(--dur-reveal) var(--ease-expo), translate var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-slow) var(--ease-out);
  }
  .js [data-reveal].is-in { opacity: 1; transform: none; }
  .js [data-reveal-delay="1"] { transition-delay: calc(var(--stagger) * 1); }
  .js [data-reveal-delay="2"] { transition-delay: calc(var(--stagger) * 2); }
  .js [data-reveal-delay="3"] { transition-delay: calc(var(--stagger) * 3); }
  .js [data-reveal-delay="4"] { transition-delay: calc(var(--stagger) * 4); }
  .js [data-reveal-delay="5"] { transition-delay: calc(var(--stagger) * 5); }
  /* Nach dem Einblenden keine Verzögerung mehr für Hover-Übergänge */
  .js [data-reveal].is-done { transition-delay: 0s; }

  /* Kicker-Linie zeichnet sich, nachdem der Text steht. Zweck: führt den Blick zur Überschrift. */
  .js .kicker[data-reveal]::before { transform: scale(0); transition: transform var(--dur-slow) var(--ease-pop) 240ms; }
  .js .kicker[data-reveal].is-in::before { transform: none; }

  /* Hero: rein CSS, ohne auf JS/IntersectionObserver zu warten (schneller LCP).
     Zweck: Die Kernaussage baut sich Zeile für Zeile ruhig auf. */
  .js .hero [data-reveal] {
    opacity: 1; transform: none; transition: none;
    animation: mo-hero-fade var(--dur-reveal) var(--ease-expo) both;
  }
  .js .hero .hero__line[data-reveal] { animation-name: mo-hero-in; }
  .js .hero [data-reveal-delay="1"] { animation-delay: 120ms; }
  .js .hero [data-reveal-delay="2"] { animation-delay: 240ms; }
  .js .hero [data-reveal-delay="3"] { animation-delay: 420ms; }
  .js .hero [data-reveal-delay="4"] { animation-delay: 540ms; }
  .js .hero [data-reveal-delay="5"] { animation-delay: 660ms; }
  .js .hero [data-reveal-delay="6"] { animation-delay: 780ms; }
  /* „sichtbar": Verlauf schimmert ruhig durch. Zweck: der Blick landet auf der Kernaussage. */
  @keyframes mo-shimmer { to { background-position: -200% 0; } }
  .js .hero__highlight { animation: mo-shimmer 6s linear infinite; }

  /* ---------- 2. Header ----------
     Zweck: Beim Scrollen wird der Kopf kompakter, bekommt Milchglas und eine Haarlinie; die Fortschrittslinie wandert mit. */
  .js [data-header-bar] { transition: transform var(--dur-base) var(--ease-out), background-color var(--dur-base) var(--ease-out); }
  .js [data-header-bar]::after { transition: opacity var(--dur-base) var(--ease-out); }
  .js [data-header-inner],
  .js [data-progress] { transition: translate var(--dur-base) var(--ease-out); }
  .js [data-header].is-scrolled [data-header-bar] { transform: scaleY(0.79); }
  .js [data-header].is-scrolled [data-header-inner] { translate: 0 calc(var(--space-2) * -1); }
  .js [data-header].is-scrolled [data-progress] { translate: 0 calc(var(--header-h-shrunk) - var(--header-h)); }
  /* Mobiles Menü hängt an der geschrumpften Kopfzeile, sonst entsteht ein Spalt (inner ist bereits um space-2 verschoben). */
  .js .site-nav { transition: translate var(--dur-base) var(--ease-out); }
  @media (max-width: 63.99em) {
    .js [data-header].is-scrolled .site-nav { translate: 0 calc(var(--header-h-shrunk) - var(--header-h) + var(--space-2)); }
  }

  /* Menü (mobil): klappt weich auf. Zweck: Antwort auf die Geste statt Sprung. */
  .js .site-nav.is-open { animation: mo-menu var(--dur-base) var(--ease-expo) both; }
  @media (min-width: 64em) { .js .site-nav.is-open { animation: none; } }
  .nav-toggle__icon span { transition: transform var(--dur-base) var(--ease-expo); }

  /* ---------- 3. Illustrationen (gesteuert über data-Attribute in den SVGs) ----------
     Start: JS setzt .is-playing am [data-illustration]-Container, sobald er sichtbar ist.
     Zweck der Abfolge: erst Struktur (Kanten), dann Objekte (Knoten), dann Ereignis (Angriff/Finding/Prüfung). */

  /* Staffel: JS vergibt --i je Kind von [data-stagger]. Basis und Schritt pro Gruppe per ID. */
  .js [data-illustration] [data-stagger] > * { --d: calc(var(--base, 0s) + var(--i, 0) * var(--step, 90ms)); }

  /* Ausgangszustände */
  .js [data-illustration] :is([data-fade], [data-rise], [data-pop], [data-stagger="fade"] > *, [data-stagger="slide"] > *) { opacity: 0; }
  .js [data-illustration] [data-stagger="pulse"] > * { opacity: 0; transform-box: fill-box; transform-origin: center; }
  .js [data-illustration] :is([data-pop], [data-stagger="pulse"] > *) { transform-box: fill-box; transform-origin: center; }
  .js [data-illustration] [data-draw] { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .js [data-illustration] [data-stagger="grow"] > * { transform: scaleY(0); transform-box: fill-box; transform-origin: bottom; }

  /* Abläufe */
  .js [data-illustration].is-playing [data-draw] { animation: mo-draw var(--dur, var(--dur-grow)) var(--ease-in-out) var(--d, 0s) both; }
  .js [data-illustration].is-playing :is([data-fade], [data-stagger="fade"] > *) { animation: mo-fade var(--dur-el) var(--ease-out) var(--d, 0s) both; }
  .js [data-illustration].is-playing [data-rise] { animation: mo-rise-s var(--dur-pop) var(--ease-out) var(--d, 0s) both; }
  .js [data-illustration].is-playing [data-stagger="slide"] > * { animation: mo-slide var(--dur-pop) var(--ease-out) var(--d, 0s) both; }
  .js [data-illustration].is-playing [data-pop] { animation: mo-pop var(--dur-pop) var(--ease-pop) var(--d, 0s) both; }
  .js [data-illustration].is-playing [data-stagger="grow"] > * { animation: mo-grow var(--dur-grow) var(--ease-out) var(--d, 0s) both; transform-origin: bottom; }
  /* Puls: leichtes Atmen der Knoten-Ringe. Zweck: „das System lebt", ohne Dauer-Glow (kleine Amplitude, lange Periode). */
  .js [data-illustration].is-playing [data-stagger="pulse"] > * {
    animation: mo-fade var(--dur-el) var(--ease-out) var(--d, 0s) both,
               mo-pulse var(--dur-loop) var(--ease-in-out) calc(var(--d, 0s) + var(--dur-el)) infinite;
  }

  /* --- Hero-Graph: Kanten → Knoten → Angriffspfad → Schild blockiert --- */
  #hg-edges    { --base: 0.26s; --step: 136ms; }
  #hg-nodes    { --base: 0.59s; --step: 153ms; }
  #hg-node-rings { --base: 9.2s; --step: 765ms; }
  #hg-labels        { --d: 1.53s; }
  /* Angriffspfad zeichnet sich, bleibt kurz „heiß" und dimmt nach dem Block (kein Dauer-Glow). */
  .js [data-illustration].is-playing #hg-attack-seg { --dur: 4.2s; animation: mo-draw 4.2s var(--ease-in-out) 2.9s both, mo-dim var(--dur-slow) var(--ease-out) 9s forwards; }
  #hg-attack-head   { --d: 6.9s; }
  #hg-shield-body   { --d: 6.6s; }
  #hg-shield-check  { --d: 7.5s; --dur: var(--dur-pop); }
  /* Aufprall: Ring expandiert einmal am Schild. Zweck: macht den Moment des Blockierens lesbar. */
  .js [data-illustration] #hg-shield-ring { transform-box: fill-box; transform-origin: center; }
  .js [data-illustration].is-playing #hg-shield-ring { animation: mo-ring var(--dur-grow) var(--ease-out) 7.1s both; }
  #hg-blocked-tag   { --d: 8s; }

  /* --- Roadmap: Route zeichnet sich, Meilensteine rasten ein, verworfener Zweig blasst ein --- */
  #rm-route       { --d: 0.34s; --dur: var(--dur-draw); }
  #rm-milestones  { --base: 0.68s; --step: 0.48s; }
  #rm-branch-rejected { --d: 2.04s; }
  #rm-rejected-mark   { --d: 2.89s; }
  #rm-labels          { --d: 2.55s; }

  /* --- Topologie: Verbindungen von oben nach unten, Geräte, dann die gesperrte Querverbindung --- */
  #tp-zones   { --base: 0s;   --step: 204ms; }
  #tp-links   { --base: 0.51s; --step: 340ms; --dur: var(--dur-el); }
  #tp-devices { --base: 0.85s; --step: 187ms; }
  #tp-labels  { --d: 2.72s; }
  #tp-blocked-path { --d: 3.4s; }
  #tp-blocked-mark { --d: 4.08s; }

  /* --- Cloud-Graph: Kanten, Dienste, Findings-Marker, Liste --- */
  #cg-edges { --base: 0.17s; --step: 204ms; --dur: var(--dur-slow); }
  #cg-nodes { --base: 0.51s; --step: 204ms; }
  #cg-findings-markers { --base: 1.87s; --step: 425ms; }
  #cg-panel { --base: 2.38s; --step: 272ms; }
  /* Findings pulsieren einmalig leicht, damit der Blick zu den Prioritäten wandert. */
  .js [data-illustration] :is(#cg-mk-1, #cg-mk-2) .mk-ring { transform-box: fill-box; transform-origin: center; }
  .js [data-illustration].is-playing :is(#cg-mk-1, #cg-mk-2) .mk-ring { animation: mo-ring var(--dur-draw-s) var(--ease-out) 3.06s 2 both; }

  /* --- Audit: Prüfpfad zeichnet sich, Zeilen laufen ein, Häkchen, Statusring füllt sich --- */
  #au-checklist { --d: 0s; }
  #au-audit-path { --d: 0.51s; --dur: var(--dur-draw-m); }
  #au-rows { --base: 0.51s; --step: 272ms; }
  #au-status-icons { --base: 0.68s; --step: 272ms; }
  #au-st-1-check { --d: 1.87s; --dur: var(--dur-check); }
  #au-st-2-check { --d: 2.21s; --dur: var(--dur-check); }
  .js [data-illustration] #au-gauge-done { stroke-dashoffset: 0.4; }
  .js [data-illustration].is-playing #au-gauge-done { animation: mo-gauge var(--dur-draw-s) var(--ease-out) 1.36s both; }
  #au-gauge-review { --d: 2.72s; }
  #au-legend { --d: 3.23s; }

  /* --- Awareness: Netz, Phishing-Mail läuft ein, Person erkennt, Meldung geht an die Meldestelle --- */
  #aw-network-edges { --base: 0.17s; --step: 170ms; --dur: var(--dur-slow); }
  #aw-people { --base: 0.51s; --step: 187ms; }
  #aw-phish-mail { --d: 1.53s; }
  #aw-phish-path-1 { --d: 2.38s; }
  #aw-phish-path-2 { --d: 2.55s; }
  #aw-phish-path-main { --d: 2.21s; --dur: var(--dur-grow); }
  /* Erkennung: zwei Signal-Ringe gehen von der Person aus. Zweck: „Aufmerksamkeit" sichtbar machen. */
  .js [data-illustration] :is(#aw-signal-ring-1, #aw-signal-ring-2) { transform-box: fill-box; transform-origin: center; }
  .js [data-illustration].is-playing #aw-signal-ring-1 { animation: mo-ring var(--dur-draw-s) var(--ease-out) 3.91s both; }
  .js [data-illustration].is-playing #aw-signal-ring-2 { animation: mo-ring var(--dur-draw-s) var(--ease-out) 4.5s both; }
  #aw-recognized-badge { --d: 4.08s; }
  #aw-recognized-check { --d: 4.25s; --dur: var(--dur-check); }
  #aw-report-path { --d: 4.93s; --dur: var(--dur-draw-s); }
  #aw-report-node { --d: 6.29s; }
  #aw-labels { --d: 2.04s; }

  /* --- KI-Ablauf: Kanten, Stationen, Rückfrage-Schleife, menschliche Freigabe --- */
  #ki-edges { --base: 0.5s; --step: 0.6s; --dur: var(--dur-draw-s); }
  #ki-nodes { --base: 0.2s; --step: 0.55s; }
  #ki-approve-badge { --d: 3.6s; }
  #ki-approve-check { --d: 3.8s; --dur: var(--dur-check); }
  #ki-labels { --d: 2.2s; }

  /* --- Regionskarte: Höhenlinien, Fluss, Einzugsgebiet, Wege von Böblingen, Orte --- */
  #rg-contours { --base: 0s; --step: 255ms; }
  #rg-river { --d: 0.68s; --dur: var(--dur-draw-l); }
  #rg-service-area { --d: 1.53s; }
  #rg-routes { --base: 2.04s; --step: 204ms; }
  #rg-towns { --base: 2.21s; --step: 204ms; }
  #rg-labels { --d: 3.06s; }
  /* Standort-Puls: ruhig, mit langer Periode. Zweck: markiert „hier sitzen wir". */
  .js [data-illustration] #rg-pin-pulse { transform-box: fill-box; transform-origin: center; }
  .js [data-illustration].is-playing #rg-pin-pulse { animation: mo-pulse var(--dur-loop) var(--ease-in-out) 3.4s infinite; }

  /* --- Risiko-Matrix: Raster, Risiken, kritische Punkte poppen, Linie zur Liste, Reihenfolge --- */
  #rc-grid    { --d: 0s; }
  #rc-cells   { --d: 0.3s; }
  #rc-lines   { --d: 0.25s; }
  #rc-dots    { --base: 0.7s; --step: 130ms; }
  #rc-risk-1  { --d: 1.7s; }
  #rc-risk-2  { --d: 1.95s; }
  #rc-link    { --d: 2.3s; --dur: var(--dur-el); }
  #rc-list    { --base: 2.75s; --step: 220ms; }
  #rc-labels  { --d: 1.2s; }
  .js [data-illustration] :is(#rc-risk-1, #rc-risk-2) { transform-box: fill-box; transform-origin: center; }

  /* --- Dauerbewegung: Datenpakete laufen entlang der Linien, Risiko- und Schutzpunkte senden Impulse.
     Zweck: Die Grafiken zeigen laufende Systeme, nicht nur einen einmaligen Aufbau. Start nach dem Aufbau. */
  .js [data-illustration].is-playing .flow { display: inline; animation: mo-fade var(--dur-slow) var(--ease-out) var(--flow-d, 2.5s) both; }
  #hg-flow { --flow-d: 1.6s; }
  #hg-flow-attack { --flow-d: 8.6s; }
  #rm-flow { --flow-d: 2.6s; }
  #tp-flow { --flow-d: 2.2s; }
  #cg-flow { --flow-d: 2.6s; }
  #au-flow { --flow-d: 2.4s; }
  #aw-flow { --flow-d: 5.2s; }
  #ki-flow { --flow-d: 3.6s; }
  #rc-flow { --flow-d: 2.6s; }
  #rg-flow { --flow-d: 3.2s; }
  /* Gestrichelte Wege „fließen" dauerhaft (verworfener Zweig, gesperrte Verbindung, Phishing-Wege). */
  @keyframes mo-march { to { stroke-dashoffset: -22; } }
  .js [data-illustration].is-playing :is(#rm-branch-rejected, #tp-blocked-path, #aw-phish-path-1, #aw-phish-path-2) {
    animation: mo-fade var(--dur-el) var(--ease-out) var(--d, 0s) both, mo-march 1.4s linear var(--d, 0s) infinite;
  }


  /* ---------- 4. Tiefe ----------
     Parallax (rAF in main.js setzt --py): Streifenmuster bewegt sich langsamer als der Inhalt.
     Neigung (main.js setzt --tx/--ty im Hero): der Graph folgt dem Zeiger um wenige Pixel. */
  .js [data-parallax] { transform: translate3d(0, var(--py, 0), 0); will-change: transform; }
  .js [data-tilt] > svg { transform: translate3d(var(--tx, 0), var(--ty, 0), 0); transition: transform var(--dur-reveal) var(--ease-expo), opacity 450ms var(--ease-out); }

  /* Neustart der Illustrations-Schleife (main.js): kurzes Aus- und Einblenden statt Sprung. */
  .js [data-illustration]:not([data-tilt]) > svg,
  .js .tool__panel[data-illustration] > :not(.tool__panel-head):not(.tool__scan) { transition: opacity 450ms var(--ease-out); }
  .js [data-illustration].is-resetting > svg,
  .js .tool__panel[data-illustration].is-resetting > :not(.tool__panel-head):not(.tool__scan) { opacity: 0; }

  /* Scroll-gebunden (nur wo der Browser Scroll-Timelines kann; sonst statisch).
     Zweck: Hero-Grafik tritt beim Weiterscrollen zurück, Regionskarte gleitet leicht gegen den Text. */
  @supports (animation-timeline: view()) {
    @keyframes mo-recede { to { opacity: 0.25; transform: translateY(6%) scale(0.96); } }
    @keyframes mo-drift  { from { transform: translateY(var(--space-6)); } to { transform: translateY(calc(var(--space-6) * -1)); } }
    .js .hero__figure { animation: mo-recede linear both; animation-timeline: view(); animation-range: exit 0% exit 90%; }
    @media (min-width: 64em) {
      .js .region__map { animation: mo-drift linear both; animation-timeline: view(); animation-range: cover 0% cover 100%; }
    }
  }

  /* ---------- 4b. Leistungs-Kacheln ----------
     Einblenden: Karte hebt sich leicht skaliert ins Bild, danach laufen Kopf, Text und Prüfpunkte gestaffelt ein.
     Dauerhaft: eine Akzentlinie läuft in ruhigen Abständen über die Oberkante (je Karte versetzt).
     Zweck: Die Kacheln wirken als lebendige Arbeitsbereiche, ohne vom Text abzulenken. */
  @keyframes mo-sweep { 0% { background-position: -60% 0; } 30%, 100% { background-position: 160% 0; } }
  .js .bento .service[data-reveal] { transform: translateY(var(--reveal-distance)) scale(0.97); }
  .js .bento .service[data-reveal].is-in { transform: none; }
  .js .bento .service[data-reveal] :is(.service__head, .service__body > p, .checks li, .link-arrow) { opacity: 0; transform: translateY(var(--space-3)); transition: opacity var(--dur-slow) var(--ease-expo), transform var(--dur-slow) var(--ease-expo); }
  .js .bento .service[data-reveal].is-in :is(.service__head, .service__body > p, .checks li, .link-arrow) { opacity: 1; transform: none; }
  .js .bento .service[data-reveal].is-in .service__head { transition-delay: 150ms; }
  .js .bento .service[data-reveal].is-in .service__body > p { transition-delay: 250ms; }
  .js .bento .service[data-reveal].is-in .checks li:nth-child(1) { transition-delay: 340ms; }
  .js .bento .service[data-reveal].is-in .checks li:nth-child(2) { transition-delay: 410ms; }
  .js .bento .service[data-reveal].is-in .checks li:nth-child(3) { transition-delay: 480ms; }
  .js .bento .service[data-reveal].is-in .checks li:nth-child(4) { transition-delay: 550ms; }
  .js .bento .service[data-reveal].is-in .checks li:nth-child(5) { transition-delay: 620ms; }
  .js .bento .service[data-reveal].is-in .link-arrow { transition-delay: 700ms; }
  .js .bento .service {
    background-image: linear-gradient(90deg, transparent, var(--c-accent), transparent);
    background-repeat: no-repeat;
    background-size: 40% 2px;
    background-position: -60% 0;
  }
  .js .bento .service.is-in { animation: mo-sweep 7s var(--ease-in-out) 1.2s infinite; }
  .js .bento .service:nth-child(2).is-in { animation-delay: 2.2s; }
  .js .bento .service:nth-child(3).is-in { animation-delay: 3.2s; }
  .js .bento .service:nth-child(4).is-in { animation-delay: 4.2s; }
  .js .bento .service:nth-child(5).is-in { animation-delay: 5.2s; }
  .js .bento .service:nth-child(6).is-in { animation-delay: 6.2s; }
  .js .bento .service:nth-child(7).is-in { animation-delay: 7.2s; }
  /* Nummer „01 /07" leuchtet beim Überfahren der Linie nicht; beim Hover der Karte füllt sie sich. */
  .service__no { transition: background-color var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out); }
  @media (hover: hover) {
    .service:hover .service__no { background: var(--c-accent); color: var(--c-btn-text); }
    .service:hover .service__no .service__total { color: inherit; }
  }

  /* ---------- 5. Vorgehen ---------- */

  /* Timeline: Linie füllt sich mit dem Scrollen; aktive Schritte rasten ein. Zweck: zeigt, wo man im Prozess steht. */
  .js [data-timeline] { --progress: 0; }
  .js .timeline__step { transition: border-color var(--dur-base) var(--ease-out), opacity var(--dur-reveal) var(--ease-expo), transform var(--dur-reveal) var(--ease-expo); }
  .js .timeline__step:not(.is-active) .timeline__num { background: var(--c-surface); color: var(--c-text); border-color: var(--c-line-strong); }
  .js .timeline__step.is-active .timeline__num { animation: mo-pop var(--dur-check) var(--ease-pop) both; }
  .js .timeline__step.is-current { border-color: var(--c-accent); }
  .js [data-step-fill] { transition: transform var(--dur-slow) var(--ease-expo); }
  .js [data-step-now] { transition: opacity var(--dur-fast) linear; }
  .js [data-step-now].is-changing { opacity: 0; }

  /* ---------- 6. Hover (nur echte Zeiger; „translate" statt „transform", damit Reveal nicht kollidiert) ---------- */
  @media (hover: hover) {
    /* Buttons: Fläche wischt von links herein, beim Verlassen nach rechts hinaus. Zweck: klare, ruhige Rückmeldung. */
    .btn { transition: translate var(--dur-fast) var(--ease-out), color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out); }
    .btn::before { transition: transform var(--dur-slow) var(--ease-expo); }
    .btn:hover::before { transform: scaleX(1); transform-origin: left; }
    .btn--primary:hover { background: var(--c-btn-bg); }
    .btn--ghost:hover { background: transparent; }
    .btn:hover { translate: 0 var(--lift-sm); }
    .btn:active { translate: 0 0; }
    .btn--arrow::after { transition: translate var(--dur-base) var(--ease-expo); }
    .btn--arrow:hover::after { translate: var(--space-1) 0; }

    .link-arrow::after { transition: translate var(--dur-base) var(--ease-expo), background-color var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out); }
    .link-arrow:hover::after { translate: var(--space-1) 0; }

    /* Navigation: Unterstrich wächst von links. */
    .site-nav > ul > li > a::after, .nav-group__btn::after { transition: transform var(--dur-base) var(--ease-expo); }
    .site-nav a, .nav-group__btn { transition: color var(--dur-fast) linear; }

    /* Karten heben sich leicht, Lichtkegel folgt dem Zeiger. Zweck: zeigt, dass die Karte ein zusammenhängendes Angebot ist. */
    .service, .tool, .fact, .direct { transition: translate var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out); }
    .service:hover, .tool:hover { translate: 0 var(--lift); }
    /* Glow-Rahmen beim Hover: Kante in Logo-Blau, weiches Leuchten darunter */
    .service, .tool, .fact, .timeline__step, .direct { transition: translate var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-slow) var(--ease-out); }
    :is(.service, .tool, .fact, .timeline__step, .direct):hover {
      border-color: color-mix(in srgb, var(--c-accent) 55%, transparent);
      box-shadow: 0 0 0 1px color-mix(in srgb, var(--c-accent) 30%, transparent), 0 24px 60px -28px color-mix(in srgb, var(--c-accent) 55%, transparent);
    }
    .btn--primary:hover { box-shadow: 0 14px 38px -12px color-mix(in srgb, var(--blue-500) 90%, transparent), inset 0 1px 0 rgb(255 255 255 / 0.22); }
    .tool:hover, .service--2:hover, .service--5:hover { border-color: var(--c-line-strong); }
    [data-spot]::before { transition: opacity var(--dur-slow) var(--ease-out); }
    [data-spot]:hover::before { opacity: 1; }
    .service__art svg { transition: transform var(--dur-slow) var(--ease-expo); }
    .service:hover .service__art svg { transform: scale(1.02); }
    .direct:hover { translate: var(--space-1) 0; }
    .timeline__step:hover { border-color: var(--c-line-strong); }

    .fab { transition: translate var(--dur-fast) var(--ease-out), background-color var(--dur-fast) linear; }
    .fab:hover { translate: 0 var(--lift-sm); }
    .site-footer__top .icon { transition: translate var(--dur-base) var(--ease-expo); }
    .site-footer__top:hover .icon { translate: 0 calc(var(--space-1) * -1); }
  }

  /* ---------- 7. FAQ ----------
     Plus dreht sich zum „×". Antworten klappen weich auf, wo der Browser Höhen interpolieren kann. */
  .faq-list summary::after { transition: transform var(--dur-base) var(--ease-expo), border-color var(--dur-base) var(--ease-out); }
  .faq-list summary { transition: color var(--dur-fast) linear; }
  @supports (interpolate-size: allow-keywords) {
    .faq-list { interpolate-size: allow-keywords; }
    .faq-list details::details-content {
      block-size: 0;
      overflow: clip;
      opacity: 0;
      transition: block-size var(--dur-slow) var(--ease-expo), opacity var(--dur-base) var(--ease-out), content-visibility var(--dur-slow) allow-discrete;
    }
    .faq-list details[open]::details-content { block-size: auto; opacity: 1; }
  }

  /* ---------- 8. Formularmeldungen: sanftes Einblenden ---------- */
  .form-status:target, .form-status.is-visible, .form-live:not([hidden]), .error-summary:not([hidden]) { animation: mo-rise-s var(--dur-base) var(--ease-out) both; }
  .field input, .field textarea { transition: border-color var(--dur-fast) linear, background-color var(--dur-fast) linear; }

  /* ---------- 9. Footer: Wortmarke steigt aus der Unterkante ---------- */
  .js .site-footer__mark[data-reveal] { transform: translateY(35%); transition-duration: 1600ms; }
  .js .site-footer__mark[data-reveal].is-in { transform: none; opacity: 0.07; }

  /* ---------- 10. Dropdown ----------
     Zweck: Das Popover wirkt als Antwort auf die Geste, nicht als Sprung. */
  @media (min-width: 64em) {
    .js .site-nav .nav-group__list { transform: translateY(calc(var(--space-2) * -1)); transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-base) var(--ease-out), visibility 0s linear var(--dur-base); }
    .js .site-nav .nav-group.is-open .nav-group__list { transform: none; transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-base) var(--ease-out), visibility 0s; }
  }
  .nav-group__chev { transition: transform var(--dur-base) var(--ease-out); }
  .nav-group.is-open .nav-group__chev { transform: rotate(180deg); }

  /* ---------- 11. Werkzeug-Vorschauen ----------
     Ring füllt sich, Balken wachsen gestaffelt, Findings laufen ein. Zweck: zeigt, wie sich ein Stand aufbaut. */
  @keyframes mo-gauge-tool { from { stroke-dashoffset: 1; } }
  @keyframes mo-bar        { from { transform: scaleX(0); } }
  .js .tool__panel[data-illustration]:not(.is-playing) .gauge__fill { stroke-dashoffset: 1; }
  .js .tool__panel[data-illustration].is-playing .gauge__fill { animation: mo-gauge-tool var(--dur-draw-s) var(--ease-out) 0.1s both; }
  .js .tool__panel[data-illustration]:not(.is-playing) .bar__fill { transform: scaleX(0); }
  .js .tool__panel[data-illustration].is-playing .bar__fill { animation: mo-bar var(--dur-grow) var(--ease-out) both; }
  .js .tool__panel[data-illustration].is-playing .bar:nth-child(2) .bar__fill { animation-delay: 90ms; }
  .js .tool__panel[data-illustration].is-playing .bar:nth-child(3) .bar__fill { animation-delay: 180ms; }
  .js .tool__panel[data-illustration] .stat,
  .js .tool__panel[data-illustration] .findings li { opacity: 0; }
  .js .tool__panel[data-illustration].is-playing .stat { animation: mo-rise-s var(--dur-pop) var(--ease-out) both; }
  .js .tool__panel[data-illustration].is-playing .stat:nth-child(2) { animation-delay: 80ms; }
  .js .tool__panel[data-illustration].is-playing .stat:nth-child(3) { animation-delay: 160ms; }
  .js .tool__panel[data-illustration].is-playing .stat:nth-child(4) { animation-delay: 240ms; }
  .js .tool__panel[data-illustration].is-playing .findings li { animation: mo-slide var(--dur-pop) var(--ease-out) both; }
  .js .tool__panel[data-illustration].is-playing .findings li:nth-child(1) { animation-delay: 320ms; }
  .js .tool__panel[data-illustration].is-playing .findings li:nth-child(2) { animation-delay: 420ms; }
  .js .tool__panel[data-illustration].is-playing .findings li:nth-child(3) { animation-delay: 520ms; }
  /* Scan-Linie: fährt ruhig über die Vorschau, dann Pause. Zweck: „Monitor" als laufender Vorgang, ohne Dauer-Glow. */
  .js .tool__panel--scan.is-playing .tool__scan { animation: mo-scan 7s var(--ease-in-out) 1.2s infinite; }
  /* Status-Punkt „Im Aufbau": ruhiger Puls mit Ring, lange Periode. Zweck: markiert „in Arbeit". */
  .tool__dot { animation: mo-pulse var(--dur-loop) var(--ease-in-out) infinite; }
  .tool__dot::after { content: ""; position: absolute; inset: 0; border-radius: 50%; border: var(--line-w) solid var(--c-accent); animation: mo-ring var(--dur-loop) var(--ease-out) infinite; }

  /* ---------- 12. Darstellung wechseln (View Transitions, main.js) ----------
     Zweck: Der neue Modus breitet sich kreisförmig vom Schalter aus, statt hart umzuspringen. */
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
}

/* Darstellungsschalter: wandert er (neben dem Cookiebot-Symbol) an seine Stelle, geschieht das sanft statt als Sprung */
@media (prefers-reduced-motion: no-preference) {
  .js .fab--mode { transition: translate var(--dur-fast) var(--ease-out), background-color var(--dur-fast) linear, inset-inline-start var(--dur-base) var(--ease-out), inset-block-end var(--dur-base) var(--ease-out); }
}
