/*
 * Sentinel Reveal — Scroll Animation Library
 * https://github.com/your-handle/sentinel-reveal
 *
 * Drop-in scroll animations for WordPress themes.
 * Usage: data-sr="fade-up" data-sr-delay="200" data-sr-duration="600"
 *
 * Effects: fade-in, fade-up, fade-down, fade-left, fade-right,
 *          slide-up, slide-down, slide-left, slide-right,
 *          reveal-up, reveal-down, reveal-left, reveal-right
 */

/* ==========================================================================
   1. BASE — Custom properties inherited by every animated element
   ========================================================================== */

[data-sr] {
  --sr-delay:             0ms;
  --sr-duration:          600ms;
  --sr-easing:            cubic-bezier(0.22, 1, 0.36, 1); /* ease-out-expo — fast arrival */
  --sr-reveal-easing:     cubic-bezier(0.65, 0, 0.35, 1); /* ease-in-out S-curve — slot snap */
  --sr-offset:            40px;   /* translation distance for fade variants */
  --sr-initial-transform: none;
}

/* Per-effect initial transforms — used by the IO hidden state */
[data-sr="fade-up"]    { --sr-initial-transform: translateY(var(--sr-offset)); }
[data-sr="fade-down"]  { --sr-initial-transform: translateY(calc(var(--sr-offset) * -1)); }
[data-sr="fade-left"]  { --sr-initial-transform: translateX(var(--sr-offset)); }
[data-sr="fade-right"] { --sr-initial-transform: translateX(calc(var(--sr-offset) * -1)); }
[data-sr="slide-up"]   { --sr-initial-transform: translateY(100%); }
[data-sr="slide-down"] { --sr-initial-transform: translateY(-100%); }
[data-sr="slide-left"] { --sr-initial-transform: translateX(100%); }
[data-sr="slide-right"]{ --sr-initial-transform: translateX(-100%); }

/* reveal-* travel 100% + the clip padding (--sr-clip-pad) so glyph ink fully
   clears the enlarged mask box at rest — see the .sr-clip rule below. */
[data-sr="reveal-up"]   { --sr-initial-transform: translateY(calc(100% + var(--sr-clip-pad, 0.3em))); }
[data-sr="reveal-down"] { --sr-initial-transform: translateY(calc(-100% - var(--sr-clip-pad, 0.3em))); }
[data-sr="reveal-left"] { --sr-initial-transform: translateX(calc(100% + var(--sr-clip-pad, 0.3em))); }
[data-sr="reveal-right"]{ --sr-initial-transform: translateX(calc(-100% - var(--sr-clip-pad, 0.3em))); }


/* ==========================================================================
   2. IO FALLBACK — Hidden initial state + transition-in
   .sr-js is added to <html> synchronously by JS before first paint.
   Without JS, this rule never matches — elements are always visible.
   ========================================================================== */

.sr-js [data-sr] {
  opacity:   0;
  transform: var(--sr-initial-transform);
}

/* Animate-in: JS adds .sr-animate on intersection (or via double-rAF for above-fold) */
.sr-js [data-sr].sr-animate {
  opacity:   1;
  transform: none;
  transition:
    opacity   var(--sr-duration) var(--sr-easing) var(--sr-delay),
    transform var(--sr-duration) var(--sr-easing) var(--sr-delay);
}

/* fade-in has no transform — ensure clean transition */
.sr-js [data-sr="fade-in"] {
  transform: none;
}

/* reveal-* start hidden via transform only — no opacity change */
.sr-js [data-sr^="reveal-"] {
  opacity: 1;
}

/* reveal animate-in: use reveal easing, no opacity transition */
.sr-js [data-sr^="reveal-"].sr-animate {
  opacity:    1;
  transform:  none;
  transition: transform var(--sr-duration) var(--sr-reveal-easing) var(--sr-delay);
}

/* Clip wrapper injected by JS around reveal-* elements */
.sr-clip {
  overflow:       hidden;
  /* When display:inline-block, overflow:hidden reassigns the element's baseline
     to the bottom margin edge (per spec), expanding the parent's line-box height.
     vertical-align:top prevents that shift. No effect on block-level wrappers. */
  vertical-align: top;
  /* The mask box is the line box, which is smaller than the glyph ink bounds —
     descenders/ascenders and italic overhang would be clipped. Enlarge the mask
     with padding, then cancel it with an equal negative margin so the net box
     size (and surrounding spacing) is unchanged. The reveal offsets above add
     --sr-clip-pad so the text still fully clears this padded mask at rest. */
  padding: var(--sr-clip-pad, 0.3em);
  margin:  calc(var(--sr-clip-pad, 0.3em) * -1);
}

/* JS uses <span class="sr-clip"> when wrapping inline elements (e.g. spans inside
   <h1>) to keep valid HTML phrasing content. A span needs display:inline-block
   for overflow:hidden to establish a clipping box. Scoped to span so block-level
   <div class="sr-clip"> wrappers are unaffected. */
span.sr-clip { display: inline-block; }

/* CSS transforms have no effect on non-replaced inline boxes. The JS
   normalizeInlineDisplay() pass promotes inline [data-sr] elements to
   inline-block; this rule is a CSS safety net for reveal-* elements (always
   inside span.sr-clip when inline) in case the JS pass is bypassed. */
.sr-js span.sr-clip > [data-sr] { display: inline-block; }


/* ==========================================================================
   3. ACCESSIBILITY — prefers-reduced-motion
   Must be last so it wins against all other rules.
   Elements become immediately visible — no animation, no transition.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  [data-sr] {
    animation:  none !important;
    transition: none !important;
    opacity:    1    !important;
    transform:  none !important;
  }
  /* Unclip any reveal wrappers so content is visible without animation */
  .sr-clip {
    overflow: visible !important;
  }
}
