/* =========================================================================
   Nomos — motion.css
   The motion orchestration layer: scroll reveals, hero entrance, and the
   reduced-motion safety net. Loaded on every page AFTER styles.css.
   Component-embedded animations (system map, operators, proof card) live
   with their components in styles.css; tier scoping lands here.
   ========================================================================= */

/* Scroll-reveal. The hidden start state applies ONLY when JS is available
   (html.js), so a no-JS or failed-script render shows all content. The .is-in
   end states are the natural, always-visible defaults. */
.js .reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 680ms var(--ease-out-expo), transform 680ms var(--ease-out-expo);
  transition-delay: var(--reveal-delay, calc(var(--i, 0) * 60ms));
}
.reveal.is-in { opacity: 1; transform: none; }

/* List stagger — the container itself doesn't animate; its direct children rise
   in sequence when it enters view. Legitimate for real lists (pains, platform
   grids, FAQ), not whole-section fade. main.js sets --n on each child; total
   stagger stays bounded (≤10 items × 60ms). */
.js .reveal.stagger { opacity: 1; transform: none; transition: none; }
.js .reveal.stagger > * {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 560ms var(--ease-out-expo), transform 560ms var(--ease-out-expo);
}
.reveal.stagger.is-in > * {
  opacity: 1;
  transform: none;
  transition-delay: calc(var(--n, 0) * 60ms);
}

/* Editorial wipe variant — the core-claim line reveals via a clip wipe rather
   than a plain rise, so the brand moment reads differently from the rest. */
.js .reveal--wipe {
  opacity: 0;
  clip-path: inset(0 0 100% 0);
  transform: translateY(8px);
  transition: opacity 700ms var(--ease-out-expo), transform 700ms var(--ease-out-expo),
    clip-path 800ms var(--ease-out-expo);
}
.reveal--wipe.is-in { opacity: 1; clip-path: inset(0 0 0 0); transform: none; }

/* Slight blur-in for media surfaces (evidence card, ledger) — a focus pull. */
.js .reveal--focus {
  opacity: 0;
  transform: translateY(22px) scale(0.985);
  filter: blur(6px);
  transition: opacity 700ms var(--ease-out-expo), transform 700ms var(--ease-out-expo),
    filter 700ms var(--ease-out-expo);
}
.reveal--focus.is-in { opacity: 1; transform: none; filter: none; }

/* Hero entrance — one orchestrated page-load (Emil: beats scattered micro-motion).
   Pure CSS, runs once; the hero copy elements rise in sequence. */
