/*! ===========================================================
   SFX Animation Framework
   Modul: Scroll · v0.2.6
   Build: 2026-05-02T15:58:00Z
   ----------------------------------------------------------
   Patches:
   - Animations-Reset bei Re-Trigger erzwungen
   - sfx-amount-* wirkt jetzt in beiden Modi
   - .sfx-scroll bekommt animation-duration: 1ms (MDN)
   =========================================================== */

/* ============================================================
   SFX — Design Tokens
   Modul: Scroll · v0.1
   ------------------------------------------------------------
   Globale Variablen, die das Look & Feel aller Animationen
   steuern. Im eigenen Theme/Bricks lassen sich alle Werte
   in :root oder einem beliebigen Container überschreiben.
   ============================================================ */

:root {
  /* ---- Distanzen (für Translate-basierte Effekte) ---- */
  --sfx-distance-sm: 16px;
  --sfx-distance-md: 32px;
  --sfx-distance-lg: 64px;

  /* ---- Skalen (für Scale/Zoom) ---- */
  --sfx-scale-sm: 0.9;
  --sfx-scale-md: 0.8;
  --sfx-scale-lg: 0.5;

  /* ---- Rotation (für Rotate/Flip) ---- */
  --sfx-rotate-sm: 5deg;
  --sfx-rotate-md: 10deg;
  --sfx-rotate-lg: 25deg;

  /* ---- Blur ---- */
  --sfx-blur-sm: 8px;
  --sfx-blur-md: 20px;
  --sfx-blur-lg: 40px;

  /* ---- Easings ---- */
  --sfx-ease-bounce:   cubic-bezier(0.68, -0.55, 0.265, 1.55);
  --sfx-ease-back:     cubic-bezier(0.34, 1.56, 0.64, 1);
  --sfx-ease-elastic:  cubic-bezier(0.5, -0.5, 0.1, 1.5);
  --sfx-ease-out-soft: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* ============================================================
   SFX — Core
   Modul: Scroll · v0.1
   ------------------------------------------------------------
   Aktivierungs-Klasse `sfx-scroll` setzt die Basis-Properties
   (Timeline, Range, Fill-Mode) sowie die aktiv genutzten
   Token-Variablen mit Defaults.
   Effekt-Klassen (sfx-fade-up, sfx-scale-in, ...) ergänzen
   nur den `animation-name`.
   ============================================================ */

.sfx-scroll {
  /* Timeline an den Viewport-Eintritt des Elements koppeln */
  animation-timeline: view();

  /* Trigger-Bereich: Defaults für Start und Ende der Animation
     Start: sobald das Element von unten in den Viewport eintritt
     Ende:  wenn das Element die Mitte des Viewports erreicht */
  --sfx-range-start: entry 0%;
  --sfx-range-end:   cover 50%;
  animation-range: var(--sfx-range-start) var(--sfx-range-end);

  /* Verhalten */
  animation-fill-mode: both;
  animation-timing-function: linear;

  /* MDN-empfohlen für Scroll-Driven Animations: nicht-null Duration
     für Firefox-Kompatibilität und konsistentes Verhalten — die
     reale "Dauer" ergibt sich aus animation-range, nicht hieraus. */
  animation-duration: 1ms;

  /* Aktiv genutzte Token-Variablen — Defaults = md */
  --sfx-distance: var(--sfx-distance-md);
  --sfx-scale:    var(--sfx-scale-md);
  --sfx-rotate:   var(--sfx-rotate-md);
  --sfx-blur:     var(--sfx-blur-md);
}

/* ------------------------------------------------------------
   Fallback 1: Browser ohne Scroll-Driven Animations
   → Element wird statisch sichtbar gerendert (kein Reveal,
   aber auch kein Bruch der Sichtbarkeit).
   ------------------------------------------------------------ */
@supports not (animation-timeline: view()) {
  .sfx-scroll {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }
}

/* ------------------------------------------------------------
   Fallback 2: Nutzer-Präferenz "weniger Bewegung"
   → Wir respektieren das System-Setting und schalten alles ab.
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .sfx-scroll {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }
}

/* ============================================================
   SFX — Reveal (Scroll-Triggered, Time-Based)
   Modul: Scroll · v0.2.4
   ------------------------------------------------------------
   Aktivierungs-Klasse `sfx-reveal` setzt das Element zunächst
   in den "from"-Zustand (über animation-fill-mode: both) und
   pausiert die Animation. Sobald das Element via Intersection-
   Observer in den Viewport eintritt, wird `sfx-revealed`
   gesetzt und die Animation spielt zeitbasiert ab.

   Benötigt: dist/sfx-scroll.js (IntersectionObserver)
   ============================================================ */

.sfx-reveal {
  /* Animation initial pausiert; "from"-Zustand wird über fill-mode gerendert */
  animation-play-state: paused;
  animation-fill-mode: both;
  animation-duration: var(--sfx-duration, var(--sfx-duration-default, 300ms));
  animation-delay: var(--sfx-delay, 0ms);
  animation-timing-function: var(--sfx-ease-default, var(--sfx-ease-out-soft));
  --sfx-trigger: var(--sfx-trigger-default, 75%);

  /* Aktiv genutzte Token-Variablen */
  --sfx-distance: var(--sfx-distance-md);
  --sfx-scale:    var(--sfx-scale-md);
  --sfx-rotate:   var(--sfx-rotate-md);
  --sfx-blur:     var(--sfx-blur-md);

  /* WICHTIG: kein will-change setzen.
     Chrome erstellt sonst dauerhafte Compositor-Layer
     (-> sichtbare horizontale Helligkeitsbänder).
     Moderne Browser optimieren laufende Animationen automatisch. */
}

/* Trigger: vom IntersectionObserver gesetzt */
.sfx-reveal.sfx-revealed {
  animation-play-state: running;
}

/* Reduced-Motion: respektieren — Element direkt im "to"-Zustand */
@media (prefers-reduced-motion: reduce) {
  .sfx-reveal {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }
}

/* ============================================================
   SFX — Scroll · Fade
   ------------------------------------------------------------
   Effekte:
     .sfx-fade-in     → nur Opacity
     .sfx-fade-up     → Opacity + Translate von unten
     .sfx-fade-down   → Opacity + Translate von oben
     .sfx-fade-left   → Opacity + Translate von rechts
     .sfx-fade-right  → Opacity + Translate von links
   Nutzt Token: --sfx-distance

   Hinweis: Keyframes enden mit `transform: none` (nicht
   `translateY(0)`), damit Chrome nach Animations-Ende keinen
   Compositor-Layer hält → vermeidet Helligkeits-Banding.
   ============================================================ */

.sfx-fade-in    { animation-name: sfx-fade-in; }
.sfx-fade-up    { animation-name: sfx-fade-up; }
.sfx-fade-down  { animation-name: sfx-fade-down; }
.sfx-fade-left  { animation-name: sfx-fade-left; }
.sfx-fade-right { animation-name: sfx-fade-right; }

@keyframes sfx-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes sfx-fade-up {
  from { opacity: 0; transform: translateY(var(--sfx-distance)); }
  to   { opacity: 1; transform: none; }
}

@keyframes sfx-fade-down {
  from { opacity: 0; transform: translateY(calc(var(--sfx-distance) * -1)); }
  to   { opacity: 1; transform: none; }
}

@keyframes sfx-fade-left {
  from { opacity: 0; transform: translateX(var(--sfx-distance)); }
  to   { opacity: 1; transform: none; }
}

@keyframes sfx-fade-right {
  from { opacity: 0; transform: translateX(calc(var(--sfx-distance) * -1)); }
  to   { opacity: 1; transform: none; }
}

/* ============================================================
   SFX — Scroll · Slide
   ------------------------------------------------------------
   Wie Fade-Direction, aber ohne Opacity-Änderung
   (Element ist die ganze Zeit sichtbar, gleitet nur in Position).
   Nutzt Token: --sfx-distance
   ============================================================ */

.sfx-slide-up    { animation-name: sfx-slide-up; }
.sfx-slide-down  { animation-name: sfx-slide-down; }
.sfx-slide-left  { animation-name: sfx-slide-left; }
.sfx-slide-right { animation-name: sfx-slide-right; }

@keyframes sfx-slide-up {
  from { transform: translateY(var(--sfx-distance)); }
  to   { transform: none; }
}

@keyframes sfx-slide-down {
  from { transform: translateY(calc(var(--sfx-distance) * -1)); }
  to   { transform: none; }
}

@keyframes sfx-slide-left {
  from { transform: translateX(var(--sfx-distance)); }
  to   { transform: none; }
}

@keyframes sfx-slide-right {
  from { transform: translateX(calc(var(--sfx-distance) * -1)); }
  to   { transform: none; }
}

/* ============================================================
   SFX — Scroll · Scale / Zoom
   ------------------------------------------------------------
   Effekte:
     .sfx-scale-in   → von kleiner (sfx-scale, default 0.8) auf 1
     .sfx-scale-out  → von größer (1.2) auf 1
     .sfx-zoom-in    → stärkere Variante (sfx-scale-lg, default 0.5)
   ============================================================ */

.sfx-scale-in  { animation-name: sfx-scale-in; }
.sfx-scale-out { animation-name: sfx-scale-out; }
.sfx-zoom-in   { animation-name: sfx-zoom-in; }

@keyframes sfx-scale-in {
  from { opacity: 0; transform: scale(var(--sfx-scale)); }
  to   { opacity: 1; transform: none; }
}

@keyframes sfx-scale-out {
  from { opacity: 0; transform: scale(1.2); }
  to   { opacity: 1; transform: none; }
}

@keyframes sfx-zoom-in {
  from { opacity: 0; transform: scale(var(--sfx-scale-lg)); }
  to   { opacity: 1; transform: none; }
}

/* ============================================================
   SFX — Scroll · Rotate / Flip
   ------------------------------------------------------------
   Effekte:
     .sfx-rotate   → Rotation gegen Uhrzeigersinn → 0°
     .sfx-flip-x   → 3D-Flip auf X-Achse
     .sfx-flip-y   → 3D-Flip auf Y-Achse
   Nutzt Token: --sfx-rotate
   ============================================================ */

.sfx-rotate { animation-name: sfx-rotate; }
.sfx-flip-x { animation-name: sfx-flip-x; }
.sfx-flip-y { animation-name: sfx-flip-y; }

@keyframes sfx-rotate {
  from { opacity: 0; transform: rotate(calc(var(--sfx-rotate) * -1)); }
  to   { opacity: 1; transform: none; }
}

@keyframes sfx-flip-x {
  from { opacity: 0; transform: perspective(1000px) rotateX(90deg); }
  to   { opacity: 1; transform: none; }
}

@keyframes sfx-flip-y {
  from { opacity: 0; transform: perspective(1000px) rotateY(90deg); }
  to   { opacity: 1; transform: none; }
}

/* ============================================================
   SFX — Scroll · Blur
   ------------------------------------------------------------
   Effekt:
     .sfx-blur-in  → von blur(--sfx-blur, default 20px) auf 0
   Nutzt Token: --sfx-blur

   Hinweis: `to`-State nutzt `filter: none` statt `blur(0)`,
   damit Chrome keinen permanenten Filter-Compositor-Layer hält.
   ============================================================ */

.sfx-blur-in { animation-name: sfx-blur-in; }

@keyframes sfx-blur-in {
  from { opacity: 0; filter: blur(var(--sfx-blur)); }
  to   { opacity: 1; filter: none; }
}

/* ============================================================
   SFX — Modifier · Easing
   ------------------------------------------------------------
   Verändert die Timing-Funktion der Animation.
   Hinweis: Mit Scroll-Driven Animations wirkt das Easing über
   den Scroll-Verlauf — `linear` (Default) entspricht direkter
   Kopplung an den Scroll, andere Werte beschleunigen/bremsen.
   ============================================================ */

.sfx-ease-linear    { animation-timing-function: linear; }
.sfx-ease-in        { animation-timing-function: ease-in; }
.sfx-ease-out       { animation-timing-function: ease-out; }
.sfx-ease-in-out    { animation-timing-function: ease-in-out; }

.sfx-ease-bounce    { animation-timing-function: var(--sfx-ease-bounce); }
.sfx-ease-back      { animation-timing-function: var(--sfx-ease-back); }
.sfx-ease-elastic   { animation-timing-function: var(--sfx-ease-elastic); }
.sfx-ease-out-soft  { animation-timing-function: var(--sfx-ease-out-soft); }

/* ============================================================
   SFX — Modifier · Range
   ------------------------------------------------------------
   Steuert wann die Animation startet und endet, gemessen am
   Viewport-Eintritt des Elements.
     .sfx-start-N  → animation-range start = entry N% (Schritte 10)
     .sfx-end-N    → animation-range end   = cover N% (Schritte 10)
   Default (ohne Modifier): start = entry 0%, end = cover 50%.
   ============================================================ */

/* ---- Start: ab wann läuft die Animation? ---- */
.sfx-start-0   { --sfx-range-start: entry 0%;   }
.sfx-start-10  { --sfx-range-start: entry 10%;  }
.sfx-start-20  { --sfx-range-start: entry 20%;  }
.sfx-start-30  { --sfx-range-start: entry 30%;  }
.sfx-start-40  { --sfx-range-start: entry 40%;  }
.sfx-start-50  { --sfx-range-start: entry 50%;  }
.sfx-start-60  { --sfx-range-start: entry 60%;  }
.sfx-start-70  { --sfx-range-start: entry 70%;  }
.sfx-start-80  { --sfx-range-start: entry 80%;  }
.sfx-start-90  { --sfx-range-start: entry 90%;  }
.sfx-start-100 { --sfx-range-start: entry 100%; }

/* ---- Ende: ab wann ist die Animation fertig? ---- */
.sfx-end-0   { --sfx-range-end: cover 0%;   }
.sfx-end-10  { --sfx-range-end: cover 10%;  }
.sfx-end-20  { --sfx-range-end: cover 20%;  }
.sfx-end-30  { --sfx-range-end: cover 30%;  }
.sfx-end-40  { --sfx-range-end: cover 40%;  }
.sfx-end-50  { --sfx-range-end: cover 50%;  }
.sfx-end-60  { --sfx-range-end: cover 60%;  }
.sfx-end-70  { --sfx-range-end: cover 70%;  }
.sfx-end-80  { --sfx-range-end: cover 80%;  }
.sfx-end-90  { --sfx-range-end: cover 90%;  }
.sfx-end-100 { --sfx-range-end: cover 100%; }

/* ============================================================
   SFX — Modifier · Duration
   ------------------------------------------------------------
   Setzt die Animations-Dauer im Reveal-Modus.
   Hinweis: Im scroll-gekoppelten Modus (sfx-scroll) ist die
   Dauer scroll-gekoppelt — duration wirkt dort nicht.
   ============================================================ */

.sfx-dur-100  { --sfx-duration: 100ms;  }
.sfx-dur-150  { --sfx-duration: 150ms;  }
.sfx-dur-300  { --sfx-duration: 300ms;  }
.sfx-dur-500  { --sfx-duration: 500ms;  }
.sfx-dur-700  { --sfx-duration: 700ms;  }
.sfx-dur-800  { --sfx-duration: 800ms;  }
.sfx-dur-1000 { --sfx-duration: 1000ms; }
.sfx-dur-1200 { --sfx-duration: 1200ms; }
.sfx-dur-1500 { --sfx-duration: 1500ms; }
.sfx-dur-2000 { --sfx-duration: 2000ms; }
.sfx-dur-3000 { --sfx-duration: 3000ms; }
.sfx-dur-4000 { --sfx-duration: 4000ms; }
.sfx-dur-5000 { --sfx-duration: 5000ms; }
.sfx-dur-6000 { --sfx-duration: 6000ms; }

/* ============================================================
   SFX — Modifier · Delay
   ------------------------------------------------------------
   Setzt einen Verzögerung vor Animations-Start im Reveal-Modus.
   Hinweis: Im scroll-gekoppelten Modus (sfx-scroll) ist Delay
   konzeptionell ungeeignet — dort die Range-Modifier nutzen.
   ============================================================ */

.sfx-delay-0    { --sfx-delay: 0ms;    }
.sfx-delay-100  { --sfx-delay: 100ms;  }
.sfx-delay-200  { --sfx-delay: 200ms;  }
.sfx-delay-300  { --sfx-delay: 300ms;  }
.sfx-delay-400  { --sfx-delay: 400ms;  }
.sfx-delay-500  { --sfx-delay: 500ms;  }
.sfx-delay-700  { --sfx-delay: 700ms;  }
.sfx-delay-1000 { --sfx-delay: 1000ms; }
.sfx-delay-1500 { --sfx-delay: 1500ms; }
.sfx-delay-2000 { --sfx-delay: 2000ms; }

/* ============================================================
   SFX — Modifier · Amount (Intensität)
   ------------------------------------------------------------
   Globale Intensitäts-Schalter, die alle aktiven Tokens
   gleichzeitig auf "klein" oder "groß" setzen.

     .sfx-amount-sm  → distance-sm, scale-sm, rotate-sm, blur-sm
     (Default = md, keine Klasse nötig)
     .sfx-amount-lg  → distance-lg, scale-lg, rotate-lg, blur-lg

   Wer nur eine einzelne Dimension anpassen will, kann die
   Variable direkt am Element überschreiben:
     style="--sfx-distance: 100px;"

   Wirkt in beiden Modi (sfx-scroll und sfx-reveal).
   ============================================================ */

.sfx-scroll.sfx-amount-sm,
.sfx-reveal.sfx-amount-sm {
  --sfx-distance: var(--sfx-distance-sm);
  --sfx-scale:    var(--sfx-scale-sm);
  --sfx-rotate:   var(--sfx-rotate-sm);
  --sfx-blur:     var(--sfx-blur-sm);
}

.sfx-scroll.sfx-amount-lg,
.sfx-reveal.sfx-amount-lg {
  --sfx-distance: var(--sfx-distance-lg);
  --sfx-scale:    var(--sfx-scale-lg);
  --sfx-rotate:   var(--sfx-rotate-lg);
  --sfx-blur:     var(--sfx-blur-lg);
}

/* ============================================================
   SFX — Modifier · Stagger (Grid-Sequenz)
   ------------------------------------------------------------
   Auf einem Container/Parent setzen — alle direkten Children
   mit .sfx-reveal werden nacheinander animiert (Schritt-Delay).

     .sfx-stagger        → Default 80ms Schritt
     .sfx-stagger-fast   → 40ms Schritt (40)
     .sfx-stagger-slow   → 160ms Schritt
     .sfx-stagger-xslow  → 240ms Schritt

   Eigener Schritt direkt am Container überschreiben:
     style="--sfx-stagger-step: 50ms;"

   Funktioniert für die ersten 20 Children. Für mehr direkt
   per CSS-Variable über JS setzen.

   Stagger gilt nur für sfx-reveal-Children — nicht für
   scroll-gekoppelte Animationen (dort wirkt Delay nicht).

   Parent-driven API:
     <ul class="sfx-stagger-slow sfx-fade-in">
       <li>...</li>
       <li>...</li>
     </ul>

   sfx-scroll.js überträgt den Effekt und Reveal-Modus auf
   die direkten Children, damit das Markup in Buildern schlank
   bleibt.
   ============================================================ */

.sfx-stagger        { --sfx-stagger-step: var(--sfx-stagger-step-default, 80ms);  }
.sfx-stagger-fast   { --sfx-stagger-step: 40ms;  }
.sfx-stagger-slow   { --sfx-stagger-step: 160ms; }
.sfx-stagger-xslow  { --sfx-stagger-step: 240ms; }

/* Per-Child-Delay über nth-child (bis zu 20 Reveal-Items) */
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(1)  { --sfx-delay: calc(var(--sfx-stagger-step) * 0);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(2)  { --sfx-delay: calc(var(--sfx-stagger-step) * 1);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(3)  { --sfx-delay: calc(var(--sfx-stagger-step) * 2);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(4)  { --sfx-delay: calc(var(--sfx-stagger-step) * 3);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(5)  { --sfx-delay: calc(var(--sfx-stagger-step) * 4);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(6)  { --sfx-delay: calc(var(--sfx-stagger-step) * 5);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(7)  { --sfx-delay: calc(var(--sfx-stagger-step) * 6);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(8)  { --sfx-delay: calc(var(--sfx-stagger-step) * 7);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(9)  { --sfx-delay: calc(var(--sfx-stagger-step) * 8);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(10) { --sfx-delay: calc(var(--sfx-stagger-step) * 9);  }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(11) { --sfx-delay: calc(var(--sfx-stagger-step) * 10); }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(12) { --sfx-delay: calc(var(--sfx-stagger-step) * 11); }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(13) { --sfx-delay: calc(var(--sfx-stagger-step) * 12); }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(14) { --sfx-delay: calc(var(--sfx-stagger-step) * 13); }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(15) { --sfx-delay: calc(var(--sfx-stagger-step) * 14); }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(16) { --sfx-delay: calc(var(--sfx-stagger-step) * 15); }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(17) { --sfx-delay: calc(var(--sfx-stagger-step) * 16); }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(18) { --sfx-delay: calc(var(--sfx-stagger-step) * 17); }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(19) { --sfx-delay: calc(var(--sfx-stagger-step) * 18); }
:is(.sfx-stagger, .sfx-stagger-fast, .sfx-stagger-slow, .sfx-stagger-xslow) > .sfx-reveal:nth-child(20) { --sfx-delay: calc(var(--sfx-stagger-step) * 19); }

/* ============================================================
   SFX — Modifier · Trigger-Position (Reveal)
   ------------------------------------------------------------
   Setzt die Viewport-Höhe, an der ein Reveal ausgelöst wird.
   Der Wert wird vom IntersectionObserver in sfx-scroll.js
   aus der CSS-Variable --sfx-trigger gelesen.

     .sfx-trigger-60  → startet bei 60 % Viewport-Höhe
     .sfx-trigger-75  → Default, unteres Drittel
     .sfx-trigger-90  → sehr früh

   Eigener Wert pro Element:
     style="--sfx-trigger: 72%;"
   ============================================================ */

.sfx-trigger-50 { --sfx-trigger: 50%; }
.sfx-trigger-60 { --sfx-trigger: 60%; }
.sfx-trigger-65 { --sfx-trigger: 65%; }
.sfx-trigger-70 { --sfx-trigger: 70%; }
.sfx-trigger-75 { --sfx-trigger: 75%; }
.sfx-trigger-80 { --sfx-trigger: 80%; }
.sfx-trigger-85 { --sfx-trigger: 85%; }
.sfx-trigger-90 { --sfx-trigger: 90%; }

/* ============================================================
   SFX — Modifier · Reveal-Optionen (JS-gesteuert)
   ------------------------------------------------------------
   Diese Klassen haben absichtlich keine eigenen CSS-Regeln —
   sie werden vom IntersectionObserver in sfx-scroll.js
   ausgewertet, um das Verhalten des Reveal-Modus zu steuern.

     .sfx-once          → Animation spielt nur einmal ab
                          (statt Default: bei jedem Wiedereintritt)

     .sfx-threshold-N   → IntersectionObserver-Threshold in Prozent
                          an der Trigger-Linie
                          (N = 0..100, Default 0)
                          Beispiele:
                            .sfx-threshold-25  → 25 % sichtbar
                            .sfx-threshold-50  → 50 % sichtbar

   Anwendung:
     <div class="sfx-reveal sfx-fade-up sfx-once sfx-threshold-25">…</div>

   Hinweis: Beide Klassen wirken nur in Kombination mit
   sfx-reveal — sie haben keinen Effekt auf sfx-scroll
   (scroll-gekoppelter Modus).
   ============================================================ */


/* ============================================================
   PARALLAX (scroll-coupled element translate, CSS-only)
   ============================================================

   Aktivierungs-Klasse `sfx-parallax` koppelt die Animation an
   `animation-timeline: view()`. Das Element driftet vertikal,
   während es den Viewport durchquert.

   Effekt-Klassen kombinieren Richtung und Intensität:
     .sfx-parallax-up-sm | -md | -lg   → Drift entgegen Scrollrichtung
     .sfx-parallax-down-sm | -md | -lg → Drift mit Scrollrichtung

   Distance-Tokens:
     --sfx-parallax-sm: 40px
     --sfx-parallax-md: 80px
     --sfx-parallax-lg: 160px

   Anwendung:
     <section class="sfx-parallax sfx-parallax-up-md">…</section>

   Keine JS-Abhängigkeit. Browser ohne scroll-timeline (z. B.
   Firefox stable) rendern das Element statisch.
   ============================================================ */

:root {
  --sfx-parallax-sm: 40px;
  --sfx-parallax-md: 80px;
  --sfx-parallax-lg: 160px;
}

.sfx-parallax {
  animation-timeline: view();
  animation-fill-mode: both;
  animation-duration: 1ms;
  will-change: transform;
}

.sfx-parallax-up-sm { animation-name: sfx-parallax-up; --sfx-parallax: var(--sfx-parallax-sm); }
.sfx-parallax-up-md { animation-name: sfx-parallax-up; --sfx-parallax: var(--sfx-parallax-md); }
.sfx-parallax-up-lg { animation-name: sfx-parallax-up; --sfx-parallax: var(--sfx-parallax-lg); }

.sfx-parallax-down-sm { animation-name: sfx-parallax-down; --sfx-parallax: var(--sfx-parallax-sm); }
.sfx-parallax-down-md { animation-name: sfx-parallax-down; --sfx-parallax: var(--sfx-parallax-md); }
.sfx-parallax-down-lg { animation-name: sfx-parallax-down; --sfx-parallax: var(--sfx-parallax-lg); }

@keyframes sfx-parallax-up {
  from { transform: translateY(var(--sfx-parallax)); }
  to   { transform: translateY(calc(var(--sfx-parallax) * -1)); }
}

@keyframes sfx-parallax-down {
  from { transform: translateY(calc(var(--sfx-parallax) * -1)); }
  to   { transform: translateY(var(--sfx-parallax)); }
}

@media (prefers-reduced-motion: reduce) {
  .sfx-parallax {
    animation: none !important;
    transform: none !important;
  }
}
