@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400;1,500&display=swap');

/* ══════════════════════════════════════════════════════════════════════
   MEGA MAYORISTA — TOKENS DE DISEÑO (maqueta 2026)
   ══════════════════════════════════════════════════════════════════════
   Única fuente de verdad de color, tipografía, escala, radios, sombras y
   espaciado del sitio público. Todo lo que se construya encima debe salir
   de aquí: si un valor no está en este archivo, no se usa.

   Se carga ANTES que mm-base.css y que cualquier hoja de sección.
   Convive con las hojas legacy (design-system.css / cards.css /
   futuristic.css / ecommerce.css), que quedan CONGELADAS: no se editan,
   sólo se sobrescriben desde las hojas mm-*.
   ══════════════════════════════════════════════════════════════════════ */

:root {
  color-scheme: light;

  /* ── COLOR · Rojos de marca ───────────────────────────────────────────
     Regla dura:
       --mm-red        → SUPERFICIES grandes (header, footer, bandas
                         full-bleed, botones sólidos).
       --mm-red-accent → TEXTO y micro-elementos (precio, pill ENVÍOS
                         GRATIS, badge de descuento, subrayado del nav).
     Nunca los dos en el mismo elemento.
     Contraste: blanco sobre --mm-red = 6.6:1 (AA para texto normal).
     --mm-red-accent sobre blanco = 4.3:1 → SÓLO ≥18px, o ≥14px en bold.
     Para texto chico sobre blanco usar --mm-red (5.9:1).                */
  --mm-red:            #A81E22;
  --mm-red-hover:      #8E1A1D;
  --mm-red-active:     #781417;
  --mm-red-accent:     #EE2E33;
  --mm-red-accent-hv:  #D42026;
  --mm-red-tint:       #FBE9EA;
  --mm-red-border:     rgba(168, 30, 34, 0.30);
  --mm-on-red:         #FFFFFF;
  --mm-on-red-soft:    rgba(255, 255, 255, 0.88);
  --mm-on-red-faint:   rgba(255, 255, 255, 0.70);
  --mm-on-red-line:    rgba(255, 255, 255, 0.15);

  /* ── COLOR · Neutros ─────────────────────────────────────────────────
     --mm-text es el color de texto POR DEFECTO de la maqueta: gris medio,
     no negro. El negro (--mm-ink) se reserva para precio y titulares.    */
  --mm-ink:            #1A1A1A;
  --mm-text:           #606161;
  --mm-text-soft:      #8A8A8A;
  --mm-text-faint:     #A9A9A9;
  --mm-line:           #D3D3D3;
  --mm-line-soft:      #E6E6E6;
  --mm-surface:        #FFFFFF;
  --mm-surface-2:      #FAFAFA;
  --mm-page:           #FFFFFF;
  --mm-band:           #F1F1F1;
  --mm-band-deep:      #ECECEC;
  --mm-panel-dark:     #101922;
  --mm-form-grey:      #E0E0DF;
  --mm-overlay:        rgba(16, 25, 34, 0.55);

  /* ── COLOR · Acentos decorativos de sección (maqueta) ──────────────── */
  --mm-peach:          #FFE4C3;
  --mm-peach-deep:     #FFD3A0;
  --mm-teal:           #00AE9E;

  /* ── COLOR · Semánticos (heredados del sitio, se mantienen) ────────── */
  --mm-ok:             #16A34A;
  --mm-ok-bg:          #ECFDF5;
  --mm-ok-border:      #A7F3D0;
  --mm-ok-text:        #15803D;
  --mm-warn:           #B45309;
  --mm-warn-bg:        #FFF7D6;
  --mm-warn-border:    #FDE68A;
  --mm-warn-text:      #92400E;
  --mm-danger:         #EF4444;
  --mm-danger-bg:      #FEF2F2;
  --mm-danger-text:    #B91C1C;
  --mm-wa:             #22C55E;
  --mm-wa-deep:        #128C7E;
  --mm-disabled-bg:    #E5E5E5;
  --mm-disabled-text:  #A3A3A3;

  /* ── TIPOGRAFÍA ──────────────────────────────────────────────────────
     Una sola familia: Poppins (geométrica redondeada) — es la de la
     maqueta. No añadir familias nuevas.                                  */
  --mm-font: 'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  /* Los iconos de categoría son glifos de color. Poppins no los trae, y
     aunque el navegador ya cae solo a la fuente de emoji del sistema,
     nombrarla evita que los de doble presentación (engranaje, lápiz,
     etiqueta, pantalla, escudo) salgan como símbolo monocromo.

     SIN genérico al final, a propósito: el emparejado de fuentes recorre
     la lista glifo a glifo, y un `sans-serif` tiene todas las letras, así
     que se quedaría también el monograma de respaldo y la tipografía de
     marca no llegaría a usarse nunca. Quien la use la encadena delante de
     --mm-font. */
  --mm-font-emoji: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji',
                   'Segoe UI Symbol';
  --mm-font-display: var(--mm-font);
  --mm-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Escala fluida — referencia 1440px */
  --mm-t-eyebrow: clamp(0.6875rem, 0.660rem + 0.12vw, 0.750rem);  /* 11→12 */
  --mm-t-xs:      clamp(0.7500rem, 0.730rem + 0.10vw, 0.8125rem); /* 12→13 */
  --mm-t-sm:      clamp(0.8125rem, 0.790rem + 0.15vw, 0.8750rem); /* 13→14 */
  --mm-t-base:    clamp(0.8750rem, 0.850rem + 0.18vw, 1.0000rem); /* 14→16 */
  --mm-t-lg:      clamp(1.0000rem, 0.960rem + 0.25vw, 1.1250rem); /* 16→18 */
  --mm-t-xl:      clamp(1.2500rem, 1.130rem + 0.50vw, 1.5000rem); /* 20→24 */
  --mm-t-2xl:     clamp(1.3750rem, 1.200rem + 0.70vw, 1.7500rem); /* 22→28 */
  --mm-t-3xl:     clamp(1.7500rem, 1.400rem + 1.40vw, 2.5000rem); /* 28→40 */
  --mm-t-4xl:     clamp(2.0000rem, 1.500rem + 2.00vw, 3.0000rem); /* 32→48 */

  --mm-w-regular: 400;
  --mm-w-medium:  500;
  --mm-w-semi:    600;
  --mm-w-bold:    700;
  --mm-w-black:   800;

  --mm-lh-none:   1.00;
  --mm-lh-tight:  1.15;
  --mm-lh-snug:   1.30;
  --mm-lh-normal: 1.45;
  --mm-lh-body:   1.60;
  --mm-lh-link:   2.20;   /* listas de links del footer */

  --mm-ls-eyebrow: 0.14em;
  --mm-ls-wide:    0.06em;
  --mm-ls-copy:    0.04em;
  --mm-ls-tight:  -0.01em;
  --mm-ls-tighter:-0.02em;

  /* ── RADIOS ──────────────────────────────────────────────────────────
     La maqueta es PLANA: 6–12px. Prohibido rounded-3xl / 2rem.          */
  --mm-r-none: 0px;
  --mm-r-xs:   6px;
  --mm-r-sm:   8px;
  --mm-r-md:   12px;
  --mm-r-lg:   16px;
  --mm-r-xl:   20px;
  --mm-r-full: 999px;

  /* ── SOMBRAS ─────────────────────────────────────────────────────────
     Reposo casi sin sombra (manda el borde 1px). La sombra fuerte se
     reserva a hover, dropdowns, modales y flechas de carrusel.          */
  --mm-sh-none:   none;
  --mm-sh-card:   0 1px 3px rgba(26, 26, 26, 0.06), 0 1px 2px rgba(26, 26, 26, 0.04);
  --mm-sh-hover:  0 8px 24px rgba(26, 26, 26, 0.10), 0 2px 6px rgba(26, 26, 26, 0.06);
  --mm-sh-pop:    0 12px 32px rgba(26, 26, 26, 0.14);
  --mm-sh-arrow:  0 2px 10px rgba(26, 26, 26, 0.16);
  --mm-sh-modal:  0 24px 64px rgba(16, 25, 34, 0.28);
  --mm-sh-header: 0 1px 0 rgba(0, 0, 0, 0.06);

  /* ── ESPACIADO (base 4px) ───────────────────────────────────────────── */
  --mm-s-0:  0px;
  --mm-s-1:  4px;
  --mm-s-2:  8px;
  --mm-s-3:  12px;
  --mm-s-4:  16px;
  --mm-s-5:  20px;
  --mm-s-6:  24px;
  --mm-s-7:  28px;
  --mm-s-8:  32px;
  --mm-s-10: 40px;
  --mm-s-12: 48px;
  --mm-s-14: 56px;
  --mm-s-16: 64px;
  --mm-s-20: 80px;
  --mm-s-24: 96px;

  /* ── LAYOUT ─────────────────────────────────────────────────────────── */
  --mm-container:   1600px;
  --mm-container-narrow: 880px;
  --mm-gutter:      clamp(16px, 3.8vw, 56px);
  --mm-section-y:   clamp(32px, 4vw, 64px);
  --mm-section-y-sm: clamp(24px, 2.6vw, 40px);

  /* Alturas del header fijo. #mainContent compensa con --mm-main-pt.
     Si se cambia una, se cambian las tres.                              */
  --mm-header-row1: 76px;
  --mm-header-row2: 56px;
  --mm-header-h:    132px;   /* row1 + row2, ≥1024px */
  --mm-header-h-mobile: 164px; /* 64 fila única + 56 buscador + 44 pill  */
  --mm-main-pt:     var(--mm-header-h);

  /* Ancho del panel lateral "Tu pedido". Lo consumen mm-cuenta.css (empuje
     del body y del header) y mm-catalogo.css (el modal de detalle se corre
     para no quedar debajo). Su gemelo en JS es CART_PANEL_W en
     js/ui/cart-preview.js: si cambia uno, cambian los dos.             */
  --mm-cart-panel-w: 380px;

  /* Grilla de producto */
  --mm-grid-gap:    clamp(12px, 1.8vw, 28px);
  --mm-card-min:    240px;
  --mm-card-pad:    16px;

  /* Controles */
  --mm-ctl-h:       40px;   /* desktop */
  --mm-ctl-h-touch: 44px;   /* móvil / botón de tarjeta */
  --mm-ctl-h-lg:    52px;
  --mm-ctl-pad-x:   20px;
  --mm-icon-btn:    40px;

  /* ── BREAKPOINTS DEL SISTEMA ──────────────────────────────────────────
     Fuente única. @media no admite var(): las consultas se escriben
     LITERALES con estos números y con el sufijo .98 en los max-width.
     Permitidos, y ninguno más:
       min-width:  480px | 768px | 1024px | 1280px
       max-width:  479.98px | 767.98px | 1023.98px | 1279.98px
     Los tokens existen para poder usarlos en clamp()/min()/calc() y para
     que el set esté escrito en un solo lugar.                            */
  --mm-bp-sm:  480px;
  --mm-bp-md:  768px;
  --mm-bp-lg: 1024px;
  --mm-bp-xl: 1280px;

  /* Área táctil mínima (WCAG 2.2 AA "Target Size (Minimum)" = 24px;
     acá se exige 44px, el mínimo cómodo de pulgar). */
  --mm-tap: 44px;


  /* ── FOCO (accesibilidad) ───────────────────────────────────────────── */
  --mm-focus-color: var(--mm-red-accent);
  --mm-focus-width: 2px;
  --mm-focus-offset: 2px;
  --mm-focus-ring: var(--mm-focus-width) solid var(--mm-focus-color);

  /* ── Z-INDEX (escala única del sitio público) ───────────────────────── */
  --mm-z-base:      1;
  --mm-z-rail:      5;    /* flechas de carrusel */
  --mm-z-sticky:    40;
  --mm-z-header:    50;
  --mm-z-dropdown:  60;   /* menú Categorías */
  --mm-z-drawer:    70;   /* panel "Tu pedido", menú móvil */
  --mm-z-modal:     80;   /* detalle, buscador, auth */
  --mm-z-popup:     90;   /* popup bloqueante de sucursal */
  --mm-z-toast:     100;
}

