/* [presentation_hero] — cinematic distillery intro
 *
 * Based on Figma "Bimmerle-Animation" / "hello.bimmerle.de - Introduction V3"
 * (easing curves are kept from Figma's exported motion data). Re-choreographed
 * as a crane-UP shot: the view starts down inside the forest with every layer
 * zoomed in, then lifts out — the foreground trees sweep downward and shrink
 * away while the far layers barely move, and the content rises into the
 * clearing that opens. Full detail in the Motion section below. Runtime ~8.5s.
 *
 * Playback: presentation_hero.js adds .is-js at once (every layer paused on
 * frame one), then .is-visible to play the intro. On a normal page that fires
 * once when the hero scrolls into view. Inside an Impreza Presentations slide
 * it follows the slide's `is-active` class: intro on enter, the .ph-leaving
 * reverse-crane on exit (see the Leave section), intro again on re-entry.
 */

.presentation_hero,
.presentation_hero *,
.presentation_hero *::before,
.presentation_hero *::after {
  box-sizing: border-box;
}

.presentation_hero {
  position: relative;

  /* Shared easing. Everything decelerates into its end value (second handle
     y = 1, x well short of 1) so nothing "pops" to a stop:
       sweep  - the big camera moves: eases gently off the mark, then a long
                slow glide to rest (no lurch at either end)
       settle - content / claims / kettles: soft, even deceleration
       fade   - opacity always eases IN to its target, never accelerates into it */
  --ph-ease-sweep: cubic-bezier(0.4, 0.05, 0.18, 1);
  --ph-ease-settle: cubic-bezier(0.25, 1, 0.35, 1);
  --ph-ease-fade: cubic-bezier(0.33, 1, 0.68, 1);

  /* Leave animation (see .ph-leaving): the reverse crane back down into the
     forest. Much quicker than the intro so it doesn't hold up a slide change. */
  --ph-leave-dur: 1.5s;

  /* Full-bleed: break out of Impreza's / WPBakery's max-width content column
       so the scene always spans the whole viewport width. */
  width: auto;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  background: #07090f;
}

.ph-stage {
  position: absolute;
  inset: 0;
}

/* ---------------------------------------------------------------------------
   Shared keyframes — parameterised per element via custom properties
   --------------------------------------------------------------------------- */

@keyframes ph-fade {
  from {
    opacity: 0;
  }
  to {
    opacity: var(--ph-o, 1);
  }
}

@keyframes ph-rise {
  from {
    translate: 0 var(--ph-y, 0);
  }
  to {
    translate: 0 var(--ph-y-end, 0);
  }
}

@keyframes ph-zoom {
  from {
    scale: var(--ph-s, 1);
  }
  to {
    scale: 1;
  }
}

/* Leave keyframes: `to` only, so the synthesised `from` is each element's
   current resting value — the reverse of the intro, jump-free. */
@keyframes ph-leave-y {
  to {
    translate: 0 var(--ph-y, 0);
  }
}

@keyframes ph-leave-s {
  to {
    scale: var(--ph-s, 1);
  }
}

@keyframes ph-leave-o {
  to {
    opacity: 0;
  }
}

/* ---------------------------------------------------------------------------
   Layout
   --------------------------------------------------------------------------- */

.ph-layer {
  position: absolute;
  /* Exactly the viewport box: object-fit: cover then only has to do the
       unavoidable source-to-display scale, nothing extra. The forest layers
       only translate vertically, and the initial scale keyframe zooms IN
       (past the edges), so 100% never exposes a side edge. */
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: center;
  pointer-events: none;
  will-change: transform, opacity;
}

.ph-forest-bg {
  z-index: 1;
}
.ph-forest-mid {
  z-index: 2;
  /* Resting offset baked into the base state (the intro animation also ends
     here). Keeps base == final so the leave animation starts without a jump. */
  translate: 0 3.6%;
}

/* The backdrop is the one layer that slides down from the top edge while
   already visible; a small vertical buffer keeps its top edge covered during
   that ~2s. Only 8% and only on the (soft, low-detail) sky layer. */
.ph-forest-bg {
  top: -4%;
  height: 108%;
}

