/* ==========================================================================
   Vértice Commerce 2 — core/motion.css
   Capa de movimiento del MOTOR (siempre encolada, al final del core): reveal
   por scroll, transiciones del chrome (drawers, dropdown), micro-feedback de
   press, contador del carrito, barra de envío, marquee de logos y FAB de
   WhatsApp. Porta la mecánica de motion-premium.css de v1 con SOLO
   tokens semánticos (nada de curvas/colores de marca: --tr, --tr-slow,
   --ease-out; --ease-spring es RESERVADO del contrato para la capa de
   diseño, el core no lo consume).

   REGLA DEL CONTRATO (fix woocommerce.css:140): TODO el movimiento vive
   dentro de @media (prefers-reduced-motion: no-preference) Y ADEMÁS el
   archivo incluye el bloque global de reduced-motion (sección 10). Doble
   cinturón: lo que un módulo declare fuera del media query correcto igual
   queda frenado. Los presets v2.1 (sección 11) van DESPUÉS de ese bloque
   pero solo mueven custom properties: el !important del bloque global actúa
   sobre las propiedades reales y les gana igual (verificado en §11).

   !important permitidos y documentados en este archivo:
   - Bloque global @media (prefers-reduced-motion: reduce) del final
     (animation-duration/iteration-count, transition-duration,
     scroll-behavior): es EL fix de auditoría y debe ganarle a cualquier
     declaración del core, de la capa de diseño o de plugins.
     animation-iteration-count:1 acompaña a la duración de 0.01ms: sin él,
     las animaciones infinitas (marquee, pulso del FAB) quedarían loopeando
     a 0.01ms y quemarían CPU.

   z-index literal permitido y documentado en este archivo (excepción al
   contrato "z-index solo vía --z-*" de Task 12):
   - z-index: -1 en .vc-whatsapp-fab::before — truco de apilamiento LOCAL al
     stacking context que crea el propio FAB (z-index: var(--z-nav)): el
     anillo del pulso queda detrás del contenido del FAB sin participar de
     la escala --z-* (no compite con ningún otro plano del sitio).

   Contratos con el JS del motor (Task 11) DEFINIDOS ACÁ:
   - Reveal: elementos [data-vc-reveal] (template-parts/home/*); el observer
     agrega .is-visible al entrar al viewport. Red de seguridad solo-CSS: si
     main.js no corre, el keyframe vc-reveal-safety muestra todo a los ~1.8s
     (el reveal es mejora progresiva: JAMÁS puede dejar contenido oculto).
   - Marquee (inc/trust.php): el JS agrega .is-enhanced a .vc-marquee para
     activar el loop (sin JS queda la grilla estática y el botón de pausa
     sigue hidden) y alterna .is-paused + aria-pressed en el toggle.
   ========================================================================== */

/* ---------------------------- Keyframes -------------------------------- */
@keyframes vc-reveal-safety { to { opacity: 1; transform: none; } }
@keyframes vc-count-pop {
	0% { transform: scale(0.6); }
	55% { transform: scale(1.25); }
	100% { transform: scale(1); }
}
@keyframes vc-fab-pulse {
	0% { transform: scale(1); opacity: 0.45; }
	70%, 100% { transform: scale(1.65); opacity: 0; }
}
/* -100% = el ancho de la propia pista; con la pista duplicada (aria-hidden)
   y el gap del viewport, el loop es continuo. */
@keyframes vc-marquee-scroll { to { transform: translateX(calc(-100% - var(--sp-6))); } }

/* ============================ 1. TRANSICIONES BASE ====================== */
/* Los estados hover/focus/abierto se DECLARAN en reset/layout/components/
   nav/shop (sin transición: funcionales solos); acá se les da el easing.
   Solo propiedades de pintura/transform (GPU-friendly, sin layout). */
@media (prefers-reduced-motion: no-preference) {
	a { transition: color var(--tr); }

	.vc-btn,
	.vc-icon-btn,
	.vc-chip,
	.vc-qty__btn,
	.vc-drawer__link,
	.vc-pagination .page-numbers,
	.vc-pagelinks > a,
	.vc-livesearch__item,
	.vc-livesearch__all,
	.vc-nav .sub-menu a,
	.vc-drawer .woocommerce-mini-cart__buttons .button,
	.vc-drawer .woocommerce-mini-cart-item a.remove {
		transition:
			background-color var(--tr),
			color var(--tr),
			border-color var(--tr),
			box-shadow var(--tr),
			opacity var(--tr),
			transform var(--tr);
	}

	.vc-input,
	.vc-textarea { transition: border-color var(--tr), background-color var(--tr); }

	.vc-nav .menu-item-has-children > a::after { transition: transform var(--tr); }
}