/* ══════════════════════════════════════════════════════════════════════
   SISTEMA DE MOVIMIENTO — reemplaza el bloque "── MOVIMIENTO ──" actual de
   web/css/tokens.css (y su @media prefers-reduced-motion final).
   Lo escribe el ORQUESTADOR. Ninguna de las 4 tareas edita tokens.css.
   Los nombres --mm-dur-fast/base/slow, --mm-ease, --mm-ease-out,
   --mm-tr-color y --mm-tr-motion YA se usan 100+ veces en las hojas mm-*:
   se conservan tal cual, solo se afinan valores y se suman los que faltan.
   ══════════════════════════════════════════════════════════════════════ */

:root {
  /* ── MOVIMIENTO · DURACIONES (cuatro, ni una más) ─────────────────────
     La duración la manda el RECORRIDO, no la importancia del elemento:
     poco recorrido = poco tiempo. Si un efecto necesita más de 440ms es
     que se está moviendo demasiado lejos.

     fast   140ms  RESPUESTA DIRECTA al dedo/mouse: hover, foco, cambio de
                   color, :active. Por debajo de ~100ms se percibe como
                   corte seco; por encima de ~180ms el hover "arrastra".
     base   220ms  CONFIRMACIÓN de un clic y aperturas ancladas: pop del
                   contador, corazón de favorito, dropdown de categorías,
                   fundido de vista. Es el techo de lo que puede tardar en
                   responder algo que el usuario acaba de tocar.
     slow   360ms  RECORRIDO REAL: drawer "Tu pedido", modal, entrada de
                   tarjeta de producto. Hay distancia que recorrer y el ojo
                   necesita seguirla.
     enter  440ms  ÚNICO caso lento: entrada de una SECCIÓN entera al hacer
                   scroll. Una por sección, nunca en algo repetido.        */
  --mm-dur-fast:  140ms;
  --mm-dur-base:  220ms;
  --mm-dur-slow:  360ms;
  --mm-dur-enter: 440ms;

  /* ── MOVIMIENTO · CURVAS (cuatro, cada una dice una cosa) ─────────────
     ease      "cambio de estado": acelera y frena parejo. El caballo de
               batalla — hover, color, borde, cualquier ida y vuelta que
               empieza y termina dentro de la pantalla. Neutra a propósito:
               no comunica nada, solo suaviza.
     ease-out  "ENTRAR / LLEGAR": sale disparado y frena largo. El elemento
               parece llegar de afuera y asentarse. Reveals, apertura de
               drawer y modal, entrada de tarjetas. Con esta curva el 70%
               del recorrido pasa en el primer tercio del tiempo: se lee
               rápido aunque dure 440ms.
     ease-in   "SALIR / IRSE": arranca lento y se acelera hasta desaparecer.
               No hay nada que mirar al final, así que no se frena. Cerrar
               drawer, cerrar modal, quitar un chip de filtro. Cerrar SIEMPRE
               dura menos que abrir (usar --mm-dur-base al cerrar).
     ease-pop  "MIRÁ ACÁ": se pasa ~7% y vuelve. Es la única curva con
               sobrepaso y está reservada a confirmar que un clic hizo algo:
               contador del carrito, corazón de favorito. PROHIBIDA en
               elementos que se repiten (grilla) y en cualquier cosa que
               ocurra sin que el usuario haya tocado nada.                  */
  --mm-ease:      cubic-bezier(0.40, 0.00, 0.20, 1.00);
  --mm-ease-out:  cubic-bezier(0.16, 1.00, 0.30, 1.00);
  --mm-ease-in:   cubic-bezier(0.40, 0.00, 1.00, 1.00);
  --mm-ease-pop:  cubic-bezier(0.34, 1.40, 0.64, 1.00);

  /* ── MOVIMIENTO · DISTANCIAS Y ESCALAS ────────────────────────────────
     Tres distancias. El desplazamiento es una PISTA de dirección, no un
     viaje: 16px es el máximo del sitio.
       move-1  4px   empujoncito (nudge) y elevación de hover
       move-2  8px   algo que aparece anclado a su origen: dropdown,
                     tooltip, ítem dentro de un grupo escalonado
       move-3 16px   entrada de una sección completa
     lift/press/pop van en tokens para que reduced-motion los anule de una
     sola vez desde el @media de abajo.                                     */
  --mm-move-1:  4px;
  --mm-move-2:  8px;
  --mm-move-3: 16px;
  --mm-lift:   -2px;    /* translateY de hover en tarjeta/botón */
  --mm-press:  0.97;    /* scale de :active */
  --mm-pop:    1.06;    /* scale del pico de confirmación */

  /* ── MOVIMIENTO · ESCALONADO ──────────────────────────────────────────
     Regla del escalonado: el grupo entero termina en ≤ 6 pasos. El paso se
     aplica a los PRIMEROS elementos y se congela; del 7º en adelante todos
     comparten el delay del 6º. Así una grilla de 60 tarjetas termina igual
     de rápido que una de 6, y nadie espera 2 segundos por la última fila.
       stagger        60ms  grupos chicos y ordenados (3–6 tarjetas de valor)
       stagger-grid   35ms  grillas densas: más ítems visibles a la vez, el
                            paso tiene que ser más corto o se nota la fila
                            entrando en diagonal.                           */
  --mm-stagger:      60ms;
  --mm-stagger-grid: 35ms;

  /* ── MOVIMIENTO · TRANSICIONES COMPUESTAS (ya en uso) ─────────────────
     tr-motion incluye box-shadow a propósito: solo se dispara en el hover
     de UN elemento a la vez. Nunca meter box-shadow en una entrada de
     grupo, ahí sí repinta cientos de cajas.                                */
  --mm-tr-color:  color var(--mm-dur-fast) var(--mm-ease),
                  background-color var(--mm-dur-fast) var(--mm-ease),
                  border-color var(--mm-dur-fast) var(--mm-ease);
  --mm-tr-motion: transform var(--mm-dur-base) var(--mm-ease),
                  box-shadow var(--mm-dur-base) var(--mm-ease);
}