/* forest_fg is a wrapper (not a bare .ph-layer): the treeline image plus an
   opaque "floor" that extends it downwards. While the wrapper is craned up out
   of frame the floor fills the whole viewport — you're still down among / below
   the near trees and can't see the distance yet. It rests just below the bottom
   edge, so it's invisible in the final frame. */
.ph-forest-fg {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  will-change: transform, opacity;
  /* Resting offset baked in (see .ph-forest-mid) — base == intro end state. */
  translate: 0 23.9%;
}

.ph-forest-fg__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: center;
}

.ph-forest-fg__floor {
  position: absolute;
  left: -2%;
  right: -2%;
  top: 80%;
  height: 300%;
  background: linear-gradient(
    to bottom,
    transparent 0,
    #07090f 7%,
    #07090f 100%
  );
}

/* The scene (forests, fog) stays full-bleed; the content — logo, claims,
   kettles — is capped so it doesn't spread edge-to-edge on very wide screens.
   Its children position against this box, not the viewport. */
.ph-content {
  position: absolute;
  inset: 0;
  max-width: 1800px;
  margin-inline: auto;
  z-index: 3;
  pointer-events: none;
  will-change: transform, opacity;
}

.ph-logo {
  position: absolute;
  left: 10%;
  top: 15%;
  width: clamp(260px, 27vw, 725px);
  height: auto;
}

.ph-claims {
  position: absolute;
  left: 10%;
  top: 38%;
  margin: 0;
  padding: 0;
  color: #fff;
  font-weight: 300;
  font-optical-sizing: auto;
  font-size: clamp(31.35px, 2.97vw, 69.3px);
  line-height: 1.2;
  letter-spacing: 0.005em;
}

.ph-claims p {
  margin: 0;
  will-change: transform, opacity;
}

.ph-claim-title {
  font-weight: 200;
  font-family:
    "DM Sans",
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    Roboto,
    sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.ph-claims .ph-claim-meta {
  display: flex;
  align-items: center;
  gap: 0.55em;
  margin-top: 1.1em;
  font-size: 0.55em;
  line-height: 1.3;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
}

.ph-claim-dot {
  color: #fff;
}

.ph-kettle {
  position: absolute;
  height: auto;
  pointer-events: none;
  will-change: transform, opacity;
}

.ph-kettle-back {
  right: 8%;
  top: 9%;
  width: clamp(190px, 21vw, 479px);
  z-index: 1;
  /* Resting opacity baked in (see .ph-forest-mid) — base == intro end state,
     so removing the intro animation on leave doesn't flash back to fully
     opaque before the leave fade-out takes over. */
  opacity: 0.5;
}
.ph-kettle-front {
  right: 20%;
  top: 3%;
  width: clamp(220px, 24vw, 560px);
  z-index: 2;
  opacity: 0.94;
}

.ph-fog {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
  will-change: opacity;
  /* Resting opacity baked in — see .ph-kettle-back above. */
  opacity: 0.5;
}

/* Two identical tiles scrolling as one track — the loop is seamless because
   fog_seamless.webp tiles horizontally, so after one image-width of travel the
   second copy sits exactly where the first started. */
.ph-fog__track {
  display: flex;
  width: 200%;
  height: 100%;
  transform: translateX(-50%);
  will-change: transform;
}

.ph-fog__track img {
  flex: 0 0 50%;
  width: 50%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: center;
}

@keyframes ph-fog-scroll {
  from {
    transform: translateX(-50%);
  }
  to {
    transform: translateX(0);
  }
}

/* Client-logo strip — a full-width marquee. The clip box carries the edge
   fade; the track inside scrolls left-to-right forever (two identical copies,
   so translateX(-50%) -> 0 loops seamlessly). */
.ph-kunden {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 6.5%;
  z-index: 6;
  overflow: hidden;
  pointer-events: none;
  /* Resting opacity baked in — see .ph-kettle-back above. */
  opacity: 0.3;
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent 0,
    #000 17%,
    #000 83%,
    transparent 100%
  );
  mask-image: linear-gradient(
    90deg,
    transparent 0,
    #000 17%,
    #000 83%,
    transparent 100%
  );
}

.ph-kunden__track {
  display: flex;
  width: max-content;
  align-items: center;
  gap: clamp(34px, 6vw, 110px);
  /* trailing gap so the two copies tile exactly: half the track width then
     equals one full copy + one gap, i.e. one seamless period */
  padding-right: clamp(34px, 6vw, 110px);
  transform: translateX(-50%);
  will-change: transform;
}

