/* ===========================================================================
   motion.css — AP Chemistry Toolkit motion system
   ---------------------------------------------------------------------------
   Load AFTER ced-topic-pages.css and student-ui.css so the tokens are
   available to everything and the reduced-motion rules here win.

   Design constraints this file is built around:
     - 88 canvas elements and multiple Chart.js instances share the main
       thread. Nothing here may run per-frame JavaScript.
     - The floor device is a low-end school Chromebook (Chromium).
     - Reading content must never scrub backwards as a student scrolls up
       to re-read it.

   So: continuous effects use CSS scroll-driven timelines (off the main
   thread in Chromium). Reading content uses one-shot IntersectionObserver
   reveals (IO fires a handful of times, never per frame). No scroll
   event listeners anywhere.
   =========================================================================== */

:root {
  /* --- Easing -------------------------------------------------------------
     The site previously used `ease` for all 90 of its transitions. These
     four cover every case. Default to --ease-out-quart.                    */
  --ease-out-quart:    cubic-bezier(0.25, 1, 0.5, 1);     /* entrances, hover */
  --ease-out-expo:     cubic-bezier(0.16, 1, 0.3, 1);     /* large reveals */
  --ease-in-out-quart: cubic-bezier(0.76, 0, 0.24, 1);    /* state swaps */
  --ease-out-back:     cubic-bezier(0.34, 1.4, 0.64, 1);  /* confirmations only */

  /* --- Duration -----------------------------------------------------------
     Anything under ~180ms reads as a state swap rather than a movement.
     The old 0.1s/0.15s transitions are below that threshold.               */
  --dur-micro:  120ms;  /* color only, no movement */
  --dur-fast:   200ms;  /* hover, focus, small controls */
  --dur-base:   320ms;  /* default: cards, panels, buttons */
  --dur-slow:   520ms;  /* section entrances */
  --dur-reveal: 720ms;  /* scroll reveals */

  --m-stagger:   70ms;  /* per-item delay in a staggered group */
  --m-rise:      18px;  /* travel distance for a reveal */
}

/* ===========================================================================
   1. Scroll reveal (one-shot, IntersectionObserver-driven)
   ---------------------------------------------------------------------------
   motion.js adds .m-js to <html>, then .m-in to each element as it enters.
   Without JS the content is simply visible — never trapped at opacity 0.
   =========================================================================== */

.m-js .m-reveal {
  opacity: 0;
  transform: translate3d(0, var(--m-rise), 0);
  transition:
    opacity   var(--dur-reveal) var(--ease-out-quart),
    transform var(--dur-reveal) var(--ease-out-expo);
  transition-delay: calc(var(--m-i, 0) * var(--m-stagger));
  will-change: opacity, transform;
}

.m-js .m-reveal.m-in {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* Drop the compositor hint once the element has arrived. */
.m-js .m-reveal.m-settled { will-change: auto; }

/* Scale variant for figures and diagrams. */
.m-js .m-reveal-fig {
  opacity: 0;
  transform: translate3d(0, calc(var(--m-rise) * 1.4), 0) scale(0.97);
  transform-origin: 50% 85%;
  transition:
    opacity   var(--dur-reveal) var(--ease-out-quart),
    transform var(--dur-reveal) var(--ease-out-expo);
  transition-delay: calc(var(--m-i, 0) * var(--m-stagger));
}
.m-js .m-reveal-fig.m-in { opacity: 1; transform: translate3d(0,0,0) scale(1); }

/* ===========================================================================
   2. Scroll-driven effects (CSS only, off the main thread)
   ---------------------------------------------------------------------------
   Chromium runs these on the compositor. Everything is wrapped in @supports
   so non-Chromium browsers get the static layout rather than a broken one.
   =========================================================================== */

@supports (animation-timeline: scroll()) {

  /* Reading-progress rail. Pure CSS: no scroll handler, no rAF. */
  .m-progress-rail {
    position: fixed;
    top: env(safe-area-inset-top, 0px);
    left: 0;
    height: 3px;
    width: 100%;
    transform-origin: 0 50%;
    transform: scaleX(0);
    background: linear-gradient(90deg, var(--gold-dark), var(--gold-light));
    z-index: 60;
    pointer-events: none;
    animation: m-progress linear both;
    animation-timeline: scroll(root block);
  }
  @keyframes m-progress { to { transform: scaleX(1); } }

  /* Lesson header depth. Small travel: parallax past ~30px reads as drift. */
  .m-parallax {
    animation: m-parallax linear both;
    animation-timeline: view();
    animation-range: entry 0% exit 100%;
  }
  @keyframes m-parallax {
    from { transform: translate3d(0, -14px, 0); }
    to   { transform: translate3d(0,  14px, 0); }
  }

  /* Figures that bloom slightly as they cross the viewport. */
  .m-bloom {
    animation: m-bloom linear both;
    animation-timeline: view();
    animation-range: entry 15% cover 45%;
  }
  @keyframes m-bloom {
    from { opacity: .55; transform: scale(.975); }
    to   { opacity: 1;   transform: scale(1); }
  }
}

/* ===========================================================================
   3. SVG draw-on
   ---------------------------------------------------------------------------
   For code-authored diagrams. motion.js measures each path and sets
   --m-len; the stroke then draws itself when the figure enters.
   =========================================================================== */

.m-js .m-draw path,
.m-js .m-draw line,
.m-js .m-draw polyline,
.m-js .m-draw circle {
  stroke-dasharray: var(--m-len, 1000);
  stroke-dashoffset: var(--m-len, 1000);
  transition: stroke-dashoffset var(--dur-reveal) var(--ease-out-quart);
  transition-delay: calc(var(--m-i, 0) * 90ms);
}
.m-js .m-draw.m-in path,
.m-js .m-draw.m-in line,
.m-js .m-draw.m-in polyline,
.m-js .m-draw.m-in circle { stroke-dashoffset: 0; }

/* ===========================================================================
   4. Route transitions (View Transitions API)
   ---------------------------------------------------------------------------
   motion.js wraps navigation in document.startViewTransition where it
   exists. Browsers without it simply swap panels as they do today.
   =========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) {
    animation: m-vt-out var(--dur-base) var(--ease-in-out-quart) both;
  }
  ::view-transition-new(root) {
    animation: m-vt-in var(--dur-slow) var(--ease-out-expo) both;
  }
  @keyframes m-vt-out { to { opacity: 0; transform: translate3d(0,-10px,0); } }
  @keyframes m-vt-in  { from { opacity: 0; transform: translate3d(0,16px,0); } }

  /* A unit card and the topic header it opens share a name, so the
     browser tweens between them instead of cross-fading. */
  ::view-transition-group(topic-title) {
    animation-duration: var(--dur-slow);
    animation-timing-function: var(--ease-out-expo);
  }
}