/* ============================ 2. FEEDBACK DE PRESS ====================== */
/* Todo pressable responde al click con un scale sutil (0.92–0.97, nunca
   bouncy). Vive completo en no-preference: bajo reduce no escala. */
@media (prefers-reduced-motion: no-preference) {
	.vc-icon-btn:active { transform: scale(0.94); }
	.vc-qty__btn:active:not(:disabled) { transform: scale(0.92); }
	.vc-btn:active:not(:disabled):not([aria-disabled="true"]) { transform: scale(0.97); }
	.vc-pagination a.page-numbers:active { transform: scale(0.95); }
	.vc-drawer .woocommerce-mini-cart__buttons .button:active { transform: scale(0.97); }
}

/* ============================ 3. REVEAL POR SCROLL ====================== */
/* [data-vc-reveal] en las secciones de la home (nunca en el hero: es el LCP).
   Solo opacity/transform. El estado base oculto vive DENTRO de no-preference:
   con reduce el contenido es visible desde el primer frame, sin trucos. */
@media (prefers-reduced-motion: no-preference) {
	[data-vc-reveal] {
		opacity: 0;
		/* v2.1: distancia tokenizada — los presets de motion (sección 11) la
		   mueven vía --vc-reveal-distance; el fallback ES el valor v2.0
		   (§2.7: sin preset declarado, cero regresión). */
		transform: translateY(var(--vc-reveal-distance, 1.25rem));
		transition: opacity var(--tr-slow), transform var(--tr-slow);
		/* Red de seguridad sin JS (documentada en cabecera). */
		animation: vc-reveal-safety 0.01s linear 1.8s forwards;
	}
	[data-vc-reveal].is-visible {
		opacity: 1;
		transform: none;
	}
}

/* ============================ 4. DRAWERS + DROPDOWN ===================== */
/* Estados finales en core/nav.css; acá SOLO la transición. visibility entra
   en la transición para que el panel siga visible durante el cierre (la
   propiedad es discreta: no-hidden durante todo el tramo animado). */
@media (prefers-reduced-motion: no-preference) {
	.vc-drawer__overlay { transition: opacity var(--tr), visibility var(--tr); }
	.vc-drawer__panel { transition: transform var(--tr-slow), visibility var(--tr-slow); }

	.vc-nav .sub-menu {
		transform-origin: top left;
		transition: opacity var(--tr), transform var(--tr), visibility var(--tr);
	}
}

/* ============================ 5. CONTADOR DEL CARRITO =================== */
/* El fragment AJAX (inc/woocommerce.php) reemplaza span.vc-cart-count al
   agregar al carrito: la animación corre sola al re-insertarse el nodo, sin
   JS extra. Mismo pop para el contador de wishlist (lo re-crea el JS). */
@media (prefers-reduced-motion: no-preference) {
	.vc-cart-count:not([hidden]),
	.vc-wishlist-count:not([hidden]) { animation: vc-count-pop 260ms var(--ease-out); }
}

/* ============================ 6. BARRA DE ENVÍO GRATIS ================== */
/* components.css documenta que el fill (.vc-shipbar__fill, inline
   transform:scaleX) se anima acá: al reemplazarse los totales por AJAX el
   progreso desliza en vez de saltar. */
@media (prefers-reduced-motion: no-preference) {
	.vc-shipbar__fill { transition: transform var(--tr-slow), background-color var(--tr); }
}

/* ============================ 7. MARQUEE DE LOGOS ======================= */
/* inc/trust.php: .vc-marquee[data-vc-marquee] > button.vc-marquee__toggle
   [hidden] + .vc-marquee__viewport > ul.vc-marquee__track ×2 (la segunda con
   aria-hidden="true").

   Degradación en cascada (contrato del módulo):
   - Sin JS: grilla estática centrada, pista duplicada oculta, toggle hidden.
   - Con JS pero reduced-motion: el bloque global del final congela el loop
     (0.01ms × 1 iteración) ⇒ en la práctica, la misma grilla estática.
   - Con JS y preset nula (sin reduce del SO): initMarquee corre igual y
     agrega .is-enhanced; las reglas de la sección 11 restauran la grilla
     estática (wrap centrado, pista duplicada y toggle ocultos).
   - Con JS y motion OK (.is-enhanced): loop infinito pausable por el toggle
     (.is-paused), por hover y por foco dentro de la pista. */