.ph-kunden__track img {
  flex: 0 0 auto;
  height: clamp(22px, 3vw, 52px);
  width: auto;
}

@keyframes ph-kunden-scroll {
  from {
    transform: translateX(-50%);
  }
  to {
    transform: translateX(0);
  }
}

/* ---------------------------------------------------------------------------
   Motion — one <animation> per animated property.

   Concept: the camera cranes straight UP past the forest. The tree layers
   enter from ABOVE the top edge and travel the full height of the frame
   downward, near layers moving much further/faster than far ones (parallax),
   settling into the resting composition. The content rises up to meet the
   camera and is revealed while the forest is still sweeping past — the two
   overlap, they don't happen in sequence.

   --ph-y = START offset (negative = above the resting spot),
   --ph-y-end = resting offset, --ph-s = START zoom. Easing from Figma where
   kept; the big vertical passes use a steady-then-settle ease-out.

   The fades are kept short so the sweep is visible from the first moment —
   otherwise the camera has already travelled halfway before you can see it.

   Rough sequence (heavy overlap, slow crane):
     forest_fg   0.0s  well above the frame  ->  6.8s  resting treeline (near: ~150% travel)
     forest_mid  0.0s  ->  6.8s   (~79% travel)
     forest_bg   0.0s  ->  6.8s   (far plane — barely moves, always covers)
     content     1.6s  rising up, revealed mid-sweep  ->  6.4s
     kettles     3.2s  ->  6.8s
     claim lines 4.0s  ->  7.2s   (staggered, 0.4s apart)
     client strip 6.0s ->  8.6s   (fade only)
   --------------------------------------------------------------------------- */

/* Master container fade (Figma node 1:347) — lift off black */
.ph-stage {
  --ph-o: 1;
  animation: ph-fade 2.6s var(--ph-ease-fade) 0s 1 both;
}

/* forest_bg (1:345) — far plane: almost stationary, just holds the frame
   covered behind the layers that do sweep. */
.ph-forest-bg {
  --ph-y: -3%;
  --ph-y-end: 0%;
  --ph-s: 1.1;
  animation:
    ph-fade 2.4s var(--ph-ease-fade) 0.2s 1 both,
    ph-rise 6.8s var(--ph-ease-sweep) 0s 1 both,
    ph-zoom 6.8s var(--ph-ease-sweep) 0s 1 both;
}

/* forest_mid (1:344) — mid plane: enters high, sweeps most of the frame down */
.ph-forest-mid {
  --ph-y: -75%;
  --ph-y-end: 3.6%;
  --ph-s: 1.18;
  animation:
    ph-fade 2.4s var(--ph-ease-fade) 0.1s 1 both,
    ph-rise 6.8s var(--ph-ease-sweep) 0s 1 both,
    ph-zoom 6.8s var(--ph-ease-sweep) 0s 1 both;
}

/* forest_fg (1:343) — THE crane move: the near trees + the opaque floor start
   well above the top edge (floor still filling the whole viewport) and sweep
   the full height of the frame downward, past the camera, uncovering the
   distance from the top down. Biggest travel = closest to camera. No fade —
   the floor must be solid from frame one. */
.ph-forest-fg {
  --ph-y: -130%;
  --ph-y-end: 23.9%;
  --ph-s: 1.3;
  animation:
    ph-rise 6.8s var(--ph-ease-sweep) 0s 1 both,
    ph-zoom 6.8s var(--ph-ease-sweep) 0s 1 both;
}

/* fog (Figma node 4:666) — fades in once with the intro … */
.ph-fog {
  --ph-o: 0.5;
  animation: ph-fade 2.6s var(--ph-ease-fade) 0s 1 both;
  animation-delay: 2.6s;
}

/* … then drifts left-to-right forever, like slow background smoke */
.ph-fog__track {
  animation: ph-fog-scroll 60s linear infinite;
}

/* Hero Content group (1:463) — logo + claims + kettles. Rises up into the
   clearing the foreground opens, with a small de-zoom so it reads as the
   view still drifting toward it. */
