/*
 * Scene motion: the start states and moments that assets/js/scene-motion.js
 * plays (read its header for the system and the vocabulary). Ported from the
 * ShareRing Solutions landing page's home.css (.home-motion rules, the reduced
 * motion blocks, .home-snap).
 *
 * Every start state lives under html.sr-motion, which the head script sets
 * before first paint only when motion is allowed, and which is taken off again
 * if the script never runs or fails as it boots. JS off and
 * prefers-reduced-motion therefore never see a start state: every final state
 * shows and nothing replays. Only opacity, transform, clip-path and text inside
 * a reserved width change here, so nothing can shift layout.
 *
 * Loaded after scene-kit.css. A page's own motion rules go in its own
 * stylesheet, written to outrank these where they differ.
 */

/* ---------- The reset frame ---------- */
/* A reset runs with every transition and animation off for one frame, while
   the element is wholly off screen, so it is never seen running backwards. An
   element with motion of its own (data-sm-keep) is left alone. */
.sr-snap {
  transition: none !important;
  animation: none !important;
}

.sr-snap *:not([data-sm-keep]):not([data-sm-keep] *) {
  transition: none !important;
  animation: none !important;
}

/* ---------- Reveals: the kit reveal, owned by the scene ---------- */
/* translateY 18px, 700 ms, --srk-ease, as scene-kit.css draws it. The start
   state outranks the kit's own is-in, so a beat waits for its slot; the final
   state outranks the kit's hidden state (.js .srk-reveal) and stays under a
   page's own hover rules. Once settled, the page's own transitions return. */
.sr-motion [data-sm-scene] :is(.srk-reveal, [data-sm-beat]):not(.sm-in),
.sr-motion .sm-loose:is(.srk-reveal, [data-sm-beat]):not(.sm-in) {
  opacity: 0;
  transform: translateY(18px);
}

.sr-motion [data-sm-scene] :is(.srk-reveal, [data-sm-beat]):not(.sm-settled),
.sr-motion .sm-loose:is(.srk-reveal, [data-sm-beat]):not(.sm-settled) {
  transition: opacity 700ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1)), transform 700ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1));
}

.sr-motion :is(.srk-reveal, [data-sm-beat]).sm-in {
  opacity: 1;
  transform: none;
}

/* ---------- Typing (the theme's industry.css typing, at the Solutions pace) ---------- */
/* Opacity only, never position: every word is already in its final place.
   Until the script has wrapped the words the element is hidden, so the line is
   never seen whole and then cleared. A typed element does not also rise. */
.sr-motion [data-sm-type]:not(.sr-typing) { opacity: 0; }

.sr-motion [data-sm-scene] [data-sm-type].sr-typing,
.sr-motion .sm-loose[data-sm-type].sr-typing {
  opacity: 1;
  transform: none;
  transition: none;
}

.sr-motion .sr-typing .sr-w {
  opacity: 0;
  transition: opacity 390ms linear;
  transition-delay: calc(var(--w, 0) * 82ms);
}

.sr-motion .sr-typing.is-typed .sr-w { opacity: 1; }

/* The caret rides the LAST word only, so it cannot fall onto a line of its own,
   and it takes no space (absolute), so no line can rewrap for it. It blinks
   three times as the line completes, then goes. */
.sr-motion .sr-typing .sr-w--last { position: relative; }

.sr-motion .sr-typing .sr-w--last::after {
  content: "";
  position: absolute;
  left: 100%;
  bottom: 0.12em;
  width: 2px;
  height: 0.9em;
  margin-left: 0.12em;
  background: var(--srk-accent-live, currentColor);
  opacity: 0;
  pointer-events: none;
}

.sr-motion .sr-typing.is-typed .sr-w--last::after {
  animation: sr-caret 900ms steps(1) 3 forwards;
  animation-delay: calc(var(--w, 0) * 82ms);
}

