/* ==========================================================================
   The living flyer stage.

   Layout (position/size of each layer) is generated into flyer-layout.css from
   the decomposition bounding boxes. This file owns the stage itself, the
   entrance, the idle loops and the interactions.
   ========================================================================== */

/* No frame, no panel, no shadow. The page's paper IS the flyer's paper, so the
   stage is just an invisible coordinate space the artwork is positioned in.
   overflow stays visible so pieces can sit past its edges rather than being
   clipped to a rectangle that would read as a card again. */
.flyer-stage {
  position: relative;
  aspect-ratio: 3 / 4;
  width: min(100%, calc(88vh * 3 / 4));
  max-width: 720px;
  margin-inline: auto;
  overflow: visible;
  background: none;
  border: 0;
}

/* Every stage layer. Position and width come from flyer-layout.css. */
.fl {
  position: absolute;
  display: block;
  height: auto;
  /* set by JS for pointer/scroll parallax; composes with the loop transforms */
  translate: var(--px, 0) var(--py, 0);
  /* Lettering sits at z-index 20, above the doodles at 4. Without this the
     text layers swallow taps aimed at a doodle underneath them. */
  pointer-events: none;
}
.fl[data-poke], .fl[data-pop] { pointer-events: auto; cursor: pointer; }

/* --------------------------------------------------------------------------
   Entrance — runs once, driven by .is-ready on the stage
   -------------------------------------------------------------------------- */

.js-reveal .flyer-stage .fl {
  opacity: 0;
  transition: opacity .7s ease, transform .9s cubic-bezier(.2,.75,.3,1),
              clip-path .9s cubic-bezier(.2,.75,.3,1);
  transition-delay: var(--in-delay, 0s);
}
.js-reveal .flyer-stage.is-ready .fl { opacity: 1; transform: none; }

/* pieces drift up into place */
.js-reveal .flyer-stage .fl-piece { transform: translateY(18px) scale(.97); }

/* lettering wipes in like a marker stroke */
.js-reveal .flyer-stage .fl-wipe { clip-path: inset(0 100% 0 0); }
.js-reveal .flyer-stage.is-ready .fl-wipe { clip-path: inset(0 -2% 0 0); }

/* underlines draw left to right */
.js-reveal .flyer-stage .fl-draw { clip-path: inset(0 100% 0 0); }
.js-reveal .flyer-stage.is-ready .fl-draw { clip-path: inset(0 -2% 0 0); }

/* the subject rises */
.js-reveal .flyer-stage .fl-subject { transform: translateY(28px); }

/* --------------------------------------------------------------------------
   Idle loops
   -------------------------------------------------------------------------- */

.flyer-stage.is-ready .anim-bob    { animation: fl-bob 2.6s ease-in-out infinite alternate; }
.flyer-stage.is-ready .anim-sway   { animation: fl-sway 3.2s ease-in-out infinite alternate; }
.flyer-stage.is-ready .anim-float  { animation: fl-float 3.8s ease-in-out infinite alternate; }
.flyer-stage.is-ready .anim-float2 { animation: fl-float 4.6s ease-in-out infinite alternate -1.4s; }
.flyer-stage.is-ready .anim-tilt   { animation: fl-tilt 3.4s ease-in-out infinite alternate; }
.flyer-stage.is-ready .anim-breathe{ animation: fl-breathe 6.5s ease-in-out infinite alternate; }
.flyer-stage.is-ready .anim-drift  { animation: fl-drift 24s linear infinite; }
.flyer-stage.is-ready .anim-shimmer{ animation: fl-shimmer 5.5s ease-in-out infinite; }
.flyer-stage.is-ready .anim-flicker{ animation: fl-flicker .5s ease-in-out infinite alternate; }

/* Offset the shared loops so the doodles don't pulse in lockstep. */
.flyer-stage.is-ready .fl-crown-right { animation-delay: -1.1s; }
.flyer-stage.is-ready .fl-music-note  { animation-delay: -2.0s; }
.flyer-stage.is-ready .fl-party-hat   { animation-delay: -0.7s; }

@keyframes fl-bob {
  from { transform: translateY(2px) rotate(-3.5deg); }
  to   { transform: translateY(-20px) rotate(4.5deg); }
}
/* Rotation alone is nearly invisible on a small doodle — the shape barely
   changes and the centre doesn't move. The travel is what reads. */
