/* ==========================================================================
   DDL Scroll-Reveal
   - "Beim Scrollen ins Blickfeld"-Animationen für Überschriften, Textblöcke,
     Bilder und Karten-/Listen-Gruppen.
   - WICHTIG (Design-Prinzip): Der Standardzustand ist immer sichtbar
     (opacity:1, kein Transform/Clip-Path). Erst wenn JavaScript läuft und
     die Klasse "js-reveal-enabled" auf <html> setzt, greifen die
     Ausgangszustände unten. Fällt JS aus (deaktiviert, blockiert, Crawler
     ohne JS-Ausführung), bleibt der komplette Inhalt sofort sichtbar –
     nichts hängt von einem clientseitigen Trigger ab.
   - Nur transform/opacity/clip-path werden animiert (GPU-beschleunigt,
     kein Layout-Reflow).
   - prefers-reduced-motion: Bewegungs- und Clip-Path-Anteile werden
     entfernt, ein dezenter Opacity-Crossfade bleibt erhalten.
   - will-change wird bewusst NICHT hier statisch gesetzt: bei mehreren
     Dutzend .reveal-Elementen pro Seite würde eine dauerhafte Deklaration
     ebenso viele Compositor-Layer für die gesamte Lebensdauer der Seite
     erzwingen (Speicher-/Compositing-Overhead ohne Nutzen, sobald die
     Animation einmal gelaufen ist). Stattdessen setzt scroll-reveal.js
     will-change nur als Inline-Style kurz vor dem Trigger und entfernt es
     wieder nach Abschluss der Transition (transitionend).
   ========================================================================== */

:root {
  /* Bewusst am oberen Rand der UX-Pro-Max-Empfehlung (150-300ms Mikro-
     Interaktionen, "nicht über 500ms"), aber nicht darüber – abgewogen
     gegen Emil Kowalskis "marketing/explanatory kann länger sein". Scroll-
     Reveal ist kein wiederholt geklickter UI-Vorgang (Ausnahme wäre erlaubt),
     wir bleiben trotzdem knapp an der 500ms-Grenze statt sie zu über- oder
     stark auszureizen. */
  --reveal-duration: 480ms;
  --reveal-duration-image: 550ms;
  --reveal-duration-card: 500ms;
  --reveal-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --reveal-distance: 1.75rem;
}

/* Sichtbarer Default – greift immer, unabhängig von JS */
.reveal,
.reveal-image,
.reveal-card,
.reveal-card-safe {
  opacity: 1;
  transform: none;
  clip-path: none;
}

/* Ausgangszustände nur, wenn JS aktiv ist und die Elemente tatsächlich
   beobachtet werden können. */
html.js-reveal-enabled .reveal {
  opacity: 0;
  transform: translateY(var(--reveal-distance));
  transition: opacity var(--reveal-duration) var(--reveal-ease),
              transform var(--reveal-duration) var(--reveal-ease);
  transition-delay: var(--reveal-delay, 0ms);
}

html.js-reveal-enabled .reveal.is-revealed {
  opacity: 1;
  transform: none;
}

/* Bilder: dezenter Wipe-Reveal von unten (clip-path) + leichtes Fade,
   passend zu Emil Kowalskis "Image reveals on scroll"-Muster. */
html.js-reveal-enabled .reveal-image {
  opacity: 0;
  clip-path: inset(0 0 12% 0);
  transition: opacity var(--reveal-duration-image) var(--reveal-ease),
              clip-path var(--reveal-duration-image) var(--reveal-ease);
  transition-delay: var(--reveal-delay, 0ms);
}

html.js-reveal-enabled .reveal-image.is-revealed {
  opacity: 1;
  clip-path: inset(0 0 0 0);
}

/* Karten/Listen-Elemente: Fade + leichtes Anheben + minimale Skalierung
   (nie von scale(0) starten – siehe Emil-Kowalski-Skill). */
html.js-reveal-enabled .reveal-card {
  opacity: 0;
  transform: translateY(var(--reveal-distance)) scale(0.97);
  transition: opacity var(--reveal-duration-card) var(--reveal-ease),
              transform var(--reveal-duration-card) var(--reveal-ease);
  transition-delay: var(--reveal-delay, 0ms);
}

html.js-reveal-enabled .reveal-card.is-revealed {
  opacity: 1;
  transform: none;
}

/* "Sichere" Karten-Variante – bewusst NUR Opacity, kein transform/scale.
   Grund: .project-item-wrapper (projekte.html) hat position:relative +
   overflow:clip und enthält ein absolut positioniertes, z-indextes Overlay
   (.project-item-bg-wrapper) sowie ein echtes, lazy-loadendes <img>. Ein
   animiertes transform auf genau diesem Element (kombiniert mit dem
   dynamischen will-change:transform beim Trigger) verändert Stacking-
   Context-/Compositing-Layer-Grenzen dieser verschachtelten Struktur – ein
   bekanntes Muster für Chrome/Safari-Repaint-Bugs, bei denen der
   Karteninhalt nach der Layer-Beförderung nicht zuverlässig neu gemalt wird
   und leer/unsichtbar bleibt. Reines Opacity-Fading vermeidet das. */
html.js-reveal-enabled .reveal-card-safe {
  opacity: 0;
  transition: opacity var(--reveal-duration-card) var(--reveal-ease);
  transition-delay: var(--reveal-delay, 0ms);
}

html.js-reveal-enabled .reveal-card-safe.is-revealed {
  opacity: 1;
}

/* Reduced motion: Bewegungs-/Clip-Path-Anteil entfernen, sanften
   Opacity-Crossfade beibehalten (hilft weiterhin beim Verständnis des
   Seitenaufbaus, ohne Bewegungsreiz). */
@media (prefers-reduced-motion: reduce) {
  html.js-reveal-enabled .reveal,
  html.js-reveal-enabled .reveal-image,
  html.js-reveal-enabled .reveal-card,
  html.js-reveal-enabled .reveal-card-safe {
    transform: none;
    clip-path: none;
    transition-property: opacity;
    transition-duration: 400ms;
    transition-delay: 0ms;
  }
}
