/* NOBLE SILENCE — motion: REVEAL · DRIFT · SETTLE
   Text verschiebt sich nicht, er klart aus leichter Unschaerfe auf — dieselbe
   Geste, die der Hero-Titel nutzt. Ruhiger als ein Anheben, weil der Blick
   keine Kante verfolgt.
   Das :not() ist Pflicht: Bildrahmen tragen teils "reveal reveal--mask"
   gemeinsam. .reveal--mask (weiter unten) setzt opacity/transform zurueck,
   aber kein filter — ohne den Ausschluss wuerden diese Fotos unscharf starten. */
.reveal:not(.reveal--mask):not(.reveal--blur):not(.reveal--frame){opacity:0;filter:blur(3px);
  transition:opacity var(--dur-reveal) var(--ease-settle),filter var(--dur-reveal) var(--ease-settle)}
/* filter:none statt blur(0): blur(0) erzeugt weiterhin einen Containing Block
   fuer absolut positionierte Kinder und bliebe dauerhaft bestehen. */
.reveal:not(.reveal--mask):not(.reveal--blur):not(.reveal--frame).is-in{opacity:1;filter:none}
/* Fotos klaren nicht auf — unscharf startende Bilder lesen sich wie ein
   Ladefehler, und blur auf grossen Flaechen kostet unnoetig GPU. */
/* Alle vier Teile brauchen BEIDE :not() — in einer Selektorliste zaehlt jeder
   Teil einzeln. "figure.reveal:not(.reveal--mask)" liegt bei (0,2,1) und
   verliert gegen die Basisregel (0,3,0); die drei Klassen-Teile liegen bei
   (0,3,0) und gewinnen nur ueber die Reihenfolge. Ohne das zweite :not() blendet
   jede <figure class="… reveal"> ohne .media/.frame unscharf auf. */
figure.reveal:not(.reveal--mask):not(.reveal--blur):not(.reveal--frame),
.media.reveal:not(.reveal--mask):not(.reveal--blur):not(.reveal--frame),
.frame.reveal:not(.reveal--mask):not(.reveal--blur):not(.reveal--frame),
.teaser__plate.reveal:not(.reveal--mask):not(.reveal--blur):not(.reveal--frame){
  filter:none;transition:opacity var(--dur-reveal) var(--ease-settle)}
.reveal--blur.is-in{filter:blur(0)}
/* Bild-Maske. Die Maske sitzt NIE auf dem beobachteten Element: ein per
   clip-path vollstaendig abgeschnittenes Element hat fuer den
   IntersectionObserver keine sichtbare Flaeche und wird nie als sichtbar
   gemeldet (Chrome rechnet die eigene clip-path ein) — deshalb blieb das
   Muster "figure.reveal.reveal--mask" frueher unsichtbar. Beobachtet wird
   der Rahmen (.reveal, meist mit .reveal--frame = ohne eigenen Fade), die
   Maske liegt auf dem Kind (.media-Span oder <img>) und oeffnet sich, sobald
   der Rahmen .is-in traegt. */
.reveal--mask{clip-path:inset(0 0 100% 0);opacity:1;transition:clip-path var(--dur-image) var(--ease-settle)}
.reveal--mask.is-in,.is-in>.reveal--mask{clip-path:inset(0 0 0 0)}
/* Der Mask-Reveal animiert ausschliesslich die clip-path des Rahmens.
   Das transform des <img> gehoert allein dem Parallax (siehe unten) — sonst
   ueberschreiben sich beide gegenseitig. */
/* Staffelung halbiert: eine lange Kette laesst jedes Element einzeln ankommen
   und genau das liest sich als "hier passiert viel". Bei 60 ms verschmelzen
   die Elemente zu einer Geste, die Reihenfolge bleibt trotzdem spuerbar. */
[data-stagger]>.reveal:nth-child(2){transition-delay:60ms}
[data-stagger]>.reveal:nth-child(3){transition-delay:120ms}
[data-stagger]>.reveal:nth-child(4){transition-delay:180ms}
[data-stagger]>.reveal:nth-child(5){transition-delay:240ms}
.seq>*{opacity:0;filter:blur(3px);transition:opacity var(--dur-reveal) var(--ease-settle),filter var(--dur-reveal) var(--ease-settle)}
.seq.is-in>*{opacity:1;filter:none}
/* Vorher bis 2000 ms — die letzte Zeile liess zwei Sekunden auf sich warten
   und das las sich als Diaschau. Jetzt ist der ganze Block nach 1,1 s fertig. */