@keyframes fl-sway {
  from { transform: translate(-3px, 5px) rotate(-8deg); }
  to   { transform: translate(3px, -11px) rotate(8deg); }
}
@keyframes fl-float {
  from { transform: translateY(3px) rotate(-1.5deg); }
  to   { transform: translateY(-9px) rotate(2deg); }
}
@keyframes fl-tilt {
  from { transform: rotate(-5deg) translateY(2px); }
  to   { transform: rotate(6deg) translateY(-7px); }
}
@keyframes fl-breathe {
  from { transform: scale(1); }
  to   { transform: scale(1.014); }
}
@keyframes fl-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-3.5%, 5%, 0); }
}
/* Alpha sweep across the gold script — mask only affects opacity, so a band
   that never drops far below 1 reads as a shine rather than a hole. */
@keyframes fl-shimmer {
  0%, 100% { -webkit-mask-position: 140% 0; mask-position: 140% 0; }
  55%      { -webkit-mask-position: -40% 0; mask-position: -40% 0; }
}
@keyframes fl-flicker {
  from { opacity: .78; transform: scaleY(.94) skewX(-2deg); }
  to   { opacity: 1;   transform: scaleY(1.06) skewX(2.5deg); }
}

.anim-shimmer {
  -webkit-mask-image: linear-gradient(100deg, #fff 0 38%, rgba(255,255,255,.55) 47%, #fff 56% 100%);
  mask-image: linear-gradient(100deg, #fff 0 38%, rgba(255,255,255,.55) 47%, #fff 56% 100%);
  -webkit-mask-size: 260% 100%;
  mask-size: 260% 100%;
}

.anim-flicker { transform-origin: 50% 100%; }
/* Balloons and the hat pivot from where they'd be tethered, not their middle. */
.anim-bob { transform-origin: 50% 96%; }
.anim-sway { transform-origin: 50% 90%; }
.anim-float, .anim-float2 { transform-origin: 50% 60%; }
.anim-tilt { transform-origin: 50% 90%; }

/* Battery: stop every loop once the poster is off screen. */
.flyer-stage.is-idle * { animation-play-state: paused !important; }

/* --------------------------------------------------------------------------
   Poke reactions
   -------------------------------------------------------------------------- */

.fl.is-poked.anim-bob  { animation: fl-bounce .9s cubic-bezier(.2,1.6,.4,1); }
.fl.is-poked.anim-sway { animation: fl-wobble .8s ease-in-out; }
.fl.is-poked           { animation: fl-wobble .8s ease-in-out; }

@keyframes fl-bounce {
  0%   { transform: translateY(0) rotate(0); }
  30%  { transform: translateY(-26px) rotate(-7deg); }
  60%  { transform: translateY(-6px) rotate(5deg); }
  100% { transform: translateY(0) rotate(0); }
}
@keyframes fl-wobble {
  0%, 100% { transform: rotate(0); }
  25%      { transform: rotate(-7deg); }
  55%      { transform: rotate(6deg); }
  80%      { transform: rotate(-3deg); }
}

/* --------------------------------------------------------------------------
   Stage surroundings
   -------------------------------------------------------------------------- */

.stage-wrap { padding: clamp(.5rem, 2.5vw, 1.75rem) 0 0; }

.stage-cta {
  display: flex; flex-wrap: wrap; justify-content: center; gap: .75rem;
  margin: clamp(1.25rem, 3vw, 2rem) auto 0;
}

/* Small screens: the artwork runs edge to edge, past the page gutters, so it
   reads as the page rather than something placed on it.

   Cancelling .wrap's gutter directly rather than using 100vw — 100vw includes
   the scrollbar, so it overflows the viewport by the scrollbar's width. */
@media (max-width: 560px) {
  .flyer-stage {
    width: auto;
    max-width: none;
    margin-inline: -1.25rem;   /* .wrap is `100% - 2.5rem`, so 1.25rem a side */
  }
}

/* --------------------------------------------------------------------------
   Motion preferences — everything above is decoration
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .js-reveal .flyer-stage .fl {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }
  .flyer-stage * { animation: none !important; }
  .fl { translate: none !important; }
  }