/* ══════════════════════════════════════════════════════════════════════
   PRIMITIVAS COMPARTIDAS
   Van al final de tokens.css. Se cargan DESPUÉS de futuristic.css (línea
   120 del index vs. 126) y ANTES de todas las mm-*, así que pisan el
   .reveal viejo y cualquier hoja mm-* puede seguir afinándolas.
   ══════════════════════════════════════════════════════════════════════ */

/* CONTRATO CSS ↔ JS (lo implementa js/ui/animations.js, tarea T4):
     html.mm-motion       → el motor de movimiento está vivo
     html.mm-scrolled     → la página está desplazada más de ~24px
     .reveal / .reveal-group + .is-visible → entró en pantalla
   Si animations.js no llega a ejecutarse, el failsafe de 3s deja TODO
   visible: ningún efecto puede dejar la página en blanco. */

.reveal {
  opacity: 0;
  transform: translate3d(0, var(--mm-move-3), 0);
  transition: opacity var(--mm-dur-enter) var(--mm-ease-out),
              transform var(--mm-dur-enter) var(--mm-ease-out);
  will-change: auto;                    /* anula el will-change permanente
                                           de futuristic.css:476 */
  animation: mm-reveal-failsafe 1ms linear 3s forwards;
}
.reveal.is-visible { opacity: 1; transform: none; animation: none; }

