/* BSLR — product frame reveal and float. Module pages only.
   Pairs with bslr-reveal.js. Base state is the finished frame; the hidden state
   applies only under .reveal-armed, which the script adds — so no JS, no
   observer or reduced motion all render normally.
   Float is on .figure, not .shot: moving the card edge is what reads as motion,
   and .shot's overflow-x would raise a scrollbar if translated. */

@media (prefers-reduced-motion:no-preference){

  @keyframes revealRise{
    from{ opacity:0; transform:translate3d(0,28px,0) }
    to  { opacity:1; transform:none }
  }
  @keyframes revealSoft{
    from{ opacity:0 }
    to  { opacity:1 }
  }

  .figure.reveal-armed{ opacity:0 }
  .figure.reveal-armed .figcap,
  .figure.reveal-armed .illus-note{ opacity:0 }

  .figure.reveal-armed.reveal-play{
    animation:revealRise .72s cubic-bezier(.22,.61,.36,1) both;
  }
  /* caption and provenance line settle after the frame, so the eye lands
     on the interface first and the disclaimer second */
  .figure.reveal-armed.reveal-play .figcap{
    animation:revealSoft .45s ease-out both; animation-delay:.34s;
  }
  .figure.reveal-armed.reveal-play .illus-note{
    animation:revealSoft .45s ease-out both; animation-delay:.44s;
  }
}

/* Continuous float. On .figure so the card, border and shadow travel against
   the page; --f is set per figure by the script and drives a negative delay so
   frames sit at different points in the cycle. Hover pauses it. */
@media (prefers-reduced-motion:no-preference){
  @keyframes figFloat{
    0%,100%{ transform:translate3d(0,0,0) }
    50%    { transform:translate3d(0,-8px,0) }
  }
  .figure:not(.reveal-armed){
    animation:figFloat 8s ease-in-out infinite;
    animation-delay:calc(var(--f,0) * -1.9s);
    will-change:transform;
  }
  .figure:hover{ animation-play-state:paused }
}
