/* How the landing page moves, in the app's own motion (docs/design/screens/Motion.dc.html for Nomi
   and answers, NavMotion.dc.html for paper and springs, Splash.dc.html for the highlighter swipe and
   Today laying in). Every keyframe starts from a state and ends on the page's own, and the load
   animations fill backwards only, so once they've run, or without this file, everything is simply
   where the page puts it. The landing page and /word load it; /word only lays its cards in.

   On load the headline settles word by word, the highlighter swipes under "keeping.", Today lays in,
   the phone arrives on a spring and the word card sticks on. Then it idles: the phone floats, the
   sparkles twinkle, Nomi bobs, blinks and glances about, and the card turns through the last few
   days' words. Below the fold each section rises in as it scrolls into view: scroll-driven where the
   browser has view timelines, site.js where it hasn't. Reduce Motion gets short fades and nothing
   that moves; "Pause motion" (a checkbox in the hero) stops the idling. */

/* ---- Keyframes ---- */

@keyframes lay-in { from { opacity: 0; transform: translateY(16px); } }
@keyframes settle { from { opacity: 0; transform: translateY(0.4em) rotate(1.5deg); } }
@keyframes sweep { from { transform: rotate(-1.5deg) scaleX(0); } to { transform: rotate(-1.5deg) scaleX(1); } }
@keyframes arrive { from { opacity: 0; transform: translateY(72px) rotate(6deg); } }
@keyframes grow { from { opacity: 0; transform: translateX(-50%) scale(0.6); } }
@keyframes stick { from { opacity: 0; transform: translateY(18px) scale(0.72) rotate(-11deg); } }
@keyframes pop-in { 0% { opacity: 0; transform: scale(0); } 60% { opacity: 1; transform: scale(1.3); } }
@keyframes peek { from { opacity: 0; transform: translateY(30px) scale(0.92, 1.08); } }
@keyframes fade { from { opacity: 0; } }

@keyframes float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -12px; } }
@keyframes bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
@keyframes shadow { 0%, 100% { transform: scaleX(1); opacity: 0.85; } 50% { transform: scaleX(0.9); opacity: 0.6; } }
@keyframes blink { 0%, 88%, 100% { transform: scaleY(1); } 92% { transform: scaleY(0.08); } 96% { transform: scaleY(1); } }
@keyframes glance {
  0%, 22% { transform: translate(0, 0); } 28%, 44% { transform: translate(-4px, 1px); } 50%, 58% { transform: translate(0, 0); }
  64%, 80% { transform: translate(4px, -1px); } 86%, 100% { transform: translate(0, 0); }
}
@keyframes twinkle { 0%, 100% { transform: scale(1) rotate(0); opacity: 1; } 50% { transform: scale(0.45) rotate(25deg); opacity: 0.4; } }

/* The word card's three words, 4.5 s each over a 13.5 s turn: the first starts shown, the others hidden. */
@keyframes turn-first {
  0%, 28.89% { opacity: 1; transform: none; }
  33.33%, 95.56% { opacity: 0; transform: translateY(-8px); }
  95.57% { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: none; }
}
@keyframes turn {
  0% { opacity: 0; transform: translateY(10px); }
  4.44%, 33.33% { opacity: 1; transform: none; }
  37.78%, 100% { opacity: 0; transform: translateY(-8px); }
}

/* Answers (Motion.dc.html): a right pick bumps and Nomi hops; a wrong one shakes and Nomi slumps. */
@keyframes bump { 40% { transform: scale(1.03); } }
@keyframes shake { 15% { transform: translateX(-9px); } 35% { transform: translateX(8px); } 55% { transform: translateX(-6px); } 75% { transform: translateX(4px); } }
@keyframes mark-pop { 0% { transform: scale(0); } 60% { transform: scale(1.3); } 100% { transform: scale(1); } }
@keyframes hop {
  16% { transform: translateY(0) scale(1.12, 0.84); } 40% { transform: translateY(-30px) scale(0.93, 1.09); }
  60% { transform: translateY(0) scale(1.14, 0.82); } 76% { transform: translateY(0) scale(0.96, 1.05); }
}
@keyframes shadow-hop { 40% { transform: scaleX(0.62); opacity: 0.35; } 60% { transform: scaleX(1.12); opacity: 0.95; } }
@keyframes slump {
  10% { transform: translateY(-6px) scale(0.95, 1.06); } 30% { transform: translateY(3px) scale(1.14, 0.82) rotate(-3deg); }
  55% { transform: translateY(3px) scale(1.08, 0.9) rotate(-2deg); } 100% { transform: translateY(4px) scale(1.05, 0.93) rotate(-2deg); }
}
@keyframes droop { 0%, 100% { transform: translateY(4px) scale(1.05, 0.93) rotate(-2deg); } 50% { transform: translateY(2px) scale(1.03, 0.95) rotate(-2deg); } }
@keyframes spark { 0% { transform: scale(0) rotate(0); opacity: 0; } 30% { transform: scale(1.3) rotate(25deg); opacity: 1; } 100% { transform: scale(1) rotate(0); opacity: 1; } }
@keyframes sweat { 0% { transform: translateY(-8px); opacity: 0; } 25% { transform: translateY(-4px); opacity: 1; } 70%, 100% { transform: translateY(12px); opacity: 0; } }