@keyframes sr-caret {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* Typing at reading pace: each character fades in at its own moment. */
.sr-visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.sr-motion .sr-typer .sr-ch { opacity: 0; transition: opacity 80ms linear; }
.sr-motion .sr-typer.is-go .sr-ch { opacity: 1; }

/* ---------- Brand sweeps: wipe, glint, glow ---------- */
.sr-motion [data-sm-brand] {
  position: relative;
  clip-path: inset(-16px 100% -16px -16px);
  transition: clip-path 1050ms cubic-bezier(0.65, 0, 0.35, 1);
}

.sr-motion [data-sm-brand].is-swept { clip-path: inset(-16px -16px -16px -16px); }
.sr-motion [data-sm-brand].is-swept.sm-settled { clip-path: none; }

/* A mark that is also a reveal fades, rises and wipes at once. */
.sr-motion [data-sm-scene] :is(.srk-reveal, [data-sm-beat])[data-sm-brand]:not(.sm-settled),
.sr-motion .sm-loose:is(.srk-reveal, [data-sm-beat])[data-sm-brand]:not(.sm-settled) {
  transition: opacity 700ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1)), transform 700ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1)), clip-path 1050ms cubic-bezier(0.65, 0, 0.35, 1);
}

/* The glint is masked to the artwork's own pixels (every image in the mark,
   measured by the script); it needs a box, so a bare <img> mark gets the wipe
   and the glow only. */
.sr-motion [data-sm-brand]::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(100deg, transparent 40%, var(--sm-glint, color-mix(in srgb, var(--srk-accent-live, #2CE5A7) 72%, #FFFFFF)) 50%, transparent 60%) no-repeat;
  background-size: 300% 100%;
  background-position: 100% 0;
  -webkit-mask: var(--sm-brand-mask, none);
          mask: var(--sm-brand-mask, none);
}

.sr-motion [data-sm-brand].is-swept::after { animation: sr-glint 1350ms cubic-bezier(0.4, 0, 0.2, 1) 570ms both; }

.sr-motion [data-sm-brand].is-swept img,
.sr-motion img[data-sm-brand].is-swept { animation: sr-glow 1650ms ease-out 750ms both; }

@keyframes sr-glint {
  0% { opacity: 1; background-position: 100% 0; }
  100% { opacity: 1; background-position: 0% 0; }
}

@keyframes sr-glow {
  0%, 100% { filter: drop-shadow(0 0 0 transparent); }
  40% { filter: drop-shadow(0 0 12px color-mix(in srgb, var(--srk-accent-live, #2CE5A7) 70%, transparent)); }
}

/* ---------- A line that draws ---------- */
.sr-motion [data-sm-draw] {
  clip-path: inset(-48px 100% -48px -48px);
  transition: clip-path 700ms cubic-bezier(0.65, 0, 0.35, 1);
}

.sr-motion [data-sm-draw].is-drawn { clip-path: inset(-48px -48px -48px -48px); }
.sr-motion [data-sm-draw].is-drawn.sm-settled { clip-path: none; }

/* ---------- Rises from first paint ---------- */
/* CSS alone plays the first rise, so an LCP image never waits on script. It
   is drawn whole from the first frame and rises 24px into place: Chrome does
   not count an image as painted while its opacity is climbing from 0 (on a
   hero phone measured at 1440 that put LCP about 280 ms after first paint). The
   script re-arms it (sm-armed) only for a replay, which fades and rises
   (sm-again). fill both: the final frame holds whatever else is set. */
@keyframes sr-rise-paint {
  from { opacity: 1; transform: translateY(24px); }
  to { opacity: 1; transform: none; }
}

@keyframes sr-rise {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: none; }
}

.sr-motion [data-sm-rise] { animation: sr-rise-paint 900ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1)) both; }
.sr-motion [data-sm-rise].sm-again { animation-name: sr-rise; }

.sr-motion [data-sm-scene] [data-sm-rise].sm-armed,
.sr-motion [data-sm-rise].sm-armed {
  animation: none;
  opacity: 0;
  transform: translateY(24px);
}

/* ---------- The slow photo zoom ---------- */
/* About the photo's own focal point (the script copies its object-position to
   transform-origin), with a clip-path matched to the scale (--sm-zoom-clip,
   worked out by the script), so the picture never shows outside its own box.
   Push-in by default (1 to 1.05, as on Solutions); "settle" runs 1.05 to 1, so
   the resting crop is exactly the approved one. Once the zoom has run
   (sm-zoomed), the page's own transitions return and a settled photo carries
   no clip at all. */
.sr-motion [data-sm-zoom]:not(.sm-zoomed) {
  transition: transform 5s cubic-bezier(0.22, 1, 0.36, 1), clip-path 5s cubic-bezier(0.22, 1, 0.36, 1);
}
.sr-motion [data-sm-zoom]:not([data-sm-zoom="settle"]) { clip-path: inset(0 0 0 0); }
.sr-motion .sm-in [data-sm-zoom]:not([data-sm-zoom="settle"]),
.sr-motion [data-sm-zoom].sm-in:not([data-sm-zoom="settle"]) { transform: scale(1.05); clip-path: var(--sm-zoom-clip, inset(0 0 0 0)); }
.sr-motion [data-sm-zoom="settle"] { transform: scale(1.05); clip-path: var(--sm-zoom-clip, inset(0 0 0 0)); }
.sr-motion .sm-in [data-sm-zoom="settle"],
.sr-motion [data-sm-zoom="settle"].sm-in { transform: none; clip-path: inset(0 0 0 0); }
.sr-motion .sm-in [data-sm-zoom="settle"].sm-zoomed,
.sr-motion [data-sm-zoom="settle"].sm-in.sm-zoomed { clip-path: none; }

/* ---------- Counters (the Solutions price roll) ---------- */
.sr-motion .sr-rolling { display: inline-block; clip-path: inset(0); font-variant-numeric: tabular-nums; }
.sr-motion .sr-rolling .sr-roll-c {
  display: inline-block;
  transform: translateY(105%);
  transition: transform 640ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1));
}
.sr-motion .sr-rolling.is-in .sr-roll-c { transform: none; }

/* ---------- The timeline ---------- */
.sr-motion [data-sm-steps] [data-sm-node] { transition: opacity 630ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1)); }
.sr-motion [data-sm-steps] [data-sm-dot] { transition: transform 780ms var(--srk-spring, cubic-bezier(0.34, 1.56, 0.64, 1)); }
.sr-motion [data-sm-steps].is-armed [data-sm-node]:not(.is-lit) { opacity: 0.32; }
.sr-motion [data-sm-steps].is-armed [data-sm-node]:not(.is-lit) [data-sm-dot] { transform: scale(0.6); }