/* ===========================================================================
   5. Interaction states
   =========================================================================== */

.ced-topic-card,
.ced-quiz-choice,
.ced-topic-switch button,
.overview-unit-card,
.overview-action-card {
  transition:
    transform    var(--dur-base) var(--ease-out-quart),
    border-color var(--dur-fast) var(--ease-out-quart),
    background   var(--dur-fast) var(--ease-out-quart),
    box-shadow   var(--dur-base) var(--ease-out-quart);
}

.ced-quiz-choice:hover:not([disabled]) {
  transform: translate3d(3px, 0, 0);
  border-color: var(--gold);
}
.overview-unit-card:hover,
.overview-action-card:hover {
  transform: translate3d(0, -3px, 0);
  box-shadow: 0 10px 28px rgba(0, 0, 0, .3);
}

/* Correct answers settle; they don't bounce. Wrong answers don't shake —
   a shake reads as punishment, and these are students. */
.ced-quiz-choice.correct {
  animation: m-confirm var(--dur-slow) var(--ease-out-back);
}
@keyframes m-confirm {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.015); }
  100% { transform: scale(1); }
}

/* Feedback text arrives rather than appearing. */
.ced-feedback:not([hidden]) {
  animation: m-feedback var(--dur-base) var(--ease-out-quart) both;
}
@keyframes m-feedback {
  from { opacity: 0; transform: translate3d(0, -5px, 0); }
}

/* <details> disclosure. */
.ced-topic-card details > summary {
  transition: color var(--dur-fast) var(--ease-out-quart);
  cursor: pointer;
}
.ced-topic-card details[open] > summary ~ * {
  animation: m-disclose var(--dur-base) var(--ease-out-quart) both;
}
@keyframes m-disclose {
  from { opacity: 0; transform: translate3d(0, -6px, 0); }
}

/* Focus should be instant — never animate a focus ring. */
:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
  transition: none;
}

/* ===========================================================================
   6. Reduced motion
   ---------------------------------------------------------------------------
   This REPLACES the blanket `*{animation:none!important}` currently in
   student-ui.css. Movement is what triggers vestibular symptoms; a plain
   opacity fade does not. Killing every transition also removes useful
   feedback, so keep fades and remove travel.
   =========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto !important; }

  .m-js .m-reveal,
  .m-js .m-reveal-fig {
    transform: none !important;
    transition: opacity var(--dur-base) linear !important;
    transition-delay: 0ms !important;
  }

  .m-parallax,
  .m-bloom,
  .m-progress-rail { animation: none !important; }
  .m-progress-rail { transform: scaleX(0) !important; }

  .m-js .m-draw path,
  .m-js .m-draw line,
  .m-js .m-draw polyline,
  .m-js .m-draw circle {
    stroke-dasharray: none !important;
    stroke-dashoffset: 0 !important;
    transition: none !important;
  }

  .ced-quiz-choice:hover:not([disabled]),
  .overview-unit-card:hover,
  .overview-action-card:hover { transform: none !important; }

  .ced-quiz-choice.correct,
  .ced-feedback:not([hidden]),
  .ced-topic-card details[open] > summary ~ * { animation: none !important; }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: m-vt-fade var(--dur-fast) linear both !important;
  }
  @keyframes m-vt-fade { from { opacity: 0; } }
}

/* ===========================================================================
   7. Low-capability downgrade
   ---------------------------------------------------------------------------
   motion.js sets data-m-tier="lite" on <html> when the device reports
   little memory, few cores, or Data Saver. Continuous effects stop;
   one-shot reveals stay, shortened.
   =========================================================================== */

html[data-m-tier="lite"] .m-parallax,
html[data-m-tier="lite"] .m-bloom { animation: none !important; }

html[data-m-tier="lite"] .m-js .m-reveal,
html[data-m-tier="lite"] .m-js .m-reveal-fig {
  --m-rise: 10px;
  --m-stagger: 40ms;
  transition-duration: var(--dur-base) !important;
}
