/* ==========================================================================
   Alignmates — Motion system.

   Motion vocabulary (per the motion pass brief):
     A. Entrance      — .reveal, .stagger-children      (section content)
     B. Product state  — waveform, tabs, explorer, offline demo (real UI)
     C. Social motion   — orbit MoodMojis, story-strip, friendship story
     D. Ambient motion  — hero float/loose, orbit drift    (very subtle, slow)
     E. Interaction     — hover/focus/press on buttons, links, cards

   §1 is the one rule that matters most: everything here must degrade to
   "fully visible, no motion" if JavaScript never runs or the visitor has
   reduced motion set. Nothing here may set a base state of opacity: 0
   outside the .motion-ready scope.
   ========================================================================== */

/* ==========================================================================
   §1 — REVEAL (progressive enhancement)

   Default (no JS, no .motion-ready on <html>): everything is visible.
   Once site.js runs, it adds .motion-ready immediately, then observes
   .reveal / .stagger-children and adds .is-visible as each scrolls in.
   ========================================================================== */

.motion-ready .reveal {
  opacity: 0;
  transform: translateY(22px);
  transition:
    opacity var(--motion-reveal) var(--ease-out),
    transform var(--motion-reveal) var(--ease-out);
}

.motion-ready .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Small, deterministic stagger for sibling groups. Apply alongside
   .reveal on the group's children themselves (flow steps, scenario
   cards, diff cards, journey steps...) via nth-child below, OR apply
   .stagger-children to a wrapper so its direct children stagger as a
   set without the wrapper itself needing its own fade. */
.motion-ready .stagger-children > * {
  --stagger-step: 70ms;
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity var(--motion-slow) var(--ease-out),
    transform var(--motion-slow) var(--ease-out);
}

.motion-ready .stagger-children.is-visible > * {
  opacity: 1;
  transform: none;
}

.stagger-children--slow {
  --stagger-step: 150ms;
}

.stagger-children--fast {
  --stagger-step: 60ms;
}

.stagger-children > *:nth-child(1) { transition-delay: calc(var(--stagger-step) * 0); }
.stagger-children > *:nth-child(2) { transition-delay: calc(var(--stagger-step) * 1); }
.stagger-children > *:nth-child(3) { transition-delay: calc(var(--stagger-step) * 2); }
.stagger-children > *:nth-child(4) { transition-delay: calc(var(--stagger-step) * 3); }
.stagger-children > *:nth-child(5) { transition-delay: calc(var(--stagger-step) * 4); }
.stagger-children > *:nth-child(6) { transition-delay: calc(var(--stagger-step) * 5); }
.stagger-children > *:nth-child(7) { transition-delay: calc(var(--stagger-step) * 6); }
.stagger-children > *:nth-child(8) { transition-delay: calc(var(--stagger-step) * 7); }
.stagger-children > *:nth-child(9) { transition-delay: calc(var(--stagger-step) * 8); }

/* Existing per-item .reveal groups (flow steps, scenario cards, diff
   cards, journey steps, journey-4 steps) get the same deterministic
   stagger — each one already observes independently, this just times
   their entrance relative to siblings instead of firing in a clump. */
.flow__step:nth-child(1),
.scenario-grid > *:nth-child(1),
.diff-grid > *:nth-child(1),
.community-journey > *:nth-child(1),
.journey-4__step:nth-child(1) { transition-delay: 0ms; }

.flow__step:nth-child(2),
.scenario-grid > *:nth-child(2),
.diff-grid > *:nth-child(2),
.community-journey > *:nth-child(2),
.journey-4__step:nth-child(2) { transition-delay: 90ms; }

.flow__step:nth-child(3),
.scenario-grid > *:nth-child(3),
.diff-grid > *:nth-child(3),
.community-journey > *:nth-child(3),
.journey-4__step:nth-child(3) { transition-delay: 180ms; }

.flow__step:nth-child(4),
.scenario-grid > *:nth-child(4),
.diff-grid > *:nth-child(4),
.community-journey > *:nth-child(4),
.journey-4__step:nth-child(4) { transition-delay: 270ms; }

.scenario-grid > *:nth-child(5),
.diff-grid > *:nth-child(5) { transition-delay: 360ms; }
.diff-grid > *:nth-child(6) { transition-delay: 450ms; }

