/* ==========================================================================
   FW-Animations — Scroll-getriggerte Einblende-Effekte
   --------------------------------------------------------------------------
   Anwendung:
     1) Per Shortcode:    [animate effekt=fade-up]Inhalt[/animate]
     2) Per HTML-Klasse:  <div class="fw-animate fw-animate--fade-up">…</div>
     3) Per data-attribut: <div data-animate="fade-up">…</div>
                          (das JS wandelt das in die Klasse um)

   Optional (alle drei Varianten):
     data-fw-delay="200"      → Verzögerung in ms vor Start (max 3000)
     data-fw-duration="800"   → Dauer der Animation in ms (max 3000)
     data-fw-once="0"         → wenn 0, animiert wieder beim Re-Eintritt
                                in den Viewport (Default = einmalig)

   Trigger:
     IntersectionObserver fügt `.is-visible` hinzu, sobald das Element zu
     mindestens 12 % im Viewport sichtbar ist. Nur dann startet die
     Animation — kein Flackern beim Laden.

   Accessibility:
     `prefers-reduced-motion: reduce` ⇒ Animation entfällt komplett, das
     Element ist sofort sichtbar (opacity:1, transform:none).
   ========================================================================== */

/* Initial-Zustand (vor Trigger): unsichtbar + transformiert */
.fw-animate {
  opacity: 0;
  transform: translateY(0) translateX(0) scale(1) rotate(0);
  transition-property: opacity, transform;
  transition-duration: var(--fw-anim-duration, 700ms);
  transition-timing-function: cubic-bezier(.22, .61, .36, 1);
  transition-delay: var(--fw-anim-delay, 0ms);
  will-change: opacity, transform;
}

/* Trigger-Zustand (im Viewport): sichtbar + Endposition */
.fw-animate.is-visible {
  opacity: 1;
  transform: translateY(0) translateX(0) scale(1) rotate(0);
}

/* ===== Effekte: Initial-Transform pro Variante ============================ */

/* Fade — nur Opacity */
.fw-animate--fade-in            { /* startet schon mit opacity:0, kein Transform */ }

/* Slide / Fade-from-direction */
.fw-animate--fade-up            { transform: translateY(28px); }
.fw-animate--fade-down          { transform: translateY(-28px); }
.fw-animate--fade-left          { transform: translateX(36px); }   /* Bewegung von rechts → links */
.fw-animate--fade-right         { transform: translateX(-36px); }  /* Bewegung von links → rechts */

/* Stronger slide (kein Fade-in, nur Bewegung) */
.fw-animate--slide-up           { transform: translateY(60px); }
.fw-animate--slide-down         { transform: translateY(-60px); }

/* Zoom / Scale */
.fw-animate--zoom-in            { transform: scale(.85); }
.fw-animate--zoom-out           { transform: scale(1.18); }
.fw-animate--scale-in           { transform: scale(.6); }

/* Rotation */
.fw-animate--rotate             { transform: rotate(-12deg) scale(.92); }
.fw-animate--flip               { transform: rotateY(85deg); }

/* Bounce-Out (mit anderer Easing-Kurve) */
.fw-animate--bounce             { transform: scale(.5); transition-timing-function: cubic-bezier(.34, 1.56, .64, 1); }

/* ===== Bühne (2026-10-15): Auftritte mit Maske und Unschärfe =============== */
/* ❗️Die Maske arbeitet mit clip-path, nicht mit opacity: der Block ist von
   Anfang an „da", nur verdeckt, und öffnet sich wie ein Vorhang. Deshalb
   opacity:1 schon vorher. Die Kurve ist bewusst steiler als bei den
   übrigen Effekten (schnell los, weich aus).
   ❗️DIE MASKE SITZT AUF DEN KINDERN, NICHT AUF DEM ELEMENT SELBST. Chrome
   rechnet die eigene clip-path eines Elements in den IntersectionObserver
   ein: ein ganz maskierter Block ist für ihn unsichtbar, bekommt nie
   `.is-visible` und öffnet sich nie (gemessen 2026-10-15: Block mitten im
   Bild, Schnittfläche 0). Bewegung und Unschärfe bleiben am Element. */
.fw-animate--mask-up,
.fw-animate--mask-left,
.fw-animate--blur-in,
.fw-animate--rise,
.fw-animate--zoom-reveal {
  transition-property: opacity, transform, filter;
  transition-timing-function: cubic-bezier(.16, 1, .3, 1);
}
.fw-animate--mask-up > *,
.fw-animate--mask-left > *,
.fw-animate--zoom-reveal > * {
  transition: clip-path var(--fw-anim-dur, var(--fw-anim-duration, 700ms)) cubic-bezier(.16, 1, .3, 1)
              var(--fw-anim-delay, 0ms);
}
.fw-animate--mask-up     { opacity: 1; transform: translateY(40px); }
.fw-animate--mask-up > * { clip-path: inset(100% 0 0 0); }
.fw-animate--mask-left   { opacity: 1; transform: translateX(-28px); }
.fw-animate--mask-left > * { clip-path: inset(0 100% 0 0); }
.fw-animate--blur-in     { filter: blur(16px); transform: scale(1.05); }
.fw-animate--rise        { transform: translateY(90px) rotate(2.5deg); transform-origin: 0 100%; }
.fw-animate--zoom-reveal { opacity: 1; transform: scale(1.14); }
.fw-animate--zoom-reveal > * { clip-path: inset(14% 12% 14% 12% round 20px); }
.fw-animate--mask-up.is-visible > *,
.fw-animate--mask-left.is-visible > *,
.fw-animate--zoom-reveal.is-visible > * { clip-path: inset(0 0 0 0 round 0px); }
.fw-animate--blur-in.is-visible     { filter: blur(0); }

/* ===== Stagger: child-Elemente nacheinander einblenden ==================== */
/* Wird bei einem Wrapper mit .fw-animate--stagger automatisch durchgereicht.
   Das JS setzt die delays auf direkten Kindern. */
.fw-animate--stagger > .fw-animate {
  /* Delays werden per inline `style="--fw-anim-delay: …"` vom JS gesetzt */
}

/* ===== Reduced-Motion: keine Animation, sofort sichtbar =================== */
@media (prefers-reduced-motion: reduce) {
  .fw-animate,
  .fw-animate.is-visible {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    clip-path: none !important;
    filter: none !important;
  }
  .fw-animate > * { clip-path: none !important; transition: none !important; }
}