.seq.is-in>*:nth-child(2){transition-delay:80ms}
.seq.is-in>*:nth-child(3){transition-delay:160ms}
.seq.is-in>*:nth-child(4){transition-delay:240ms}
.seq.is-in>*:nth-child(5){transition-delay:320ms}
/* Sechs Zeilen hat der Rahmen der Fassung C (day), sieben die Stimmen-Tafel
   der Startseite (seit 09.09.2026). */
.seq.is-in>*:nth-child(6){transition-delay:400ms}
.seq.is-in>*:nth-child(7){transition-delay:480ms}

/* ==========================================================================
   TEMPORÄR — Editorial-Scrollstory: weichere Bewegungssprache, nur .story
   ========================================================================== */
/* Eine Geste statt sechs. Seitliches Einschieben (+-32 px) und Hochskalieren
   (0,94) waren die beiden Bewegungen, die am staerksten nach Baukasten aussahen.
   .reveal-l / -r / -scale verhalten sich jetzt wie .reveal. Die Klassen bleiben
   im Markup, damit nichts umgebaut werden muss. */
.reveal-l,.reveal-r,.reveal-scale{opacity:0;filter:blur(3px);
  transition:opacity var(--dur-reveal) var(--ease-settle),filter var(--dur-reveal) var(--ease-settle)}
.reveal-l.is-in,.reveal-r.is-in,.reveal-scale.is-in{opacity:1;filter:none}
/* Das eigene, langsamere Staffelsystem der Story ist entfallen: seit .story
   keine eigene Bewegungssprache mehr hat, gab es zwei konkurrierende Ketten
   (140er global, 220er hier). Jetzt gilt ueberall dieselbe 60-ms-Staffelung.
   .reveal-r braucht dafuer einen eigenen Selektor — die globale Regel oben
   greift nur auf .reveal. */
[data-stagger]>.reveal-r:nth-child(2),[data-stagger]>.reveal-l:nth-child(2){transition-delay:60ms}
[data-stagger]>.reveal-r:nth-child(3),[data-stagger]>.reveal-l:nth-child(3){transition-delay:120ms}
[data-stagger]>.reveal-r:nth-child(4),[data-stagger]>.reveal-l:nth-child(4){transition-delay:180ms}
[data-stagger]>.reveal-r:nth-child(5),[data-stagger]>.reveal-l:nth-child(5){transition-delay:240ms}

/* DRIFT — ruhige, aber wahrnehmbare Bewegung.
   linear statt ein-/ausschwingend: eine weiche Kurve steht an beiden Enden fast
   still, und genau dort trifft der Blick sie beim Scrollen meistens. Deshalb
   wirkte der Zoom bisher eingefroren. Konstantes Tempo ist jederzeit sichtbar
   und trotzdem unaufgeregt. Der fruehere Mikro-Versatz entfaellt — reiner Zoom
   liest sich sauberer. */
@keyframes drift{0%{transform:scale(1.02)}100%{transform:scale(1.09)}}
/* Dauer-Zoom abgeschaltet: auf dem Schirm bewegt sich jetzt nur noch etwas,
   wenn gescrollt wird. Die Marke heisst Noble Silence. Der statische Wert
   haelt den Ausschnitt, damit sich die Bildwirkung nicht aendert. */
.drift img{transform:scale(1.04)}
/* Ken-Burns fuer die grossen Foto-Sections. Startet erst, wenn das Bild ins
   Fenster kommt (.is-in) — sonst laeuft die Animation seit dem Seitenaufruf und
   der Besucher trifft sie in beliebiger Phase, oft am Umkehrpunkt. So beginnt
   jedes Bild bei 1.0, genau wenn man es sieht. */