.ph-content {
  --ph-y: 16%;
  --ph-y-end: 0%;
  --ph-s: 1.06;
  animation:
    ph-fade 4.4s var(--ph-ease-fade) 1.9s 1 both,
    ph-rise 5s var(--ph-ease-settle) 1.6s 1 both,
    ph-zoom 5s var(--ph-ease-settle) 1.6s 1 both;
}

/* Claim title and metadata — identical motion, staggered start */
.ph-claims p {
  --ph-y: 99.735px;
  animation:
    ph-fade 2s var(--ph-ease-fade) var(--ph-d, 0s) 1 both,
    ph-rise 2s var(--ph-ease-settle) var(--ph-d, 0s) 1 both;
}

.ph-claims p:nth-child(1) {
  --ph-d: 4s;
}
.ph-claims p:nth-child(2) {
  --ph-d: 4.2s;
}
.ph-claims p:nth-child(3) {
  --ph-d: 4.4s;
}

/* Brennkessel back (1:554) — rises inside the content group, out from behind fg */
.ph-kettle-back {
  --ph-o: 0.5;
  --ph-y: 132.54px;
  animation:
    ph-fade 3.6s var(--ph-ease-fade) 3.2s 1 both,
    ph-rise 3.6s var(--ph-ease-settle) 3.2s 1 both;
}

/* Brennkessel front (1:555) */
.ph-kettle-front {
  --ph-o: 0.94;
  --ph-y: 111.54px;
  animation:
    ph-fade 3.6s var(--ph-ease-fade) 3.4s 1 both,
    ph-rise 3.6s var(--ph-ease-settle) 3.4s 1 both;
}

/* Client-logo strip (1:491) — fades in last … */
.ph-kunden {
  --ph-o: 0.3;
  animation: ph-fade 2.6s var(--ph-ease-fade) 6s 1 both;
}

/* … and drifts left-to-right forever behind the fade */
.ph-kunden__track {
  animation: ph-kunden-scroll 55s linear infinite;
}

/* ---------------------------------------------------------------------------
   Playback gating
   --------------------------------------------------------------------------- */

/* JS present: hold every layer on its first frame until the element is seen */
.presentation_hero.is-js .ph-anim {
  animation-play-state: paused;
}

.presentation_hero.is-js.is-visible .ph-anim,
.presentation_hero.is-js.ph-leaving .ph-anim {
  animation-play-state: running;
}

/* ---------------------------------------------------------------------------
   Leave — the reverse crane: back down into the forest.

   presentation_hero.js adds .ph-leaving (and drops .is-visible) when the hero's
   presentation slide is navigated away from. The foreground floor rises back up
   and re-covers the scene, the content sinks and fades, every layer returns to
   its pre-intro position. Removing .ph-leaving on re-entry restarts the intro.
   --------------------------------------------------------------------------- */

.presentation_hero.ph-leaving .ph-anim {
  animation:
    ph-leave-y var(--ph-leave-dur) var(--ph-ease-sweep) 0s 1 forwards,
    ph-leave-s var(--ph-leave-dur) var(--ph-ease-sweep) 0s 1 forwards,
    ph-leave-o 0.7s ease 0s 1 forwards;
}

/* Stage + forest layers + the opaque floor move/zoom back but never fade —
   they are the forest we drop into, and the floor must stay solid as it
   climbs back over everything. */
.presentation_hero.ph-leaving .ph-stage,
.presentation_hero.ph-leaving .ph-layer,
.presentation_hero.ph-leaving .ph-forest-fg {
  animation:
    ph-leave-y var(--ph-leave-dur) var(--ph-ease-sweep) 0s 1 forwards,
    ph-leave-s var(--ph-leave-dur) var(--ph-ease-sweep) 0s 1 forwards;
}

/* ---------------------------------------------------------------------------
   Reduced motion — skip the intro, render the final composed frame
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .presentation_hero .ph-anim {
    animation: none !important;
  }

  /* forest_mid / forest_fg resting offsets are base rules now. */
  .ph-stage {
    opacity: 1;
  }
  .ph-fog {
    opacity: 0.5;
  }
  .ph-kettle-back {
    opacity: 0.5;
  }
  .ph-kettle-front {
    opacity: 0.94;
  }
  .ph-kunden {
    opacity: 0.3;
  }
}
