/* ── Scroll animations — reusable on-scroll effects ──────────────────────
   Pairs with js/animations.js, which adds `.is-visible` when an element first
   scrolls into view, sets `pathLength="1"` on drawable SVG children and numbers
   them into `--anim-i`. New effect = a block here + its class in the JS
   SELECTORS list. */


/* ── .anim-draw — draw an SVG's strokes on ──────────────────────────────── */

.anim-draw {
  --draw-duration : 2.4s;
  --draw-ease     : ease-in-out;
  --draw-delay    : 0s;
  --draw-stagger  : 0s;

  display   : block;
  overflow  : visible;  /* round caps and thick strokes sit outside the viewBox */
  width     : 100%;
  height    : auto;
  max-width : var(--draw-max-width, none);
  /* Decoration — never swallow a click meant for something underneath. */
  pointer-events : none;
}

.anim-draw :is( path, line, polyline, polygon, circle, ellipse, rect ) {
  /* Gap 2, not 1 — at 1 a dash boundary lands on the end point and the round
     cap paints a stray dot there before the animation starts. */
  stroke-dasharray  : 1 2;
  stroke-dashoffset : 1;
  stroke-linecap    : var(--draw-cap, round);
}

.anim-draw.is-visible :is( path, line, polyline, polygon, circle, ellipse, rect ) {
  animation       : eos-draw var(--draw-duration) var(--draw-ease) forwards;
  animation-delay : calc( var(--draw-delay) + var(--draw-stagger) * var(--anim-i, 0) );
}

@keyframes eos-draw {
  to { stroke-dashoffset: 0; }
}


/* ── .anim-pop — stamp a sticker on ─────────────────────────────────────────
   Scales up from small with a twist and an overshoot. Goes on a native Divi
   image module's CSS Class field; knobs go in Advanced → Custom CSS → Main
   Element. */

.anim-pop {
  --pop-duration : 0.5s;
  --pop-delay    : 0s;
  --pop-from     : 0.6;
  --pop-rotate   : -6deg;
  --pop-origin   : center;
  --pop-ease     : cubic-bezier( 0.34, 1.56, 0.64, 1 );
}

/* The child animates, so Divi's positioning transform on the module survives. */
.anim-pop > * {
  display          : block;
  opacity          : 0;
  transform        : scale( var(--pop-from) ) rotate( var(--pop-rotate) );
  transform-origin : var(--pop-origin);
  will-change      : transform, opacity;
}

.anim-pop.is-visible > * {
  opacity    : 1;
  transform  : none;
  transition : opacity   calc( var(--pop-duration) * 0.5 ) ease-out          var(--pop-delay),
               transform var(--pop-duration)               var(--pop-ease)   var(--pop-delay);
}

/* ── Reduced motion — jump straight to the finished state ─────────────────
   Never leave content hidden behind a disabled animation. */

@media (prefers-reduced-motion: reduce) {

  .anim-draw :is( path, line, polyline, polygon, circle, ellipse, rect ) {
    stroke-dashoffset : 0;
    animation         : none;
  }

  .anim-pop > *,
  .anim-pop.is-visible > * {
    opacity    : 1;
    transform  : none;
    transition : none;
  }
}

/* ── .anim-contain — stretch a drawing to fill its box ≥981px ──────────────
   Needs all three: class `anim-draw anim-contain` on the <svg>,
   `preserveAspectRatio="none"` on the <svg>, and Design → Sizing Height 100%
   (desktop) / auto (tablet) on the module. Organic linework only — `none`
   distorts anything with a readable shape.
   Never add `vector-effect: non-scaling-stroke` — it makes the browser ignore
   `pathLength="1"` and silently breaks the draw reveal. */

@media (min-width: 981px) {

  /* Passes the height through Divi's `.et_pb_code_inner` (height:auto), which a
     percentage height on the SVG has nothing to resolve against. Matches only
     the drawing's direct parent, whatever element that is. */
  *:has( > .anim-contain ) {
    height : 100%;
  }

  .anim-contain {
    height : 100%;
  }
}


/* ── .anim-bleed — position a drawing against its SECTION ──────────────────
   Makes the row and column static so an absolutely positioned drawing resolves
   against the section instead of the column, and one percentage means the same
   thing at every breakpoint. `:has()` scopes it to the row and column that hold
   a bleed drawing. Pair with `overflow: hidden` on the section to clip it. */

.et_pb_row:has( .anim-bleed ),
.et_pb_column:has( > .anim-bleed ) {
  position : static;
}


/* ── Below 981px — let a drawing run on past the card's bottom ────────────
   Stacked rows leave the card far shorter than the drawing, so the column was
   slicing the line flat part-way down with empty card beneath it. Open the
   BOTTOM edge only: the line carries on downward and is covered by the photo
   below (a drawing's module precedes that photo in the DOM, so it paints
   behind it). Top and sides stay clipped — opening the top instead lets the
   line run over the photo in the section above. */

@media (max-width: 980px) {

  .et_pb_column:has( svg.anim-draw ) {
    overflow  : visible;
    clip-path : inset( 0 0 -100vh 0 );
  }
}


