/* NYXIVO — Diseño Moderno · animaciones que en el original ejecutaba Framer Motion.
 * Aquí se replican como CSS keyframes para mantener fidelidad sin runtime JS.
 *
 * Convención: cada bloque indica qué componente React lo originaba y su transición.
 */

/* ============================================================
 * NAVBAR — entrance
 * <motion.nav initial={{y:-30,opacity:0}} animate={{y:0,opacity:1}}
 *   transition={{duration:1, ease:[0.16,1,0.3,1]}} />
 * ============================================================ */
@keyframes mod-nav-enter {
    from { transform: translateY(-30px); opacity: 0; }
    to   { transform: none;              opacity: 1; }
}
.mod-navbar {
    opacity: 0; /* matched to `from` so no flash during the pre-animation delay */
    animation: mod-nav-enter 1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

/* ============================================================
 * HAMBURGUESA — Menu ↔ X swap
 * AnimatePresence con rotate ±90deg, opacity 0↔1, duration 0.25s
 * ============================================================ */
.mod-burger-icon {
    display: inline-flex;
    transition: transform .25s ease, opacity .25s ease;
}
.mod-burger[data-open="true"]  .mod-burger-icon--menu { transform: rotate(-90deg); opacity: 0; pointer-events: none; }
.mod-burger[data-open="false"] .mod-burger-icon--x    { transform: rotate( 90deg); opacity: 0; pointer-events: none; }
.mod-burger[data-open="true"]  .mod-burger-icon--x    { transform: rotate(0);      opacity: 1; }
.mod-burger[data-open="false"] .mod-burger-icon--menu { transform: rotate(0);      opacity: 1; }
/* Stack the two icons inside the same button */
.mod-burger { position: relative; }
.mod-burger .mod-burger-icon { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.mod-burger > .mod-burger-spacer { visibility: hidden; }

/* ============================================================
 * MOBILE DRAWER — overlay + panel
 * Overlay: opacity 0→1, duration 0.3s
 * Panel:   x 100%→0, duration 0.5s, ease luxury
 * ============================================================ */
.mod-drawer-overlay {
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s ease;
}
.mod-drawer-overlay[data-open="true"] {
    opacity: 1;
    pointer-events: auto;
}
.mod-drawer-panel {
    transform: translateX(100%);
    transition: transform .5s cubic-bezier(0.16, 1, 0.3, 1);
}
.mod-drawer-panel[data-open="true"] {
    transform: translateX(0);
}

/* ============================================================
 * PARTICLE BURST — heart click
 * 9 partículas radiales, opacity 1→0, scale 0.5→1, distance ~32-40px
 * duration 0.8s, ease [0.16, 1, 0.3, 1], delay random 0-0.05s
 * Cada span recibe --tx, --ty, --sz, --dly inline desde JS.
 * ============================================================ */
@keyframes mod-particle {
    0%   { transform: translate(0,0) scale(.5); opacity: 1; }
    100% { transform: translate(var(--tx), var(--ty)) scale(1); opacity: 0; }
}
.mod-particle {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: calc(var(--sz) / -2);
    margin-top:  calc(var(--sz) / -2);
    width:  var(--sz);
    height: var(--sz);
    border-radius: 9999px;
    background: var(--color-brand-300, #93c5fd);
    box-shadow: 0 0 8px rgb(var(--brand-glow) / .9);
    animation: mod-particle .8s cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: var(--dly, 0s);
    pointer-events: none;
}

/* Press/scale on action buttons (replaces motion.button whileTap={{scale:0.92}}) */
.mod-action-btn:active { transform: scale(.92); }
.mod-action-btn { transition: transform .12s ease; }

/* ============================================================
 * BRANDMARK — metallic shimmer sweeping the logo silhouette
 * <motion.span x: ["-160%", "160%", "160%"] times:[0,0.18,1]
 *              duration: 6s, ease luxury, repeat infinite>
 * Mask is the logo itself so the sweep is clipped to the NX shape.
 * ============================================================ */
@keyframes mod-logo-shimmer {
    0%   { transform: translateX(-160%); }
    18%  { transform: translateX( 160%); }
    100% { transform: translateX( 160%); }
}
.mod-brandmark-shimmer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(110deg, transparent 30%, rgba(255,255,255,.85) 50%, transparent 70%);
    mix-blend-mode: overlay;
    -webkit-mask-image: var(--mod-logo-mask);
            mask-image: var(--mod-logo-mask);
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-position: center;
            mask-position: center;
    animation: mod-logo-shimmer 6s cubic-bezier(0.16, 1, 0.3, 1) infinite;
}
@media (prefers-reduced-motion: reduce) {
    .mod-brandmark-shimmer { animation: none !important; }
    /* Sin la animación de entrada, la navbar quedaría con su `opacity: 0`
       declarado (estado inicial del keyframe) → header invisible. Forzamos
       el estado final visible cuando el usuario pidió reducir motion. */
    .mod-navbar { animation: none !important; opacity: 1 !important; }
}

/* ============================================================
 * BACKGROUND BLOBS — orbital drift
 * Cuatro <Blob/> en Background.tsx con animate=[from, to, from] y duración
 * por bloque. Replicado con keyframes 0/50/100% por blob (mismo waypoint
 * de ida-vuelta).
 * ============================================================ */
.mod-blob {
    /* Common: posición vía left/top en keyframes; el blob se centra
       con translate(-50%,-50%) (mismo trick que el original). */
    transform: translate(-50%, -50%);
    pointer-events: none;
    will-change: left, top;
}

@keyframes mod-blob-1 {
    0%, 100% { left: 65%; top: 55%; }
    50%      { left: 75%; top: 45%; }
}
.mod-blob--1 {
    width: 900px; height: 900px;
    background-color: #3b82f6;
    opacity: .22;
    filter: blur(180px);
    left: 65%; top: 55%;
    animation: mod-blob-1 28s ease-in-out infinite;
}
@keyframes mod-blob-2 {
    0%, 100% { left: 85%; top: 50%; }
    50%      { left: 70%; top: 65%; }
}
.mod-blob--2 {
    width: 1100px; height: 1100px;
    background-color: #1e3a8a;
    opacity: .45;
    filter: blur(180px);
    left: 85%; top: 50%;
    animation: mod-blob-2 36s ease-in-out infinite;
}
@keyframes mod-blob-3 {
    0%, 100% { left: 20%; top: 70%; }
    50%      { left: 30%; top: 55%; }
}
.mod-blob--3 {
    width: 700px; height: 700px;
    background-color: #0e9899;
    opacity: .18;
    filter: blur(180px);
    left: 20%; top: 70%;
    animation: mod-blob-3 32s ease-in-out infinite;
}
@keyframes mod-blob-4 {
    0%, 100% { left: 10%; top: 20%; }
    50%      { left: 25%; top: 30%; }
}
.mod-blob--4 {
    width: 800px; height: 800px;
    background-color: #1d4ed8;
    opacity: .25;
    filter: blur(180px);
    left: 10%; top: 20%;
    animation: mod-blob-4 40s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
    .mod-blob { animation: none !important; }
    .mod-blob--1, .mod-blob--3, .mod-blob--4 { display: none; }
    /* Replica EXACTA del fallback de Background.tsx (useReducedMotion = true):
       <div className="absolute top-[45%] right-0 w-[1100px] h-[1000px]
                       bg-brand-800 blur-[200px] rounded-full opacity-40
                       mix-blend-plus-lighter" />
       Color brand-800 = #1e40af (NO #1e3a8a) y blur 200px (NO 180px). */
    .mod-blob--2 {
        left: auto !important;
        top: 45% !important;
        right: 0 !important;
        width: 1100px;
        height: 1000px;
        background-color: #1e40af !important;
        filter: blur(200px) !important;
        opacity: .4;
        transform: none !important;
    }
}

/* ============================================================
 * STARDUST — partículas flotantes
 * 28 spans con drift diagonal y fade in/out en loop.
 * Variables: --sz, --gl, --dx, --dy, --dur, --dly (vía PHP).
 * times: [0, 0.25, 0.75, 1] — opacity 0→.7→.7→0
 * ============================================================ */
@keyframes mod-stardust {
    0%   { opacity: 0;   transform: translate(0, 0); }
    25%  { opacity: .7;  transform: translate(calc(var(--dx) * 0.25), calc(var(--dy) * 0.25)); }
    75%  { opacity: .7;  transform: translate(calc(var(--dx) * 0.75), calc(var(--dy) * 0.75)); }
    100% { opacity: 0;   transform: translate(var(--dx), var(--dy)); }
}
.mod-stardust {
    position: absolute;
    width: var(--sz);
    height: var(--sz);
    border-radius: 9999px;
    background: #fff;
    box-shadow: 0 0 var(--gl) rgba(147,197,253,.6);
    filter: blur(.4px);
    animation: mod-stardust var(--dur) ease-in-out infinite;
    animation-delay: var(--dly);
    will-change: transform, opacity;
}
@media (prefers-reduced-motion: reduce) {
    .mod-stardust { display: none; }
}

/* ============================================================
 * HERO — title dissolve (line-by-line stagger) + price + buttons
 * lineVariants: opacity 0→1, y 24→0, blur 14→0px, letter-spacing 0.02→-0.04em
 * duration 1.2s, delay 0.05 + i*0.18s, ease luxury
 * ============================================================ */
/* Entrada del título: espejo dramático de la salida. El saliente sube y
 * se desintegra (y 0 → -16, blur 0 → 10); el nuevo "se re-materializa"
 * desde aún más arriba con más blur (y -28 → 0, blur 16 → 0) para que
 * la entrada se perciba con la misma fuerza visual que la salida. */
@keyframes mod-line-dissolve {
    from {
        opacity: 0;
        transform: translateY(-28px);
        filter: blur(16px);
        letter-spacing: 0.02em;
    }
    to {
        opacity: 1;
        transform: translateY(0);
        filter: blur(0);
        letter-spacing: -0.04em;
    }
}
/* Entrada de desc/precio/variantes: espejo de mod-fade-up-out con magnitud
 * aumentada para que se sienta como reverso visible de la salida
 * (saliente: y 0 → -10 + blur 8 / entrante: y -18 → 0 + blur 12). */
@keyframes mod-fade-up {
    from {
        opacity: 0;
        transform: translateY(-18px);
        filter: blur(12px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
        filter: blur(0);
    }
}
/* Buttons/badges: cargan una sola vez al primer slide, mantenemos el
 * "lift desde abajo" para diferenciar la entrada inicial del swap. */
@keyframes mod-fade-up-lite {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Hero slides: solo el activo se muestra; el resto se oculta.
 * Aplicamos las animaciones (entrada + stagger) re-disparándolas con la
 * técnica de "remontaje" (display:none → block hace que las animaciones
 * vuelvan a ejecutarse en el slide nuevo). */
.mod-hero-slide { display: none; }
.mod-hero-slide.is-active { display: block; }
/* Showcase slides: same toggle. Cada slide se posiciona absolute para
 * compartir el mismo espacio dentro del wrap (lo necesita <model-viewer>). */
.mod-showcase-slide {
    display: none;
    position: absolute;
    inset: 0;
    align-items: center;
    justify-content: center;
}
.mod-showcase-slide.is-active { display: flex; }
.mod-hero-line {
    display: block;
    animation: mod-line-dissolve 1.2s cubic-bezier(0.16, 1, 0.3, 1) both;
}
/* INITIAL_LOAD_DELAY = 0.4s (offset cinemático tras la cortina PageEnter) */
.mod-hero-line--1 { animation-delay: calc(0.4s + 0.05s + 0 * 0.18s); }
.mod-hero-line--2 { animation-delay: calc(0.4s + 0.05s + 1 * 0.18s); }
.mod-hero-line--model { animation-delay: calc(0.4s + 0.45s); animation-duration: 1.4s; }
.mod-hero-desc      { animation: mod-fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) both; animation-delay: calc(0.4s + 0.5s); }
.mod-hero-variants  { animation: mod-fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) both; animation-delay: calc(0.4s + 0.7s); }
.mod-hero-price     { animation: mod-fade-up 0.7s cubic-bezier(0.16, 1, 0.3, 1) both; animation-delay: calc(0.4s + 0.55s); }
.mod-hero-buttons   { animation: mod-fade-up-lite 1s cubic-bezier(0.16, 1, 0.3, 1) both; animation-delay: calc(0.4s + 1.05s); }
.mod-hero-badges    { animation: mod-fade-up-lite 1s cubic-bezier(0.16, 1, 0.3, 1) both; animation-delay: calc(0.4s + 1.2s); }

/* Cuando el primer slide ya cargó, los slides siguientes no necesitan el
 * offset inicial (INITIAL_LOAD_DELAY). Aplicamos clase .past-first para reducirlo a 0. */
.mod-hero[data-past-first="true"] .mod-hero-line--1     { animation-delay: 0.05s; }
.mod-hero[data-past-first="true"] .mod-hero-line--2     { animation-delay: 0.23s; }
.mod-hero[data-past-first="true"] .mod-hero-line--model { animation-delay: 0.45s; }
.mod-hero[data-past-first="true"] .mod-hero-desc        { animation-delay: 0.50s; }
.mod-hero[data-past-first="true"] .mod-hero-variants    { animation-delay: 0.70s; }
.mod-hero[data-past-first="true"] .mod-hero-price       { animation-delay: 0.55s; }
/* .mod-hero-buttons / .mod-hero-badges omitidos: están fuera del slide-div,
   solo animan en el primer page-load y luego permanecen visibles. */

/* Exit animations — replican Framer's AnimatePresence mode="wait":
 * el slide saliente anima fuera; cuando termina, el entrante anima dentro.
 * lineVariants.exit:  opacity:0, y:-16, blur:10px
 * fadeVariants.exit:  opacity:0, y:-10, blur:8px
 */
@keyframes mod-line-dissolve-out {
    from { opacity: 1; transform: translateY(0);     filter: blur(0); }
    to   { opacity: 0; transform: translateY(-16px); filter: blur(10px); }
}
@keyframes mod-fade-up-out {
    from { opacity: 1; transform: translateY(0);     filter: blur(0); }
    to   { opacity: 0; transform: translateY(-10px); filter: blur(8px); }
}

/* Durante la transición el slide saliente queda visible con is-leaving.
 * Exit de título = MISMA duración que entry (1.2s) + MISMO stagger (0.05/0.23/0.45s)
 * → replica exactamente lineVariants.exit con transition.duration=1.2s de Hero.tsx.
 * Última línea termina en 0.45 + 1.2 = 1.65s.
 * TRANSITION_OUT_MS en hero.js = 1650ms para que el nuevo slide entre justo después. */
.mod-hero-slide.is-leaving { display: block; }
.mod-hero-slide.is-leaving .mod-hero-line         { animation: mod-line-dissolve-out 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards; animation-delay: 0s; }
.mod-hero-slide.is-leaving .mod-hero-line--1      { animation-delay: 0.05s; }
.mod-hero-slide.is-leaving .mod-hero-line--2      { animation-delay: 0.23s; }
.mod-hero-slide.is-leaving .mod-hero-line--model  { animation-delay: 0.45s; }
/* Desc/variants/price: mismo ease luxury que React para el exit */
.mod-hero-slide.is-leaving .mod-hero-desc,
.mod-hero-slide.is-leaving .mod-hero-variants,
.mod-hero-slide.is-leaving .mod-hero-price        { animation: mod-fade-up-out 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
/* Stagger del exit (replica delays de Hero.tsx del original): desc 0.5s, price 0.55s,
   variantes 0.7s. Cada elemento empieza a "desintegrarse" en cascada — el efecto
   tipo Thanos snap. */
.mod-hero-slide.is-leaving .mod-hero-desc         { animation-delay: 0.50s; }
.mod-hero-slide.is-leaving .mod-hero-price        { animation-delay: 0.55s; animation-duration: 0.7s; }
.mod-hero-slide.is-leaving .mod-hero-variants     { animation-delay: 0.70s; }
/* Buttons y badges están ahora fuera del slide div (persistentes como en React). */

/* ============================================================
 * PRODUCT IMAGE / 3D — entrada del slide
 * Imagen: opacity 0→1, scale .95→1, blur 12→0, duration 1s
 * 3D:     opacity 0→1, scale .94→1, blur 20→0, duration 1.2s
 * ============================================================ */
/* Entrada de imagen 2D: espejo amplificado de mod-image-exit
 * (saliente scale 1 → .95 + blur 0 → 12 / entrante scale .92 → 1 + blur 16 → 0). */
@keyframes mod-image-enter {
    from { opacity: 0; transform: scale(.92); filter: blur(16px); }
    to   { opacity: 1; transform: scale(1);   filter: blur(0); }
}
/* Entrada del modelo 3D: espejo amplificado de mod-3d-exit ("Thanos
 * materialize"). Saliente: scale 1 → 1.04 + blur 0 → 20 (sale agrandando).
 * Entrante: scale 1.08 → 1 + blur 28 → 0 (se re-arma desde "más allá"
 * con la misma firma visual del snap, garantizando lectura clara). */
@keyframes mod-3d-enter {
    from { opacity: 0; transform: scale(1.08); filter: blur(28px); }
    to   { opacity: 1; transform: scale(1);    filter: blur(0); }
}
/* Aplicamos las animaciones de entrada solo cuando el slide está activo
 * (se reinician al toggle display:none → display:flex). Sin esto, el `both`
 * deja la 3D wrapper en opacity:0 si por algún motivo el navegador no
 * retriggera la animación al show. */
.mod-showcase-slide.is-active .mod-showcase-image { animation: mod-image-enter 1s cubic-bezier(0.16, 1, 0.3, 1) both; }
/* Modelo 3D combina dos animaciones: entrada cinemática (1.2s) seguida
 * de un "breathing" idle infinito (5s, micro-escala 1↔1.008) que arranca
 * tras la entrada para que el producto se sienta vivo aún sin interacción. */
.mod-showcase-slide.is-active .mod-showcase-3d    {
    animation: mod-3d-enter 1.2s cubic-bezier(0.16, 1, 0.3, 1) both,
               mod-3d-breathing 5s ease-in-out 1.4s infinite;
}
@keyframes mod-3d-breathing {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.008); }
}
/* Own GPU compositing layer — prevents backdrop-filter on lower z siblings
   from capturing model-viewer's WebGL canvas in their backdrop snapshot. */
.mod-showcase-3d { will-change: transform; isolation: isolate; }

/* Exit animations para showcase image / 3D — replican AnimatePresence mode="wait"
 * del original ProductShowcase.tsx:
 *   exit imagen: opacity 0, scale 0.95, filter blur(12px), duration 1s
 *   exit 3D:     opacity 0, scale 1.04, filter blur(20px), duration 1.2s
 * El blur(20px) en 3D-exit es lo que crea el efecto "Thanos snap" del modelo. */
@keyframes mod-image-exit {
    from { opacity: 1; transform: scale(1);   filter: blur(0); }
    to   { opacity: 0; transform: scale(.95); filter: blur(12px); }
}
@keyframes mod-3d-exit {
    from { opacity: 1; transform: scale(1);    filter: blur(0); }
    to   { opacity: 0; transform: scale(1.04); filter: blur(20px); }
}
.mod-showcase-slide.is-leaving { display: flex !important; }
.mod-showcase-slide.is-leaving .mod-showcase-image { animation: mod-image-exit 1s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
.mod-showcase-slide.is-leaving .mod-showcase-3d    { animation: mod-3d-exit 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards; }

/* ProductShowcase wrapper — entrance: opacity 0→1, scale .92→1, y 30→0,
 * duration 1.6s, delay 0.2s
 *
 * CRITICAL: The `to` keyframe must NOT include a transform (not even the
 * identity transform: scale(1) translateY(0)). Any non-none transform value
 * — including an identity — creates a new CSS containing block, which
 * captures `position: fixed` descendants (the mobile features sheet panel
 * and backdrop) and makes them position relative to this wrapper instead of
 * the viewport. With `fill-mode: both`, the identity transform would persist
 * forever after the animation ends, permanently breaking the sheet's layout.
 *
 * Fix: `to` ends with `transform: none`, and we use `forwards` fill-mode.
 * The `opacity: 0` on the element itself covers the pre-animation delay
 * (which `both` would have handled via the `from` state). */
@keyframes mod-showcase-wrap-enter {
    from { opacity: 0; transform: scale(.92) translateY(30px); }
    to   { opacity: 1; transform: none; }
}
.mod-showcase-wrap {
    opacity: 0; /* covers the 0.2s delay so no flash before animation starts */
    animation: mod-showcase-wrap-enter 1.6s cubic-bezier(0.16, 1, 0.3, 1) .2s forwards;
}

/* Idle float for the 2D image (mismo .float-idle que ya está en moderno.css) */

/* Hotspot aside oculto hasta que model-viewer dispara 'load'.
 * Replica React: HotspotsList solo monta cuando hasLoadedOnce=true.
 * Sin esto, el backdrop-blur del aside captura el canvas WebGL vacío
 * durante la carga y el modelo queda invisible en desktop. */
[data-mod-hotspots-list] {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
[data-mod-hotspots-list].mod-hotspots-loaded {
    opacity: 1;
    pointer-events: auto;
}

/* ============================================================
 * HOTSPOTS LIST (aside) — cards salen del modelo hacia la derecha.
 * Las cards arrancan invisibles (mismo estado que el `from` del keyframe);
 * la animación SOLO se dispara cuando el aside recibe `.mod-hotspots-loaded`
 * (showcase.js lo añade en model-viewer 'load'), así nunca aparecen antes
 * de que el modelo esté en pantalla.
 * Trayectoria: translateX(-60px) → 0 (vuelan desde el centro/modelo a su
 * posición natural a la derecha) + blur 12 → 0 + opacity 0 → 1.
 * ============================================================ */
@keyframes mod-hotspot-card {
    from { opacity: 0; transform: translateX(-60px); filter: blur(12px); }
    to   { opacity: 1; transform: translateX(0);     filter: blur(0); }
}
.mod-hotspot-card {
    opacity: 0;
    transform: translateX(-60px);
    filter: blur(12px);
}
[data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card {
    animation: mod-hotspot-card .95s cubic-bezier(0.16, 1, 0.3, 1) both;
}
[data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card:nth-child(1) { animation-delay: calc(.15s + 0 * .12s); }
[data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card:nth-child(2) { animation-delay: calc(.15s + 1 * .12s); }
[data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card:nth-child(3) { animation-delay: calc(.15s + 2 * .12s); }
[data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card:nth-child(4) { animation-delay: calc(.15s + 3 * .12s); }

/* ============================================================
 * PAGINATION — active dot expand + progress fill
 * width 8 ↔ 56, transition spring-like (duration 0.6s ease luxury)
 * progress: scaleX 0→1 linear over durationMs
 * ============================================================ */
.mod-dot {
    transition: width .6s cubic-bezier(0.16, 1, 0.3, 1);
}
.mod-progress {
    transform: scaleX(0);
    transform-origin: left center;
}
.mod-progress.is-running {
    animation: mod-progress var(--mod-progress-dur, 9s) linear forwards;
}
@keyframes mod-progress {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}

/* ============================================================
 * VIEW TOGGLE — pill that slides between Imagen/3D
 * (replicates layoutId="view-toggle-active")
 * ============================================================ */
.mod-toggle-active {
    transition: transform .35s cubic-bezier(0.4, 1.4, 0.4, 1);
    will-change: transform;
}

/* ============================================================
 * PAGE ENTER — curtain + sweep + line retract
 * curtain: opacity 1→0, .35s ease [.4,0,.2,1]
 * sweep:   x -100%→100%, opacity 0→.5→0, 1.2s, .1s delay
 * lines:   scaleX 1→0, .9s, .5s delay
 * ============================================================ */
@keyframes mod-curtain {
    from { opacity: 1; }
    to   { opacity: 0; }
}
@keyframes mod-sweep {
    0%   { transform: translateX(-100%); opacity: 0; }
    50%  { opacity: .5; }
    100% { transform: translateX(100%);  opacity: 0; }
}
@keyframes mod-line-retract-r {
    from { transform: scaleX(1); }
    to   { transform: scaleX(0); }
}
.mod-curtain {
    position: fixed; inset: 0; z-index: 200; pointer-events: none;
    background: #080d1a;
    animation: mod-curtain .35s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.mod-sweep {
    position: fixed; inset: 0; z-index: 201; pointer-events: none;
    background: linear-gradient(110deg, transparent 25%, rgba(96,165,250,.35) 50%, transparent 75%);
    mix-blend-mode: screen;
    transform: translateX(-100%);
    animation: mod-sweep 1.2s cubic-bezier(0.16, 1, 0.3, 1) .1s forwards;
}
.mod-line {
    position: fixed; left: 0; right: 0; height: 2px; z-index: 202; pointer-events: none;
    background: linear-gradient(90deg, transparent, #60a5fa, transparent);
    animation: mod-line-retract-r .9s cubic-bezier(0.16, 1, 0.3, 1) .5s forwards;
    will-change: transform;
}
.mod-line--top    { top: 0;    transform-origin: right center; }
.mod-line--bottom { bottom: 0; transform-origin: left center; }
@media (prefers-reduced-motion: reduce) {
    .mod-curtain, .mod-sweep, .mod-line { display: none !important; }
}

/* ============================================================
 * SCROLL PROGRESS — top bar
 * scaleX driven by JS (window scrollY / scrollHeight)
 * ============================================================ */
.mod-scroll-progress {
    position: fixed; top: 0; left: 0; right: 0; height: 2px;
    z-index: 150; pointer-events: none;
    background: linear-gradient(90deg, #60a5fa, #3b82f6, #1d4ed8);
    box-shadow: 0 0 12px rgb(var(--brand-glow) / .5);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform .15s linear;
    will-change: transform;
}

/* ============================================================
 * VIEW TOGGLE & VARIANT RING — small helpers
 * ============================================================ */
.mod-variant-ring {
    transition: transform .35s cubic-bezier(0.4, 1.4, 0.4, 1),
                opacity .25s ease;
}

/* ============================================================
 * MOBILE FEATURES MODAL — floating modal (not bottom sheet)
 * Backdrop: opacity 0↔1, .3s
 * Panel:    scale+y → aparece desde abajo con scale
 * ============================================================ */
.mod-sheet-backdrop {
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s ease;
}
.mod-sheet-backdrop[data-open="true"] { opacity: 1; pointer-events: auto; }

.mod-sheet-panel {
    opacity: 0 !important;
    transform: translateY(32px) scale(0.95) !important;
    pointer-events: none !important;
    transition:
        opacity .35s cubic-bezier(0.16, 1, 0.3, 1),
        transform .45s cubic-bezier(0.16, 1, 0.3, 1);
}
.mod-sheet-panel[data-open="true"] {
    opacity: 1 !important;
    transform: translateY(0) scale(1) !important;
    pointer-events: auto !important;
}

/* Sheet card stagger entrance */
@keyframes mod-sheet-card {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}
.mod-sheet-panel[data-open="true"] .mod-sheet-card {
    animation: mod-sheet-card .4s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.mod-sheet-panel[data-open="true"] .mod-sheet-card:nth-child(1) { animation-delay: 0.08s; }
.mod-sheet-panel[data-open="true"] .mod-sheet-card:nth-child(2) { animation-delay: 0.15s; }
.mod-sheet-panel[data-open="true"] .mod-sheet-card:nth-child(3) { animation-delay: 0.22s; }
.mod-sheet-panel[data-open="true"] .mod-sheet-card:nth-child(4) { animation-delay: 0.29s; }

/* Hover state para sheet cards */
.mod-sheet-card:hover, .mod-sheet-card:focus-visible {
    background: rgba(59,130,246,0.06) !important;
    border-color: rgba(59,130,246,0.2) !important;
}
.mod-sheet-card[aria-pressed="true"] {
    background: rgba(59,130,246,0.1) !important;
    border-color: rgba(59,130,246,0.35) !important;
}
.mod-sheet-card[aria-pressed="true"] [data-mod-hotspot-dot] {
    background: #60a5fa !important;
    box-shadow: 0 0 8px rgba(96,165,250,0.6);
}

/* ============================================================
 * COLOR WASH — al elegir variante el showcase recibe un pulso
 * radial del color seleccionado (0.4s). Refuerza visualmente el
 * cambio de color del producto. Color via --mod-wash-color (JS).
 * ============================================================ */
@keyframes mod-color-wash {
    0%   { opacity: 0;   transform: scale(.9); }
    35%  { opacity: .55; transform: scale(1.05); }
    100% { opacity: 0;   transform: scale(1.15); }
}
.mod-color-wash {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    pointer-events: none;
    background: radial-gradient(circle at center,
                var(--mod-wash-color, rgb(var(--brand-glow))) 0%,
                transparent 60%);
    mix-blend-mode: screen;
    animation: mod-color-wash .6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
    z-index: 25; /* sobre los halos pero bajo el modelo (z-30) */
    will-change: transform, opacity;
}

/* ============================================================
 * HOTSPOT RIPPLE — ring que sale del dot al hacer click
 * scale 1 → 5, opacity .55 → 0, duration .8s, ease luxury
 * ============================================================ */
@keyframes mod-hotspot-ring {
    from { transform: translate(-50%, -50%) scale(1);   opacity: .55; }
    to   { transform: translate(-50%, -50%) scale(5);   opacity: 0; }
}
.mod-hotspot-ring {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 8px;
    height: 8px;
    border-radius: 9999px;
    border: 1.5px solid rgb(var(--brand-glow) / .9);
    box-shadow: 0 0 12px rgb(var(--brand-glow) / .6);
    pointer-events: none;
    animation: mod-hotspot-ring .8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
    will-change: transform, opacity;
}

/* ============================================================
 * VARIANT PICKER — swatch ripple
 * scale 0.4→18, opacity .7→0, duration 1s, ease luxury
 * ============================================================ */
@keyframes mod-ripple {
    from { transform: translate(-50%, -50%) scale(.4); opacity: .7; }
    to   { transform: translate(-50%, -50%) scale(18); opacity: 0; }
}
.mod-ripple {
    position: absolute;
    width: 36px; height: 36px;
    border-radius: 9999px;
    pointer-events: none;
    mix-blend-mode: screen;
    animation: mod-ripple 1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
    will-change: transform, opacity;
}

/* Sections fade-in on scroll (whileInView). Class .mod-reveal initially hidden,
 * .is-visible activates the keyframe. JS via IntersectionObserver. */
@keyframes mod-reveal {
    from { opacity: 0; transform: translateY(30px); }
    to   { opacity: 1; transform: translateY(0); }
}
.mod-reveal { opacity: 0; transform: translateY(30px); }
.mod-reveal.is-visible {
    animation: mod-reveal 1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
    animation-delay: var(--mod-reveal-delay, 0s);
}

/* ============================================================
 * REDUCED MOTION — regla GLOBAL idéntica a src/index.css del proyecto React.
 * Anula cualquier animación/transición que no haya sido cubierta por
 * reglas específicas de @media (prefers-reduced-motion: reduce) más arriba.
 * Sin esto, NYXIVO ejecuta animaciones que en el original quedan
 * suprimidas (entrada del título, ripple, hotspots, reveal, etc.).
 * ============================================================ */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-delay: 0s !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        transition-delay: 0s !important;
        scroll-behavior: auto !important;
    }
    .float-idle { animation: none !important; }

    /* Excepción: las animaciones de SALIDA (.is-leaving) deben correr con
     * su duración real Y con su stagger original — replica el "Thanos snap"
     * efecto del original. En React, framer-motion anima vía rAF y CSS
     * reduced-motion no le afecta; los `exit` keyframes de AnimatePresence
     * corren a su duración + delay aún con useReducedMotion.
     *
     * El blur progresivo (0 → 10px) + opacity (1 → 0) + translateY (0 → -16)
     * + el stagger escalonado producen el efecto de desintegración cascada.
     */
    .mod-hero-slide.is-leaving .mod-hero-line {
        animation-duration: 1.2s !important;
    }
    .mod-hero-slide.is-leaving .mod-hero-line--model {
        animation-duration: 1.4s !important;
    }
    .mod-hero-slide.is-leaving .mod-hero-line--1     { animation-delay: 0.05s !important; }
    .mod-hero-slide.is-leaving .mod-hero-line--2     { animation-delay: 0.23s !important; }
    .mod-hero-slide.is-leaving .mod-hero-line--model { animation-delay: 0.45s !important; }

    .mod-hero-slide.is-leaving .mod-hero-desc,
    .mod-hero-slide.is-leaving .mod-hero-variants,
    .mod-hero-slide.is-leaving .mod-hero-price {
        animation-duration: 0.8s !important;
    }
    .mod-hero-slide.is-leaving .mod-hero-desc      { animation-delay: 0.50s !important; }
    .mod-hero-slide.is-leaving .mod-hero-price     { animation-delay: 0.55s !important; animation-duration: 0.7s !important; }
    .mod-hero-slide.is-leaving .mod-hero-variants  { animation-delay: 0.70s !important; }

    .mod-showcase-slide.is-leaving .mod-showcase-image {
        animation-duration: 1s !important;
    }
    .mod-showcase-slide.is-leaving .mod-showcase-3d {
        animation-duration: 1.2s !important;
    }

    /* ───────────────────────────────────────────────────────────────────
     * Excepciones simétricas para ENTRADA bajo reduced-motion.
     * El `*` rule de arriba bajaría toda animation-duration a 0.001ms
     * (= aparece de golpe). Aquí restauramos la duración real para que
     * el primer load Y los cambios de slide tengan su animación completa.
     *
     * Las DURACIONES son las mismas siempre — los DELAYS difieren entre
     * el primer load (con offset de 0.4s para sincronizar con la cortina
     * PageEnter) y los swaps subsiguientes (data-past-first="true",
     * empiezan inmediatamente).
     * ─────────────────────────────────────────────────────────────────── */

    /* Duraciones de entrada (aplican siempre) */
    .mod-hero-slide.is-active .mod-hero-line          { animation-duration: 1.2s !important; }
    .mod-hero-slide.is-active .mod-hero-line--model   { animation-duration: 1.4s !important; }
    .mod-hero-slide.is-active .mod-hero-desc,
    .mod-hero-slide.is-active .mod-hero-variants      { animation-duration: 0.8s !important; }
    .mod-hero-slide.is-active .mod-hero-price         { animation-duration: 0.7s !important; }

    /* Delays — primer load (data-past-first="false"): offset 0.4s + stagger original */
    .mod-hero[data-past-first="false"] .mod-hero-slide.is-active .mod-hero-line--1     { animation-delay: 0.45s !important; }
    .mod-hero[data-past-first="false"] .mod-hero-slide.is-active .mod-hero-line--2     { animation-delay: 0.63s !important; }
    .mod-hero[data-past-first="false"] .mod-hero-slide.is-active .mod-hero-line--model { animation-delay: 0.85s !important; }
    .mod-hero[data-past-first="false"] .mod-hero-slide.is-active .mod-hero-desc        { animation-delay: 0.90s !important; }
    .mod-hero[data-past-first="false"] .mod-hero-slide.is-active .mod-hero-price       { animation-delay: 0.95s !important; }
    .mod-hero[data-past-first="false"] .mod-hero-slide.is-active .mod-hero-variants    { animation-delay: 1.10s !important; }

    /* Delays — swaps subsiguientes (data-past-first="true"): sin offset inicial */
    .mod-hero[data-past-first="true"]  .mod-hero-slide.is-active .mod-hero-line--1     { animation-delay: 0.05s !important; }
    .mod-hero[data-past-first="true"]  .mod-hero-slide.is-active .mod-hero-line--2     { animation-delay: 0.23s !important; }
    .mod-hero[data-past-first="true"]  .mod-hero-slide.is-active .mod-hero-line--model { animation-delay: 0.45s !important; }
    .mod-hero[data-past-first="true"]  .mod-hero-slide.is-active .mod-hero-desc        { animation-delay: 0.50s !important; }
    .mod-hero[data-past-first="true"]  .mod-hero-slide.is-active .mod-hero-price       { animation-delay: 0.55s !important; }
    .mod-hero[data-past-first="true"]  .mod-hero-slide.is-active .mod-hero-variants    { animation-delay: 0.70s !important; }

    /* Botones y badges del hero — solo animan en el primer load */
    .mod-hero-buttons   { animation-duration: 1s !important; animation-delay: 1.45s !important; }
    .mod-hero-badges    { animation-duration: 1s !important; animation-delay: 1.60s !important; }

    /* Showcase wrap — entrada inicial del contenedor del producto */
    .mod-showcase-wrap  { animation-duration: 1.6s !important; animation-delay: 0.2s !important; }

    /* Showcase entry: imagen 2D y modelo 3D al activar el slide.
     * Para el 3D usamos shorthand completa para descartar el "breathing"
     * idle (que en reduced-motion sería un pulso único innecesario). */
    .mod-showcase-slide.is-active .mod-showcase-image { animation-duration: 1s !important; }
    .mod-showcase-slide.is-active .mod-showcase-3d    {
        animation-name: mod-3d-enter !important;
        animation-duration: 1.2s !important;
        animation-delay: 0s !important;
        animation-fill-mode: both !important;
        animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1) !important;
        animation-iteration-count: 1 !important;
    }

    /* Hotspots aside fade + cards "salen del modelo" tras model load */
    [data-mod-hotspots-list]                                        { transition-duration: 0.6s !important; }
    [data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card  { animation-duration: 0.95s !important; }
    [data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card:nth-child(1) { animation-delay: 0.15s !important; }
    [data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card:nth-child(2) { animation-delay: 0.27s !important; }
    [data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card:nth-child(3) { animation-delay: 0.39s !important; }
    [data-mod-hotspots-list].mod-hotspots-loaded .mod-hotspot-card:nth-child(4) { animation-delay: 0.51s !important; }
}

/* TiltCard glare overlay (handled via JS inline style; nothing extra here). */

/* Hide native cursor when custom cursor is on (already in moderno.css for html.custom-cursor-on)
 * No need to duplicate; we set the class via CustomCursor JS. */

/* ============================================================
 * MOBILE — animaciones comprimidas para carga percibida rápida
 *
 * Problema: en desktop las animaciones tienen un offset de 0.4s
 * (sincronizado con la cortina PageEnter). En móvil con red lenta
 * eso significa que los botones aparecen >1.45s después de que
 * el CSS carga — se percibe como carga fragmentada.
 *
 * Fix: eliminar la cortina, llevar todo a <0.35s total, y
 * deshabilitar efectos GPU costosos (blobs blur-180px, stardust).
 * ============================================================ */
@media (max-width: 767px) {
    /* ── PageEnter: sin cortina en móvil (ahorra el offset 0.4s) ── */
    .mod-curtain,
    .mod-sweep,
    .mod-line { display: none !important; }

    /* ── Navbar ── */
    .mod-navbar {
        animation-duration: 0.35s !important;
        animation-delay: 0s !important;
    }

    /* ── Hero título: stagger mínimo, sin offset de cortina ── */
    .mod-hero-line                { animation-duration: 0.55s !important; }
    .mod-hero-line--1             { animation-delay: 0.04s !important; }
    .mod-hero-line--2             { animation-delay: 0.1s  !important; }
    .mod-hero-line--model         { animation-duration: 0.6s !important; animation-delay: 0.16s !important; }

    /* ── Hero contenido: todos visibles en < 0.35s ── */
    .mod-hero-desc    { animation-duration: 0.45s !important; animation-delay: 0.12s !important; }
    .mod-hero-price   { animation-duration: 0.45s !important; animation-delay: 0.16s !important; }
    .mod-hero-variants{ animation-duration: 0.45s !important; animation-delay: 0.2s  !important; }
    .mod-hero-buttons { animation-duration: 0.45s !important; animation-delay: 0.22s !important; }
    .mod-hero-badges  { animation-duration: 0.45s !important; animation-delay: 0.28s !important; }

    /* ── Showcase wrap ── */
    .mod-showcase-wrap {
        animation-duration: 0.6s !important;
        animation-delay: 0.08s !important;
    }

    /* ── Blobs: filter:blur(180px) mata la GPU móvil ── */
    .mod-blob { display: none !important; }

    /* ── Stardust: 28 partículas animadas → innecesario en móvil ── */
    .mod-stardust { display: none !important; }

    /* ── Scroll progress: transición más rápida en móvil ── */
    .mod-scroll-progress { transition-duration: 0.08s !important; }
}


