/* ========================================
   10-MOTION.CSS — NK Insurance
   Sistema de motion unificado (premium, sutil).

   Se carga ÚLTIMO (después de 99-responsive) → es la autoridad sobre la
   entrada de secciones (reveal por scroll), la orquestación del hero y el
   respeto a `prefers-reduced-motion`.

   Principios:
   - Progressive enhancement: el JS (animations.js) agrega `.reveal` para
     OCULTAR y `.is-visible` para MOSTRAR. Sin JS o con reduced-motion, el
     contenido queda visible por defecto (nunca escondido).
   - Curvas ease-out (definidas en 00-global :root) para deceleración natural.
   - Un solo sistema de entrada (antes había 3 compitiendo: 2 IntersectionObserver
     + animaciones on-load por sección).
   ======================================== */

/* ----------------------------------------
   1) Reveal por scroll — sistema único
   El JS marca cada tarjeta/bloque con `.reveal` (+ un índice de stagger por
   grupo en `--reveal-i`) y le agrega `.is-visible` al entrar en viewport.
   ---------------------------------------- */
/* Ritmo (ajustado 2026-08-07 tras MEDIR contra una referencia — ver §9).
   · 0.9s de duración y `--ease-out-quart`: SE QUEDAN COMO ESTABAN. El número
     del CSS engaña — con quart, la card llega a opacidad ~1 a los **633ms**
     medidos, y el resto es cola imperceptible. La animación nunca fue lenta.
   · Stagger 110ms → **70ms** (y el tope de grupo baja de 5 a 3 en
     animations.js). Acá SÍ estaba el problema: 5 × 110ms eran 550ms de espera
     antes de que el elemento empezara siquiera a aparecer.
   · Recorrido 22px → **28px**. Medido contra la referencia, que usa 28-30px:
     por debajo de ~25px el gesto se lee como un fundido, no como que el
     elemento LLEGA.

   🔑 Lo que de verdad se sentía lento NO vive en este archivo, vive en el
   `rootMargin` de animations.js. Ver el comentario largo de ahí antes de
   volver a tocar estos números. */
/* 🔑 El ritmo vive en VARIABLES, no en números sueltos — 2026-08-07.
   Motivo: `.reveal` declara una `transition`, y toda regla de sección que quiera
   animar OTRA propiedad (el `translate` del hover, `box-shadow`…) tiene que
   repetir las dos del reveal o las pierde, porque una shorthand `transition` con
   más especificidad reemplaza la lista entera (§0, regla 4). Eso hacía que el
   ritmo estuviera copiado en TRES archivos: acá, `05-about.css` y
   `08-testimonials.css`. Al bajar el stagger de 110 a 70ms se actualizó solo
   éste y las otras dos siguieron en 110 — se detectó midiendo, no leyendo.
   Con variables, esas reglas heredan el ritmo y no hay número que sincronizar. */
:root {
    --reveal-dur: 0.9s;
    --reveal-stagger: 70ms;
}

.reveal {
    opacity: 0;
    transform: translateY(28px);
    transition:
        opacity var(--reveal-dur) var(--ease-out-quart),
        transform var(--reveal-dur) var(--ease-out-quart);
    transition-delay: calc(var(--reveal-i, 0) * var(--reveal-stagger));
}

.reveal.is-visible {
    opacity: 1;
    transform: none;
}

/* Variantes direccionales para bloques de dos columnas enfrentadas: uno entra
   desde la izquierda y el otro desde la derecha.
   ⚠️ 2026-08-03: SIN USO. Las estrenó el "Nosotros" de dos columnas, que se
   reemplazó por el mosaico "Así se vive NK" (05-about.css). Se conservan porque
   son genéricas y `animations.js` puede volver a aplicarlas a cualquier par de
   bloques; hoy ningún elemento recibe estas clases. */
.reveal--left { transform: translateX(-28px); }
.reveal--right { transform: translateX(28px); }
.reveal--left.is-visible,
.reveal--right.is-visible { transform: none; }

/* ----------------------------------------
   1b) "Cómo Funciona" — la secuencia se explica sola
   Es la única sección cuyo CONTENIDO es un orden (4 pasos): el motion acá no
   decora, demuestra que es un proceso. Por eso lleva un stagger más generoso
   que el genérico, encadenando 1 → 2 → 3 → 4.

   2026-08-07: 190ms → **110ms**. Sigue siendo ~1.6× el genérico (70ms), así que
   la lectura de secuencia se mantiene, pero 190 × 3 eran 570ms hasta el cuarto
   paso — medido, el desfase real entre pasos daba 179/201/183ms y la sección
   entera tardaba casi 1.9s en armarse.

   Nota (2026-07-26): antes había acá reglas para animar la entrada de las
   flechas `.hiw-step::after` entre pasos. Esas flechas se eliminaron al pasar
   la sección al layout de "recorrido" (curva punteada + avión, ver
   `03-features.css`), así que las reglas se quitaron por quedar muertas.
   ---------------------------------------- */
