/**
 * ============================================================
 * NAHUEN GOURMET MARKET — Motion System (Fase 7)
 * ============================================================
 * Solo activo porque existe Motion Intent APROBADO en
 * 05-diseno/direccion-arte.md, sección 22.
 *
 * Archivo separado de framework.css (decisión documentada en
 * motion-system.md, Paso 4): más de 4 clases de estado/comportamiento
 * propias del motion system y timing gobernado en parte desde JS.
 *
 * Reglas:
 * - Solo anima `transform` y `opacity` (frontend-guidelines).
 * - Sin `.motion-ready` en <body>, todo el contenido es visible por
 *   defecto — JS añade `.motion-ready` cuando GSAP está listo. Nunca
 *   ocultar contenido antes de confirmar que el JS está activo.
 * - `prefers-reduced-motion` reduce todo a fade simple sin desplazamiento
 *   (o lo desactiva), sin excepción — ver bloque final de este archivo.
 * - No modificar sin actualizar motion-system.md (catálogo canónico).
 * ============================================================
 */

:root {
  /* Variables propias de motion — timing de reveals gobernado desde JS
     (gsap.utils.mapRange en motion.js), estas son solo referencia/fallback
     para el caso sin JS o para transiciones CSS puras del catálogo. */
  --motion-reveal-duration: 0.7s; /* fallback CSS — JS recalcula 0.6s-0.9s por matchMedia */
  --motion-reveal-ease: cubic-bezier(
    0.16,
    1,
    0.3,
    1
  ); /* ease-out-expo, igual a --ease-reveal */
  --motion-stagger-delay: 100ms; /* mínimo entre elementos en stagger (regla anti-caos) */
  --motion-scroll-ease: none; /* parallax ligado a scroll: sin easing, gobierna scrub */
}

/* ============================================================
   REVEAL ON SCROLL — IntersectionObserver
   Aplica a bloques, cards y líneas de texto en (casi) todas las páginas.
   Fallback: sin JS o con .motion-ready ausente, el contenido es visible.
   ============================================================ */
.motion-ready .motion-reveal {
  opacity: 0;
  transform: translateY(var(--space-md));
}
/* La transición CSS del reveal SOLO aplica como fallback real (sin GSAP) —
   motion.js añade .gsap-ready al <body> cuando GSAP está disponible y
   gobierna la animación con gsap.fromTo. Sin este :not(), cada frame que
   escribe GSAP quedaba re-suavizado por esta transición (duración efectiva
   ~2x, ease expo.out anulado, stagger duplicado — anti-patrón GSAP+CSS-
   transition sobre las mismas propiedades, corregido en refinamiento). */
.motion-ready:not(.gsap-ready) .motion-reveal.is-visible {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--motion-reveal-duration) var(--motion-reveal-ease),
    transform var(--motion-reveal-duration) var(--motion-reveal-ease);
}
.motion-ready.gsap-ready .motion-reveal.is-visible {
  opacity: 1;
  transform: none;
}
/* Cuerpo de lectura larga (Single:Post, legal): SOLO fade simple, nunca
   desplazamiento (anti-regla de motion #2, direccion-arte.md sección 22) */
.motion-ready .motion-reveal--fade-only {
  transform: none;
}
.motion-ready .motion-reveal--fade-only.is-visible {
  transform: none;
}

/* Stagger — delay escalonado por data-motion-index. SOLO en el fallback
   CSS (sin GSAP): con GSAP activo el stagger lo aplica motion.js (delay =
   índice × 0.1s en el tween de reveal).
   FIX ronda cliente 2026-07-06 f ("las animaciones de entrada no cargan
   en las cards"): sin el :not(.gsap-ready), esta regla dejaba un
   transition-delay con transition-property:all (valor inicial) sobre los
   elementos indexados — cada write por frame de GSAP reiniciaba la
   transición pendiente y el navegador NO PINTABA ningún valor intermedio:
   el elemento quedaba invisible durante todo el tween y aparecía de golpe
   al terminar (medido en Chrome: tween con delay 0.1s correcto, opacity
   computada clavada en 0 hasta el frame final + 100ms). Todos los grupos
   de cards (accesos / sweet / shop / sedes) "no animaban" salvo su primer
   ítem (índice 0 = delay 0s = sin transición pendiente). Mismo anti-patrón
   GSAP+CSS-transition ya documentado arriba — el delay se había escapado
   del :not(). */
.motion-ready:not(.gsap-ready) .motion-reveal[data-motion-index] {
  transition-delay: calc(var(--motion-stagger-delay) * var(--motion-index, 0));
}

/* ============================================================
   PARALLAX MULTI-VELOCIDAD — GSAP ScrollTrigger (scrub)
   Vitrina de Antojo, Sweet Bakery hub, Archive:Cake. Transform puro,
   gobernado por GSAP (translateY vía scrub) — sin clase de estado inicial
   oculto, el elemento es visible siempre, solo se desplaza.
   Nota: sin will-change permanente aquí (direccion-arte.md, sección 20:
   "sin will-change permanente" para overlays animados) — GSAP promueve la
   capa automáticamente (force3D) mientras el tween está activo y la libera
   al terminar. El selector apunta a [data-motion="parallax"], el target
   real que usa motion.js — .motion-parallax/.motion-parallax--slow eran
   clases sin uso (código muerto, removidas en refinamiento post-panel).
   ============================================================ */

/* ============================================================
   ENTRELAZADO DEL HERO — capas del cutout respecto al titular
   Composición discreta por rango de dispositivo (ver motion-system.md).
   Desktop: cutout pasa por detrás Y por delante de caracteres específicos.
   Tablet: solo detrás del bloque completo. Mobile: solo detrás, sin capa
   frontal (regla ya aplicada en framework.css .hero-monumental__cutout--front).
   Sin will-change permanente (mismo criterio que el parallax de arriba).
   ============================================================ */
.hero-entangle {
  position: relative;
}
.hero-entangle__layer--back {
  z-index: 0;
}
.hero-entangle__layer--front {
  z-index: var(--z-cutout);
}

/* ============================================================
   MAGNETISMO — ícono funcional protagonista
   Solo `(pointer: fine)`, gsap.quickTo. En táctil el ícono queda estático,
   sin sustituto forzado (direccion-arte.md, sección 22). Nunca en overlays
   compositivos (anti-regla de motion #1). Sin will-change permanente (mismo
   criterio que arriba).
   ============================================================ */
[data-motion="magnetic"] {
  display: inline-block;
}

/* ============================================================
   ESTADOS DE FORMULARIO — feedback utilitario (150-250ms)
   Cero parallax/reveal decorativo dentro de formularios (anti-regla #3).
   Ya cubierto por framework.css (.form-field__input transitions) — esta
   sección solo documenta que NO se añade motion adicional aquí.
   ============================================================ */

/* ============================================================
   PREFERS-REDUCED-MOTION — obligatorio, sin excepción
   Reduce todo el catálogo de motion a fade simple sin desplazamiento (o lo
   desactiva). Cubre además gsap.matchMedia() en motion.js.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .motion-reveal,
  .hero-entangle__layer,
  [data-motion] {
    animation: none !important;
    transition: none !important;
    transform: none !important;
    opacity: 1 !important;
    will-change: auto !important;
  }
}
