/* ============================================================================
   EFECTOS.CSS — Capa de efectos a pedido (Sistema Web HKR)
   ----------------------------------------------------------------------------
   - Todo vive bajo `.fx` (clase en <html>). Sin esa clase, este archivo NO
     afecta nada -> la base queda intacta y los efectos son 100% removibles.
   - Estado base = estado final: el contenido se ve completo aunque el efecto
     no llegue a dispararse (JS off, navegador viejo, reduced-motion).
   - Tokens: usa --ease* / --brand* / --accent / --radius de tokens.css y
     bloques.css. No hay valores de color hardcodeados.
   - Niveles: [1] sutil (solo CSS) · [2] scroll (coopera con efectos.js+vendor)
     · [3] cinematic (contenedores; el render lo hace efectos.js).
   ============================================================================ */

/* Easing de respaldo por si el sitio no los define (bloques.css ya los trae) */
.fx{
  --fx-ease: var(--ease-out, cubic-bezier(.23,1,.32,1));
  --fx-ease-soft: var(--ease, cubic-bezier(.22,1,.36,1));
  --fx-dur: .7s;
}

/* ========================= NIVEL 1 — SUTIL (solo CSS) ====================== */

/* reveal-* : aparecen al entrar en viewport. efectos.js añade .in.
   Solo se ocultan si hay js+fx; sin JS todo queda visible. */
.fx [data-fx^="reveal"]{will-change:opacity,transform}
.fx [data-fx="reveal-fade"]{opacity:0;transition:opacity var(--fx-dur) var(--fx-ease)}
.fx [data-fx="reveal-up"]{opacity:0;transform:translateY(26px);
  transition:opacity var(--fx-dur) var(--fx-ease),transform var(--fx-dur) var(--fx-ease)}
.fx [data-fx="reveal-clip"]{clip-path:inset(0 0 100% 0);
  transition:clip-path .9s var(--fx-ease)}
.fx [data-fx="reveal-blur"]{opacity:0;filter:blur(10px);transform:translateY(14px);
  transition:opacity var(--fx-dur) var(--fx-ease),filter var(--fx-dur) var(--fx-ease),transform var(--fx-dur) var(--fx-ease)}
.fx [data-fx^="reveal"].in{opacity:1;transform:none;filter:none;clip-path:inset(0 0 0 0)}

/* stagger : hijos directos entran escalonados al recibir .in en el padre */
.fx [data-fx="stagger"]>*{opacity:0;transform:translateY(18px);
  transition:opacity .55s var(--fx-ease),transform .55s var(--fx-ease)}
.fx [data-fx="stagger"].in>*{opacity:1;transform:none}
.fx [data-fx="stagger"].in>*:nth-child(1){transition-delay:0ms}
.fx [data-fx="stagger"].in>*:nth-child(2){transition-delay:60ms}
.fx [data-fx="stagger"].in>*:nth-child(3){transition-delay:120ms}
.fx [data-fx="stagger"].in>*:nth-child(4){transition-delay:180ms}
.fx [data-fx="stagger"].in>*:nth-child(5){transition-delay:240ms}
.fx [data-fx="stagger"].in>*:nth-child(6){transition-delay:300ms}
.fx [data-fx="stagger"].in>*:nth-child(7){transition-delay:360ms}
.fx [data-fx="stagger"].in>*:nth-child(8){transition-delay:420ms}

/* text-lines / text-words : revelado de titular. efectos.js parte el texto en
   <span class="fx-line"><span class="fx-inner">…</span></span>. Sin JS = texto plano. */
.fx [data-fx^="text-"] .fx-line{display:block;overflow:hidden}
.fx [data-fx^="text-"] .fx-word{display:inline-block;overflow:hidden;vertical-align:top}
.fx [data-fx^="text-"] .fx-inner{display:inline-block;transform:translateY(110%);
  transition:transform .7s var(--fx-ease)}
.fx [data-fx^="text-"].in .fx-inner{transform:none}
.fx [data-fx="text-words"].in .fx-word:nth-child(n) .fx-inner{transition-delay:calc(var(--i,0)*55ms)}