@keyframes drift-strong{0%{transform:scale(1)}100%{transform:scale(1.14)}}
.full-image__drift{transform:scale(1.05)}
@keyframes atmos{0%{transform:translate(0,0) rotate(0deg)}50%{transform:translate(3%,-2%) rotate(2deg)}100%{transform:translate(0,0) rotate(0deg)}}
/* 40-s-Drift mit Rotation abgeschaltet — siehe oben. */
.atmos__layer{animation:none}
@keyframes scroll-bounce{0%,100%{transform:translateY(0);opacity:.6}50%{transform:translateY(6px);opacity:1}}
/* Bounce abgeschaltet; der Pfeil steht. */
.hero__scroll-arrow{animation:none}

/* Hero Load: Unschärfe → Fokus, Dunkel → Licht */
.hero__media{opacity:0;filter:blur(14px);transform:scale(1.04);transition:opacity 1600ms var(--ease-soft),filter 2200ms var(--ease-settle),transform 2600ms var(--ease-settle)}
body.is-ready .hero__media{opacity:1;filter:blur(0);transform:none}
/* Die Einlauf-Choreografie lief ueber 2,65 s (Titel ab 700 ms, Claim ab
   1250 ms, Pfeil ab 1450 ms) — dadurch baute sich der Hero vor den Augen des
   Besuchers auf. Jetzt rund 1,4 s, weniger Unschaerfe, kuerzerer Weg. */
.hero__title{opacity:0;filter:blur(4px);transform:translateY(5px);transition:opacity 900ms var(--ease-settle) 300ms,filter 900ms var(--ease-settle) 300ms,transform 900ms var(--ease-settle) 300ms}
body.is-ready .hero__title{opacity:1;filter:none;transform:none}
.hero__claim{opacity:0;transform:translateY(4px);transition:opacity 800ms var(--ease-settle) 620ms,transform 800ms var(--ease-settle) 620ms}
body.is-ready .hero__claim{opacity:1;transform:none}
.hero__scroll{opacity:0;transition:opacity 800ms 780ms}
body.is-ready .hero__scroll{opacity:1}

/* Hero-Einlauf der Unterseiten (day, retreat, selma): dieselbe Choreografie
   wie .hero__media — Unschaerfe → Fokus, danach Headline (300 ms) und Lead
   (620 ms). Kein transform: das Bild traegt data-parallax, die Skalierung
   gehoert dort der Schleife. Die Kennzeile ueber der Headline steht sofort. */
.page-hero--image img{opacity:0;filter:blur(14px);transition:opacity 1600ms var(--ease-soft),filter 2200ms var(--ease-settle)}
body.is-ready .page-hero--image img{opacity:1;filter:blur(0)}
/* Die Zeilenmaske der Headline laeuft ueber den Observer (.is-in) — im Hero
   feuert er sofort. Die Verzoegerung setzt sie hinter das Bild; die Staffel
   der Zeilen (50 ms) bleibt erhalten. Spezifitaet (0,4,1) schlaegt die
   nth-child-Regeln von .cut (0,3,1). */
.page-hero .cut .line>span{transition-delay:300ms}
.page-hero .cut .line:nth-child(2)>span{transition-delay:350ms}
.page-hero .cut .line:nth-child(3)>span{transition-delay:400ms}
.page-hero .lead.reveal{transition-delay:620ms}
/* Bildpanel der Anmeldung: derselbe Einlauf wie die Unterseiten-Heros. */
.book__img img{opacity:0;filter:blur(14px);transition:opacity 1600ms var(--ease-soft),filter 2200ms var(--ease-settle)}
body.is-ready .book__img img{opacity:1;filter:blur(0)}
.book__caption .cut .line>span{transition-delay:300ms}
.book__caption .cut .line:nth-child(2)>span{transition-delay:350ms}
.book__caption .book__meta.reveal{transition-delay:620ms}
.page-hero .page-hero__meta.reveal{transition-delay:820ms}

/* Parallax (2–5 %) — die Grundskalierung gibt dem Bild Reserve, damit die
   Verschiebung nie den Rahmen freilegt. 1.1 = 5 % Ueberhang oben und unten,
   die Verschiebung betraegt hoechstens 4 %. */