.vc-marquee { position: relative; }
.vc-marquee__viewport {
	display: flex;
	gap: var(--sp-6);
	overflow: hidden;
}
.vc-marquee__track {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: var(--sp-5) var(--sp-6);
	flex: none;
	min-width: 100%;
}
/* Estático (sin JS / sin animación): la pista duplicada no aporta nada. */
.vc-marquee__track[aria-hidden="true"] { display: none; }
.vc-marquee__item { display: flex; align-items: center; }
.vc-marquee__logo {
	width: auto;
	height: auto;
	max-height: 48px;
	max-width: 160px;
	object-fit: contain;
}

/* Toggle de pausa (nace hidden; lo revela el JS junto con .is-enhanced).
   Target 36px + estados con tokens; el foco lo da el anillo global. */
.vc-marquee__toggle {
	position: absolute;
	right: 0;
	bottom: calc(100% + var(--sp-2));
	display: inline-grid;
	place-items: center;
	width: 36px;
	height: 36px;
	border: var(--border-w) solid var(--line);
	border-radius: var(--radius-pill);
	background: var(--surface);
	color: var(--ink-2);
}
.vc-marquee__toggle:hover { color: var(--brand-strong); border-color: var(--brand); }
.vc-marquee__toggle[aria-pressed="true"] { background: var(--surface-2); color: var(--ink); }
.vc-marquee__toggle svg { width: 16px; height: 16px; }

@media (prefers-reduced-motion: no-preference) {
	.vc-marquee.is-enhanced .vc-marquee__track {
		flex-wrap: nowrap;
		justify-content: flex-start;
		animation: vc-marquee-scroll var(--vc-marquee-duration, 32s) linear infinite;
	}
	.vc-marquee.is-enhanced .vc-marquee__track[aria-hidden="true"] { display: flex; }

	/* Pausa: toggle, hover o foco de teclado dentro del marquee. */
	.vc-marquee.is-paused .vc-marquee__track,
	.vc-marquee.is-enhanced:hover .vc-marquee__track,
	.vc-marquee.is-enhanced:focus-within .vc-marquee__track {
		animation-play-state: paused;
	}
}

/* ============================ 8. ESTADO DE CARGA ======================== */
/* Estado de carga genérico en botones (el JS deshabilita + agrega la clase;
   requests en vuelo, contrato de Task 11). */
.vc-btn.is-loading { opacity: 0.7; cursor: progress; }

/* ============================ 9. FAB DE WHATSAPP ======================== */
/* footer.php: a.vc-whatsapp-fab (SOLO se renderiza con número cargado).
   Posición + pulso = mecánica del motor; el color va con el par --cta-*
   (contraste AA por contrato). La capa de diseño suele re-pintarlo con el
   verde reconocible de WhatsApp (#25D366 con tinta oscura) — decisión de
   piel, no del motor.
   z-index: --z-nav (bajo --z-overlay/--z-modal): con un drawer abierto el
   overlay lo cubre y captura el click — sin las clases de body que usaba v1
   para esconderlo a mano. */
.vc-whatsapp-fab {
	position: fixed;
	right: clamp(1rem, 3vw, 1.75rem);
	bottom: clamp(1rem, 3vw, 1.75rem);
	z-index: var(--z-nav);
	display: grid;
	place-items: center;
	width: 56px;
	height: 56px;
	border-radius: var(--radius-pill);
	background: var(--cta-bg);
	color: var(--cta-ink);
	box-shadow: var(--shadow-2);
}
.vc-whatsapp-fab:hover { background: var(--cta-bg-hover); color: var(--cta-ink); }
.vc-whatsapp-fab svg { width: 28px; height: 28px; }
.vc-whatsapp-fab:focus-visible { outline-offset: 3px; }