/* ── .anim-contain below 981px — size to the card, not to the width ───────
   Left at its natural aspect a tall drawing runs to 2.5x the stacked card's
   height, so the crop shows a meaningless slice of it. Give it 130% of the
   card — the same proportion as the drawings that already sit well — and the
   whole shape reads, with the overshoot running on behind the photo below.
   Divi sets `height: auto` on the module here, and `.et_pb_code_inner` is
   auto too, so both need a height for the SVG's percentage to resolve. */

@media (max-width: 980px) {

  .et_pb_module:has( .anim-contain ) {
    height : 130%;
  }

  .et_pb_module:has( .anim-contain ) *:has( > .anim-contain ) {
    height : 100%;
  }

  .anim-contain {
    height : 100%;
    width  : 100%;
  }
}


/* ── .anim-wag — idle motion for a flat, one-piece drawing ────────────────
   For artwork where the subject and its motion marks are a single picture:
   the SVG carries the SAME image twice, each copy clipped to one of the two,
   so the subject can swing while its whoosh marks hold their place and pulse
   on the beat. Without the split, a rotation drags the whoosh marks along and
   the motion reads stiff.

   `--wag-origin` is the pivot — where the subject leaves the frame — written
   as percentages of the viewBox. `.anim-wag--flap` keeps the same pivot and
   knobs but swaps the even swing for a lighter double-beat flutter.

   Markup (in a Divi code module; `.anim-pop` keeps the stamp-on entrance):
     <div class="anim-pop">
       <svg class="anim-wag" viewBox="0 0 W H" data-anim-repeat
            style="--wag-origin: 0% 64%">
         <defs>
           <clipPath id="…-body"><path clip-rule="evenodd" d="<frame><marks>"/></clipPath>
           <clipPath id="…-lines"><path d="<marks>"/></clipPath>
         </defs>
         <g class="anim-wag__body"  clip-path="url(#…-body)"> <image …/></g>
         <g class="anim-wag__lines" clip-path="url(#…-lines)"><image …/></g>
       </svg>
     </div>

   `data-anim-repeat` matters here: this animation loops forever, so letting
   the observer drop `.is-visible` on exit parks it while it's off-screen. */

.anim-wag {
  --wag-angle     : 3deg;      /* peak of the swing */
  --wag-back      : -2deg;     /* the back-swing it returns to */
  --wag-duration  : 2.6s;
  --wag-delay     : 0s;
  --wag-ease      : ease-in-out;
  --wag-origin    : 0% 50%;    /* pivot, in viewBox percentages */
  --wag-lines-dim : 0.45;      /* whoosh opacity as the swing passes centre */

  display  : block;
  width    : 100%;
  height   : auto;
  overflow : visible;  /* the swing carries the drawing past the viewBox */
  /* Decoration — never swallow a click meant for something underneath. */
  pointer-events : none;
}

.anim-wag__body,
.anim-wag__lines {
  /* view-box, so --wag-origin reads against the viewBox rather than the
     group's own bbox — a clip-path does NOT shrink an element's bbox, so
     fill-box here would silently mean the whole image either way. */
  transform-box    : view-box;
  transform-origin : var(--wag-origin);
  will-change      : transform, opacity;
}

/* Dim until the swing reaches an extreme — and the resting state before the
   animation starts, so the marks don't sit at full strength over a still tail. */
.anim-wag__lines {
  opacity : var(--wag-lines-dim);
}

.anim-wag.is-visible .anim-wag__body {
  animation : eos-wag var(--wag-duration) var(--wag-ease) var(--wag-delay) infinite;
}

.anim-wag.is-visible .anim-wag__lines {
  animation : eos-wag-lines var(--wag-duration) var(--wag-ease) var(--wag-delay) infinite;
}

@keyframes eos-wag {
  0%, 100% { transform : rotate( var(--wag-back) ); }
  50%      { transform : rotate( var(--wag-angle) ); }
}

/* Brightest at the two ends of the swing, dimmest as it passes centre. */
@keyframes eos-wag-lines {
  0%, 50%, 100% { opacity : 1; }
  25%, 75%      { opacity : var(--wag-lines-dim); }
}


/* ── .anim-wag--flap — a lighter double-beat ──────────────────────────────
   A feathered tail doesn't swing evenly; it flicks out, half-settles, then
   flicks again. Same pivot and the same knobs, different rhythm. */

.anim-wag--flap {
  --wag-duration : 2.2s;
  --wag-angle    : 3.5deg;
  --wag-back     : -2.5deg;
}

.anim-wag--flap.is-visible .anim-wag__body {
  animation-name : eos-flap;
}

@keyframes eos-flap {
  0%, 100% { transform : rotate( var(--wag-back) ); }
  28%      { transform : rotate( var(--wag-angle) ); }
  46%      { transform : rotate( calc( var(--wag-angle) * 0.3 ) ); }
  70%      { transform : rotate( calc( var(--wag-angle) * 0.85 ) ); }
}


/* ── Reduced motion — hold the drawing still, fully visible ───────────────
   Sits here rather than in the shared block above: these rules have to come
   AFTER the ones they switch off, or equal specificity hands the win back to
   the animation. */

@media (prefers-reduced-motion: reduce) {

  .anim-wag.is-visible .anim-wag__body,
  .anim-wag.is-visible .anim-wag__lines {
    animation : none;
  }

  .anim-wag__lines {
    opacity : 1;
  }
}
