/**
 * reveal.css — Scroll reveals ([data-reveal])
 *
 * @layer      variables, utilities
 * @requires   layers.css, _variables.css
 * @uses       prefers-reduced-motion — native media feature
 * @uses       sibling-index(), sibling-count() — native stagger delays (@supports
 *             gated — no Firefox yet; where unsupported, reveal.js writes a
 *             per-child --ui-reveal-wait instead)
 * @uses       Behaviour requires zazz/scripts/reveal.js (IntersectionObserver)
 * @tokens     --ui-reveal-global-* (@layer variables);
 *             per-instance --ui-reveal-* set from data-* attributes by reveal.js;
 *             --ui-reveal-stagger-base / --ui-reveal-stagger-step written on stagger
 *             groups by reveal.js for the native sibling-index() path
 * @version    0.1.0
 * @since      0.1.0
 *
 * A lightweight, configurable animation system for scroll-based reveals. Focuses
 * on viewport entry animations for single elements and direct children of stagger
 * groups. Available animations: slide-up, slide-down, slide-left, slide-right,
 * fade, grow, shrink.
 *
 * @example  Single element
 *   <div data-reveal="slide-up">Content</div>
 *
 * @example  Staggered children — animation applied to each direct child
 *   <div data-reveal-each="fade" data-reveal-step="100">
 *     <div>Item 1</div>
 *     <p>Item 2</p>
 *   </div>
 *
 * Data attributes (on the element with data-reveal or data-reveal-each):
 *   data-reveal="animation-name"        single elements
 *   data-reveal-each="animation-name"   groups; applied to children
 *   data-reveal-duration="300"          ms
 *   data-reveal-wait="0"                ms, base delay
 *   data-reveal-step="80"               ms, stagger delay for groups
 *   data-reveal-ease="ease-in-out"
 *   data-reveal-distance="2rem"         translation distance
 *   data-reveal-order="reversed"        stagger order for groups
 *   data-reveal-margin="0px"            IntersectionObserver rootMargin
 *   data-reveal-threshold="0.2"         IntersectionObserver threshold
 *   data-reveal-scale="0.5"             optional initial scale (requires JS mod)
 */

@layer variables {
  :root {
    --ui-reveal-global-duration: var(--default-transition-duration);
    --ui-reveal-global-ease: var(--default-transition-timing-function);
    --ui-reveal-global-wait: 0ms;
    --ui-reveal-global-distance: 1rem;
    --ui-reveal-global-grow: 0.97;
    --ui-reveal-global-shrink: 1.03;
  }
}

