/* hero.css — pose swapping for the HQ hero sprite.
 *
 * Owns nothing but the swap. It never touches width, margin, display or the
 * existing `bob` animation in styles.css: the only properties animated here
 * are `opacity` and `scale`, and `scale` is its own property in CSS, so it
 * composes with the transform `bob` is already using instead of replacing it.
 *
 * Nothing here is longer than 300ms.
 */

.hero-sprite,
.hud-sprite {
  /* The box is reserved so a pose with different proportions cannot reflow the
     page mid-swap. `--hero-ratio` is set by hero.js from the idle image's own
     measured dimensions, so the reserved box is exactly the box the page
     already had — and until idle has loaded this is `auto`, which is what the
     sprites do today. `contain` letterboxes an odd pose instead of stretching
     it. */
  aspect-ratio: var(--hero-ratio, auto);
  object-fit: contain;
  transition: opacity 160ms ease-out, scale 160ms ease-out;
}

/* Applied for exactly one frame as the new src goes on, then removed — the
   sprite fades and scales back up to rest. No pre-delay, so the pose is on
   screen the instant it is asked for. */
.hero-sprite.hero-swapping,
.hud-sprite.hero-swapping {
  transition: none;
  opacity: .3;
  scale: .93;
}

/* Reduced motion: the pose still changes, it just arrives flat. hero.js also
   skips adding the class, so this is a belt-and-braces rule. */
@media (prefers-reduced-motion: reduce) {
  .hero-sprite,
  .hud-sprite {
    transition: none;
  }
  .hero-sprite.hero-swapping,
  .hud-sprite.hero-swapping {
    opacity: 1;
    scale: 1;
  }
}

/* A pose whose plate was drawn at the wrong size corrects itself here.
 * `scale` is its own property, so this composes with the `bob` transform in
 * styles.css rather than replacing it - same reason the fade uses it too. */
.hero-sprite,
.hud-sprite {
  --pose-scale: 1;
  scale: var(--pose-scale);
  transition: scale 160ms ease;
}
@media (prefers-reduced-motion: reduce) {
  .hero-sprite,
  .hud-sprite { transition: none; }
}