[data-parallax]{transform:translate3d(0,0,0) scale(1.1);will-change:transform;backface-visibility:hidden}
/* Bilder mit groesserem Ausschlag brauchen von Anfang an mehr Ueberhang,
   sonst springt die Skalierung beim ersten JS-Frame sichtbar um. Der Wert
   muss mit data-parallax-scale im Markup uebereinstimmen. */
[data-parallax-scale="1.18"]{transform:translate3d(0,0,0) scale(1.18)}

/* SETTLE: Reduced Motion = Stillstand */
@media (prefers-reduced-motion:reduce){
  .reveal,.reveal--blur,.reveal--mask,.reveal-l,.reveal-r,.reveal-scale,.seq>*,.lift__item,.hero__media,.hero__title,.hero__claim,.hero__scroll,.menu__main,.menu__panel,.exp__plate img,.plate-hover img,.page-hero--image img,.book__img img{opacity:1!important;transform:none!important;filter:none!important;clip-path:none!important;transition:none!important}
  .drift img,.full-image__drift,.atmos__layer,.hero__scroll-arrow{animation:none!important}
  [data-parallax]{transform:none!important}
  .step.is-active{animation:none!important}
  .steps::after{transition:none!important}
}

/* --- Ergänzungen aus 21st.dev-Research (vanilla Ports) --- */
/* Line-Mask Reveal (Vertical Cut Reveal, ohne Spring) */
.cut .line{display:block;overflow:hidden;padding-bottom:0.12em}
/* Weg von 112 % auf 24 % zurueckgenommen: bei voller Zeilenhoehe faehrt jede
   Zeile sichtbar ein, bei den grossen Display-Graden ueber 100 px weit — das
   war die auffaelligste Textbewegung der Seite. Kurzer Weg PLUS Aufblenden
   (vorher gab es keins) liest sich als Setzen statt als Einfahren; ohne die
   Opazitaet saehe der angeschnittene Text bei 24 % wie ein Fehler aus. */
.cut .line>span{display:block;transform:translateY(24%);opacity:0;
  transition:transform var(--dur-reveal) var(--ease-settle),opacity var(--dur-reveal) var(--ease-settle)}
.cut.is-in .line>span{transform:none;opacity:1}
.cut .line:nth-child(2)>span{transition-delay:50ms}
.cut .line:nth-child(3)>span{transition-delay:100ms}
.cut .line:nth-child(4)>span{transition-delay:150ms}
.cut .line:nth-child(5)>span{transition-delay:200ms}
/* Fog: geblurrte tonale Kreise = Licht im Raum (kein Digital-Gradient) */
.fog{position:absolute;inset:-25%;filter:blur(80px);opacity:.9;z-index:-1;pointer-events:none}
.fog i{position:absolute;left:var(--x);top:var(--y);width:55vmax;height:55vmax;border-radius:50%;background:var(--c);
  animation:fogdrift 64s ease-in-out infinite alternate;will-change:transform;transform:translate(-50%,-50%)}
.fog i:nth-child(2){animation-duration:78s;animation-delay:-22s}
.fog i:nth-child(3){animation-duration:92s;animation-delay:-40s}
@keyframes fogdrift{0%{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-58%,-44%) scale(1.08)}100%{transform:translate(-44%,-55%) scale(.96)}}
/* !important bleibt bewusst stehen: die Basisregel oben hat dieselbe
   Spezifitaet (0,2,1) und diese hier gewinnt sonst nur ueber die Quell-
   reihenfolge. opacity muss mit zurueck, seit .cut .line>span aufblendet. */
@media (prefers-reduced-motion:reduce){.fog i{animation:none}
  .cut .line>span{transform:none!important;opacity:1!important;transition:none!important}}

/* --- Wofuer-Liste: mobil faehrt der Text aus einem Verlauf herein ---
   Kein Auslöser und keine Einblendung je Thema (beides wirkte wie „aufpoppen").
   Stattdessen klebt ueber dem unteren Fensterrand ein Schleier in der
   Grundfarbe, der nach oben ausgeht — .wirk__veil, Geometrie in layout.css.
   Der Text laeuft beim Scrollen von unten durch ihn hindurch und wird dabei
   sichtbar. Weil der Schleier am Fenster klebt und der Text sich bewegt, haengt
   der Verlauf 1:1 am Scrollen: nichts springt, nichts setzt ein.
   Die Liste als Ganzes blendet mobil deshalb auch nicht mehr auf — sonst laege
   eine Einblendung ueber dem Verlauf. Ab 900px bleibt alles wie gehabt, dort
   fuehrt die Buehne die Themen durch ihr eigenes Fenster. */
