/* ============================================================
   DDL Bühne v1 — die gepinnte Produkt-Bühne (11.09.2026)
   ============================================================
   Kochbuch DDL-MOTION-REZEPTE.md, Rezept 3, Variante „Bühnen-Seite":
   eine Sektion 250vh (mobil 220svh), die Bühne klebt 1,5 Fensterhöhen,
   das Mockup schwenkt ein und baut sich Element für Element auf, drei
   Text-Beats blenden in EINER Zelle durch. Ursprung: dasdensch.de-Hub
   (08.–10.09.), Ansage Dennis 11.09.: „auf ALLE Projektunterseiten".

   SSOT: /densch-shared/ddl/ddl-buehne.css — keine Kopien.
   Braucht: ddl-motion.js (schreibt --scroll-progress auf
   [data-scroll-progress]) + ddl-typography.css (Typo-Tokens, .ddl-eyebrow).
   Mockups + ihre Choreografie: ddl-mockups.css (Präfix mk-).
   Markup: partials/landing/buehne.php (PHP-Landings) — statische Seiten
   (Hub) tragen dasselbe Markup von Hand.

   Farbe kommt von außen:
     --akzent  Eyebrow/Overline/Link   (Fallback --ddl-accent, --accent)
     --marke   Wortmarken-Teil im Titel (Fallback --ddl-accent)
     --wolke   Farbwolke hinter dem Mockup (Fallback --ddl-accent-glow)
     Text aus --ddl-text / --ddl-text-soft (Fallback --text-primary/-secondary).
   Die Produktklassen .showcase--<produkt> (ddl-mockups.css) setzen --marke
   und --wolke nach FARBEN.md; --akzent nur, wo die Seite dunkel ist (Hub).
     --buehne-bg    Hintergrund der Sektion (Hub #000, Landings transparent)
     --buehne-rand  Seitenrand, --buehne-bahn maximale Breite (1200px)

   Presets (.zoom__item, .zoom__mockup) wirken nur INNERHALB von .zoom — dieselben
   Mockups stehen auch in Umschaltern (Markenseite) und bleiben dort still.
   Regeln: kein data-scrub-item in der Bühne · Deko darin ohne
   data-parallax (rect.top steht still) · perspective am direkten Eltern ·
   Reduced Motion löst die Bühne HIER · noscript muss die SEITE lösen
   (Block HINTER dem Seiten-CSS, gleiche Regeln wie der RM-Block unten,
   Variablen mit !important — siehe Kochbuch Rezept 3/10).
   Geometrie: --p = (sp − vh/(vh+H)) / ((H − vh)/(vh+H)); bei 250vh
   0,2857…0,7143, bei 220svh 0,3125…0,6875. Beats 1 ≤ 0,38 · 2 0,30–0,76 ·
   3 ≥ 0,68; Elemente per --ab (Einsatzpunkt) auf die Beats legen.
   ============================================================ */

/* ── Kopf + Link der Bühne ─────────────────────────────── */
.showcase-overline {
    font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.08em;
    text-transform: uppercase; margin-bottom: 16px;
    color: var(--akzent, var(--ddl-accent, var(--accent)));
}
.showcase-title {
    font-size: var(--ddl-fs-title);
    font-weight: 800;
    letter-spacing: var(--ddl-track-title);
    line-height: var(--ddl-lh-title);
    margin-bottom: 12px;
    color: var(--ddl-text, var(--text-primary, inherit));
}
.showcase-title .wm { color: var(--marke, var(--ddl-accent, var(--accent))); }
.showcase-tagline {
    font-size: var(--ddl-fs-subtitle); font-weight: 600; color: var(--ddl-text-soft, var(--text-secondary, inherit));
    margin-bottom: 20px; letter-spacing: var(--ddl-track-tight);
}
.showcase-link {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 1.3125rem; font-weight: 600; text-decoration: none;
    color: var(--akzent, var(--ddl-accent, var(--accent)));
}
/* Hover bewegt nur transform (Regel 4) */
.showcase-link svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; transition: transform var(--ddl-dur-ui) var(--ddl-ease); }
.showcase-link:hover svg { transform: translateX(4px); }

/* ── Die Bühne ──────────────────────────────────────────
   250vh hoch, die Bühne klebt 1,5 Fensterhöhen. Die Engine
   schreibt --scroll-progress über die GANZE Sektion; geklebt
   wird von vh/(vh+H) = 0,2857 bis H/(vh+H) = 0,7143 → --p.
   Drei Text-Beats blenden in EINER Zelle durch, das Mockup
   schwenkt ein und baut sich Element für Element auf.
   Kein data-scrub-item in der Bühne: ohne umgebendes data-scrub
   schriebe nie jemand etwas hin — das Mockup bliebe auf opacity:0.
   ─────────────────────────────────────────────────────── */
