/* ===========================================================================
 * logo-motion.css — the ONE shared grammar for the living Magogi mark.
 *
 * The mark is one organism (orbit + heartbeat); every scenario is a MOOD
 * composed from three primitives defined here exactly once — no copy-pasted
 * keyframes per surface:
 *
 *   pulse        the core beats (heartbeat / warm hover pulse)   — scale
 *   orbit        the three ring-riding nodes advance / sweep     — rotate
 *   emit-ring    a thin ring radiates from centre and fades      — scale+opacity
 *
 * SOURCE OF TRUTH for the fleet. Ships in mgf-brand-web; the operator console
 * shell loads it, apps that extend the shell inherit it, and the marketing
 * site (mgf-website) vendors this same file — one grammar, no drift. Consume
 * it with the inline mark the `logo_mark()` macro emits (an <img>-referenced
 * SVG has no animatable internals). It depends only on brand tokens
 * (--mgf-gold/-indigo/-mist/-teal, --mgf-duration-*, --mgf-ease-*) — including
 * the living-logo motion primitives promoted to the brand truth
 * (--mgf-duration-sweep / --mgf-duration-heartbeat / --mgf-ease-orbit).
 *
 * Everything animates TRANSFORM and OPACITY only (compositor-friendly, 60fps).
 *
 * MOTION RESPECTS THE VISITOR: every animation below is gated behind
 * `@media (prefers-reduced-motion: no-preference)`. Under `reduce` NONE apply,
 * so the mark rests as a clean, complete static drawing — a dignified resting
 * state, not a half-stopped frame.
 * ========================================================================= */

/* ---- structure + theme (works with zero JS, both themes, on-dark grounds) ---- */
.logo-mark {
  display: block;
  /* structural ink follows the theme (indigo on light, mist on dark); the gold
   * twin (keystone + core) is theme-independent. One inline SVG, both themes. */
  --lm-ink: var(--mgf-indigo);
}
[data-theme="dark"] .logo-mark {
  --lm-ink: var(--mgf-mist);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .logo-mark {
    --lm-ink: var(--mgf-mist);
  }
}
/* on-dark grounds (console top bar, hero / page-header indigo fields): ink is mist */
.logo-mark.on-dark {
  --lm-ink: var(--mgf-mist);
}

.logo-mark .lm-ring {
  fill: none;
  stroke: var(--lm-ink);
}
.logo-mark .lm-ring-outer {
  stroke-width: 2.75;
  opacity: 0.3;
}
.logo-mark .lm-ring-primary {
  stroke-width: 4.75;
}
.logo-mark .lm-station {
  fill: var(--lm-ink);
}
.logo-mark .lm-gold {
  fill: var(--mgf-gold);
}
.logo-mark .lm-word {
  fill: var(--lm-ink);
}
/* the success ring lives in the SVG but is invisible until a mood fires */
.logo-mark .lm-emit {
  fill: none;
  stroke: var(--mgf-gold);
  stroke-width: 2.5;
  opacity: 0;
}

/* All three primitives pivot on the mark centre — (48,48) in USER UNITS, the
 * mark centre in BOTH modes (mark view-box 0 0 96 96 AND lockup 0 0 306 96).
 * `50% 50%` would resolve to the view-box centre — correct for the bare mark but
 * (153,48) for the lockup, flinging the nodes off-centre. Pin to 48px 48px under
 * transform-box: view-box (SVG user coordinates) to hold the mark centre. */
.logo-mark .lm-core,
.logo-mark .lm-orbit,
.logo-mark .lm-emit {
  transform-box: view-box;
  transform-origin: 48px 48px;
}

/* ===========================================================================
 * PRIMITIVE KEYFRAMES (defined once)
 * ========================================================================= */

/* pulse — the double-beat heartbeat (lub-dub, then a long rest). Peaks sit
 * ~1.8% of a 10s cycle apart ≈ 180ms; the remaining ~9.6s is stillness, so
 * idle reads as "near-still with a faint pulse", not a metronome. */
@keyframes lm-heartbeat {
  0%   { transform: scale(1); }
  1%   { transform: scale(1.18); } /* lub */
  2.2% { transform: scale(1); }
  3.4% { transform: scale(1.12); } /* dub (~180ms after lub) */
  4.8% { transform: scale(1); }
  100% { transform: scale(1); }    /* rest */
}