.reveal-group > * {
  opacity: 0;
  transform: translate3d(0, var(--mm-move-2), 0);
  transition: opacity var(--mm-dur-slow) var(--mm-ease-out),
              transform var(--mm-dur-slow) var(--mm-ease-out);
  animation: mm-reveal-failsafe 1ms linear 3s forwards;
}
.reveal-group.is-visible > * { opacity: 1; transform: none; animation: none; }
.reveal-group.is-visible > *:nth-child(1) { transition-delay: 0ms; }
.reveal-group.is-visible > *:nth-child(2) { transition-delay: calc(1 * var(--mm-stagger)); }
.reveal-group.is-visible > *:nth-child(3) { transition-delay: calc(2 * var(--mm-stagger)); }
.reveal-group.is-visible > *:nth-child(4) { transition-delay: calc(3 * var(--mm-stagger)); }
.reveal-group.is-visible > *:nth-child(5) { transition-delay: calc(4 * var(--mm-stagger)); }
.reveal-group.is-visible > *:nth-child(n+6) { transition-delay: calc(5 * var(--mm-stagger)); }

/* Con el motor vivo, el failsafe se desactiva y manda el observer. */
/* failsafe SIEMPRE armado: ni el motor vivo lo desarma.
   Si el observer no llega a marcar un elemento, el keyframe lo hace visible
   igual. Un efecto no puede dejar contenido oculto. */
