/* ==========================================================================
   04 — Motion: keyframes, scroll reveals, screen transitions
   ========================================================================== */

@keyframes spin      { to { transform: rotate(360deg); } }
@keyframes fadeIn    { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeOut   { from { opacity: 1; } to { opacity: 0; } }
@keyframes shimmer   { to { transform: translateX(100%); } }

@keyframes popIn {
  from { opacity: 0; transform: translateY(-8px) scale(.96); }
  to   { opacity: 1; transform: none; }
}
@keyframes modalIn {
  from { opacity: 0; transform: translateY(26px) scale(.96); }
  to   { opacity: 1; transform: none; }
}
@keyframes modalOut {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(12px) scale(.98); }
}
@keyframes toastIn {
  from { opacity: 0; transform: translateX(40px) scale(.94); }
  to   { opacity: 1; transform: none; }
}
/* On the phone, messages come in from below. */
@keyframes toastUp {
  from { opacity: 0; transform: translateY(22px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
/* Sheets (modal, action sheet) slide in from the bottom edge. */
@keyframes sheetIn {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
@keyframes sheetOut {
  from { transform: translateY(0); }
  to   { transform: translateY(100%); }
}
@keyframes toastOut {
  from { opacity: 1; transform: none; max-height: 140px; }
  to   { opacity: 0; transform: translateY(14px) scale(.96); max-height: 0; margin-top: -10px; }
}
@keyframes slideDownFade {
  from { opacity: 0; transform: translateY(-5px); }
  to   { opacity: 1; transform: none; }
}
@keyframes checkPop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.22); }
  100% { transform: scale(1); }
}
@keyframes shake {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(4px); }
  30%, 50%, 70% { transform: translateX(-7px); }
  40%, 60% { transform: translateX(7px); }
}
/* Deliberately uses `translate` rather than `transform`, so a reveal
   animation can run on `transform` at the same time without clashing. */
@keyframes floaty {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -9px; }
}
@keyframes drawStroke { to { stroke-dashoffset: 0; } }
@keyframes confettiFall {
  0%   { transform: translate3d(0, -12vh, 0) rotate(0deg); opacity: 1; }
  100% { transform: translate3d(var(--dx, 0), 104vh, 0) rotate(var(--rot, 540deg)); opacity: 0; }
}
@keyframes successRing {
  0%   { transform: scale(.4); opacity: 0; }
  55%  { transform: scale(1.08); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes waveHand {
  0%, 60%, 100% { transform: rotate(0deg); }
  10%, 30%, 50% { transform: rotate(15deg); }
  20%, 40%      { transform: rotate(-9deg); }
}
@keyframes gradientPan {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}
@keyframes blobDrift {
  0%, 100% { transform: translate3d(0,0,0) scale(1); }
  33%      { transform: translate3d(3%, -4%, 0) scale(1.06); }
  66%      { transform: translate3d(-3%, 3%, 0) scale(.96); }
}

.u-shake { animation: shake .5s var(--e-in-out); }

/* --------------------------------------------------------------------------
   Scroll reveals
   Elements with [data-reveal] start invisible and fade in as they enter the
   viewport. --d = delay, for staggering a group.
   -------------------------------------------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--reveal-y, 26px), 0) scale(var(--reveal-s, 1));
  transition:
    opacity var(--t-xslow) var(--e-out) var(--d, 0ms),
    transform var(--t-xslow) var(--e-out) var(--d, 0ms),
    filter var(--t-xslow) var(--e-out) var(--d, 0ms);
  filter: blur(var(--reveal-blur, 0px));
  will-change: opacity, transform;
}
[data-reveal="left"]  { transform: translate3d(-32px, 0, 0); }
[data-reveal="right"] { transform: translate3d(32px, 0, 0); }
[data-reveal="zoom"]  { transform: scale(.93); }
[data-reveal="blur"]  { --reveal-blur: 8px; }
[data-reveal].is-in {
  opacity: 1; transform: none; filter: blur(0);
  /* Release the compositing hint once the animation is done. */
  will-change: auto;
}

/* --------------------------------------------------------------------------
   Router screen transitions
   -------------------------------------------------------------------------- */
#screen { position: relative; }
.screen-enter { animation: screenIn var(--t-slow) var(--e-out) both; }
.screen-leave { animation: screenOut 180ms var(--e-in-out) both; }
@keyframes screenIn {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes screenOut {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translate3d(0, -8px, 0); }
}
.screen-enter--back { animation-name: screenInBack; }
@keyframes screenInBack {
  from { opacity: 0; transform: translate3d(-18px, 0, 0); }
  to   { opacity: 1; transform: none; }
}

/* Blocks that stagger in on a screen change -------------------------------- */
.stagger > * {
  opacity: 0;
  animation: staggerIn var(--t-slow) var(--e-out) both;
}
.stagger > *:nth-child(1) { animation-delay:  60ms; }
.stagger > *:nth-child(2) { animation-delay: 120ms; }
.stagger > *:nth-child(3) { animation-delay: 180ms; }
.stagger > *:nth-child(4) { animation-delay: 240ms; }
.stagger > *:nth-child(5) { animation-delay: 300ms; }
.stagger > *:nth-child(6) { animation-delay: 360ms; }
.stagger > *:nth-child(7) { animation-delay: 420ms; }
.stagger > *:nth-child(8) { animation-delay: 480ms; }
.stagger > *:nth-child(n+9) { animation-delay: 540ms; }
@keyframes staggerIn {
  from { opacity: 0; transform: translate3d(0, 16px, 0); }
  to   { opacity: 1; transform: none; }
}

/* Confetti ----------------------------------------------------------------- */
.confetti {
  position: fixed; inset: 0; pointer-events: none; z-index: var(--z-toast);
  overflow: hidden;
}
.confetti i {
  position: absolute; top: 0; width: 9px; height: 15px; border-radius: 2px;
  animation: confettiFall var(--dur, 2.6s) var(--e-in-out) var(--delay, 0s) forwards;
}

/* Reduced motion ----------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; filter: none !important; }
  .confetti { display: none; }
}