@media (prefers-reduced-motion: no-preference) {
	/* opacity en la lista: sin ella el opacity:0 del @starting-style de abajo
	   saltaría a 1 en el primer frame (solo animaría el scale). */
	.vc-whatsapp-fab {
		transition:
			background-color var(--tr),
			box-shadow var(--tr),
			transform var(--tr),
			opacity var(--tr);
	}
	@media (hover: hover) and (pointer: fine) {
		.vc-whatsapp-fab:hover { transform: translateY(-2px); box-shadow: var(--shadow-3); }
	}
	/* Entrada suave al cargar (solo realza un default ya visible). */
	@starting-style {
		.vc-whatsapp-fab { opacity: 0; transform: scale(0.85); }
	}
	/* Pulso de atención: anillo que emana y se desvanece (lento, sutil). */
	.vc-whatsapp-fab::before {
		content: "";
		position: absolute;
		inset: 0;
		z-index: -1;
		border-radius: inherit;
		background: var(--cta-bg);
		animation: vc-fab-pulse 2.6s var(--ease-out) infinite;
	}
}

/* ============================ 10. REDUCED MOTION (GLOBAL) =============== */
/* Fix woocommerce.css:140 — bloque global del contrato. Frena TODO el
   movimiento del sitio (core, capa de diseño y plugins): animaciones a un
   frame, transiciones instantáneas y scroll sin smooth (complementa el gate
   de scroll-behavior de reset.css). !important documentado en la cabecera;
   animation-iteration-count evita loops infinitos a 0.01ms (CPU). */
@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;
	}
}

/* ============================ 11. PRESETS DE PERSONALIDAD (v2.1) ======== */
/* project-config.php['motion'] → inc/setup.php agrega vc-motion-{preset} al
   <body> vía body_class (validado por vc_project contra el registry). Cada
   preset SOLO redefine tokens de movimiento a nivel body (los custom
   properties cascadean a todo el contenido): no agrega transiciones nuevas
   ni toca selectores del motor.

   Tokens que mueve un preset:
   - --tr / --tr-slow            duraciones (contrato v2.0: garantizados en
                                 tokens.css, van sin fallback)
   - --ease-out / --ease-spring  curvas (contrato v2.0)
   - --vc-reveal-distance        NUEVO v2.1 — distancia del reveal por scroll
                                 (consumido en la sección 3 con fallback
                                 1.25rem = valor v2.0)
   - --vc-hover-scale            NUEVO v2.1 — escala del zoom de la imagen de
                                 tarjeta al hover (consumido en core/shop.css
                                 con fallback 1.03 = valor v2.0; recetas y
                                 pieles lo consumen igual:
                                 var(--vc-hover-scale, 1.03))

   vc-motion-seca — EL DEFAULT DE FÁBRICA: a propósito NO emite reglas.
   "Seca" ES el comportamiento v2.0 (los valores de tokens.css + los
   fallbacks de los var() de arriba): al no existir ninguna regla, un
   proyecto v2.0 que no declara nada no puede sufrir regresión (§2.7). Si
   algún día "seca" necesitara valores propios, dejaría de ser el default.

   LÍMITE DOCUMENTADO: las transiciones DE ESTE ARCHIVO (secciones 1, 4 y 6)
   no declaran easing (usan el default `ease`) y sus reglas BASE no se
   tocan: cambiarlas a var(--ease-out) alteraría el render v2.0 y viola
   §2.7. OJO: eso NO vale para todo el core — account.css,
   cart-checkout.css, product.css, shop.css y wishlist.css componen
   var(--ease-out) en sus transiciones desde v2.0, así que un preset SÍ las
   re-curva (correcto y §2.7-safe: ya consumían el token). La personalidad
   de curva de un preset la aprovechan además las animaciones del core que
   ya consumen --ease-out (count-pop, pulso del FAB), toda transición que
   la piel o una receta declare componiendo var(--ease-out) /
   var(--ease-spring), y el reveal — pero SOLO vía la regla scoped por
   preset de abajo (sin la clase del preset, la base conserva `ease`).

   PRECEDENCIA con reduced-motion (verificado): el bloque global de la
   sección 10 pisa TODOS los presets aunque estos estén declarados después,
   porque actúa con !important sobre las propiedades REALES
   (animation-duration / transition-duration / scroll-behavior); los presets
   solo mueven VALORES de custom properties y ninguna cascada de custom
   properties puede ganarle a un !important sobre la propiedad final.
   Además, el estado oculto del reveal vive dentro de no-preference: con
   reduce el contenido es visible desde el primer frame, con o sin preset. */