html.jamas-mm-motion .reveal,
html.mm-motion .reveal-group > * { animation: none; }

@keyframes mm-reveal-failsafe { to { opacity: 1; transform: none; } }

/* Sin JS no hay animación posible: mostrar el contenido y punto. */
@media (scripting: none) {
  .reveal, .reveal-group > * { opacity: 1; transform: none; animation: none; }
}

/* ── Keyframes compartidos ────────────────────────────────────────────
   Los cuatro únicos que hacen falta para lo nuevo. Todos tocan SOLO
   opacity y transform. Los de las hojas congeladas (cardEnter,
   skeletonShine, noResultsBob…) no se usan: animan background-position o
   corren infinitos. El shimmer bueno es .mm-skel de mm-base.css, que
   desplaza un pseudo-elemento con translateX. */
@keyframes mm-in-up    { from { opacity: 0; transform: translate3d(0, var(--mm-move-2), 0); } }
@keyframes mm-in-scale { from { opacity: 0; transform: scale(0.97); } }
@keyframes mm-pop      { 50% { transform: scale(var(--mm-pop)); } }
@keyframes mm-nudge    { 50% { transform: translate3d(0, calc(var(--mm-move-1) * -1), 0); } }

/* ── Utilidades one-shot (las pone y las saca el JS en animationend) ── */
.mm-anim-pop   { animation: mm-pop   var(--mm-dur-base) var(--mm-ease-pop); }
.mm-anim-nudge { animation: mm-nudge var(--mm-dur-base) var(--mm-ease); }
.mm-anim-in-up { animation: mm-in-up var(--mm-dur-slow) var(--mm-ease-out) both; }

