/* ============================================================
   MOTION — fromSpec
   Component micro-transitions (atoms) + reduced-motion guards.
   Reveal from-states are set in JS (motion.js) under
   gsap.matchMedia(no-preference), so no-JS / reduced-motion always
   render fully visible. Animate transform / opacity / filter only.
   Durations + easing come from tokens/effects.css (no bounce).
   ============================================================ */

/* Note: reveal "from" states use a horizontal offset ≤ the section side padding
   (var(--space-6) = 24px), so an offset element never crosses the viewport edge —
   no sideways scrollbar at any width, and no root-level overflow clip (which would
   interfere with ScrollTrigger's scroller). body already has overflow-x: clip. */

/* ---- reveal pre-hide (flash guard) ----
   The section reveals (headers + [data-reveal] blocks) get their JS "from" state at
   module-eval; hide them from frame 1 so the gap before the script runs can't flash
   fully-visible content. IntersectionObserver then reveals them (GSAP's autoAlpha:1
   inline overrides this). html.js-motion is set in <head> only when reduced-motion is
   off, so reduced-motion (where the reveals never run) keeps everything visible.
   NOTE: list only elements animated *directly* — never a SplitText parent whose child
   lines animate, or the parent's opacity:0 would hide the revealed lines. */
html.js-motion [data-reveal],
html.js-motion .sec-head:not(.sec-head--static) > * { opacity: 0; }

/* ---- icon-swap (theme toggle sun/moon, nav burger/close) ----
   The displayed icon plays a quick scale+blur pop when it appears
   (display:none→block restarts the animation). Non-invasive: no layout
   change, just animation on the already-shown icon. Frequent/utility →
   fast, no spring. */
@media (prefers-reduced-motion: no-preference) {
  html[data-theme="dark"]  .theme-toggle .ic-sun,
  html[data-theme="light"] .theme-toggle .ic-moon,
  .nav-burger .ic-burger,
  .site-nav.menu-open .nav-burger .ic-close {
    animation: t-icon-in var(--duration-fast) var(--ease-out);
  }
}
@keyframes t-icon-in {
  from { opacity: 0; transform: scale(0.6); filter: blur(3px); }
  to   { opacity: 1; transform: none;       filter: blur(0); }
}

/* ---- error shake (invalid waitlist email) — purposeful feedback, auto-reverts ---- */
.cta-input.is-error { animation: t-shake 320ms var(--ease-out); border-color: var(--fs-del, #d4574e); }
@keyframes t-shake {
  10%, 90% { transform: translateX(-2px); }
  30%, 70% { transform: translateX(4px); }
  50%      { transform: translateX(-6px); }
}
@media (prefers-reduced-motion: reduce) { .cta-input.is-error { animation: none; } }

/* ---- card hover: shadow-led lift (Jakub: shadow over scale; adapts on the grid bg) ---- */
.own-sec .tile:hover, .pricing-sec .ptier:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--border-strong);
}
@media (prefers-reduced-motion: no-preference) {
  .own-sec .tile, .pricing-sec .ptier {
    transition: box-shadow var(--duration-normal) var(--ease-out),
                border-color var(--duration-normal) var(--ease-out),
                transform var(--duration-normal) var(--ease-out);
  }
  .own-sec .tile:hover, .pricing-sec .ptier:hover { transform: translateY(-2px); }
}
