/*
  Synchrotech motion.
  Restraint is the point: short distances (10 to 16 px), fast exits, one easing curve, and
  nothing that moves twice. Every value here is motion only. No colour, type or spacing
  decision lives in this file.

  Nothing is hidden until motion.js adds .st-motion to <html>, so the page is fully readable
  if the script never runs, and everything is inert under prefers-reduced-motion.
*/

:root {
  --mo-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --mo-fast: 180ms;
  --mo-base: 520ms;
  --mo-step: 70ms;
  --mo-rise: 14px;
}

/* Capture, print and background tabs: the end state, applied outright. A transition needs a
   running timeline to reach its end; these situations have none. */
.st-motion[data-motion-off] [data-reveal] {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}

/* Blanket, not a list: any animation that starts from a hidden or faded keyframe holds that
   keyframe while the timeline is stopped, so every animation and transition on the page has
   to be cancelled, not an enumerated few. */
.st-motion[data-motion-off] *,
.st-motion[data-motion-off] *::before,
.st-motion[data-motion-off] *::after {
  animation: none !important;
  transition: none !important;
}

/* The three families that animate themselves rather than through the reveal system: pin the
   finished state, since cancelling their animation alone leaves a keyframe-only value behind
   (the story card's faded, scaled-down first frame). */
.st-motion[data-motion-off] .ab-word,
.st-motion[data-motion-off] .st-story-card,
.st-motion[data-motion-off] .st-step__n {
  opacity: 1 !important;
  transform: none !important;
}

/* ---------- Scroll reveal ---------- */
.st-motion [data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--mo-rise), 0);
  transition:
    opacity var(--mo-base) var(--mo-ease),
    transform var(--mo-base) var(--mo-ease);
  transition-delay: calc(var(--mo-i, 0) * var(--mo-step));
}

.st-motion [data-reveal][data-in] {
  opacity: 1;
  transform: none;
}

/* Cards arrive with a touch of scale, so a grid reads as cards settling rather than text sliding. */
.st-motion [data-reveal="card"] {
  transform: translate3d(0, var(--mo-rise), 0) scale(0.985);
}

.st-motion [data-reveal="card"][data-in] { transform: none; }

/* The hero label and h1 lines lead, so the eye lands on the headline first. They rise
   without a fade: the headline is the largest paint on every page, and a start state at
   opacity 0 holds the page's Largest Contentful Paint until the fade has finished. A
   transform alone paints the text in the first frame and the motion still reads. */
.st-motion [data-reveal="lead-in"] {
  opacity: 1;
  transform: translate3d(0, 12px, 0);
  transition: transform 560ms var(--mo-ease);
  transition-delay: calc(var(--mo-i, 0) * 60ms);
}

.st-motion [data-reveal="lead-in"][data-in] {
  transform: none;
}

/* ---------- Hover and press ---------- */
.st-service,
.st-case,
.st-product,
.st-feature,
.st-story-card,
.st-compare__col {
  transition:
    transform var(--mo-base) var(--mo-ease),
    border-color var(--mo-fast) ease,
    box-shadow var(--mo-base) var(--mo-ease);
}

.st-service:hover,
.st-case:hover,
.st-product:hover,
.st-feature:hover {
  transform: translateY(-3px);
  border-color: var(--ink-3);
}

.st-btn {
  transition:
    background-color var(--mo-fast) ease,
    border-color var(--mo-fast) ease,
    color var(--mo-fast) ease,
    transform var(--mo-fast) var(--mo-ease);
}

.st-btn:hover { transform: translateY(-1px); }
.st-btn:active { transform: translateY(0); }

/* Links draw their underline from the left rather than blinking it on. */
.st-nav a,
.st-link,
.st-footer a,
.ab-menu__nav a {
  position: relative;
  transition: color var(--mo-fast) ease;
}

.st-nav a::after,
.st-link::after,
.st-footer a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -3px;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 260ms var(--mo-ease);
}

.st-nav a:hover::after,
.st-link:hover::after,
.st-footer a:hover::after { transform: scaleX(1); }

.st-footer a::after { bottom: -2px; }

/* Icon tiles tilt very slightly with their card. */
.st-service:hover .st-tile,
.st-feature:hover .st-tile {
  transform: scale(1.06);
}

.st-tile { transition: transform var(--mo-base) var(--mo-ease); }

/* ---------- Story cards: the offline to reconciled sequence ---------- */
.st-motion .st-story__connector svg {
  animation: mo-nudge 2.8s var(--mo-ease) infinite;
}