/* Solo redefine la variable: el `transition-delay` lo calcula `.reveal` y se
   resuelve por elemento, así que no hace falta repetir la regla. */
.hiw-step.reveal {
    --reveal-stagger: 110ms;
}

/* ----------------------------------------
   2) Hero "NK Network" — orquestación de entrada (on-load, la "firma")

   Reescrito el 2026-08-07 contra `.nkn-*`. Lo que había acá apuntaba a
   `.hero-col` / `.hero-figure`, del hero DUAL descartado el 08-06 (§3): estaba
   inerte y figuraba en las limpiezas pendientes de §12. Con esto, la página deja
   de abrir sin ningún movimiento.

   Secuencia: la foto se asienta mientras el texto entra escalonado.

   🔑 **LA FOTO NO LLEVA FADE, Y NO ES UN DETALLE DE ESTILO.** Es el elemento
   **LCP** de la página (medido: `hero-net-w2880.webp`, 596ms a 2560×1440).
   Ponerle `opacity: 0` retrasa el LCP real que miden las Core Web Vitals hasta
   que termina la animación — es el error clásico de los heros animados, y en un
   sitio cuyo LCP es una foto a sangre se paga entero. Acá solo se ESCALA: el
   navegador la pinta en el primer frame y el LCP no se mueve.

   ⚠️ El zoom sale de `scale(1.04)` y de ahí no se sube. Con 1.08+ deja de leerse
   como una imagen que se asienta y pasa a leerse como Ken Burns de plantilla. El
   `overflow: hidden` de `.nkn-hero` es lo único que impide que ese 4% empuje
   scroll horizontal — si alguna vez se quita, esto lo reintroduce.

   🔑 El texto se mueve con `translate`, NO con `transform`. Los dos `.nkn-btn`
   usan `transform: translateY(-2px)` en hover; acá se anima el contenedor
   `.nkn-cta` y no el botón, así que hoy no chocarían — pero usar la propiedad
   independiente deja el `transform` libre para siempre. Es la misma regla que ya
   gobierna el resto del sitio (§0, regla 2).

   El `opacity: 0` inicial vive SOLO dentro de este bloque no-preference, así
   quien pide menos movimiento nunca ve nada oculto.
   ---------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
    /* `backwards` (no `both`): mantiene el estado inicial durante el delay, pero
       al TERMINAR revierte al CSS normal en vez de fijar el estado final. Con
       `both`/`forwards` el valor del keyframe queda fijado y pisa cualquier regla
       normal sobre la misma propiedad — así fue como una versión anterior de este
       bloque dejó sin hover a los botones del hero. */
    .nkn-bg img {
        animation: nknPhotoSettle 1.6s var(--ease-out-quart) backwards;
    }

    /* El texto: fade + 14px de subida, escalonado.
       ⚠️ 14px y no los 28 del reveal por scroll (bloque 1). Son gestos
       distintos: el reveal narra algo que LLEGA desde fuera de la pantalla; el
       hero ya está a la vista y solo se asienta. Y con cinco bloques encadenados
       el recorrido se acumula: a 28px cada uno, la entrada se siente pesada. */
    .nkn-content > * {
        animation: nknRise 0.6s var(--ease-out-quart) backwards;
    }

    .nkn-content > *:nth-child(1) { animation-delay: 0.08s; }  /* eyebrow      */
    .nkn-content > *:nth-child(2) { animation-delay: 0.16s; }  /* h1           */
    .nkn-content > *:nth-child(3) { animation-delay: 0.30s; }  /* subtítulo    */
    .nkn-content > *:nth-child(4) { animation-delay: 0.38s; }  /* botones      */
    .nkn-content > *:nth-child(5) { animation-delay: 0.46s; }  /* stats        */

    /* El crédito de la foto entra último y SOLO con fade: es metadato, no
       contenido. Moverlo lo pondría al mismo nivel jerárquico que el título. */
    .nkn-cap {
        animation: nknFade 0.8s var(--ease-out-quart) 0.7s backwards;
    }

    /* ⚠️ El strip de carriers NO entra en la secuencia, y no es olvido: ya tiene
       su propio movimiento (la marquesina, 02-hero.css §8). Sumarle una entrada
       pone dos animaciones distintas compitiendo en la misma franja — la
       marquesina arrancaría desplazándose mientras el strip todavía aparece. */
}

@keyframes nknRise {
    from { opacity: 0; translate: 0 14px; }
    to { opacity: 1; translate: 0 0; }
}