/* ══════════════════════════════════════════════════════════════════════
   MOVIMIENTO REDUCIDO — un solo interruptor
   Con las duraciones en 0, las distancias en 0 y las escalas en 1, todo lo
   que esté construido sobre los tokens queda quieto sin escribir una regla
   por efecto. Lo de abajo cubre lo que no pasa por tokens.
   ══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  :root {
    --mm-dur-fast: 0ms;
    --mm-dur-base: 0ms;
    --mm-dur-slow: 0ms;
    --mm-dur-enter: 0ms;
    --mm-move-1: 0px;
    --mm-move-2: 0px;
    --mm-move-3: 0px;
    --mm-lift: 0px;
    --mm-press: 1;
    --mm-pop: 1;
    --mm-stagger: 0ms;
    --mm-stagger-grid: 0ms;
  }
  .reveal,
  .reveal-group > * {
    opacity: 1;
    transform: none;
    animation: none;
    transition: none;
  }
  .mm-anim-pop,
  .mm-anim-nudge,
  .mm-anim-in-up { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════
   PRINCIPIOS — obligatorios para las 4 tareas
   ══════════════════════════════════════════════════════════════════════

   1. QUÉ SE ANIMA. Solo `transform` y `opacity`. Excepción única:
      `box-shadow` y `border-color`/`color`/`background-color` en el hover o
      el foco de UN elemento a la vez (nunca en entradas de grupo, nunca en
      algo que corra en bucle). PROHIBIDO animar width, height, top, left,
      margin, padding, filter, backdrop-filter y background-position.

   2. CUÁNTO DURA. Respuesta a un clic o a un hover: 140–220ms, y empieza en
      el mismo frame del evento. Entrada en pantalla: 360ms una tarjeta,
      440ms una sección. Cerrar siempre más rápido que abrir (base vs slow).
      Nada supera los 440ms. Nada se repite en bucle: no hay ni un solo
      `infinite` fuera del skeleton de carga, que existe justamente para
      decir "seguí esperando".

   3. CÓMO SE ESCALONA UN GRUPO. Paso corto y tope duro: 6 pasos como
      máximo (300ms con --mm-stagger, 175ms con --mm-stagger-grid). Del
      elemento 7 en adelante, todos con el delay del 6º. Nunca calcular el
      delay con el índice real del ítem: en una grilla de 300 tarjetas eso
      son 18 segundos de espera y una página que parece rota.

   4. UNA ANIMACIÓN POR GESTO. Si al agregar al carrito ya salta el
      contador, el botón no necesita además girar, ni la tarjeta rebotar.
      Cada acción se confirma UNA vez, en el lugar donde el usuario está
      mirando (el elemento que tocó) o donde queremos que mire (el contador).

   5. SIN SALTOS. Ningún efecto cambia el tamaño de la caja ni el alto del
      header (mover --mm-header-h desplaza #mainContent = CLS). El espacio
      se reserva antes: aspect-ratio en imágenes y skeletons con la misma
      caja que la tarjeta real. Los desplazamientos siempre en translate3d,
      nunca en top/left/margin.

   6. will-change. Solo durante la interacción y se saca al terminar. No hay
      un solo `will-change` permanente en el CSS nuevo (el de
      futuristic.css:476 queda anulado arriba).

   7. FOCO. El anillo de foco aparece INSTANTÁNEO (0ms, sin transición): es
      información de dónde estoy, no decoración. Siempre `:focus-visible`
      con --mm-focus-ring y --mm-focus-offset.

   8. HOVER SOLO CON MOUSE. Todo efecto de hover va dentro de
      `@media (hover: hover) and (pointer: fine)`; en táctil el hover se
      queda pegado después del tap.

   9. CONTENIDO INYECTADO. El IntersectionObserver de animations.js consulta
      el DOM UNA sola vez, al arrancar. Lo que pinta el JS después (tarjetas
      de la grilla, filas del home, resultados del buscador) NUNCA puede
      depender de .reveal: quedaría en opacity 0 para siempre. Eso se anima
      con `animation:` CSS pura, que se dispara sola al insertarse el nodo.

  10. LO QUE NO SE HACE. Parallax, gradientes de fondo animados, brillos
      permanentes, confeti, rebotes infinitos, entradas de 800ms, y
      cualquier efecto que el usuario vea más de dos veces por sesión sin
      haberlo pedido con un clic. */