@keyframes rise { from { opacity: 0; transform: translateY(56px); } to { opacity: 1; transform: none; } }
@keyframes arrive-in { from { opacity: 0; transform: translateY(80px) rotate(var(--turn, 0deg)); } to { opacity: 1; transform: none; } }

/* "Pause motion" holds every idle loop where it is. */
:root:has(#hold:checked) { --idle: paused; }

/* Only the parts that move: the sparkles' wrapping groups place them with their own transform. */
.mascot .m-body, .mascot .m-shadow, .mascot .m-open, .mascot .m-look, .mascot .m-spark, .mascot .m-sweat {
  transform-box: fill-box; transform-origin: center;
}
.mascot .m-body { transform-origin: 50% 100%; }

@media (prefers-reduced-motion: no-preference) {
  /* ---- On load ---- */
  .lay { animation: lay-in 440ms var(--out) backwards; }
  .lay-1 { animation-delay: 0ms; }
  .lay-2 { animation-delay: 380ms; }
  .lay-3 { animation-delay: 450ms; }
  .lay-4 { animation-delay: 520ms; }
  .lay-5 { animation: lay-in 560ms var(--spring) 600ms backwards; }
  .settle { display: inline-block; animation: settle 760ms var(--spring) backwards; }
  .settle-1 { animation-delay: 60ms; }
  .settle-2 { animation-delay: 150ms; }
  .settle-3 { animation-delay: 240ms; }
  .hl-sweep::before { animation: sweep 560ms var(--swipe) 560ms backwards; }

  .hero-art::before { animation: grow 900ms var(--out) 80ms backwards; }
  .phone-stage { animation: arrive 860ms var(--spring) 180ms backwards; }
  .word-sticker { animation: stick 560ms var(--pop) 820ms backwards; }
  .hero-art .sparkle { animation: pop-in 480ms var(--pop) backwards, twinkle 3.2s ease-in-out infinite; animation-play-state: var(--idle, running); }
  .hero-art .s1 { animation-delay: 1000ms, 1500ms; }
  .hero-art .s2 { animation-delay: 1120ms, 2100ms; animation-duration: 480ms, 3.8s; }
  .hero-art .s3 { animation-delay: 1240ms, 1800ms; animation-duration: 480ms, 2.8s; }
  .hero .mascot { animation: peek 560ms var(--spring) 1000ms backwards; }

  /* ---- Idle ---- */
  .float { animation: float 6s ease-in-out 1.2s infinite; animation-play-state: var(--idle, running); }
  .float-soft { animation: bob 3.2s ease-in-out infinite; animation-play-state: var(--idle, running); }
  .mascot .m-body { animation: bob 2.6s ease-in-out 1.6s infinite; animation-play-state: var(--idle, running); }
  .mascot .m-shadow { animation: shadow 2.6s ease-in-out 1.6s infinite; animation-play-state: var(--idle, running); }
  .mascot .m-open { animation: blink 4.2s ease-in-out 1.8s infinite; animation-play-state: var(--idle, running); }
  .mascot .m-look { animation: glance 6s ease-in-out 2.4s infinite; animation-play-state: var(--idle, running); }

  .recent-word { animation: turn 13.5s ease-in-out infinite both; animation-play-state: var(--idle, running); }
  .recent-word:first-child { animation-name: turn-first; animation-delay: 1.6s; }
  .recent-word:nth-child(2) { animation-delay: 5.5s; }
  .recent-word:nth-child(3) { animation-delay: 10s; }
  .recent-word:only-child { animation: none; }

  /* ---- Answers ---- */
  .pick-right:checked + .option { animation: bump 360ms ease-out; }
  .pick-wrong:checked + .option { animation: shake 440ms ease-in-out; }
  .pick:checked + .option .mark,
  .quiz-card:has(.pick:checked) .pick-right + .option .mark { animation: mark-pop 420ms var(--pop) backwards; }
  .pick-right:checked ~ .verdict p,
  .pick-wrong:checked ~ .verdict p,
  .pick:checked ~ .verdict .verdict-link { animation: lay-in 320ms var(--out) 120ms backwards; }

  .pick:checked ~ .mascot .m-look,
  .pick:checked ~ .mascot .m-open { animation: none; }
  .pick-right:checked ~ .mascot .m-body { animation: hop 720ms ease-out, bob 2.6s ease-in-out 720ms infinite; animation-play-state: var(--idle, running); }
  .pick-right:checked ~ .mascot .m-shadow { animation: shadow-hop 720ms ease-out, shadow 2.6s ease-in-out 720ms infinite; animation-play-state: var(--idle, running); }
  .pick-right:checked ~ .mascot .m-spark { animation: spark 900ms ease-out backwards; }
  .pick-right:checked ~ .mascot g:nth-child(2) > .m-spark { animation-delay: 90ms; }
  .pick-right:checked ~ .mascot g:nth-child(3) > .m-spark { animation-delay: 180ms; }
  .pick-right:checked ~ .mascot g:nth-child(4) > .m-spark { animation-delay: 60ms; }
  .pick-wrong:checked ~ .mascot .m-body { animation: slump 900ms ease-in-out backwards, droop 3.4s ease-in-out 900ms infinite; animation-play-state: var(--idle, running); }
  .pick-wrong:checked ~ .mascot .m-shadow { animation: none; }
  .pick-wrong:checked ~ .mascot .m-sweat { animation: sweat 2.4s ease-in 200ms infinite; animation-play-state: var(--idle, running); }

  /* ---- Chunky buttons: a little lift on hover, down by the lip on press, a spring back up ---- */
  a.btn, .option { transition: transform 240ms var(--spring), box-shadow 240ms var(--spring), border-color 160ms ease, background-color 160ms ease, opacity 200ms ease; }
  a.btn:hover { transform: translateY(-2px); box-shadow: 0 6px 0 var(--ink); }
  a.btn:active { transform: translateY(4px); box-shadow: 0 0 0 var(--ink); transition-duration: 60ms; }
  .btn-small:hover { box-shadow: 0 5px 0 var(--ink); }
  .closing a.btn:hover { box-shadow: 0 6px 0 var(--ink-deep); }
  .closing a.btn:active { box-shadow: 0 0 0 var(--ink-deep); }
  .option:hover { transform: translateY(-2px); box-shadow: 0 6px 0 var(--peri); }
  .option:active { transform: translateY(4px); box-shadow: 0 0 0 var(--line); transition-duration: 60ms; }
  .quiz-card:has(.pick:checked) .option:hover { transform: none; }

  /* ---- On scroll: words lay in like Today's cards; the pictures arrive as the hero phone does,
     turning into their tilt, a beat behind ---- */
  /* A phone turning needs room either side of it, which a phone screen hasn't got. */
  @media (min-width: 720px) {
    .reveal-media { --turn: 3deg; }
    .feature.flip .reveal-media { --turn: -3deg; }
  }
  @supports (animation-timeline: view()) {
    .reveal {
      animation: rise var(--out) both;
      animation-timeline: view();
      animation-range: entry 0% entry 75%;
    }
    .reveal-media { animation-name: arrive-in; animation-range: entry 10% entry 95%; }
  }
  /* site.js, where there are no view timelines: only what starts below the fold waits. */
  .reveal.is-waiting { opacity: 0; transform: translateY(56px); }
  .reveal.is-shown { animation: rise 760ms var(--out) backwards; }
  .reveal-media.is-shown { animation: arrive-in 860ms var(--spring) 120ms backwards; }
}

/* Paper has no scroll position: print every section where it sits. */
@media print {
  .reveal { animation: none !important; opacity: 1 !important; transform: none !important; }
}

/* Reduce Motion: no movement, only short fades, everything in place. */
@media (prefers-reduced-motion: reduce) {
  .lay, .settle, .hero-art, .hero .mascot { animation: fade 240ms ease-out backwards; }
  .hold, .hold-input { display: none; }
}
