/* ============================================================
   MOTION SYSTEM — thumbprinted.com (Forge / S022 / 2026-06-05)
   A1-A3: a16z-tier motion with substrate-honest defaults.
   - A1: @keyframes fadeUp (200ms hero fade-up), 150ms CTA hover,
         200ms card hover (scale 1.02), 100ms submit-button state
         with disabled visual.
   - A2: Scroll-triggered reveals (data-reveal,
         data-reveal-stagger) via IntersectionObserver.
   - A3: Spinner + Toast (success/error) for /intake.
   A4: Layout-level route cross-fade lives in /assets/css/transitions.css
       and /assets/js/transitions.js (loaded by build.js on every page).
       The body-level fade-in animation that USED to live here is removed
       so it doesn't conflict with the 150ms body opacity transition that
       A4 owns.
   Honors prefers-reduced-motion: reduce throughout.
   ============================================================ */

/* === A1: @keyframes ===
   The 4 micro-interactions spec is:
     (a) Hero fade-up on load         → @keyframes fadeUp        200ms ease-out
     (b) CTA hover color/bg           → 150ms transition
     (c) Portrait card hover (scale)  → 200ms transition + scale(1.02)
     (d) Form submit feedback         → 100ms transition + disabled state
   We keep the prefixed `tp-*` aliases for any consumer that already
   references them; the unprefixed names are the canonical A1 contract. */

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