.st-motion .st-story__connector:nth-of-type(4) svg { animation-delay: 0.5s; }

@keyframes mo-nudge {
  0%, 62%, 100% { transform: translateY(0); opacity: 0.75; }
  76% { transform: translateY(3px); opacity: 1; }
}

.st-motion .st-progress span {
  animation: mo-sync 3.4s var(--mo-ease) infinite;
}

@keyframes mo-sync {
  0% { width: 8%; }
  60%, 100% { width: 64%; }
}

/* ---------- Steps: the connecting line draws itself ---------- */
.st-motion .st-steps [data-reveal][data-in] .st-step__n {
  animation: mo-pop 620ms var(--mo-ease) backwards;
  animation-delay: calc(var(--mo-i, 0) * var(--mo-step) + 120ms);
}

@keyframes mo-pop {
  from { transform: scale(0.82); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

/* ---------- Dark hero: the glow breathes, the grid drifts ---------- */
.st-motion .st-glow::before {
  animation: mo-breathe 9s ease-in-out infinite;
}

@keyframes mo-breathe {
  0%, 100% { opacity: 0.85; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.06); }
}

/* ---------- FAQ ---------- */
.st-faq summary { transition: color var(--mo-fast) ease; }

.st-faq summary::after { transition: transform 300ms var(--mo-ease); }

.st-motion .st-faq details[open] > *:not(summary) {
  animation: mo-open 420ms var(--mo-ease);
}

/* Slide only, never opacity: an error message and an open answer must be readable even if
   the animation never runs (a background tab, a paused timeline, a still capture). */
@keyframes mo-open {
  from { transform: translateY(-6px); }
  to { transform: none; }
}

/* ---------- Form fields ---------- */
.st-input,
.st-select,
.st-textarea {
  transition:
    border-color var(--mo-fast) ease,
    box-shadow var(--mo-fast) ease;
}

.st-field__error {
  animation: mo-open 300ms var(--mo-ease);
}

/* ---------- Nothing moves when the reader asked for that ---------- */
@media (prefers-reduced-motion: reduce) {
  .st-motion [data-reveal],
  .st-motion [data-reveal="card"],
  .st-motion [data-reveal="lead-in"] {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .st-motion .st-glow::before,
  .st-motion .st-story__connector svg,
  .st-motion .st-progress span,
  .st-motion .st-faq details[open] > *:not(summary),
  .st-motion .st-steps [data-reveal][data-in] .st-step__n {
    animation: none;
  }

  .st-btn:hover,
  .st-service:hover,
  .st-case:hover,
  .st-product:hover,
  .st-feature:hover { transform: none; }
}

/* ---------- Hero story: the scenario rotates ---------- */
.ab-tick {
  width: 22px;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.22);
  transition: background-color 420ms var(--mo-ease);
}

.ab-tick.is-on { background: var(--purple); }

/* The whole card settles into its new content: it lifts, softens, and comes back. Each
   card runs this on its own beat, so the three read as before, then process, then after.
   Two identical animations, alternated per change, because an animation only restarts
   when its name changes. */
.st-motion .ab-swap-a { animation: mo-card-a 480ms var(--mo-ease); }
.st-motion .ab-swap-b { animation: mo-card-b 480ms var(--mo-ease); }

@keyframes mo-card-a {
  0% { opacity: 0; transform: translateY(-10px) scale(0.985); }
  100% { opacity: 1; transform: none; }
}

@keyframes mo-card-b {
  0% { opacity: 0; transform: translateY(-10px) scale(0.985); }
  100% { opacity: 1; transform: none; }
}

/* Hero label: the three words arrive in turn, and say themselves again on each cycle. */
.ab-say .ab-word { display: inline-block; }

.st-motion .ab-say-a .ab-word { animation: mo-word-a 620ms var(--mo-ease) backwards; }
.st-motion .ab-say-b .ab-word { animation: mo-word-b 620ms var(--mo-ease) backwards; }

.st-motion .ab-say .ab-word:nth-of-type(2) { animation-delay: 150ms; }
.st-motion .ab-say .ab-word:nth-of-type(3) { animation-delay: 300ms; }

@keyframes mo-word-a {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

@keyframes mo-word-b {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .st-motion .ab-swap-a,
  .st-motion .ab-swap-b,
  .st-motion .ab-say-a .ab-word,
  .st-motion .ab-say-b .ab-word { animation: none; }
}
