/* Forge Motion (frameworkless build) for SophiaXT client sites.
 * Adapted from @sophiaxt/forge-motion motion.css. Copyright (c) 2026 SOPHIA XT LLC.
 *
 * Everything animates transform, opacity, filter or clip-path only, so it
 * runs on the compositor and never triggers layout. Reveals are scrubbed by
 * view-timeline where supported; elsewhere /js/motion.js marks elements
 * [data-fxm-seen] and the same keyframes play once.
 */
:root {
  --fxm-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --fxm-dur: 480ms;
  --fxm-rise: 16px;
  --fxm-step: 70ms;
  --fxm-range: entry 5% cover 30%;
  --fxm-distance: 10%;
}

@keyframes fxm-fade  { from { opacity: 0; } to { opacity: 1; } }
@keyframes fxm-rise  { from { opacity: 0; transform: translateY(var(--fxm-rise)); } to { opacity: 1; transform: none; } }
@keyframes fxm-scale { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: none; } }
@keyframes fxm-blur  { from { opacity: 0; filter: blur(10px); transform: translateY(calc(var(--fxm-rise) * 0.5)); } to { opacity: 1; filter: blur(0); transform: none; } }
@keyframes fxm-clip  { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes fxm-grow  { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes fxm-parallax { from { transform: translateY(calc(var(--fxm-distance) * -1)); } to { transform: translateY(var(--fxm-distance)); } }

/* ---- Reveal (scroll) ----
 * Style lock: reveal-only. Things arrive once as they come into view, then
 * hold still, so every browser uses the observer path (motion.js stamps
 * [data-fxm-seen]); scroll-scrubbed timelines would replay on scroll back.
 * Scoped under html[data-fxm-observed] so a failed script leaves content visible. */
.fxm-reveal { animation-duration: 1ms; animation-fill-mode: both; animation-timing-function: var(--fxm-ease); }
html[data-fxm-observed] .fxm-reveal { animation-duration: var(--fxm-dur); animation-timing-function: var(--fxm-ease); animation-play-state: paused; animation-delay: calc(var(--fxm-i, 0) * var(--fxm-step)); }
html[data-fxm-observed] .fxm-reveal[data-fxm-seen] { animation-play-state: running; }
html[data-fxm-observed] .fxm-reveal[data-effect="fade"]  { animation-name: fxm-fade; }
html[data-fxm-observed] .fxm-reveal[data-effect="rise"]  { animation-name: fxm-rise; }
html[data-fxm-observed] .fxm-reveal[data-effect="scale"] { animation-name: fxm-scale; }
html[data-fxm-observed] .fxm-reveal[data-effect="blur"]  { animation-name: fxm-blur; }
html[data-fxm-observed] .fxm-reveal[data-effect="clip"]  { animation-name: fxm-clip; animation-duration: 600ms; }
html[data-fxm-observed] .fxm-reveal[data-effect="grow"]  { animation-name: fxm-grow; animation-duration: 600ms; transform-origin: left center; }

/* ---- Stagger (plays on load, one child at a time) ---- */
.fxm-stagger > * {
  animation-duration: var(--fxm-dur);
  animation-timing-function: var(--fxm-ease);
  animation-fill-mode: both;
  animation-delay: calc(var(--fxm-i, 0) * var(--fxm-step) + var(--fxm-base, 0ms));
}
.fxm-stagger[data-effect="fade"]  > * { animation-name: fxm-fade; }
.fxm-stagger[data-effect="rise"]  > * { animation-name: fxm-rise; }
.fxm-stagger[data-effect="scale"] > * { animation-name: fxm-scale; }
.fxm-stagger[data-effect="blur"]  > * { animation-name: fxm-blur; }

/* ---- Parallax ---- */
@supports (animation-timeline: scroll()) {
  .fxm-parallax { animation: fxm-parallax linear both; animation-timeline: view(); }
}

/* ---- TextReveal (words split by motion.js) ---- */
.fxm-textreveal .fxm-part {
  display: inline-block;
  white-space: pre;
  animation-duration: var(--fxm-dur);
  animation-timing-function: var(--fxm-ease);
  animation-fill-mode: both;
  animation-delay: calc(var(--fxm-i, 0) * var(--fxm-step, 40ms) + var(--fxm-base, 0ms));
}
.fxm-textreveal[data-effect="rise"] .fxm-part { animation-name: fxm-rise; }
.fxm-textreveal[data-effect="blur"] .fxm-part { animation-name: fxm-blur; }
/* Headings below the fold wait until they are on screen. */
html[data-fxm-observed] .fxm-textreveal:not([data-fxm-seen]) .fxm-part { animation-play-state: paused; }
.fxm-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ---- Page-to-page transitions (same-origin navigations) ---- */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: fxm-fade 260ms var(--fxm-ease) reverse both; }
::view-transition-new(root) { animation: fxm-fade 360ms var(--fxm-ease) both; }

/* ---- Reduced motion: reduced, not broken ---- */
@media (prefers-reduced-motion: reduce) {
  .fxm-reveal, .fxm-stagger > *, .fxm-textreveal .fxm-part, .fxm-parallax {
    animation: none !important; opacity: 1 !important; filter: none !important;
    transform: none !important; clip-path: none !important;
  }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}

/* A split heading inside a stagger animates by word, not as a block. */
.fxm-stagger > .fxm-textreveal { animation: none; }