/* ==========================================================================
   §2 — NAVBAR ENTRANCE  (once, on load — pure CSS, no JS dependency)
   ========================================================================== */

@keyframes nav-in {
  from {
    opacity: 0;
    transform: translate(-50%, -8px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

.nav {
  animation: nav-in var(--motion-slow) var(--ease-out) both;
}

/* ==========================================================================
   §3 — PRODUCT LOOP + COMMUNITY JOURNEY: connector draws in once revealed
   ========================================================================== */

.flow__step::after,
.journey-step::after {
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--motion-slow) var(--ease-out) 200ms;
}

.flow__step.is-visible::after,
.journey-step.is-visible::after {
  transform: scaleX(1);
}

/* ==========================================================================
   §4 — WAVEFORM  (idle drift, hover energy, active/playing state)

   Negative animation-delay staggers start points so bars don't move in
   lockstep from just two duration values. transform-only — never
   touches layout.
   ========================================================================== */

@keyframes waveform-idle {
  0%, 100% { transform: scaleY(1); }
  50% { transform: scaleY(0.78); }
}

.waveform span {
  transform-origin: bottom;
  animation: waveform-idle 2.4s ease-in-out infinite;
}

.waveform span:nth-child(2n) { animation-duration: 2.1s; animation-delay: -0.6s; }
.waveform span:nth-child(3n) { animation-duration: 2.8s; animation-delay: -1.4s; }
.waveform span:nth-child(5n) { animation-duration: 1.9s; animation-delay: -0.9s; }

/* Hover: waveform reads slightly more energetic (desktop only) */
@media (hover: hover) and (pointer: fine) {
  .post-card:hover .waveform span,
  .chat-window__voice:hover .waveform span,
  .collage__audio:hover .waveform span {
    animation-duration: 1.1s;
  }
}

/* Active/playing: driven by JS toggling .is-playing on the nearest
   .post-card or .chat-window__voice. Visual-only — no fake progress. */
.is-playing .waveform span {
  animation-duration: 0.6s;
}

.audio-post__play {
  transition:
    transform var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-fast) var(--ease-standard);
}

@media (hover: hover) and (pointer: fine) {
  .audio-post__play:hover {
    transform: scale(1.04);
    box-shadow: var(--glow-purple);
  }
}

.audio-post__play:active {
  transform: scale(0.97);
}

/* Composer screenshot: extremely slow ambient drift, secondary only */
@keyframes composer-drift {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-5px); }
}

.audio-stage__composer {
  animation: composer-drift 10s ease-in-out infinite;
}

/* ==========================================================================
   §5 — COMMUNITIES: stage entrance (left, then right +100ms), story-strip
   ========================================================================== */

.motion-ready .communities-stage .community-frame {
  opacity: 0;
  transform: translateY(20px) scale(0.98);
  transition:
    opacity var(--motion-reveal) var(--ease-out),
    transform var(--motion-reveal) var(--ease-out);
}

.motion-ready .communities-stage.is-visible .community-frame--overview {
  opacity: 1;
  transform: none;
}

.motion-ready .communities-stage.is-visible .community-frame--discovery {
  opacity: 1;
  transform: none;
  transition-delay: 100ms;
}

/* Story-strip: the community→friendship mini-story plays once, in order */
.motion-ready .story-strip__row > * {
  opacity: 0;
  transform: translateY(8px);
  transition:
    opacity var(--motion-base) var(--ease-out),
    transform var(--motion-base) var(--ease-out);
}

.motion-ready .story-strip.is-visible .story-strip__row > *:nth-child(1) { opacity: 1; transform: none; transition-delay: 0ms; }
.motion-ready .story-strip.is-visible .story-strip__row > *:nth-child(2) { opacity: 1; transform: none; transition-delay: 150ms; }
.motion-ready .story-strip.is-visible .story-strip__row > *:nth-child(3) { opacity: 1; transform: none; transition-delay: 300ms; }
.motion-ready .story-strip.is-visible .story-strip__row > *:nth-child(4) { opacity: 1; transform: none; transition-delay: 450ms; }
.motion-ready .story-strip.is-visible .story-strip__row > *:nth-child(5) { opacity: 1; transform: none; transition-delay: 600ms; }
.motion-ready .story-strip.is-visible .story-strip__row > *:nth-child(6) { opacity: 1; transform: none; transition-delay: 750ms; }
.motion-ready .story-strip.is-visible .story-strip__row > *:nth-child(7) { opacity: 1; transform: none; transition-delay: 900ms; }
.motion-ready .story-strip.is-visible .story-strip__row > *:nth-child(8) { opacity: 1; transform: none; transition-delay: 1050ms; }
.motion-ready .story-strip.is-visible .story-strip__row > *:nth-child(9) { opacity: 1; transform: none; transition-delay: 1200ms; }

