/* ===========================================================================
   FIRMA DE MOVIMIENTO — xingyun-international · "estiba"
   ---------------------------------------------------------------------------
   El sitio se mueve como se estiba un contenedor: cada cosa entra por su eje de
   carga, cae en su hueco y queda trabada. La carga no rebota; esto tampoco.

   Todo vive bajo .fx (sin la clase, no existe) y consume los motion tokens de
   más abajo. Estado base = estado final: sin JS o con prefers-reduced-motion la
   banda aparece llena y las tarjetas en su sitio.
   Partitura: _motion-brief.md
   =========================================================================== */

.fx{
  --fx-dur-1: 110ms;   /* micro: el trabado */
  --fx-dur-2: 300ms;   /* estándar: reveal, stow */
  --fx-dur-3: 520ms;   /* amplio: tramo de estiba */
  --fx-ease-out:      cubic-bezier(.23,1,.32,1);
  --fx-ease-out-expo: cubic-bezier(.16,1,.3,1);
  --fx-stagger: 58ms;
}

/* ---------------------------------------------------- plan de estiba (firma) */

/* con la capa activa, el tramo entra vacío y se llena de izquierda a derecha */
/* El vaciado es INSTANTANEO y el llenado es lo que se anima. Si la transicion vive en
   `.armado`, el tramo arranca lleno y se VACIA animando antes de volver a llenarse
   (medido: 1 -> 0,76 -> 0,43 -> 0,18 -> 1), que no es la firma, es un parpadeo. */
.fx [data-fx="estiba"].armado .estiba__t::before{transform:scaleX(0)}
.fx [data-fx="estiba"].armado.in .estiba__t::before{transform:scaleX(1);
  transition:transform calc(var(--fx-dur-3) * .8) var(--fx-ease-out-expo);
  transition-delay:calc(var(--fx-stagger) * .7 * var(--i, 0))}
/* el texto del tramo aparece cuando su hueco ya está cargado */
.fx [data-fx="estiba"].armado .estiba__t b,
.fx [data-fx="estiba"].armado .estiba__t i{opacity:.001}
.fx [data-fx="estiba"].armado.in .estiba__t b,
.fx [data-fx="estiba"].armado.in .estiba__t i{opacity:1;
  transition:opacity var(--fx-dur-2) var(--fx-ease-out);
  transition-delay:calc(var(--fx-stagger) * .7 * var(--i, 0))}

/* ------------------------------------------------------- asiento en el hueco */
/* Los hijos entran por el eje de carga y traban. El orden lo fija --i, que el
   módulo escribe según el DOM o según el peso declarado (data-fx-stow="peso").

   OJO con el orden de las clases: la grilla arranca VISIBLE y solo se "arma"
   (queda a la espera de entrar) si el módulo comprobó que está bajo el pliegue.
   Al revés —ocultar por defecto— una grilla alta como la del catálogo puede no
   llegar nunca al umbral del observador y el shop entero se ve vacío. Pasó. */
.fx [data-fx="stow"].armado > *{opacity:.001;transform:translate3d(0,-18px,0)}
.fx [data-fx="stow"].armado.in > *{animation:fxStow var(--fx-dur-2) var(--fx-ease-out) both;
  animation-delay:calc(var(--fx-stagger) * var(--i, 0))}
@keyframes fxStow{
  0%  {opacity:0;transform:translate3d(0,-18px,0)}
  76% {opacity:1;transform:translate3d(0,1px,0)}    /* apoya */
  100%{opacity:1;transform:translate3d(0,0,0)}      /* traba */
}
@media(max-width:900px){
  .fx [data-fx="stow"].armado > *{transform:translate3d(0,-10px,0)}
  .fx [data-fx="stow"]{--fx-stagger:40ms}
  @keyframes fxStow{
    0%{opacity:0;transform:translate3d(0,-10px,0)}
    78%{opacity:1;transform:translate3d(0,1px,0)}
    100%{opacity:1;transform:translate3d(0,0,0)}
  }
}