.hero__copy > * {
  animation: hero-rise 620ms var(--ease-out) both;
}
.hero__copy > .eyebrow { animation-delay: 60ms; }
.hero__copy > h1 { animation-delay: 130ms; }
.hero__copy > .hero__sub { animation-delay: 220ms; }
.hero__copy > .hero__cta { animation-delay: 320ms; }
@keyframes hero-rise {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

/* =========================================================================
   Tier "full" — the showpiece layer. Everything here is additive on top of
   calm; the heavy lifting (hero timeline, govern scrub, pipeline scrub,
   parallax) is GSAP-driven from main.js and mounts only at this level.
   ========================================================================= */

/* GSAP owns the hero entrance in full — disable the CSS hero-rise so the two
   never double-animate. If the CDN fails, the hero simply renders static. */
html.js[data-motion-level="full"] .hero__copy > * { animation: none; }

/* Richer reveals: a slight scale + focus-pull generalized from reveal--focus.
   Plain reveals only — stagger containers, wipes, and focus variants keep
   their own choreography. */
html.js[data-motion-level="full"] .reveal:not(.stagger):not(.reveal--wipe):not(.reveal--focus) {
  transform: translateY(30px) scale(0.99);
  filter: blur(3px);
  transition: opacity 700ms var(--ease-out-expo), transform 700ms var(--ease-out-expo),
    filter 700ms var(--ease-out-expo);
  transition-delay: var(--reveal-delay, calc(var(--i, 0) * 60ms));
}
html.js[data-motion-level="full"] .reveal:not(.stagger):not(.reveal--wipe):not(.reveal--focus).is-in {
  opacity: 1; transform: none; filter: none;
}
html.js[data-motion-level="full"] .reveal.stagger > * { filter: blur(2px); }
html.js[data-motion-level="full"] .reveal.stagger.is-in > * { filter: none; transition-property: opacity, transform, filter; }

/* Govern rail — visible only in full; GSAP scrubs scaleX 0 → 1. */
html[data-motion-level="full"] .tiers__rail {
  display: block;
  position: absolute;
  top: -1.3rem;
  left: 1%;
  right: 1%;
  height: 2px;
  border-radius: 1px;
  background: linear-gradient(90deg, var(--civic), color-mix(in srgb, var(--civic) 45%, var(--paleblue)));
  transform: scaleX(0);
  transform-origin: left center;
}

/* Pipeline arrows carry the civic current in full (the scrub draws the nodes). */
html[data-motion-level="full"] .pipeline .pipeline__arrow { color: var(--civic); }

/* Proof upgrade: as the verify band sweeps, the lineage row pulses once —
   the walk-back path lights up before the figures settle. */
html[data-motion-level="full"] .evidence-card.is-proving .evidence-row--lineage {
  animation: lineage-pulse 1.3s var(--ease-out) 0.1s 1;
}
@keyframes lineage-pulse {
  0%   { background: transparent; }
  35%  { background: color-mix(in srgb, var(--civic) 13%, transparent); }
  100% { background: transparent; }
}

/* =========================================================================
   Tier "off" — the explicit zero-animation state. Mirrors the reduced-motion
   block below, but driven by the user's Motion control (data-motion-level),
   which also inherits the OS reduce-motion preference as its default. Content
   is always fully visible; nothing waits on an observer.
   ========================================================================= */
html[data-motion-level="off"] .hero__copy > * { animation: none; }
html[data-motion-level="off"] .systemmap__flow path { stroke-dashoffset: 0; animation: none !important; }
html[data-motion-level="off"] .systemmap__packet { display: none; }
html[data-motion-level="off"] .systemmap__stagedot { animation: none !important; opacity: 1; }
html[data-motion-level="off"] .systemmap__stagepulse { animation: none !important; opacity: 0; }
html[data-motion-level="off"] { scroll-behavior: auto; }
html[data-motion-level="off"].js .reveal,
html[data-motion-level="off"].js .reveal--wipe,
html[data-motion-level="off"].js .reveal--focus,
html[data-motion-level="off"].js .reveal.stagger > * {
  opacity: 1; transform: none; filter: none; clip-path: none; transition: none;
}
html[data-motion-level="off"] .evidence-card[data-proof]::after { display: none; }
html[data-motion-level="off"] .op .op-ink { stroke-dashoffset: 0; }
html[data-motion-level="off"] .op.is-animating .op-ink,
html[data-motion-level="off"] .op.is-animating .op-pen-hand,
html[data-motion-level="off"] .op.is-animating .op-tile-drop,
html[data-motion-level="off"] .op.is-animating .op-stamp,
html[data-motion-level="off"] .op.is-animating .op-scan,
html[data-motion-level="off"] .op.is-animating .op-check,
html[data-motion-level="off"] .op.is-animating .op-roll { animation: none; transform: none; opacity: 1; }
html[data-motion-level="off"] .badge--now::before { animation: none !important; opacity: 1; transform: none; }
html[data-motion-level="off"] .btn:active { transform: none; }
html[data-motion-level="off"] .btn .arrow { transition: none; }
html[data-motion-level="off"] * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }

/* =========================================================================
   OS reduced-motion safety net. Also the no-JS path: without JS there is no
   data-motion-level attribute, so this media block is what protects
   motion-sensitive users. An explicit calm/full choice (attribute present)
   re-enables the JS-driven moments; see the b7 floor rule in theme-b7.css.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  .hero__copy > * { animation: none; }
  .systemmap__flow path { stroke-dashoffset: 0; animation: none; }
  .systemmap__packet { display: none; }
  .systemmap__stagedot { animation: none; opacity: 1; }
  .systemmap__stagepulse { animation: none; opacity: 0; }
  html { scroll-behavior: auto; }
  /* Prefixed with .js so these beat the .js-gated hidden start states (equal
     specificity, later source) — otherwise motion-sensitive users would still
     depend on the IntersectionObserver firing to reveal below-fold content. */
  .js .reveal,
  .js .reveal--wipe,
  .js .reveal--focus,
  .js .reveal.stagger > * {
    opacity: 1; transform: none; filter: none; clip-path: none; transition: none;
  }
  .evidence-card[data-proof]::after { display: none; }
  .op .op-ink { stroke-dashoffset: 0; }
  .op.is-animating .op-ink,
  .op.is-animating .op-pen-hand,
  .op.is-animating .op-tile-drop,
  .op.is-animating .op-stamp,
  .op.is-animating .op-scan,
  .op.is-animating .op-check,
  .op.is-animating .op-roll { animation: none; transform: none; opacity: 1; }
  .badge--now::before { animation: none; opacity: 1; transform: none; }
  .btn:active { transform: none; }
  .btn .arrow { transition: none; }
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