/* The "Friends ✓" node gets one small confirming pop, not a bounce */
.motion-ready .story-strip.is-visible .story-node--success {
  animation: friend-pop 0.4s var(--ease-out) 1200ms both;
}

@keyframes friend-pop {
  from { transform: scale(0.92); }
  to { transform: scale(1); }
}

/* ==========================================================================
   §6 — POST → FRIENDSHIP: the "Friends ✓" card confirms once visible
   ========================================================================== */

.motion-ready .friends-card {
  animation: friend-pop 0.4s var(--ease-out) 400ms both;
}

/* ==========================================================================
   §7 — CONVERSATIONS: messages arrive in sequence, once
   ========================================================================== */

.motion-ready .chat-window__body > * {
  opacity: 0;
  transform: translateY(10px);
  transition:
    opacity var(--motion-base) var(--ease-out),
    transform var(--motion-base) var(--ease-out);
}

.motion-ready .chat-window__body.is-visible > *:nth-child(1) { opacity: 1; transform: none; transition-delay: 0ms; }
.motion-ready .chat-window__body.is-visible > *:nth-child(2) { opacity: 1; transform: none; transition-delay: 160ms; }
.motion-ready .chat-window__body.is-visible > *:nth-child(3) { opacity: 1; transform: none; transition-delay: 320ms; }
.motion-ready .chat-window__body.is-visible > *:nth-child(4) { opacity: 1; transform: none; transition-delay: 480ms; }
.motion-ready .chat-window__body.is-visible > *:nth-child(5) { opacity: 1; transform: none; transition-delay: 640ms; }

/* Offline-message demo: Sending… → Offline → Delivered, played once via
   JS toggling data-state on the container. Visual only. */
[data-offline-demo] .pill {
  opacity: 0.35;
  transition: opacity var(--motion-base) var(--ease-standard);
}

[data-offline-demo][data-state="sending"] .pill:nth-child(1),
[data-offline-demo][data-state="offline"] .pill:nth-child(2),
[data-offline-demo][data-state="delivered"] .pill:nth-child(3) {
  opacity: 1;
}

/* ==========================================================================
   §8 — MOODMOJI ORBIT: slow ambient drift + entrance
   ========================================================================== */

@keyframes orbit-drift {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-7px) rotate(1.5deg); }
}

.orbit__item {
  animation: orbit-drift 26s ease-in-out infinite;
}

.orbit__item:nth-child(2n) { animation-duration: 32s; animation-direction: reverse; }
.orbit__item:nth-child(3n) { animation-duration: 22s; }
.orbit__item:nth-child(5n) { animation-duration: 38s; }

@media (hover: hover) and (pointer: fine) {
  .orbit__item {
    transition: transform var(--motion-fast) var(--ease-standard);
  }
}

.motion-ready .orbit__card {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.94);
  transition:
    opacity var(--motion-reveal) var(--ease-out),
    transform var(--motion-reveal) var(--ease-out);
}

