/* ============================================================================
   TALANTON — animations.css
   All @keyframes and every animation / transition declaration on the site.

   Motion lives here rather than in style.css so timing can be reviewed and
   tuned in one place. Keyframe names and every duration, delay, and easing
   value are reproduced verbatim from the approved v7 export.

   CONTENTS
     01  Keyframes
     02  Ambient motion — hero, core, network
     03  State transitions — hover, selection
     04  Sequenced reveals — Ask panel, scroll reveal
     05  Reduced motion
   ========================================================================== */


/* ============================================================================
   01  KEYFRAMES
   ========================================================================== */

/* Slow opacity breath — hero status dot. */
@keyframes pulseDot {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 1; }
}

/* The two ambient hero glows, drifting on different periods so they never
   visibly sync up. */
@keyframes drift1 {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(60px, -40px); }
}

@keyframes drift2 {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(-50px, 30px); }
}

@keyframes ringSpin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* Network edges in the "with Talanton" panel. */
@keyframes edgePulse {
  0%, 100% { opacity: 0.25; }
  50%      { opacity: 0.8; }
}

/* Fragments drifting in the "without Talanton" panel. Holds the -50%/-50%
   centring offset while nudging 4px, so the node never jumps on animation
   start or end. */
@keyframes nodeDrift {
  0%, 100% { transform: translate(-50%, -50%); }
  50%      { transform: translate(calc(-50% + 4px), calc(-50% - 4px)); }
}

/* Data travelling along a horizontal wire. */
@keyframes flowRight {
  0%   { left: 0; opacity: 0; }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { left: calc(100% - 5px); opacity: 0; }
}

/* Data travelling down a vertical wire. */
@keyframes flowDown {
  0%   { top: 0; opacity: 0; }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { top: calc(100% - 5px); opacity: 0; }
}

/* Talanton Core, breathing. */
@keyframes corePulse {
  0%, 100% { box-shadow: 0 0 24px rgba(45, 127, 249, 0.25); }
  50%      { box-shadow: 0 0 44px rgba(57, 208, 255, 0.45); }
}


/* ============================================================================
   02  AMBIENT MOTION
   ========================================================================== */

.hero__tag-dot     { animation: pulseDot 3s ease-in-out infinite; }
.hero__glow--blue  { animation: drift1 18s ease-in-out infinite; }
.hero__glow--cyan  { animation: drift2 22s ease-in-out infinite; }

.flow__core        { animation: corePulse 5s ease-in-out infinite; }

/* Horizontal wires — Fragments to Memory. Three pulses per wire, staggered so
   the flow reads as continuous. */
.flow__pulse       { animation: flowRight 3s linear infinite; }
.flow__pulse--d05  { animation-delay: 0.5s; }
.flow__pulse--d1   { animation-delay: 1s; }
.flow__pulse--d2   { animation-delay: 2s; }

/* Vertical wires — Fragments to Memory. */
.flow__drop-pulse      { animation: flowDown 2s linear infinite; }
.flow__drop-pulse--d1  { animation-delay: 1s; }

/* Governance pipeline — Institutional Trust. */
.pipeline__pulse       { animation: flowRight 2.5s linear infinite; }
.pipeline__pulse--d04  { animation-delay: 0.4s; }
.pipeline__pulse--d08  { animation-delay: 0.8s; }
.pipeline__pulse--d12  { animation-delay: 1.2s; }
.pipeline__pulse--d2   { animation-delay: 2s; }

/* Scattered fragments — Impact. Duration is set per node in the markup so each
   fragment drifts on its own period. */
.scatter    { animation: nodeDrift var(--dur) ease-in-out infinite; }

/* Network edges — Impact. Delay is set per edge in components.js. */
.net__edge  { animation: edgePulse 4s ease-in-out infinite; animation-delay: var(--delay); }


/* ============================================================================
   03  STATE TRANSITIONS
   ========================================================================== */

.card--hover        { transition: all 0.25s ease; }
.card--hover:hover  { border-color: rgba(45, 127, 249, 0.45); transform: translateY(-3px); }

.chip               { transition: border-color 0.25s ease; }
.chip:not(.chip--active):not(.chip--ok):hover { border-color: rgba(45, 127, 249, 0.5); }

.maturity__bar      { transition: border-color 0.25s ease; }
.ask__question      { transition: all 0.25s ease; }
.loop__node,
.loop__node-dot,
.loop__node-label   { transition: all 0.25s ease; }
.stack__row         { transition: all 0.25s ease; }
.stack__row-dot     { transition: background 0.25s ease; }
.stack__row-code    { transition: color 0.25s ease; }
.trace__step        { transition: all 0.25s ease; }

/* Confidence meters ease to their new width when the selection changes. */
.meter__fill        { transition: width 0.6s ease; }


/* ============================================================================
   04  SEQUENCED REVEALS
   ========================================================================== */

/* Ask the Institution — each block of the scripted answer eases in as the
   walkthrough advances. Resting and revealed states live in style.css. */
.ask__step { transition: all 0.5s ease; }

/* Scroll reveal — applied by interactions.js only to sections that are still
   below the fold on load, so nothing already visible animates. */
[data-reveal].is-hidden {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.8s ease, transform 0.8s ease;
}

/* The approved build settles a revealed section on an explicit `translateY(0)`
   rather than clearing the transform, and that is deliberately preserved here.
   An identity transform still establishes a stacking context, which changes how
   the browser rasterises the text inside it — grayscale rather than subpixel
   antialiasing. Dropping it makes every revealed section's type render visibly
   differently from the approved design. */
[data-reveal].is-revealed {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.8s ease, transform 0.8s ease;
}


/* ============================================================================
   05  REDUCED MOTION

   Not present in the approved v7 export. Added because the page runs a dozen
   infinite animations, which is a genuine accessibility problem for users with
   vestibular disorders. It changes nothing for anyone who has not explicitly
   asked their OS to reduce motion, so the approved appearance is unaffected.
   ========================================================================== */

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

  /* Reveals must land in their final state, never stay faded out. The identity
     transform is kept so text rasterises identically either way. */
  [data-reveal].is-hidden {
    opacity: 1;
    transform: translateY(0);
  }
}