/* count-up : el número final SIEMPRE está en el HTML (fallback). efectos.js anima. */
.fx [data-fx="count-up"]{font-variant-numeric:tabular-nums}

/* hover-lift / img-zoom / link-underline : micro-interacciones (puro hover) */
@media (hover:hover){
  .fx [data-fx="hover-lift"]{transition:transform .25s var(--fx-ease),box-shadow .25s}
  .fx [data-fx="hover-lift"]:hover{transform:translateY(-5px);box-shadow:var(--shadow)}
  .fx [data-fx="img-zoom"]{overflow:hidden}
  .fx [data-fx="img-zoom"] img{transition:transform .6s var(--fx-ease)}
  .fx [data-fx="img-zoom"]:hover img{transform:scale(1.06)}
  .fx [data-fx="link-underline"]{background-image:linear-gradient(currentColor,currentColor);
    background-size:0% 1.5px;background-position:0 100%;background-repeat:no-repeat;
    transition:background-size .3s var(--fx-ease)}
  .fx [data-fx="link-underline"]:hover{background-size:100% 1.5px}
}

/* shimmer : brillo sutil recorriendo un CTA premium */
.fx [data-fx="shimmer"]{position:relative;overflow:hidden;isolation:isolate}
.fx [data-fx="shimmer"]::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(105deg,transparent 40%,color-mix(in oklab,var(--accent-ink,#fff) 35%,transparent) 50%,transparent 60%);
  transform:translateX(-120%);animation:fxShimmer 4.5s var(--fx-ease) infinite}
@keyframes fxShimmer{0%,60%{transform:translateX(-120%)}100%{transform:translateX(120%)}}

/* sticky-fade : transición de sección con scroll-driven NATIVO (donde haya soporte) */
@supports (animation-timeline: view()){
  .fx [data-fx="sticky-fade"]{
    animation:fxStickyFade linear both;animation-timeline:view();animation-range:entry 0% entry 60%}
  @keyframes fxStickyFade{from{opacity:0;transform:translateY(30px)}to{opacity:1;transform:none}}
}

/* ===================== NIVEL 2 — SCROLL (coopera con JS) =================== */

/* parallax : efectos.js mueve el elemento por transform. Reserva will-change. */
.fx [data-fx="parallax"],.fx [data-fx="parallax-mouse"]{will-change:transform}

/* progress : barra de progreso de lectura. Nativa si hay soporte; si no, efectos.js. */
.fx [data-fx="progress"]{position:fixed;top:0;left:0;height:3px;width:100%;transform-origin:0 50%;
  transform:scaleX(0);background:var(--accent,currentColor);z-index:80}
@supports (animation-timeline: scroll()){
  .fx [data-fx="progress"]{animation:fxProgress linear both;animation-timeline:scroll(root)}
  @keyframes fxProgress{to{transform:scaleX(1)}}
}

/* pin / h-scroll : contenedores. La mecánica la maneja GSAP/ScrollTrigger en efectos.js.
   Estado base sin JS: scroll vertical normal (no rompe nada). */
.fx [data-fx="h-scroll"]{overflow:hidden}
.fx [data-fx="h-scroll"]>.fx-track{display:flex;gap:clamp(16px,3vw,40px);will-change:transform}

/* seq-frames : canvas "tipo Apple". El contenedor reserva alto; el canvas se fija. */
.fx [data-fx="seq-frames"]{position:relative}
.fx [data-fx="seq-frames"] canvas,.fx [data-fx="seq-frames"] img.fx-seq-fallback{
  position:sticky;top:0;width:100%;height:100svh;object-fit:cover;display:block}
.fx [data-fx="seq-frames"] canvas{display:none}          /* se muestra al precargar */
.fx.fx-seq-ready [data-fx="seq-frames"] canvas{display:block}
.fx.fx-seq-ready [data-fx="seq-frames"] img.fx-seq-fallback{display:none}

/* ===================== NIVEL 3 — CINEMATIC (WebGL/3D) ===================== */

/* Contenedores 3D/Lottie: reservan dimensiones (CLS=0) y muestran fallback
   estático hasta que efectos.js monta el render. Sin WebGL/JS = se ve la imagen. */
.fx [data-fx="hero-3d"],.fx [data-fx="webgl-bg"],.fx [data-fx="particles-morph"],
.fx [data-fx="camera-fly"],.fx [data-fx="model-scroll"]{position:relative;overflow:hidden;isolation:isolate}
.fx [data-fx$="-3d"] canvas,.fx [data-fx="webgl-bg"] canvas,
.fx [data-fx="particles-morph"] canvas,.fx [data-fx="camera-fly"] canvas{
  position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none;z-index:0}
/* el contenido directo de un fondo WebGL queda por encima del canvas */
.fx [data-fx="webgl-bg"]>*:not(canvas){position:relative;z-index:1}
.fx [data-fx-fallback]{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.fx.fx-3d-ready [data-fx-fallback]{opacity:0;transition:opacity .6s var(--fx-ease);pointer-events:none}

/* loader mínimo para 3D pesado (cinematic) */
.fx [data-fx-loader]{position:absolute;inset:0;display:grid;place-items:center;z-index:2;
  background:var(--surface,#fff);color:var(--ink-faint,#888);font:500 .85rem/1 var(--font-body,system-ui)}
.fx.fx-3d-ready [data-fx-loader]{display:none}

/* lottie : el contenedor reserva caja; lottie-web pinta dentro. */
.fx [data-fx="lottie"]{display:block;line-height:0}
.fx [data-fx="lottie"]>svg,.fx [data-fx="lottie"]>canvas{width:100%;height:100%}

/* video : hero de video self-host. Cubre el contenedor; el poster es el LCP (estado base si no reproduce).
   El contenido (badges, cards) va por encima. Degrada a poster con JS off / reduced-motion. */
.fx [data-fx="video"]{position:relative;overflow:hidden;isolation:isolate}
.fx [data-fx="video"] video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;z-index:0;background:var(--ink,#0b1c33)}
/* overlays (caption, etc.): solo elevamos z-index; NO forzamos position (romperia un overlay
   posicionado en absolute, p.ej. la caption con bottom:14px -> caia al flujo y se cortaba arriba). */
.fx [data-fx="video"]>*:not(video){z-index:2}

/* globe : globo cobe. efectos.js crea <canvas class="fx-globe-canvas"> centrado.
   Reserva caja (CLS=0); el [data-fx-fallback] estático se ve hasta montar (fx-3d-ready lo desvanece). */
.fx [data-fx="globe"]{position:relative;overflow:hidden;isolation:isolate}
.fx [data-fx="globe"] canvas.fx-globe-canvas{position:absolute;left:50%;top:50%;
  transform:translate(-50%,-50%);max-width:100%;height:auto;display:block;pointer-events:none;z-index:1;contain:layout paint}
.fx [data-fx="globe"]>*:not(canvas){z-index:2}

/* pin/scrub: barra de avance de la escena fijada (la mecánica la mueve efectos.js) */
.fx [data-fx="pin"] [data-fx-progress]{transform-origin:0 50%;will-change:transform}

/* ============================ ACCESIBILIDAD ============================== */
/* Anula TODO movimiento si el usuario pide menos animación. Contenido visible. */
@media (prefers-reduced-motion: reduce){
  /* LEY REDUCED-MOTION (regla #2): se ANULA el movimiento. efectos.js ya NO inicializa animaciones
     bajo esta preferencia (reveals visibles sin animar, count-up al valor final, video->póster, 3D
     off). Este bloque es la RED DE SEGURIDAD sin JS: garantiza que el contenido quede completo y
     visible en su estado final aunque el driver no corra, y apaga el brillo infinito. */
  .fx [data-fx^="reveal"]:not(.in),
  .fx [data-fx="stagger"]:not(.in)>*,
  .fx [data-fx^="text-"]:not(.in) .fx-inner{opacity:1!important;transform:none!important;
    filter:none!important;clip-path:none!important}
  .fx [data-fx="shimmer"]::after{animation:none!important}
}
