/* ===========================================================
   PI Creative House — capa de movimiento
   ------------------------------------------------------------
   NO es invento. Sale de medir dos referencias del rubro
   (clay.global y refokus.com) el 2026-09-22 leyendo el estilo
   computado de cada elemento de sus paginas en vivo.

   HALLAZGO CENTRAL: ambos estudios, con stacks distintos
   (Clay = Next.js; Refokus = Webflow + GSAP 3.12.5 + ScrollTrigger),
   convergen en la MISMA curva firma:

        cubic-bezier(0.16, 1, 0.3, 1)   -- expo-out

   En Refokus es su transicion dominante: 'transform 0.75s' con esa
   curva aparece 73 veces. En Clay gobierna casi todo el sitio.

   SEGUNDO HALLAZGO: las duraciones son LARGAS. Clay corre a
   0.7 / 0.9 / 1.15 / 1.2 / 1.3 / 1.55 y hasta 2.5 s. Nadie del
   rubro anima a 300 ms. Lo premium se lee en la lentitud.

   TERCER HALLAZGO: escalonan por propiedad dentro de una misma
   transicion. Clay: 'opacity, transform 0.9s, 1.2s'. La opacidad
   resuelve ANTES que el desplazamiento, asi el elemento ya se lee
   mientras todavia termina de acomodarse. Ese desfase es el detalle
   que separa el movimiento caro del generico.

   CUARTO: contraste deliberado de escalas. Lo grande va lento
   (0.7-1.3 s); la micro-interaccion de hover va a 0.15 s. Clay
   usa 'color 0.15s' en 46 elementos.

   QUINTO: revelan con clip-path, no solo con opacity
   (Clay: 37 elementos con clip-path activo).
   =========================================================== */

:root {
  /* ---- Curvas ---- */
  --ease-expo:      cubic-bezier(0.16, 1, 0.3, 1);      /* firma compartida Clay + Refokus */
  --ease-expo-alt:  cubic-bezier(0.19, 1, 0.22, 1);     /* Clay, revelados largos */
  --ease-quint:     cubic-bezier(0.86, 0, 0.07, 1);     /* Refokus, curva en S para alturas */
  --ease-cubic:     cubic-bezier(0.33, 1, 0.68, 1);     /* Refokus, salidas cortas */
  --ease-quart:     cubic-bezier(0.165, 0.84, 0.44, 1); /* Refokus, desplazamientos medios */

  /* ---- Duraciones ---- */
  --t-micro:  150ms;   /* color/borde en hover */
  --t-corta:  350ms;
  --t-media:  700ms;
  --t-larga:  900ms;
  --t-xl:    1300ms;
  --t-atmos: 2500ms;   /* capas de atmosfera, fondos */

  /* ---- Desfase por propiedad (el truco de Clay) ---- */
  --t-op: 900ms;       /* la opacidad llega primero */
  --t-tr: 1200ms;      /* el desplazamiento remata */

  /* ---- Desfase en hover (mismo principio, otra escala) ---- */
  --t-hover-op: 350ms;
  --t-hover-tr: 1300ms;

  /* ---- Escalonado entre hermanos ---- */
  --paso: 80ms;

  /* ---- Distancias ---- */
  --sube: 28px;        /* recorrido de entrada; discreto, nunca 60px */
  --sube-corto: 14px;
}

/* ===========================================================
   Revelados. Reglas de la casa:
   1. El contenido es visible por defecto. Solo se oculta cuando
      <html> lleva .con-reveal, que el JS pone tras confirmar que
      puede devolverlo. Ver [gotchas] en la memoria del proyecto.
   2. Nada de animar propiedades que provoquen layout.
      Solo transform, opacity y clip-path.
   =========================================================== */

.con-reveal [data-rev] {
  opacity: 0;
  transform: translate3d(0, var(--sube), 0);
  transition:
    opacity   var(--t-op) var(--ease-expo),
    transform var(--t-tr) var(--ease-expo);
  transition-delay: var(--retardo, 0ms);
}
.con-reveal [data-rev].visible {
  opacity: 1;
  transform: none;
}

/* Revelado por mascara: la linea se descubre de abajo hacia arriba.
   Es lo que usan para titulares; pesa menos que mover cajas. */
/* OJO con los insets: un inset(0%) al final parece lo correcto pero
   RECORTA descendentes y tildes. Medido en serif con line-height < 1.2:
   se come la 'g' de "aguja" y la tilde de "Como". El estado final lleva
   margen negativo para que la letra respire; el inicial lo conserva en
   los lados para que ambos estados interpolen con la misma forma. */
.con-reveal [data-rev="mascara"] {
  opacity: 1;
  transform: none;
  clip-path: inset(105% -6% -32% -6%);
  transition: clip-path var(--t-xl) var(--ease-expo-alt);
  transition-delay: var(--retardo, 0ms);
}
.con-reveal [data-rev="mascara"].visible {
  clip-path: inset(-32% -6% -32% -6%);
}

/* Revelado LINEAL: recorta la CAJA, no el objetivo.
   Preferible a "mascara" cuando el HTML puede envolver el texto en
   un <span>: el elemento observado nunca se oculta, asi que no cae
   en el punto ciego del IntersectionObserver descrito en reveal.js.
   Resultado visual identico.
       <h1 data-rev="lineal"><span>Titular</span></h1>            */