/* ---------- Strike-through and payoff beats ---------- */
/* Each old way fades in, then a line is drawn through it (one per line of
   text); each answer comes in from the left. */
.sr-motion [data-sm-strike].is-staged [data-sm-old] {
  opacity: 0;
  background: linear-gradient(currentColor, currentColor) 0 58% / 0% 0.08em no-repeat;
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
  transition: opacity 450ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1)), background-size 600ms cubic-bezier(0.65, 0, 0.35, 1);
}
.sr-motion [data-sm-strike].is-staged [data-sm-old].is-old { opacity: 1; }
.sr-motion [data-sm-strike].is-staged [data-sm-old].is-struck { background-size: 100% 0.08em; }
.sr-motion [data-sm-strike].is-staged [data-sm-new] {
  opacity: 0;
  transform: translateX(-16px);
  transition: opacity 500ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1)), transform 500ms var(--srk-ease, cubic-bezier(0.22, 1, 0.36, 1));
}
.sr-motion [data-sm-strike].is-staged [data-sm-new].is-new { opacity: 1; transform: none; }

/* ---------- Motion off ---------- */
/* Without html.sr-motion none of the above applies. These hold the line if a
   browser reports reduced motion after the head script has run. */
@media (prefers-reduced-motion: reduce) {
  .sr-motion [data-sm-scene] :is(.srk-reveal, [data-sm-beat]),
  .sr-motion .sm-loose:is(.srk-reveal, [data-sm-beat]),
  .sr-motion [data-sm-type],
  .sr-motion .sr-typing .sr-w,
  .sr-motion .sr-typer .sr-ch,
  .sr-motion [data-sm-rise],
  .sr-motion [data-sm-zoom],
  .sr-motion .sr-rolling .sr-roll-c,
  .sr-motion [data-sm-strike].is-staged [data-sm-old],
  .sr-motion [data-sm-strike].is-staged [data-sm-new] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  .sr-motion [data-sm-brand],
  .sr-motion [data-sm-draw] { clip-path: none !important; transition: none !important; }
  .sr-motion [data-sm-brand]::after,
  .sr-motion .sr-typing .sr-w--last::after { display: none; }
  .sr-motion [data-sm-brand] img { animation: none !important; }
}
