/* ==========================================================================
   animations.css
   Scroll-triggered animation utilities.

   HOW IT WORKS
   ------------
   1. Add `.animate` + one direction class to any element.
   2. The element starts hidden/offset. When it scrolls into view, a JS helper
      adds `.in-view`, and CSS transitions it to its final state.
   3. Stack modifier classes (duration / delay / distance / easing) to
      customise each element individually.

   Everything is driven by CSS custom properties, so you can also override a
   single element inline:  style="--anim-duration: 1200ms; --anim-distance: 120px"

   EXAMPLE
   -------
   <div class="animate">…</div>                 <!-- bare class: fade + rise -->
   <div class="animate from-left duration-500 delay-200 ease-bounce">…</div>

   DEFAULTS (when you only write class="animate")
   ----------------------------------------------
   motion   fade + rise from below
   duration 600ms
   delay    0ms
   distance 40px
   easing   soft ease-out
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. Global defaults  (override per-element via a modifier class or inline)
   -------------------------------------------------------------------------- */
:root {
    --anim-duration: 600ms;                          /* how long it lasts        */
    --anim-delay:    0ms;                             /* wait before it starts    */
    --anim-distance: 40px;                            /* slide travel distance    */
    --anim-scale:    0.92;                            /* zoom start scale         */
    --anim-easing:   cubic-bezier(0.16, 1, 0.3, 1);  /* easing curve             */
}


/* --------------------------------------------------------------------------
   2. Base class — sets the resting (hidden) state and the transition.
      Pair with a direction class below.
   -------------------------------------------------------------------------- */
.animate {
    opacity: 0;

    /* Composable transform pieces. Each direction/zoom class sets just ONE of
       these, and they're combined here into a single transform — so they stack:
         from-left zoom-in   → slide from left WHILE scaling up
         from-left from-top  → diagonal, from the top-left corner
       Resting defaults are neutral (no movement) so single classes stay pure. */
    --tx: 0;   /* horizontal offset */
    --ty: 0;   /* vertical offset   */
    --sc: 1;   /* scale             */
    transform: translate3d(var(--tx), var(--ty), 0) scale(var(--sc));

    transition-property: opacity, transform;
    transition-duration: var(--anim-duration);
    transition-delay: var(--anim-delay);
    transition-timing-function: var(--anim-easing);
    will-change: opacity, transform;
}

/* Bare default: a plain class="animate" (no effect class) gently rises + fades.
   Scoped with :not(.in-view) so the offset switches OFF once the element is in
   view — it then falls back to the neutral base (0,0,1) and settles into place.
   The :not(.from-*) chain means any direction/zoom/fade class disables this
   default so your chosen effect stays pure. */
.animate:not(.in-view):not(.from-left):not(.from-right):not(.from-top):not(.from-bottom):not(.zoom-in):not(.zoom-out):not(.fade) {
    --ty: var(--anim-distance);
}

/* Final state — added by the JS helper when the element enters the viewport.
   The offset rules below are all scoped :not(.in-view), so once this class is
   present every transform piece falls back to the neutral base values (0,0,1)
   and the element transitions into place. We only need to flip opacity here. */
.animate.in-view {
    opacity: 1;
}


/* --------------------------------------------------------------------------
   3. Direction / type classes  (the "what")
      Each sets ONE transform piece, so they stack cleanly. Combine freely:
        from-left            → slide in from the left
        from-left zoom-in    → slide from left WHILE scaling up
        from-left from-top   → diagonal, from the top-left corner
      Names read as "where it comes FROM".

      All scoped :not(.in-view): they define the RESTING offset only. When the
      element enters view the class match drops, the value reverts to the base
      neutral (0/1), and `transform` transitions between the two — that's the
      animation. This is what makes the movement reliable regardless of the
      order rules appear in the file.
   -------------------------------------------------------------------------- */
.animate:not(.in-view).from-left   { --tx: calc(-1 * var(--anim-distance)); }
.animate:not(.in-view).from-right  { --tx: var(--anim-distance); }
.animate:not(.in-view).from-bottom { --ty: var(--anim-distance); }
.animate:not(.in-view).from-top    { --ty: calc(-1 * var(--anim-distance)); }

.animate:not(.in-view).zoom-in     { --sc: var(--anim-scale); }
.animate:not(.in-view).zoom-out    { --sc: calc(2 - var(--anim-scale)); }

/* fade = opacity only. It sets no transform, so it's pure fade on its own and
   is also the explicit "no movement" marker. */


/* --------------------------------------------------------------------------
   4. Duration modifiers  (the "how long")
   -------------------------------------------------------------------------- */
.duration-150  { --anim-duration: 150ms; }
.duration-200  { --anim-duration: 200ms; }
.duration-300  { --anim-duration: 300ms; }
.duration-500  { --anim-duration: 500ms; }
.duration-700  { --anim-duration: 700ms; }
.duration-1000 { --anim-duration: 1000ms; }
.duration-1500 { --anim-duration: 1500ms; }


/* --------------------------------------------------------------------------
   5. Delay modifiers  (the "when" — useful for staggering siblings)
   -------------------------------------------------------------------------- */
.delay-100 { --anim-delay: 100ms; }
.delay-200 { --anim-delay: 200ms; }
.delay-300 { --anim-delay: 300ms; }
.delay-500 { --anim-delay: 500ms; }
.delay-700 { --anim-delay: 700ms; }
.delay-1000{ --anim-delay: 1000ms; }


/* --------------------------------------------------------------------------
   6. Distance modifiers  (how far slides travel)
   -------------------------------------------------------------------------- */
.distance-sm { --anim-distance: 20px; }
.distance-md { --anim-distance: 40px; }   /* = default */
.distance-lg { --anim-distance: 80px; }
.distance-xl { --anim-distance: 140px; }


/* --------------------------------------------------------------------------
   7. Easing modifiers  (the "feel")
   -------------------------------------------------------------------------- */
.ease-linear  { --anim-easing: linear; }
.ease-out     { --anim-easing: cubic-bezier(0.16, 1, 0.3, 1); }      /* default-ish, soft landing */
.ease-in-out  { --anim-easing: cubic-bezier(0.65, 0, 0.35, 1); }
.ease-bounce  { --anim-easing: cubic-bezier(0.34, 1.56, 0.64, 1); } /* slight overshoot */


/* --------------------------------------------------------------------------
   8. Accessibility — respect users who ask for less motion.
      Elements appear instantly instead of animating.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .animate {
        transition: none;
        opacity: 1;
        transform: none;
    }
}