/* pulse — one warm beat for hover/focus, settles back to rest in the same run */
@keyframes lm-pulse-warm {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.22); }
  100% { transform: scale(1); }
}

/* orbit — the brief entry/route sweep (continuity cue): the nodes swing in and
 * settle. ease-in-out (via --mgf-ease-orbit) per the visual directive. */
@keyframes lm-sweep {
  from { transform: rotate(-32deg); opacity: 0.85; }
  to   { transform: rotate(0deg); opacity: 1; }
}

/* emit-ring — a single ring expands outward from centre and fades to nothing. */
@keyframes lm-ring-emit {
  from { transform: scale(0.5); opacity: 0.55; }
  60%  { opacity: 0.3; }
  to   { transform: scale(1.9); opacity: 0; }
}

/* the success ripple for non-SVG hosts (copy buttons): the same expand+fade. */
@keyframes lm-ripple {
  from { transform: scale(0.6); opacity: 0.5; }
  to   { transform: scale(1.7); opacity: 0; }
}

/* error — subdued: one slow contraction with a slight opacity dip and back.
 * NEVER a shake or flash. Transform+opacity only (not filter:saturate) to hold
 * the 60fps compositor-only rule — the dip reads as "subdued". */
@keyframes lm-contract {
  0%   { transform: scale(1); opacity: 1; }
  45%  { transform: scale(0.9); opacity: 0.7; }
  100% { transform: scale(1); opacity: 1; }
}

/* ===========================================================================
 * MOODS — composed from the primitives. All gated on no-preference so the
 * reduced-motion resting state is simply the static mark.
 * ========================================================================= */
@media (prefers-reduced-motion: no-preference) {
  /* Idle: near-still, faint double-beat every ~10s. */
  .logo-mark .lm-core {
    animation: lm-heartbeat var(--mgf-duration-heartbeat, 10s)
      var(--mgf-ease-standard) infinite;
  }

  /* Load / route change: the orbit sweeps in once. `backwards` shows the
   * from-frame at start without locking the end value, so the hover advance
   * below still wins. */
  .logo-mark .lm-orbit {
    animation: lm-sweep var(--mgf-duration-sweep, 640ms)
      var(--mgf-ease-orbit) backwards;
    transition: transform var(--mgf-duration-sweep, 640ms) var(--mgf-ease-orbit);
  }

  /* Hover / focus on the logo's own link: one warm pulse + a slight orbit
   * advance that settles back on release. `.lm-host` is the interactive
   * ancestor (e.g. the console mark link or the nav brand link). */
  .lm-host:hover .logo-mark .lm-core,
  .lm-host:focus-visible .logo-mark .lm-core {
    animation: lm-pulse-warm var(--mgf-duration-slow) var(--mgf-ease-standard);
  }
  .lm-host:hover .logo-mark .lm-orbit,
  .lm-host:focus-visible .logo-mark .lm-orbit {
    transform: rotate(11deg);
  }

  /* Success — a single expanding ring, fades. Fired by adding `.is-success`
   * to a mark (SVG ring) or by `.done` on a `.mgf-emit-host` button. */
  .logo-mark.is-success .lm-emit {
    animation: lm-ring-emit var(--mgf-duration-slow) var(--mgf-ease-standard);
  }

  /* Error — subdued slow contraction, once, then rest. */
  .logo-mark.is-error {
    animation: lm-contract var(--mgf-duration-sweep, 640ms) var(--mgf-ease-orbit);
  }
}

/* ---- reusable success ripple for buttons (copy-to-clipboard, form sent) ----
 * Add `mgf-emit-host` to a button; when your existing success class toggles
 * `.done`, a single teal ring emits and fades. Zero extra JS — it rides the
 * success state the component already sets. */
.mgf-emit-host {
  position: relative;
}
@media (prefers-reduced-motion: no-preference) {
  .mgf-emit-host.done::after {
    content: "";
    position: absolute;
    inset: -2px;
    border: 1.5px solid var(--mgf-teal);
    border-radius: calc(var(--mgf-radius-sm) + 2px);
    transform-origin: 50% 50%;
    pointer-events: none;
    animation: lm-ripple var(--mgf-duration-slow) var(--mgf-ease-standard);
  }
}