@layer zazz.utilities {
  /* --- Animation Styles --- */
  @media screen and (prefers-reduced-motion: no-preference) {
    /* Effective values + hidden state. Kept at :where() (0,0,0) so the
       .in-viewport final state overrides opacity with minimal specificity. */
    :where(
      [data-reveal]:not([data-gjs-type]),
      [data-reveal-each]:not([data-gjs-type]) > *:not([data-gjs-type])
    ) {
      --ui-reveal-effective-duration: var(--ui-reveal-duration, var(--ui-reveal-global-duration));
      --ui-reveal-effective-ease: var(--ui-reveal-ease, var(--ui-reveal-global-ease));
      --ui-reveal-effective-wait: var(--ui-reveal-wait, var(--ui-reveal-global-wait));
      --ui-reveal-effective-distance: var(--ui-reveal-distance, var(--ui-reveal-global-distance));
      will-change: opacity, transform;
      opacity: 0;
    }

    /* Transition config — deliberately NOT wrapped in :where(), so its (0,1,0)
       beats bare transition utilities (e.g. .transition is :where(.transition)
       = 0,0,0) that share this layer and would otherwise reset transition-delay,
       wiping out the per-child stagger. The gjs exclusion uses :not(:where())
       to add no specificity, so the fade override below still wins on order. */
    [data-reveal]:not(:where([data-gjs-type])),
    [data-reveal-each]:not(:where([data-gjs-type])) > *:not(:where([data-gjs-type])) {
      transition-timing-function: var(--ui-reveal-effective-ease);
      transition-duration: var(--ui-reveal-effective-duration);
      transition-delay: var(--ui-reveal-effective-wait);
      transition-property: opacity, transform;
    }

    /* --- Stagger timing (native) --- */
    /* Each stagger child derives its own delay from its DOM position:
       wait = base + step × (index − 1); reversed groups count from the end.
       reveal.js writes --ui-reveal-stagger-base/-step on the group (from
       data-reveal-wait / data-reveal-step, config defaults applied) and skips
       the per-child --ui-reveal-wait it falls back to where sibling-index() is
       unsupported (Firefox). The literal 80ms mirrors the JS default step. */
    @supports (top: calc(sibling-index() * 1px)) {
      [data-reveal-each] > * {
        --ui-reveal-wait: calc(
          var(--ui-reveal-stagger-base, var(--ui-reveal-global-wait)) +
            var(--ui-reveal-stagger-step, 80ms) * (sibling-index() - 1)
        );
      }

      [data-reveal-each][data-reveal-order="reversed"] > * {
        --ui-reveal-wait: calc(
          var(--ui-reveal-stagger-base, var(--ui-reveal-global-wait)) +
            var(--ui-reveal-stagger-step, 80ms) * (sibling-count() - sibling-index())
        );
      }
    }

    /* Slide animations */
    [data-reveal="slide-up"]:not(:where(.in-viewport, [data-gjs-type])),
    [data-reveal-each="slide-up"] > *:not(:where(.in-viewport, [data-gjs-type])) {
      transform: translateY(var(--ui-reveal-effective-distance));
    }

    [data-reveal="slide-down"]:not(:where(.in-viewport, [data-gjs-type])),
    [data-reveal-each="slide-down"] > *:not(:where(.in-viewport, [data-gjs-type])) {
      transform: translateY(calc(-1 * var(--ui-reveal-effective-distance)));
    }

    [data-reveal="slide-left"]:not(:where(.in-viewport, [data-gjs-type])),
    [data-reveal-each="slide-left"] > *:not(:where(.in-viewport, [data-gjs-type])) {
      transform: translateX(var(--ui-reveal-effective-distance));
    }

    [data-reveal="slide-right"]:not(:where(.in-viewport, [data-gjs-type])),
    [data-reveal-each="slide-right"] > *:not(:where(.in-viewport, [data-gjs-type])) {
      transform: translateX(calc(-1 * var(--ui-reveal-effective-distance)));
    }

    /* Fade animation */
    [data-reveal="fade"]:not(:where(.in-viewport, [data-gjs-type])),
    [data-reveal-each="fade"] > *:not(:where(.in-viewport, [data-gjs-type])) {
      transition-property: opacity;
      transform: none;
    }

    /* Scale animations */
    [data-reveal="grow"]:not(:where(.in-viewport, [data-gjs-type])),
    [data-reveal-each="grow"] > *:not(:where(.in-viewport, [data-gjs-type])) {
      transform: scale(var(--ui-reveal-scale, var(--ui-reveal-global-grow)));
    }

    [data-reveal="shrink"]:not(:where(.in-viewport, [data-gjs-type])),
    [data-reveal-each="shrink"] > *:not(:where(.in-viewport, [data-gjs-type])) {
      transform: scale(var(--ui-reveal-scale, var(--ui-reveal-global-shrink)));
    }

    /* Final state */
    [data-reveal]:where(.in-viewport, [data-gjs-type]),
    [data-reveal-each] > *:where(.in-viewport, [data-gjs-type]) {
      opacity: 1;
      transform: none;
    }
  }

  /* --- Accessibility: Reduced Motion --- */
  @media (prefers-reduced-motion: reduce) {
    /* Immediately show elements without transition */
    :where([data-reveal], [data-reveal-each] > *) {
      transition: none !important;
      transform: none !important;
      opacity: 1 !important;
      will-change: auto !important;
    }
  }
}
