/* ==========================================================================
   Scroll-reveal initial state.
   Applied only while the Motion engine is live (html.reveal-on, added by
   scripts/reveal.js). If Motion is absent, fails to load, or reduced-motion is
   on, the class is never added / is removed, so content stays fully visible.
   Only opacity is hidden here; the 16px lift is supplied by Motion keyframes,
   so layout never shifts and there is no flash of offset content.
   ========================================================================== */
.reveal-on [data-reveal],
.reveal-on [data-reveal-item] {
  opacity: 0;
}

/* Belt-and-braces: never hide content for reduced-motion users. */
@media (prefers-reduced-motion: reduce) {
  .reveal-on [data-reveal],
  .reveal-on [data-reveal-item] {
    opacity: 1;
  }
}

/* --------------------------------------------------------------------------
   Lazy-image fade-in.
   Images fade to full opacity once they finish loading instead of popping in.
   The hidden state is opt-in PER IMAGE: .lazy-fade is only ever added by JS
   (scripts/reveal.js at boot, collections.js when hydrating), always paired
   with a load listener that adds .is-loaded. Never match on a bare attribute
   here: dynamically created imgs (e.g. modal gallery thumbs) would be
   stranded invisible, with no JS wired to reveal them.
   -------------------------------------------------------------------------- */
img.lazy-fade {
  transition: opacity 0.25s ease;
}
img.lazy-fade:not(.is-loaded) {
  opacity: 0;
}