.zoom {
    position: relative;
    height: 250vh;
    background: var(--buehne-bg, transparent);
    color: var(--ddl-text, var(--text-primary, inherit));
    --p: clamp(0, calc((var(--scroll-progress, 0) - 0.2857) / 0.4286), 1);
}
.zoom__buehne {
    position: sticky; top: 0;
    height: 100svh; overflow: hidden; isolation: isolate;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto auto;
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem;
    padding: 4rem var(--buehne-rand, clamp(1.25rem, 5vw, 3rem)) 1.5rem;
    max-width: var(--buehne-bahn, 1200px); margin: 0 auto;
}
.zoom__kopf { text-align: center; }
.zoom__kopf .showcase-overline { margin-bottom: 8px; }
.zoom__kopf .showcase-title { font-size: clamp(1.75rem, 1.1rem + 2vw, 2.75rem); margin-bottom: 4px; }
.zoom__kopf .showcase-tagline { margin-bottom: 0; font-size: var(--ddl-fs-lead); }
/* perspective am DIREKTEN Eltern des Tickets — .zoom__buehne hat
   overflow + isolation, dort wäre sie wirkungslos */
.zoom__glas { perspective: 1200px; display: grid; place-items: center; min-height: 0; }
/* Drei Text-Beats in EINER Zelle, per --p überblendet (kein abs(): min + clamp) */
.zoom__texte { display: grid; grid-template-areas: 'stapel'; align-items: end; }
.zoom__text {
    grid-area: stapel; text-align: center;
    opacity: var(--o);
    transform: translateY(calc((1 - var(--o)) * 1rem));
}
/* Aus- und Einblendfenster deckungsgleich (0,30–0,38 / 0,68–0,76):
   die Summe der Opacities bleibt ≈ 1, keine leere Totzone dazwischen */
.zoom__text--1 { --o: clamp(0, calc((0.38 - var(--p)) / 0.08), 1); }
.zoom__text--2 { --o: min(clamp(0, calc((var(--p) - 0.30) / 0.08), 1), clamp(0, calc((0.76 - var(--p)) / 0.08), 1)); }
.zoom__text--3 { --o: clamp(0, calc((var(--p) - 0.68) / 0.08), 1); }
.zoom__text .ddl-eyebrow { font-size: 0.75rem; color: var(--akzent, var(--ddl-accent, var(--accent))); margin-bottom: 0.4rem; }
.zoom__text h3 {
    margin: 0; font-size: clamp(1.375rem, 1rem + 1.8vw, 2.25rem);
    font-weight: 800; letter-spacing: var(--ddl-track-title); line-height: 1.1;
}
.zoom__text p:last-child {
    margin: 0.5rem auto 0; max-width: 34ch;
    font-size: var(--ddl-fs-small); color: var(--ddl-text-soft, var(--text-secondary, inherit));
}
.zoom__link { justify-self: center; }
/* NICHT data-parallax: in der gepinnten Bühne steht rect.top still —
   die Wolke wächst mit --p */
.zoom__wolke {
    position: absolute; z-index: -1;
    left: -10vw; top: 10vh; width: 50vw; max-width: 680px; aspect-ratio: 1;
    border-radius: 50%; filter: blur(60px);
    background: radial-gradient(circle, var(--wolke, var(--ddl-accent-glow, var(--accent-glow, transparent))), transparent 62%);
    transform: scale(calc(0.85 + 0.25 * var(--p)));
    opacity: calc(0.5 + 0.5 * var(--p));
}
/* ── Mockup-Einflug + Elemente. Die Presets der Scrub-Engine
   (phone3d, tile-pop, slide-x, stack) in CSS nachgebaut, nur aus --p
   gerechnet — dieselben Zahlen wie in ddl-motion.js, ohne Exit.
   --dir: Mockup links = 1 (Elemente kommen von links), rechts = −1 */