.motion-ready .orbit.is-visible .orbit__card {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

/* ==========================================================================
   §9 — FEATURE EXPLORER: panel crossfade + re-triggerable visual entrance
   ========================================================================== */

.explorer__panel.is-active {
  animation: panel-in var(--motion-base) var(--ease-out);
}

@keyframes panel-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* display:none → display:block (on every tab switch) already restarts
   CSS animations with no JS help — so the primary visual inside a panel
   naturally re-states itself each time a category is selected. This is
   a user-initiated interaction, not a passive scroll reveal, so
   replaying it is expected rather than annoying. */
.explorer__panel.is-active .explorer__visual > * {
  opacity: 0;
  transform: translateY(10px) scale(0.97);
  animation: panel-visual-in var(--motion-slow) var(--ease-out) both;
}

.explorer__panel.is-active .explorer__visual > *:nth-child(1) { animation-delay: 80ms; }
.explorer__panel.is-active .explorer__visual > *:nth-child(2) { animation-delay: 160ms; }
.explorer__panel.is-active .explorer__visual > *:nth-child(3) { animation-delay: 240ms; }

@keyframes panel-visual-in {
  to {
    opacity: 1;
    transform: none;
  }
}

/* ==========================================================================
   §10 — RETURN: notifications arrive one by one, unread dot pulses once
   ========================================================================== */

.motion-ready .notif-stack > * {
  opacity: 0;
  transform: translateY(10px);
  transition:
    opacity var(--motion-base) var(--ease-out),
    transform var(--motion-base) var(--ease-out);
}

.motion-ready .notif-stack.is-visible > *:nth-child(1) { opacity: 1; transform: none; transition-delay: 0ms; }
.motion-ready .notif-stack.is-visible > *:nth-child(2) { opacity: 1; transform: none; transition-delay: 150ms; }
.motion-ready .notif-stack.is-visible > *:nth-child(3) { opacity: 1; transform: none; transition-delay: 300ms; }

@keyframes dot-pulse-once {
  0% { box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.55); }
  100% { box-shadow: 0 0 0 8px rgba(52, 211, 153, 0); }
}

.motion-ready .return-profile.is-visible .return-profile__dot {
  animation: dot-pulse-once 1s var(--ease-out) 500ms both;
}

/* ==========================================================================
   §11 — FINAL CTA: avatars arrive with a small stagger, then settle
   ========================================================================== */

.motion-ready .final-cta__people img {
  opacity: 0;
  transform: scale(0.85);
  transition:
    opacity var(--motion-slow) var(--ease-out),
    transform var(--motion-slow) var(--ease-out);
}

.motion-ready .final-cta__people.is-visible img {
  opacity: 1;
}

/* preserve the alternating -14px lift already used for the overlap
   composition — combine it with the scale-in on even children only */
.motion-ready .final-cta__people.is-visible img:nth-child(odd) { transform: scale(1); }
.motion-ready .final-cta__people.is-visible img:nth-child(even) { transform: translateY(-14px) scale(1); }

.final-cta__people img:nth-child(1) { transition-delay: 0ms; }
.final-cta__people img:nth-child(2) { transition-delay: 70ms; }
.final-cta__people img:nth-child(3) { transition-delay: 140ms; }
.final-cta__people img:nth-child(4) { transition-delay: 210ms; }
.final-cta__people img:nth-child(5) { transition-delay: 280ms; }
.final-cta__people img:nth-child(6) { transition-delay: 350ms; }
.final-cta__people img:nth-child(7) { transition-delay: 420ms; }

/* ==========================================================================
   §12 — LINKS: underline grows from left on hover/focus
   ========================================================================== */

.footer__col a {
  position: relative;
  width: fit-content;
}

.footer__col a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--motion-fast) var(--ease-standard);
  opacity: 0.6;
}

.footer__col a:hover::after,
.footer__col a:focus-visible::after {
  transform: scaleX(1);
}

/* ==========================================================================
   §13 — HERO PARALLAX
   Handled entirely by the `bob` keyframe reading --px/--py (site.css) —
   initHeroParallax (site.js) only ever calls setProperty on those two
   custom properties, never el.style.transform. See that function's
   comment for why: an infinite CSS animation already owns `transform`
   on these elements and would silently win over an inline style.
   ========================================================================== */

/* ==========================================================================
   §14 — REDUCED MOTION (mandatory, consolidated, wins by cascade order)
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  .reveal,
  .stagger-children > *,
  .chat-window__body > *,
  .notif-stack > *,
  .story-strip__row > *,
  .final-cta__people img,
  .communities-stage .community-frame,
  .orbit__card {
    opacity: 1 !important;
    transform: none !important;
  }

  .nav {
    animation: none !important;
  }

  [data-offline-demo] .pill {
    opacity: 1 !important;
  }

  .post-card:hover,
  .note-card:hover,
  .scenario-card:hover,
  .lift:hover,
  .btn:hover,
  .orbit__item:hover {
    transform: none !important;
  }

  .hero-feed,
  .hero-float,
  .hero-loose {
    transform: none !important;
  }
}