/* Canonical A1 hero fade-up: 200ms ease-out, 16px lift. */
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Prefixed alias kept for backward-compat with A2/A3 callers. */
@keyframes tp-fade-up {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes tp-fade-in-soft {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes tp-toast-in {
  from { opacity: 0; transform: translateY(12px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

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

/* === A1 (a): hero fade-up on load ===
   Apply the canonical 200ms fadeUp to any element flagged as the
   page hero. Substrate-honest: runs once on first paint via
   `animation-fill-mode: both` so the element lands in its final
   state and never re-fires on tab focus / route revisit. The
   .hero { animation: ... } rule below is the only place we define
   this micro-interaction — body-level fade-in is A4's job. */
.hero {
  animation: fadeUp 0.2s ease-out both;
  /* If a child of .hero also has an animation, the child wins
     because we only target the hero block, not its descendants. */
}
@media (prefers-reduced-motion: reduce) {
  .hero { animation: none; }
}

/* === A1: color transitions + hover lifts + focus rings ===
   Per A1 spec: 150ms on color/background-color, 200ms on
   transform/box-shadow, 100ms on submit-button state. We keep
   a defensive baseline below, then layer the A1-specific
   durations on the four primary selectors so other animations
   tasks (A2/A3/A4) can compose on top. */

a, button,
.cta, .cta-filled, .cta-button, .gate-button,
.portrait-card, .gate,
input, textarea, select,
.btn-next, .btn-back, .radio-option {
  /* The motion baseline. Per-page rules can layer on top. */
  transition:
    color           0.15s ease-out,
    background      0.15s ease-out,
    background-color 0.15s ease-out,
    border-color    0.15s ease-out,
    transform       0.2s  ease-out,
    box-shadow      0.2s  ease-out,
    opacity         0.2s  ease-out;
}

/* Visible focus ring for keyboard users. */
a:focus-visible,
button:focus-visible,
.cta:focus-visible,
.cta-filled:focus-visible,
.cta-button:focus-visible,
.gate-button:focus-visible,
.portrait-card:focus-visible,
.btn-next:focus-visible,
.btn-back:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
.radio-option:focus-visible {
  outline: 2px solid var(--accent, var(--color-ember, #b04a2f));
  outline-offset: 2px;
  border-radius: 2px;
}

/* === A1 (c): portrait card hover — scale(1.02) + shadow lift, 200ms ===
   Card hover is already defined in some pages; we add a baseline that
   fills the gap on pages that don't define one. The 200ms transform +
   box-shadow timing is the A1 contract. */
.portrait-card:hover,
.gate:hover {
  transform: scale(1.02);
  box-shadow: 0 8px 24px rgba(26, 23, 20, 0.10);
}
/* Defensive: if a page disables hover via pointer-events, we
   don't fire the scale on the card. */
@media (hover: none) {
  .portrait-card:hover,
  .gate:hover { transform: none; }
}

/* === A1 (b): CTA hover — color/background-color only, 150ms ===
   We rely on the baseline transition above (150ms color/bg). Pages
   that override .cta / .cta-filled with their own transition should
   keep 150ms to match the A1 contract. */

/* === A1 (d): form submit button — 100ms background-color + disabled state ===
   Substrate-honest: when the submit button is disabled, the
   background fades to a muted state in 100ms and the cursor
   reflects that no further action is possible. Pages can call
   `btn.disabled = true` in JS to enter the loading state. */
button[type="submit"],
.btn-submit,
.btn-next,
.btn-back {
  transition:
    background-color 0.1s ease-out,
    color            0.1s ease-out,
    border-color     0.1s ease-out,
    opacity          0.1s ease-out,
    transform        0.15s ease-out,
    box-shadow       0.15s ease-out;
}
button[type="submit"]:disabled,
button[type="submit"][aria-disabled="true"],
.btn-submit:disabled,
.btn-submit[aria-disabled="true"],
.btn-next:disabled,
.btn-next[aria-disabled="true"],
.btn-back:disabled,
.btn-back[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  /* Subtle: keep the background the same hue but lower the
     contrast so the disabled state is unmistakable but on-brand. */
  filter: saturate(0.7);
}
button[type="submit"]:disabled:hover,
.btn-submit:disabled:hover,
.btn-next:disabled:hover,
.btn-back:disabled:hover {
  /* No transform/hover lift on disabled — that would be misleading. */
  transform: none;
  box-shadow: none;
}

/* === A2: scroll-triggered reveals === */

/* Single-element reveal. */
[data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity   0.6s ease-out,
    transform 0.6s ease-out;
  will-change: opacity, transform;
}
[data-reveal].is-revealed {
  opacity: 1;
  transform: translateY(0);
}

/* Staggered group reveal — each child fades in with a small delay. */
[data-reveal-stagger] > * {
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity   0.5s ease-out,
    transform 0.5s ease-out;
}
[data-reveal-stagger].is-revealed > * {
  opacity: 1;
  transform: translateY(0);
}
[data-reveal-stagger].is-revealed > *:nth-child(1) { transition-delay:   0ms; }
[data-reveal-stagger].is-revealed > *:nth-child(2) { transition-delay:  80ms; }
[data-reveal-stagger].is-revealed > *:nth-child(3) { transition-delay: 160ms; }
[data-reveal-stagger].is-revealed > *:nth-child(4) { transition-delay: 240ms; }
[data-reveal-stagger].is-revealed > *:nth-child(5) { transition-delay: 320ms; }
[data-reveal-stagger].is-revealed > *:nth-child(6) { transition-delay: 400ms; }
[data-reveal-stagger].is-revealed > *:nth-child(7) { transition-delay: 480ms; }
[data-reveal-stagger].is-revealed > *:nth-child(8) { transition-delay: 560ms; }
[data-reveal-stagger].is-revealed > *:nth-child(n+9) { transition-delay: 640ms; }

/* === A3: spinner + toast === */

.tp-spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: tp-spin 0.6s linear infinite;
  vertical-align: middle;
  margin-right: 0.5rem;
}

.tp-toast {
  position: fixed;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%) translateY(12px);
  opacity: 0;
  pointer-events: none;
  z-index: 1000;
  padding: 0.85rem 1.25rem;
  border-radius: 4px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
               "Helvetica Neue", Arial, sans-serif;
  font-size: 0.9rem;
  line-height: 1.45;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.10);
  transition:
    opacity   0.3s ease-out,
    transform 0.3s ease-out;
  max-width: 90vw;
}
.tp-toast.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
  animation: tp-toast-in 0.3s ease-out;
}
.tp-toast--success {
  background: #f0fdf4;
  color: #166534;
  border: 1px solid #bbf7d0;
}
.tp-toast--error {
  background: #fef2f2;
  color: #991b1b;
  border: 1px solid #fecaca;
}

/* === A4: route cross-fade lives in /assets/css/transitions.css ===
   The 150ms body opacity transition (paired with /assets/js/transitions.js)
   is the canonical A4 implementation. Loaded at build time by build.js on
   every HTML page via /assets/css/transitions.css + /assets/js/transitions.js.
   No CSS rules needed in motion.css for A4 — keeping this comment as a
   pointer for future readers. */

/* === Honor prefers-reduced-motion: reduce ===
   Substrate-honest: we collapse every animation and transition to its
   final state, and we reveal all [data-reveal] elements immediately. */
@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;
  }
  /* Note: body-level animation is gone (moved to /assets/css/transitions.css
     which has its own prefers-reduced-motion override). The A1/A2/A3
     animation/transition collapses below remain. */
  [data-reveal],
  [data-reveal-stagger] > * {
    opacity: 1 !important;
    transform: none !important;
  }
  [data-reveal-stagger].is-revealed > * {
    transition-delay: 0ms !important;
  }
  .tp-toast {
    transition-duration: 0.01ms !important;
  }
}