.con-reveal [data-rev="lineal"] {
  opacity: 1;
  overflow: hidden;
  display: block;
  /* overflow:hidden sobre line-height < 1.1 recorta tildes y
     descendentes. La holgura va aqui y no en cada autor: dejarlo a
     criterio de quien lo use es una trampa que se repite. */
  padding-block: 0.16em 0.18em;
  margin-block: -0.16em -0.18em;
}
.con-reveal [data-rev="lineal"] > span {
  display: block;
  transform: translate3d(0, 115%, 0);
  transition: transform var(--t-xl) var(--ease-expo-alt);
  transition-delay: var(--retardo, 0ms);
}
.con-reveal [data-rev="lineal"].visible > span { transform: none; }

/* Variante EN LINEA: misma idea que 'lineal' pero sin romper el flujo,
   para revelar palabra por palabra. 'lineal' fuerza display:block y
   mandaria cada palabra a su propio renglon.
       <h1><b data-rev="lineal-inline"><span>Una</span></b> ...</h1>   */
.con-reveal [data-rev="lineal-inline"] {
  opacity: 1;
  display: inline-block;
  overflow: hidden;
  /* Compensa el recorte de descendentes que impone overflow:hidden. */
  padding-bottom: 0.18em;
  margin-bottom: -0.18em;
}
.con-reveal [data-rev="lineal-inline"] > span {
  display: inline-block;
  transform: translate3d(0, 115%, 0);
  transition: transform var(--t-xl) var(--ease-expo-alt);
  transition-delay: var(--retardo, 0ms);
}
.con-reveal [data-rev="lineal-inline"].visible > span { transform: none; }

/* Revelado CORTE: la opacidad queda fija en 1 y solo se anima la
   geometria de marca. Es el unico revelado apto para un heroe o un CTA,
   porque la EXISTENCIA del contenido nunca depende del observador: si
   nada dispara, el bloque ya estaba visible, solo sin su corte final.
   El elemento define --corte-ini y --corte-fin.
       <a data-rev="corte" style="--corte-fin:24px">                   */
.con-reveal [data-rev="corte"] {
  opacity: 1;
  transform: none;
  --corte-vivo: var(--corte-ini, 0px);
  clip-path: polygon(
    var(--corte-vivo) 0, 100% 0,
    100% calc(100% - var(--corte-vivo)),
    calc(100% - var(--corte-vivo)) 100%,
    0 100%, 0 var(--corte-vivo)
  );
  transition: clip-path var(--t-xl) var(--ease-expo);
  transition-delay: var(--retardo, 0ms);
}
.con-reveal [data-rev="corte"].visible {
  --corte-vivo: var(--corte-fin, 24px);
}

/* Revelado de linea/regla: crece desde el origen. */
.con-reveal [data-rev="regla"] {
  opacity: 1;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-xl) var(--ease-expo);
  transition-delay: var(--retardo, 0ms);
}
.con-reveal [data-rev="regla"].visible { transform: scaleX(1); }

/* ===========================================================
   Carrusel de clientes: desfile continuo de logos.
   El mecanismo vive aqui porque es movimiento; cada direccion
   viste las fichas a su manera.
   Nombre de keyframe propio para no chocar con el desfile de
   servicios de la direccion editorial.
   =========================================================== */
.desfile-clientes { overflow: hidden; }
.desfile-pista {
  display: flex;
  width: max-content;
  animation: desfile-cli 40s linear infinite;
}
.desfile-grupo { display: flex; align-items: center; }
@keyframes desfile-cli {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
/* Se detiene al pasar por encima, para poder leer un logo. */
.desfile-clientes:hover .desfile-pista { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  /* Doble freno: ademas del iteration-count de tokens.css, aqui la
     animacion se apaga del todo y las fichas se reparten en lineas. */
  .desfile-pista {
    animation: none !important;
    width: auto;
    flex-wrap: wrap;
    justify-content: center;
  }
  .desfile-grupo:last-child { display: none; }
}

/* Estado de rescate: si el observador nunca respondio, todo queda
   visible de golpe. Mejor sin animacion que sin contenido. */
/* El rescate tiene que alcanzar tambien al HIJO cuando la mascara vive
   dentro (patron 'cinta'). Si no, el padre queda visible y el texto no. */
.sin-anim [data-rev],
.sin-anim [data-rev] > span,
.sin-anim [data-rev] [data-mascara],
.sin-anim [data-rev] .cinta {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}

/* CUIDADO con clip-path en el rescate: hay dos usos distintos y anular
   los dos es un error. Un clip puede OCULTAR (la mascara de un titular)
   o puede DAR FORMA (el chaflan de marca en una celda). Anular el
   segundo salva el contenido destruyendo la identidad: en la direccion
   bento le borraba el chaflan a las 11 celdas.
   Por eso aqui solo se anula donde el clip oculta. */
.sin-anim [data-rev="mascara"],
.sin-anim [data-mascara],
.sin-anim .cinta {
  clip-path: none !important;
}

/* Sin movimiento: se respeta la preferencia del sistema. */
@media (prefers-reduced-motion: reduce) {
  .con-reveal [data-rev],
  .con-reveal [data-rev].visible,
  .con-reveal [data-rev] > span,
  .con-reveal [data-rev] [data-mascara],
  .con-reveal [data-rev] .cinta {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  /* Igual que en el rescate: solo se anula el clip que OCULTA. */
  .con-reveal [data-rev="mascara"],
  .con-reveal [data-mascara],
  .con-reveal .cinta {
    clip-path: none !important;
  }
}