/* ⚠️ Sin `opacity` A PROPÓSITO — ver el 🔑 del LCP, arriba. Si alguien agrega un
   `from { opacity: 0 }` acá, rompe la métrica sin que se note a ojo. */
@keyframes nknPhotoSettle {
    from { transform: scale(1.04); }
    to { transform: scale(1); }
}

@keyframes nknFade {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* ----------------------------------------
   3) Micro-interacciones (feedback sobrio)
   Los hovers (lift de cards, translateY de botones, deslizamiento de la flecha)
   ya viven en el CSS de cada sección; acá solo agregamos el feedback de "press".
   ---------------------------------------- */
/* Press: al hacer click, el botón se "asienta" a su reposo desde el translateY(-2px)
   del hover — el MISMO gesto que `.btn:active` global. Los botones del hero
   (`.hero-btn`) no son `.btn`, así que sin esto no tenían press. NO se usa escala:
   escalar el botón difumina el texto (glifo a subpíxel). */
.hero-btn:active {
    transform: translateY(0);
}

/* Botones que levantaban en hover pero no tenían press.
   El `translateY(0)` solo se percibe en DESKTOP (asienta el lift del hover);
   en móvil el botón ya está en reposo, así que ese gesto sería invisible justo
   donde el press es la ÚNICA confirmación de que el toque se registró — no hay
   hover que haya avisado nada. Por eso se suma `brightness`: se ve en los dos
   lados. No se usa scale: escalar difumina el texto (glifo a subpíxel), que es
   el motivo por el que el sistema ya lo evitaba en .btn y .hero-btn.
   Sin transición a propósito — un press que tarda no se siente como un press. */
.hiw-cta-btn:active,
.back-to-top:active {
    transform: translateY(0);
    filter: brightness(0.93);
}

/* "Conoce más" de las service-cards: es un link de texto, no levanta en hover,
   así que no hay lift que deshacer. Se atenúa. */
.service-link:active {
    opacity: 0.72;
}

/* ----------------------------------------
   3b) Estado "enviando" del botón de submit
   El label NO se reemplaza por texto ("Enviando...") como hacía forms.js: eso
   cambiaba el ancho del botón de golpe. Acá el texto se desvanece (el
   `transition: all` de .btn lo interpola) y el spinner ocupa su lugar, con el
   botón clavado en su tamaño original.

   ⚠️ Vive acá y no junto al resto del feedback de formularios en 00-global
   porque `09-footer.css` declara `.contact-form button[type="submit"]
   { color: white }` — especificidad (0,2,1). Un `.btn.is-sending` a secas es
   (0,2,0): pierde SIEMPRE, esté donde esté, y el label quedaría blanco encima
   del spinner. Por eso el selector incluye `button` (empata en 0,2,1) y vive
   en un archivo que carga después de 09-footer.
   ---------------------------------------- */
.btn.is-sending,
button.btn.is-sending {
    color: transparent;
    pointer-events: none;
}

.btn.is-sending::after,
button.btn.is-sending::after {
    content: '';
    position: absolute;
    inset: 0;
    margin: auto;
    width: 18px;
    height: 18px;
    border: 2px solid rgba(255, 255, 255, 0.35);
    border-top-color: #fff;
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
    /* El spinner sigue girando (es lo que comunica "en curso"), pero lento. */
    .btn.is-sending::after,
    button.btn.is-sending::after {
        animation-duration: 1.4s;
    }
}

/* ----------------------------------------
   4b) Barra de progreso de lectura (overdrive) — driven por scroll, CSS puro.
   Corre en el COMPOSITOR vía animation-timeline: scroll(). Solo donde la API
   existe (@supports); en Firefox cae a un fallback JS (scroll-fx.js).
   ---------------------------------------- */

/* Barra de progreso de lectura: línea fina arriba que se llena con el scroll. */
.scroll-progress {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 3px;
    transform: scaleX(0);
    transform-origin: 0 50%;
    background: linear-gradient(90deg, var(--secondary), var(--secondary-light));
    z-index: 2000;
    pointer-events: none;
}

@supports (animation-timeline: scroll()) {
    .scroll-progress {
        animation: scrollProgress linear both;
        animation-timeline: scroll(root block);
    }
}

@keyframes scrollProgress {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
}

/* ----------------------------------------
   4) Accesibilidad — respeto real a reduced-motion
   El JS ya evita ocultar cuando hay reduced-motion; esto es la red de seguridad
   por si algún `.reveal` quedó aplicado (y frena la orquestación del hero).
   ---------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .reveal,
    .reveal--left,
    .reveal--right {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }

    .nkn-content > *,
    .nkn-bg img,
    .nkn-cap {
        opacity: 1 !important;
        animation: none !important;
        translate: none !important;
        transform: none !important;
    }

    /* Barra de progreso oculta con reduced-motion. */
    .scroll-progress {
        display: none;
    }
}
