/* ═══════════════════════════════════════════════════════════════════════════
   ULA MOTION
   ═══════════════════════════════════════════════════════════════════════════

   Two tiers, because the brand and the product want different things.

   docs/06-motion.md is explicit: working interfaces stay quick, nothing
   bounces, nothing routine runs past 300ms — and separately, "the Ula logo
   animation may be more expressive in marketing contexts". Those are not in
   conflict; they are two tiers that were never named. This file names them.

     WORKING    the default. Everything in Studio, Check and Spec. Short,
                linear-feeling, no overshoot. Motion explains a change and
                then gets out of the way.

     EXPRESSIVE opt-in, via [data-motion="expressive"] on any ancestor.
                Marketing surfaces, hero moments, genUI reveals, the lab.
                Longer, spring-eased, staged. This is where the logo
                assembly lives.

   A component written once picks up whichever tier it is dropped into, so a
   card is calm in the product and alive on a landing page without forking.

   Set the tier on a subtree:  <section data-motion="expressive">
   Force calm anywhere:        <div data-motion="working">
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* Working tier — mirrors docs/06-motion.md exactly. */
  --motion-instant: 80ms;
  --motion-fast: 120ms;
  --motion-standard: 180ms;
  --motion-deliberate: 240ms;
  --motion-stage: 240ms;

  --motion-ease: cubic-bezier(0.2, 0, 0, 1);
  --motion-enter: cubic-bezier(0, 0, 0.2, 1);
  --motion-exit: cubic-bezier(0.4, 0, 1, 1);
  --motion-spring: cubic-bezier(0.2, 0, 0, 1);

  /* Distance a thing travels as it arrives. */
  --motion-rise: 6px;
  --motion-scale-from: 1;

  /* Stagger step for lists and grids. */
  --motion-stagger: 40ms;
}

[data-motion="expressive"] {
  --motion-instant: 120ms;
  --motion-fast: 220ms;
  --motion-standard: 420ms;
  --motion-deliberate: 680ms;
  --motion-stage: 900ms;

  --motion-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --motion-enter: cubic-bezier(0.16, 1, 0.3, 1);
  --motion-exit: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  --motion-spring: cubic-bezier(0.18, 0.9, 0.24, 1.35);

  --motion-rise: 20px;
  --motion-scale-from: 0.965;
  --motion-stagger: 70ms;
}

/* ── Reveal ───────────────────────────────────────────────────────────────
   .ula-reveal starts hidden and plays when .is-revealed is added — the lab
   and any genUI page do that with an IntersectionObserver. Staggering is a
   custom property so a container can walk its children:
   style="--reveal-index: 3"
   ───────────────────────────────────────────────────────────────────────── */

.ula-reveal {
  opacity: 0;
}

.ula-reveal.is-revealed {
  animation: ula-reveal-in var(--motion-deliberate) var(--motion-enter) both;
  animation-delay: calc(var(--reveal-index, 0) * var(--motion-stagger));
}

@keyframes ula-reveal-in {
  from {
    opacity: 0;
    transform: translateY(var(--motion-rise)) scale(var(--motion-scale-from));
    filter: blur(6px);
  }
  60% { filter: blur(0); }
  to {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

/* Expressive tier gets the spring on arrival; working tier stays flat. */
[data-motion="expressive"] .ula-reveal.is-revealed {
  animation-timing-function: var(--motion-spring);
}

/* ── Shared keyframes ─────────────────────────────────────────────────── */

@keyframes ula-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes ula-pop-in {
  0% { opacity: 0; transform: scale(0.9); }
  60% { opacity: 1; transform: scale(1.02); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes ula-slide-up {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

@keyframes ula-shimmer {
  from { background-position: -180% 0; }
  to { background-position: 180% 0; }
}

@keyframes ula-spin {
  to { transform: rotate(360deg); }
}

@keyframes ula-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* The mark's own idle float, reused by loaders and empty states. */
@keyframes ula-breathe {
  0%, 100% { transform: translateY(0) scale(1); }
  50% { transform: translateY(-3%) scale(1.012); }
}

/* Draws an SVG stroke on. Pair with pathLength="1". */
@keyframes ula-draw {
  from { stroke-dashoffset: 1; }
  to { stroke-dashoffset: 0; }
}

/* Counts a number up by stepping a registered custom property. */
@property --ula-count {
  syntax: "<integer>";
  inherits: false;
  initial-value: 0;
}

/* ── Reduced motion ───────────────────────────────────────────────────────
   docs/06-motion.md: "reduced motion is a complete experience". Everything
   lands in its final state immediately; nothing is left mid-animation and
   nothing stays invisible.
   ───────────────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  :root,
  [data-motion="expressive"] {
    --motion-instant: 0ms;
    --motion-fast: 0ms;
    --motion-standard: 0ms;
    --motion-deliberate: 0ms;
    --motion-stage: 0ms;
    --motion-rise: 0px;
    --motion-scale-from: 1;
    --motion-stagger: 0ms;
  }

  .ula-reveal { opacity: 1; }

  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}