.zoom { --dir: 1; }
.zoom--rechts { --dir: -1; }
.zoom .zoom__mockup {
    zoom: var(--mz, 1);
    transform: translateX(calc((1 - var(--p)) * -120px * var(--dir)))
               translateY(calc((1 - var(--p)) * 60px))
               rotateY(calc((8deg + (1 - var(--p)) * 34deg) * var(--dir)))
               rotateX(calc(2deg + (1 - var(--p)) * 10deg))
               scale(calc(0.7 + 0.3 * var(--p)));
    opacity: calc(0.45 + 0.55 * var(--p));
    will-change: transform;
}
.zoom .zoom__item {
    --o: clamp(0, calc((var(--p) - var(--ab, 0.3)) / 0.08), 1);
    opacity: var(--o);
}
.zoom .zoom__item--pop {
    transform: translateY(calc((1 - var(--o)) * 30px))
               scale(calc(0.8 + 0.2 * var(--o)))
               rotateX(calc((1 - var(--o)) * 15deg));
}
.zoom .zoom__item--x {
    transform: translateX(calc((1 - var(--o)) * -60px * var(--dir)))
               rotateY(calc((1 - var(--o)) * 25deg * var(--dir)))
               scale(calc(0.9 + 0.1 * var(--o)));
}
.zoom .zoom__item--stapel {
    transform: translateX(calc((1 - var(--o)) * -80px * var(--dir)))
               translateY(calc((1 - var(--o)) * 40px))
               rotateY(calc((1 - var(--o)) * 30deg * var(--dir)))
               rotateZ(calc((1 - var(--o)) * -4deg * var(--dir)))
               scale(calc(0.85 + 0.15 * var(--o)));
}
/* Mockup rechts: Glas in Spalte 2, Beats + Link in Spalte 1, Wolke rechts */
.zoom--rechts .zoom__wolke { left: auto; right: -10vw; }
@media (min-width: 56.25rem) {
    .zoom__buehne {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        grid-template-rows: auto minmax(0, 1fr) auto;
        column-gap: clamp(2rem, 6vw, 5rem);
        padding-top: 5rem;
    }
    .zoom__kopf { grid-column: 1 / -1; }
    .zoom__glas { grid-row: 2 / 4; }
    .zoom__texte { grid-row: 2; align-items: center; }
    .zoom__text { text-align: left; }
    .zoom__text p:last-child { margin-inline: 0; }
    .zoom__link { grid-row: 3; grid-column: 2; justify-self: start; }
    .zoom--rechts .zoom__glas { grid-column: 2; }
    .zoom--rechts .zoom__texte { grid-column: 1; }
    .zoom--rechts .zoom__link { grid-column: 1; }
}
/* Niedrige Desktop-Fenster (1280×720): das iPhone (570 px) passte
   nicht mehr in die 1fr-Zeile — Mockups eine Stufe kleiner */
@media (min-width: 56.25rem) and (max-height: 47.5rem) {
    .zoom .zoom__mockup { --mz: 0.8; }
}
/* Schmal: 220svh (→ Fenster 0,3125…0,6875), flacherer Schwenk,
   sonst ragt das gekippte Ticket über die Bahn */
@media (max-width: 56.24rem) {
    .zoom { height: 220svh; --p: clamp(0, calc((var(--scroll-progress, 0) - 0.3125) / 0.375), 1); }
    .zoom .zoom__mockup {
        --mz: 0.74;                       /* iPhone: 570 px → 422 in 481 (390×844) */
        transform: translateY(calc((1 - var(--p)) * 2rem))
                   rotateY(calc((1 - var(--p)) * 12deg * var(--dir)))
                   scale(calc(0.72 + 0.28 * var(--p)));
    }
}
/* Kleine Handys (≤ 768 px hoch, z. B. 360×740, iOS mit Leisten):
   das volle Ticket wäre 432 px hoch und passte nicht mehr in die
   1fr-Zeile der klebenden Bühne — die zeigt ihren unteren Teil nie. */
@media (max-height: 48rem) and (max-width: 56.24rem) {
    .zoom__kopf .showcase-tagline { display: none; }
}

/* ── Reduced Motion (Regel 8): die Bühne LÖSEN, nicht nur --p:1 —
   mit --p:1 allein läge das ungeclippte Mockup in der 100svh-Zeile
   mobil über Titel und Beat. initScrollProgress hat kein RM-Gate. ── */
@media (prefers-reduced-motion: reduce) {
    .zoom { --p: 1; height: auto; }
    .zoom__buehne { position: static; height: auto; overflow: visible; }
    .zoom__mockup { transform: none; opacity: 1; }
    .zoom__item { opacity: 1; transform: none; }
    .zoom__texte { gap: 1.5rem; grid-template-areas: none; }
    .zoom__text { grid-area: auto; opacity: 1; transform: none; }
    .zoom__wolke { transform: none; opacity: 1; }
}
@media print {
    .zoom { height: auto; }
    .zoom__buehne { position: static; height: auto; }
    .zoom__wolke { display: none; }
}
