/* ==========================================================================
   Anti - Motion
   Scroll entrances and the band clipping that makes them safe.
   Loaded last so it wins over component transitions.
   ========================================================================== */

/* --- The safety rule -------------------------------------------------------
   The hidden state is applied ONLY when motion is allowed and the element has
   not been seen. Reduced motion, no JavaScript, or a missing
   IntersectionObserver all render the final state immediately.

   Nothing may ever be stranded invisible. Every selector below is gated on
   `.js` (added by main.js) inside a `no-preference` query, so the default
   state of the document is "visible".
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  .js [data-reveal] {
    opacity: 0;
    transition:
      opacity var(--reveal-duration) var(--ease-out),
      translate var(--reveal-duration) var(--ease-out),
      scale var(--reveal-duration) var(--ease-out);
    transition-delay: var(--reveal-delay, 0ms);
    will-change: opacity, translate;
  }

  /* Individual properties, not the `transform` shorthand: they compose
     without clobbering each other, and `translate: none` resets cleanly. */
  .js [data-reveal="up"],
  .js [data-reveal=""]           { translate: 0 var(--reveal-rise); }
  .js [data-reveal="down"]       { translate: 0 calc(var(--reveal-rise) * -1); }
  .js [data-reveal="left"]       { translate: calc(var(--reveal-shift) * -1) 0; }
  .js [data-reveal="right"]      { translate: var(--reveal-shift) 0; }
  .js [data-reveal="scale"]      { scale: 0.94; }
  .js [data-reveal="fade"]       { translate: none; }

  /* growX draws a rule out from its start edge. */
  .js [data-reveal="growX"] {
    scale: 0 1;
    transform-origin: left center;
  }

  /* `translate` is a physical property and does not flip with the document,
     so a list dealt from the start edge in English would arrive from the end
     edge in Arabic - backwards against the reading direction. Mirror the
     lateral variants explicitly. */
  [dir="rtl"] .js [data-reveal="left"]  { translate: var(--reveal-shift) 0; }
  [dir="rtl"] .js [data-reveal="right"] { translate: calc(var(--reveal-shift) * -1) 0; }
  [dir="rtl"] .js [data-reveal="growX"] { transform-origin: right center; }

  .js [data-reveal].is-visible {
    opacity: 1;
    translate: none;
    scale: none;
    will-change: auto;
  }
}

/* --- Print -----------------------------------------------------------------
   A printed page never scrolls, so an observer that never fires would leave
   everything below the first sheet blank. Force the finished state. */

@media print {
  [data-reveal] {
    opacity: 1 !important;
    translate: none !important;
    scale: none !important;
    transition: none !important;
  }
}

/* --- Reduced motion --------------------------------------------------------
   Belt and braces: the hidden state is already scoped to `no-preference`, but
   an element mid-transition when the preference changes must land. */

@media (prefers-reduced-motion: reduce) {
  [data-reveal] {
    opacity: 1 !important;
    translate: none !important;
    scale: none !important;
    transition: none !important;
  }
}

/* --- Band clipping ---------------------------------------------------------
   A `left`/`right` reveal starts its element 32px to one side, and until it
   reveals those 32px are real page width. Clipping on the band contains the
   overshoot.

   Note this is `overflow-x: clip`, not `hidden`: `hidden` would make the
   element a scroll container and break `position: sticky` inside it. It must
   never go on `html` or `body` - `clip` paired with a visible overflow-y
   propagates "no vertical scrolling mechanism" to the viewport and the page
   stops scrolling altogether. */

.section,
.section--tight,
.band,
.trust,
.site-footer {
  overflow-x: clip;
}