/* --------------------------------------------------------------- degradación */
/* LEY: prefers-reduced-motion anula el movimiento y deja todo en su estado final. */
@media (prefers-reduced-motion: reduce){
  .fx [data-fx="estiba"] .estiba__t::before{transform:none !important;transition:none !important}
  .fx [data-fx="estiba"] .estiba__t b,
  .fx [data-fx="estiba"] .estiba__t i{opacity:1 !important;transition:none !important}
  .fx [data-fx="stow"].armado > *{opacity:1 !important;transform:none !important;animation:none !important}
}
/* sin JS no hay clase .js: la capa no oculta nada */
html:not(.js) .fx [data-fx="stow"] > *{opacity:1;transform:none}
/* y la banda solo se anima si el módulo la armó (misma razón que arriba) */
.fx [data-fx="estiba"]:not(.armado) .estiba__t::before{transform:none}
.fx [data-fx="estiba"]:not(.armado) .estiba__t b,
.fx [data-fx="estiba"]:not(.armado) .estiba__t i{opacity:1}

/* --------------------------------------------- cajas que aceptan el parallax */
/* El parallax mueve el elemento: si la caja calza justo, el desplazamiento
   descubre el borde. Se le da aire por dentro y se recorta afuera.
   El hero del home ya no lleva foto (es el plan de carga real), así que ya no
   necesita esta regla: la dejan las páginas que sí abren con foto. */
.fx .hero__fondo[data-fx="parallax"]{inset:-9% 0}
.fx .subhero--foto .subhero__bg[data-fx="parallax"]{inset:-9% 0}
@media(max-width:900px){
  /* en móvil el parallax se apaga (el driver ya lo hace en gama baja) */
  .fx .hero__fondo[data-fx="parallax"],
  .fx .subhero--foto .subhero__bg[data-fx="parallax"]{inset:0;transform:none !important}
  .fx .perfil__media img[data-fx="parallax"]{aspect-ratio:4/3;transform:none !important}
}

/* ---------------------------------------------- alineacion con los motion tokens */
/* La tabla (c) de la partitura declara 300/520 ms y el driver compartido entraba con
   700 ms (reveal) y 900 ms (clip). Un token que el sitio no usa es ficcion. */
.fx [data-fx^="reveal"]{--fx-dur:var(--fx-dur-2)}
.fx [data-fx="reveal-clip"]{transition-duration:var(--fx-dur-3)}
/* El stagger entraba DESDE ABAJO, en contra del eje de carga de toda la firma, con
   550ms y pasos de 60ms ajenos a los tokens. Ahora habla el idioma de la casa. */
.fx [data-fx="stagger"] > *{transform:translate3d(0,-18px,0);
  transition-duration:var(--fx-dur-2);transition-timing-function:var(--fx-ease-out)}
.fx [data-fx="stagger"].in > *{transform:translate3d(0,0,0)}
/* M3: el aire del parallax lo da la caja, no un aspect-ratio que la imagen ya fija.
   Mismo patron que en el subhero, donde el hueco medido es 0. */
.fx .perfil__media{aspect-ratio:4/3;overflow:hidden;border-radius:var(--radio)}
.fx .perfil__media img[data-fx="parallax"]{position:relative;top:-6%;height:112%;
  width:100%;object-fit:cover;aspect-ratio:auto;border-radius:0;box-shadow:none}

/* L5 — mientras un contenedor esta armado y todavia no entro, sus hijos estan en
   opacity:.001 pero siguen siendo focusables: un usuario de teclado podia poner el
   foco en una tarjeta invisible. `visibility` lo saca del orden de foco y vuelve en .in. */
.fx [data-fx="stow"].armado:not(.in) > *{visibility:hidden}
.fx [data-fx="stow"].armado.in > *{visibility:visible}
