/* Motion layer for "Nahar bel-haraka".

   - Only opacity and transforms (translate, scale, rotate) animate.
   - Every effect plays once when its piece is first seen, or when the
     visitor taps something. The one thing that repeats is the small blob
     tap hint, and it stops for good after the first poke.
   - Hidden start states exist only under html.js and
     prefers-reduced-motion: no-preference, so without the script, or for a
     visitor who asked for less motion, the page is simply all there.
   - html.lite (2 GB of memory or less, or data saver on) drops the staggering
     and the confetti and keeps one quick fade. */

:root {
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

@media screen and (prefers-reduced-motion: no-preference) {
  /* Hero: the three lines land one after another, then the rest */
  .hero h1 span,
  .hero__sub,
  .invite,
  .guests li,
  .asks li,
  .fan li,
  .path li,
  .closing {
    transition: opacity 0.35s, translate 0.7s var(--ease), scale 0.6s var(--ease), rotate 0.7s var(--ease);
  }

  .hero h1 span:nth-child(2) { transition-delay: 0.1s; }
  .hero h1 span:nth-child(3) { transition-delay: 0.2s; }
  .hero__sub { transition-delay: 0.32s; }

  .js .hero__text:not(.is-in) h1 span,
  .js .hero__text:not(.is-in) .hero__sub {
    opacity: 0;
    translate: 0 1.5rem;
  }

  /* The button lands with a keyframe animation, not a transition, so its
     own quick press and hover transition is left alone */
  .js .hero__text:not(.is-in) .btn {
    opacity: 0;
  }

  .js .hero__text.is-in .btn {
    animation: btn-in 0.6s var(--ease) 0.42s backwards;
  }

  @keyframes btn-in {
    from {
      opacity: 0;
      scale: 1.3;
    }
  }

  /* The invite is slapped on like a sticker */
  .invite {
    transition-delay: 0.3s;
  }

  .js .invite:not(.is-in) {
    opacity: 0;
    scale: 1.25;
    rotate: 10deg;
  }

  /* Answering it throws confetti once */
  .burst.go i {
    animation: burst 1.1s var(--ease) both;
  }

  @keyframes burst {
    0% {
      opacity: 1;
      transform: translate(0, 0) rotate(0deg) scale(0.4);
    }

    70% {
      opacity: 1;
    }

    100% {
      opacity: 0;
      transform: translate(var(--x), var(--y)) rotate(var(--r)) scale(1);
    }
  }

  /* Before: the guests are stuck on in turn */
  .guests li {
    transition-delay: calc(var(--i) * 80ms);
  }

  .js .guests:not(.is-in) li {
    opacity: 0;
    scale: 1.4;
    translate: 0 -1rem;
  }

  /* The day: the question shows, the stroke crosses it out, the answer lands */
  .ask span::after {
    transition: scale 0.45s 0.35s var(--ease);
  }

  .answer {
    transition: opacity 0.3s 0.65s, scale 0.5s 0.65s var(--spring);
  }

  .js .asks li:not(.is-in) {
    opacity: 0;
    translate: 0 1.5rem;
  }

  .js .asks li:not(.is-in) .ask span::after {
    scale: 0 1;
  }

  .js .asks li:not(.is-in) .answer {
    opacity: 0;
    scale: 0.7;
  }

  /* After: the prints start as one pile and fan out */
  .fan li {
    transition: opacity 0.3s, rotate 0.9s var(--spring);
    transition-delay: calc(var(--i) * 50ms);
  }

  .js .fan:not(.is-in) li {
    opacity: 0;
    rotate: 0deg;
  }

  /* Occasions: the new word pops in */
  .swap b.swapped {
    animation: swap 0.45s var(--spring);
  }

  @keyframes swap {
    from {
      scale: 0.6;
      rotate: 6deg;
    }
  }

  /* Steps: each stop arrives as you reach it, one after another on desktop */
  .js .path li:not(.is-in) {
    opacity: 0;
    translate: 0 2rem;
  }

  @media (min-width: 54rem) {
    .path li:nth-child(2) { transition-delay: 0.15s; }
    .path li:nth-child(3) { transition-delay: 0.3s; }
    .asks li:nth-child(2) { transition-delay: 0.12s; }
    .asks li:nth-child(3) { transition-delay: 0.24s; }
  }

  /* Closing: the big sticker lands */
  .js .closing:not(.is-in) {
    opacity: 0;
    scale: 0.8;
    rotate: 5deg;
  }

  /* The "؟" in the top bar hops when tapped; the hidden message pops in */
  .wordmark__q.is-jump { animation: mark-jump 0.55s ease-out; }
  @keyframes mark-jump {
    0% { transform: none; }
    18% { transform: translateY(3px) scale(1.12, 0.86); }
    48% { transform: translateY(-0.55em) scale(0.94, 1.1) rotate(-10deg); }
    78% { transform: translateY(2px) scale(1.06, 0.94); }
    100% { transform: none; }
  }
  .secret[open] { animation: secret-in 0.45s var(--spring, ease-out); }
  @keyframes secret-in {
    from { opacity: 0; transform: scale(0.8) rotate(4deg); }
    to { opacity: 1; transform: none; }
  }

  /* The tap mark: the arcs travel outwards one after another and fade,
     then rest a moment before the next wave */
  .blob__wave {
    animation: wave 2.8s 1.2s ease-out infinite both;
  }

  .blob__wave--2 { animation-delay: 1.36s; }
  .blob__wave--3 { animation-delay: 1.52s; }

  @keyframes wave {
    0% {
      opacity: 0;
      transform: translateX(-4px);
    }

    12% {
      opacity: 1;
    }

    38%,
    100% {
      opacity: 0;
      transform: translateX(5px);
    }
  }

  /* Weak phones and data saver: one quick fade per piece, nothing else */
  .lite .hero h1 span,
  .lite .hero__sub,
  .lite .invite,
  .lite .guests li,
  .lite .asks li,
  .lite .answer,
  .lite .ask span::after,
  .lite .fan li,
  .lite .path li {
    transition-delay: 0s;
    transition-duration: 0.3s;
  }

  .lite .hero__text.is-in .btn {
    animation: none;
  }

  .lite .burst {
    display: none;
  }
}

/* Nothing may widen the page */
html,
body {
  max-width: 100%;
  overflow-x: hidden;
  overflow-x: clip;
}
