/* Scroll-owned individual transforms compose with the existing reveal/hover
   transform. No transitions: each pose stops at the exact scroll position. */
.gallery-shell .project-photo.gm-photo {
  translate: var(--gm-x, 0px) var(--gm-y, 0px);
  rotate: var(--gm-angle, 0deg);
  transform-origin: 50% 50%;
}

.gallery-shell .project-photo.gm-photo > img {
  translate: var(--gm-image-x, 0px) var(--gm-image-y, 0px);
  scale: var(--gm-image-scale, 1);
  transform-origin: 50% 50%;
}

.gallery-shell .project-page.gm-illustration {
  translate: var(--gm-x, 0px) var(--gm-y, 0px);
  rotate: var(--gm-angle, 0deg);
  transform-origin: 48% 68%;
}

.gallery-shell .gm-in-range {
  will-change: translate, rotate;
}

.gallery-shell .gm-photo.gm-in-range > img {
  will-change: translate, scale;
}

/* The passage measures the real image box. Its first frame and final landing
   must have exactly the same geometry, including individual CSS transforms. */
body.passage-active .gallery-shell .gm-photo,
body.passage-active .gallery-shell .gm-photo > img,
body.passage-active .gallery-shell .gm-illustration,
body.transition-lock .gallery-shell .gm-photo,
body.transition-lock .gallery-shell .gm-photo > img,
body.transition-lock .gallery-shell .gm-illustration,
body.intro-active .gallery-shell .gm-photo,
body.intro-active .gallery-shell .gm-photo > img,
body.intro-active .gallery-shell .gm-illustration {
  translate: none !important;
  rotate: none !important;
  scale: 1 !important;
}

@media (prefers-reduced-motion: reduce) {
  .gallery-shell .gm-photo,
  .gallery-shell .gm-photo > img,
  .gallery-shell .gm-illustration {
    translate: none !important;
    rotate: none !important;
    scale: 1 !important;
    will-change: auto !important;
  }
}