/* --------------------- vc-motion-elastica ------------------------------ */
/* Con rebote: curvas con overshoot, reveal más profundo y un poco más
   largo. El zoom de hover queda en el valor base (la elasticidad es curva,
   no amplitud). */
body.vc-motion-elastica {
	--tr: 260ms;
	--tr-slow: 480ms;
	--ease-out: cubic-bezier(0.34, 1.56, 0.64, 1);
	--ease-spring: cubic-bezier(0.22, 1.61, 0.36, 1);
	--vc-reveal-distance: 28px;
	--vc-hover-scale: 1.03;
}

/* --------------------- vc-motion-teatral ------------------------------- */
/* Lento y ceremonial: entradas largas desde más lejos, curvas suaves sin
   rebote (expo-out para el gesto, in-out "telón" para la capa de diseño),
   zoom de hover más notorio. */
body.vc-motion-teatral {
	--tr: 340ms;
	--tr-slow: 700ms;
	--ease-out: cubic-bezier(0.19, 1, 0.22, 1);
	--ease-spring: cubic-bezier(0.83, 0, 0.17, 1);
	--vc-reveal-distance: 3rem;
	--vc-hover-scale: 1.05;
}

/* La curva del preset SÍ llega al reveal (el efecto insignia de elástica y
   teatral): la regla base de la sección 3 no declara timing-function (corre
   con el default `ease` y NO se toca, ver LÍMITE arriba), así que acá se
   re-curva SOLO bajo la clase del preset — sin la clase, el render v2.0
   queda intacto (§2.7). Sin preset o bajo reduce no cambia nada: fuera de
   no-preference [data-vc-reveal] no tiene transición, y con reduce la
   duración forzada a 0.01ms del bloque global vuelve la curva irrelevante. */
body.vc-motion-elastica [data-vc-reveal],
body.vc-motion-teatral [data-vc-reveal] {
	transition-timing-function: var(--ease-out);
}

/* --------------------- vc-motion-nula ---------------------------------- */
/* Sin movimiento perceptible: duraciones a 1ms y distancia/escala en
   neutro. OJO: NO pisa el MECANISMO de reveal — [data-vc-reveal] conserva
   su transición (ahora de 1ms) y su animación de seguridad
   vc-reveal-safety: los elementos SIEMPRE aparecen (con JS, al entrar al
   viewport; sin JS, a los ~1.8s). Por eso acá NO hay `animation: none`
   sobre [data-vc-reveal]. */
body.vc-motion-nula {
	--tr: 1ms;
	--tr-slow: 1ms;
	--vc-reveal-distance: 0px;
	--vc-hover-scale: 1;
}
/* Micro-animaciones con duración propia (no tokenizada): bajo nula se
   apagan con el mismo criterio del bloque reduced-motion — un frame para
   las finitas, `none` para las decorativas infinitas (una animación
   infinita a 1ms quema CPU). Especificidad: cada selector suma
   body.vc-motion-nula al original y además aparece más tarde en el
   archivo ⇒ gana sin !important. El reveal NO figura acá (ver arriba). */
body.vc-motion-nula .vc-cart-count:not([hidden]),
body.vc-motion-nula .vc-wishlist-count:not([hidden]) { animation-duration: 1ms; }
body.vc-motion-nula .vc-whatsapp-fab::before { animation: none; }
/* Marquee bajo nula — restaurar la grilla estática COMPLETA. main.js gatea
   initMarquee solo con prefers-reduced-motion, así que con nula (sin
   preferencia reduce del SO) igual llega .is-enhanced, y las reglas de la
   sección 7 (dentro de no-preference, que acá SÍ aplica) impondrían
   nowrap + flex-start + pista duplicada visible: con la animación apagada
   eso sería una tira estática de UNA fila recortada por el overflow:hidden
   del viewport — logos invisibles. Acá: sin animación, con wrap centrado
   (la grilla estática del contrato), pista duplicada oculta (el
   [aria-hidden="true"] en el selector le gana en especificidad a la regla
   display:flex de la sección 7) y toggle oculto (pausar una animación
   apagada no hace nada). */
body.vc-motion-nula .vc-marquee.is-enhanced .vc-marquee__track {
	animation: none;
	flex-wrap: wrap;
	justify-content: center;
}
body.vc-motion-nula .vc-marquee.is-enhanced .vc-marquee__track[aria-hidden="true"] { display: none; }
body.vc-motion-nula .vc-marquee__toggle { display: none; }