@media (max-width:900px){
  .wirk__list.reveal:not(.reveal--mask):not(.reveal--blur){opacity:1;filter:none}
}

/* --- 2.0: Modell-Bühne --- */
.stack__card,.venn,.venn__ring,.venn__pair,.venn__result{transition:none}
/* Ausstieg aus der Buehne — zwei Bedingungen, ein Regelsatz. Die Liste im
   @media ist ein ODER; Duplizieren waere die einzige Alternative. Unter 820px
   wird nicht mehr fixiert: dort steht ohnehin schon die einspaltige Fassung
   (Bild ueber Text, layout.css), und ein sticky-Kasten mit 100svh wird auf dem
   Telefon bei jedem Ein- und Ausfahren der Adressleiste neu umbrochen — genau
   der Sprung mitten in der Sequenz, den man nicht wegstimmen kann.
   Wer die Buehne auf dem Handy doch fixiert haben will, streicht die zweite
   Bedingung hier und STATISCH in js/main.js.
   Die !important bleiben und sind hier das Werkzeug, nicht das Problem: sie
   muessen die Inline-Stile ueberschreiben, die drawStage im fixierten Betrieb
   geschrieben haben kann — beim Drehen des Geraets oder wenn die System-
   einstellung bei laufender Sitzung umgeschaltet wird. */
@media (prefers-reduced-motion:reduce),(max-width:820px){
  .model-stage__track{height:auto!important}
  .model-stage__sticky{position:static!important;height:auto!important;padding-block:var(--chapter-gap)}
  /* Gestapelt: die zwei Rahmen loesen sich auf (display:contents), Bild und
     Text jedes Moments folgen per order wieder aufeinander. */
  .beats{position:static!important;width:100%!important;height:auto!important;display:grid;grid-template-columns:1fr!important;gap:var(--s-6);
    transform:none!important;opacity:1!important;visibility:visible!important}
  .beats__frame,.beats__copy{display:contents!important}
  /* grid-area:auto loest die geteilte Zelle der Texte auf, sonst laegen alle drei uebereinander. */
  .beat__img,.beat__text{position:static!important;grid-area:auto!important;transform:none!important;opacity:1!important;visibility:visible!important}
  .beat__img{aspect-ratio:16/9;overflow:hidden}
  .beat__img[data-beat="0"]{order:0}.beat__text[data-beat="0"]{order:1}
  .beat__img[data-beat="1"]{order:2}.beat__text[data-beat="1"]{order:3}
  .beat__img[data-beat="2"]{order:4}.beat__text[data-beat="2"]{order:5}
  .beat__text{padding-bottom:var(--s-4)}
  /* Die naechsten Zeilen fangen ab, was drawStage sonst noch inline setzt. */
  .beat__img img{transform:none!important;filter:none!important}
  .beat__def{opacity:1!important;transform:none!important}
  .venn__outer{opacity:1!important}
  .venn__unit{transform:none!important}
  /* Gestapelt heisst auch: Diagramm UNTER den Momenten. Ohne grid-area:auto
     teilen sich .beats und .venn weiter die Zelle 1/1 des Sticky-Rasters und
     das Diagramm laege mitten ueber Moment 02 (Befund 10.09.2026). */
  .model-stage__sticky{display:block!important}
  .beats,.venn{grid-area:auto!important}
  .venn{display:block;width:min(92vw,660px);opacity:1!important;transform:none!important;margin:var(--s-7) auto 0}
  .venn__ring{stroke-dashoffset:0!important;opacity:1!important}
  .venn__pair,.venn__result{opacity:1!important}
  .venn__result{filter:none!important}
}
/* Nur bei reduzierter Bewegung — auf dem Telefon bleibt die Fortschrittslinie
   sichtbar, sie ist Teil des normalen Auftritts. */
@media (prefers-reduced-motion:reduce){.progress{display:none!important}}
