/**
 * motion.css — Creative Human shared motion vocabulary
 *
 * Shared keyframes and utility tokens for the stage-signature animation system.
 * Linked from glasshouse.html (the reference implementation) and any future
 * page that needs the same vocabulary.
 *
 * Full specification: docs/motion-system.md
 * Design philosophy: content/brand-reinvention-2026.md §2.4
 *
 * Last Updated: 2026-04-17
 */

/* ============================================================
   TOKENS
   ============================================================ */

:root {
  --gh-ease:          cubic-bezier(0.4, 0, 0.2, 1);
  --gh-dur-exit:      140ms;
  --gh-dur-state:     200ms;
  --gh-dur-enter:     320ms;
  --gh-dur-signature: 560ms;
}


/* ============================================================
   REDUCED-MOTION CONTRACT
   This block MUST wrap all animation declarations below.
   Do not add animations outside of this media query.
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ============================================================
   SAFETY NET
   Belt-and-suspenders rule: once a stage-panel has neither
   .hidden nor data-entering, its children must render at their
   natural state. Covers the case where a stagger chain's long
   tail runs past the cleanup timeout or the JS fails to remove
   data-entering. Applies outside the reduced-motion gate because
   it is structural, not animated.
   ============================================================ */

.stage-panel:not(.hidden):not([data-entering]):not([data-leaving]) svg circle,
.stage-panel:not(.hidden):not([data-entering]):not([data-leaving]) svg text,
.stage-panel:not(.hidden):not([data-entering]):not([data-leaving]) svg line,
.stage-panel:not(.hidden):not([data-entering]):not([data-leaving]) .persona-card-wrapper,
.stage-panel:not(.hidden):not([data-entering]):not([data-leaving]) #nt-bands path,
.stage-panel:not(.hidden):not([data-entering]):not([data-leaving]) #sim-stream > div,
.stage-panel:not(.hidden):not([data-entering]):not([data-leaving]) #sim-roster > button,
.stage-panel:not(.hidden):not([data-entering]):not([data-leaving]) .report-reveal {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: no-preference) {

  /* ----------------------------------------------------------
     SHARED EXIT
     All departing stage panels use this regardless of stage.
     JS applies data-leaving="true"; CSS handles the rest.
     ---------------------------------------------------------- */

  .stage-panel[data-leaving="true"] {
    animation: panel-exit var(--gh-dur-exit) var(--gh-ease) forwards;
    pointer-events: none;
  }

  @keyframes panel-exit {
    to {
      opacity: 0;
      transform: translateY(4px);
    }
  }


  /* ----------------------------------------------------------
     STAGE 0 — SEEDS
     Left-to-right clip-path reveal. Mirrors the act of scanning
     a document being typeset. No per-child stagger — the whole
     panel reveals as a single printed surface.
     ---------------------------------------------------------- */

  .stage-panel[data-entering="true"][data-panel="0"] > div {
    animation: seeds-scan 560ms var(--gh-ease) both;
  }

  @keyframes seeds-scan {
    from {
      clip-path: inset(0 100% 0 0);
      opacity: 0.65;
    }
    to {
      clip-path: inset(0 0% 0 0);
      opacity: 1;
    }
  }


  /* ----------------------------------------------------------
     STAGE 1 — GRAPH
     Three-layer entrance: nodes pop → labels fade → edges draw.
     JS staggers each layer via animationDelay on individual
     elements; see applyStagger() in docs/motion-system.md §7.

     SVG transform notes:
     - transform-box: fill-box  → anchors transform to the shape
     - transform-origin: center → scale from the node's own centre
       (without this, SVG scales from the viewport origin)
     ---------------------------------------------------------- */

  /* Entrance keyframes deliberately never start from opacity 0: the panel's
     content must remain visible even if a stagger chain is long or the
     cleanup timeout fires before the longest-delayed element finishes.
     Invisible starting states + per-element animationDelay + a fixed
     cleanup window is a brittle combination; 0.35 is the floor. */
  .stage-panel[data-entering="true"][data-panel="1"] svg circle {
    animation: graph-node-pop 260ms var(--gh-ease) both;
    transform-box: fill-box;
    transform-origin: center;
  }

  @keyframes graph-node-pop {
    from {
      opacity: 0.35;
      transform: scale(0.82);
    }
    to {
      opacity: 1;
      transform: scale(1);
    }
  }

  .stage-panel[data-entering="true"][data-panel="1"] svg text {
    animation: graph-label-fade 240ms var(--gh-ease) both;
  }

  @keyframes graph-label-fade {
    from { opacity: 0.3; }
    to   { opacity: 0.75; }
  }

  /* Lines fade in on opacity only. The previous stroke-dashoffset draw
     required a fully-hidden starting state (offset == total length),
     which left edges invisible for the entire stagger chain + the
     animation window — too brittle against cleanup timing. */
  .stage-panel[data-entering="true"][data-panel="1"] svg line {
    animation: graph-edge-draw 360ms var(--gh-ease) both;
  }

  @keyframes graph-edge-draw {
    from { opacity: 0.25; }
    to   { opacity: 1; }
  }


  /* ----------------------------------------------------------
     STAGE 2 — PERSONAS
     Card cascade: each .persona-card-wrapper arrives from a
     diagonal offset (8px right, 10px down), staggered 90ms apart
     by JS. Reads as a population sweep, not a queued file.
     ---------------------------------------------------------- */

  .stage-panel[data-entering="true"][data-panel="2"] .persona-card-wrapper {
    animation: persona-card-in 360ms var(--gh-ease) both;
  }

  @keyframes persona-card-in {
    from {
      opacity: 0.35;
      transform: translate(6px, 8px);
    }
    to {
      opacity: 1;
      transform: translate(0, 0);
    }
  }


  /* ----------------------------------------------------------
     STAGE 3 — SIMULATION
     Three element types, three motion vectors, ordered by
     narrative role: roster (who) → bands (what happened) → posts
     (real-time feed).

     SVG transform note for bands:
     - transform-origin: center bottom  → bands grow upward from
       the baseline, not from their midpoint
     ---------------------------------------------------------- */

  .stage-panel[data-entering="true"][data-panel="3"] #nt-bands path {
    animation: sim-band-grow 500ms var(--gh-ease) both;
    transform-box: fill-box;
    transform-origin: center bottom;
  }

  @keyframes sim-band-grow {
    from {
      transform: scaleY(0.4);
      opacity: 0.35;
    }
    to {
      transform: scaleY(1);
      opacity: 1;
    }
  }

  .stage-panel[data-entering="true"][data-panel="3"] #sim-stream > div {
    animation: sim-post-slide 360ms var(--gh-ease) both;
  }

  @keyframes sim-post-slide {
    from {
      opacity: 0.35;
      transform: translateX(16px);
    }
    to {
      opacity: 1;
      transform: translateX(0);
    }
  }

  .stage-panel[data-entering="true"][data-panel="3"] #sim-roster > button {
    animation: sim-roster-fade 240ms var(--gh-ease) both;
  }

  @keyframes sim-roster-fade {
    from {
      opacity: 0.4;
      transform: translateX(-4px);
    }
    to {
      opacity: 1;
      transform: translateX(0);
    }
  }


  /* ----------------------------------------------------------
     STAGE 4 — REPORT
     Reading-order cascade: kicker → verdict → confidence bar fill
     → PDM rows. .report-reveal elements staggered 110ms apart by
     JS; .report-confidence-fill gets a fixed 340ms delay so the
     bar fills after its row has arrived.
     ---------------------------------------------------------- */

  .stage-panel[data-entering="true"][data-panel="4"] .report-reveal {
    animation: report-fade-up 360ms var(--gh-ease) both;
  }

  @keyframes report-fade-up {
    from {
      opacity: 0.35;
      transform: translateY(8px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }

  .stage-panel[data-entering="true"][data-panel="4"] .report-confidence-fill {
    animation: report-bar-fill 560ms var(--gh-ease) both;
  }

  @keyframes report-bar-fill {
    from { width: 0%; }
    to   { width: 100%; }
  }

} /* end @media (prefers-reduced-motion: no-preference) */
