/* ==========================================================================
   Animaciones — keyframes y estados de entrada
   Todo lo que se anime aquí queda desactivado (y visible) bajo
   prefers-reduced-motion.
   ========================================================================== */

/* ---------- Hero: respiración del brillo central ----------
   Solo opacidad y escala (se resuelven en el compositor, sin repintar). */
@keyframes glow-breathe {
  0%,
  100% {
    opacity: 0.25;
    transform: scale(0.95);
  }

  50% {
    opacity: 0.35;
    transform: scale(1.05);
  }
}

.hero__glow {
  will-change: transform, opacity;
  animation: glow-breathe 9s ease-in-out infinite;
}

/* < 1024px el brillo vive en la escena (responsive.css) y respira igual */
@media (max-width: 63.999em) {
  .hero__scene::after {
    will-change: transform, opacity;
    animation: glow-breathe 9s ease-in-out infinite;
  }
}

/* ---------- Hero: ruta que se dibuja ----------
   Se anima la copia sólida de la máscara, nunca el trazo punteado.
   --route-length lo pone main.js (heroRoute). Sin JS, o sin .is-armed,
   la ruta se ve completa. main.js llama arm() al arrancar y draw() con
   .is-loaded; el delay de la transición la acomoda en la coreografía.
   Es la única entrada que no usa transform/opacity/filter/clip-path:
   un recorte rectangular no seguiría la curva (ver PENDIENTES). */
.scene__route-reveal {
  stroke-dasharray: var(--route-length);
  stroke-dashoffset: 0;
}

.scene__route.is-armed .scene__route-reveal {
  stroke-dashoffset: var(--route-length);
}

.scene__route.is-armed.is-drawn .scene__route-reveal {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset var(--dur-route) var(--ease-in-out) var(--delay-route);
}

/* ---------- Badges: anillo de texto girando ----------
   El texto da una vuelta cada 40s. En hover se suma un segundo giro
   (120s) en el contenedor: 1/40 + 1/120 = una vuelta cada 30s.
   Se pausa y se reanuda con play-state, así nunca brinca. */
@keyframes badge-spin {
  to {
    rotate: 360deg;
  }
}

.badge__text {
  animation: badge-spin 40s linear infinite;
}

.badge__spin {
  animation: badge-spin 120s linear infinite paused;
}

.badge:hover .badge__spin {
  animation-play-state: running;
}

/* ---------- Menú móvil: entrada escalonada ----------
   Cada elemento del overlay lleva --i en el HTML; al abrir entra
   60ms después del anterior. Al cerrar sale todo junto (sin delay). */
@media (max-width: 56.249em) {
  .site-header__panel .main-nav__list > li,
  .site-header__panel .site-header__aside {
    opacity: 0;
    transform: translateY(1rem);
    transition:
      opacity var(--duration-base) var(--ease-out),
      transform var(--duration-slow) var(--ease-out);
  }

  .site-header__panel.is-open .main-nav__list > li,
  .site-header__panel.is-open .site-header__aside {
    opacity: 1;
    transform: none;
    transition-delay: calc(var(--i, 0) * 60ms + 80ms);
  }
}

/* ==========================================================================
   Fase 6: coreografía de entrada del hero
   Cómo funciona:
   - Cada entrada es una animación con fill "backwards" que arranca en
     pausa (--intro-play). Mientras espera, el elemento se queda en su
     primer keyframe (invisible). main.js agrega .is-loaded al body
     cuando hay fuentes e imagen de la van (o a los 2.5 s) y el
     navegador ya terminó de pintar la página; todas corren a la vez,
     cada una con su delay.
   - Las entradas parten de opacity 0.01, no de 0: a la vista es lo
     mismo, pero así el navegador pinta cada capa durante la espera.
     Con 0 se salta lo invisible y lo pinta justo cuando aparece, y
     ese primer pintado (luz, brillo, van) trababa la entrada.
   - "backwards" y no "both": al terminar, cada elemento vuelve a sus
     estilos normales y no pisa hovers, el menú móvil ni los giros.
   - Solo con JS (.js, puesto en el <head>) y sin movimiento reducido.
     Si no, nada de esto existe y todo se ve de inmediato.
   - Tiempos y easing en variables.css (--delay-*, --dur-*, --stagger-*).
   ========================================================================== */
body {
  --intro-play: paused;
}

body.is-loaded {
  --intro-play: running;
}

/* Entradas genéricas: los valores de partida van en el keyframe y el
   final es el estilo normal del elemento */
@keyframes fade-in {
  from {
    opacity: 0.01;
  }
}

/* Brillo diagonal del CTA: de la izquierda a la derecha, fuera del botón */
@keyframes cta-shine {
  from {
    transform: translateX(-120%) skewX(-20deg);
  }

  to {
    transform: translateX(320%) skewX(-20deg);
  }
}

@keyframes rise-in {
  from {
    opacity: 0.01;
    transform: translateY(var(--rise-y, 1rem));
  }
}

@keyframes drop-in {
  from {
    opacity: 0.01;
    transform: translateY(var(--drop-y, -1.25rem));
  }
}

/* Brillo: primera respiración más fuerte. Termina en el 0% de
   glow-breathe para que el ciclo sutil siga sin brinco. */
@keyframes glow-ignite {
  0% {
    opacity: 0.01;
    transform: scale(0.85);
  }

  45% {
    opacity: 0.55;
    transform: scale(1.1);
  }

  100% {
    opacity: 0.25;
    transform: scale(0.95);
  }
}

/* Van: sube y crece. Solo transform y opacidad (las resuelve el
   compositor); animar el filter (blur + sombras) la repintaba en cada
   cuadro sobre una imagen grande y trababa la entrada. */
@keyframes van-in {
  from {
    opacity: 0.01;
    transform: translateY(2rem) scale(0.96);
  }
}

/* Flotación de la van (desktop) y su sombra, que se encoge un poco
   cuando la van sube. Usan translate/scale, no transform, para no
   chocar con las entradas. */
@keyframes van-float {
  50% {
    translate: 0 -0.25rem;
  }
}

/* El reflejo baja cuando la van sube, como en un espejo */
@keyframes gloss-float {
  50% {
    translate: 0 0.25rem;
  }
}

@keyframes shadow-float {
  50% {
    opacity: 0.85;
    scale: 0.96 1;
  }
}

/* Sombra de contacto: crece a lo ancho a la par que llega la van.
   Los núcleos (::before y ::after) van inclinados dentro y crecen con ella. */
@keyframes shadow-grow {
  from {
    opacity: 0.01;
    transform: scaleX(0.6);
  }
}

/* H1: la línea recorta (clip-path) mientras su texto sube. Los insets
   negativos dejan aire para acentos, la "g" y la cola de la "f"
   itálica; al terminar el recorte desaparece. El texto parte de 150%
   (no 110%) porque con line-height 0.95 los ascendentes se asomarían. */
@keyframes line-mask {
  from,
  to {
    clip-path: inset(-0.15em -0.5em -0.2em);
  }
}

@keyframes line-up {
  from {
    transform: translateY(150%);
  }
}

/* Pin: cae con aceleración, aterriza al 40% (se aplasta un poco) y da
   dos rebotes cortos. --delay-pin-end usa ese 40% para aterrizar
   justo cuando llega la ruta. */
@keyframes pin-drop {
  0% {
    opacity: 0.01;
    transform: translateY(-140%);
    animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45);
  }

  25% {
    opacity: 1;
  }

  40% {
    transform: translateY(0) scale(1.08, 0.9);
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); /* expo out */
  }

  60% {
    transform: translateY(-14%);
    animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45);
  }

  78% {
    transform: translateY(0) scale(1.03, 0.97);
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); /* expo out */
  }

  89% {
    transform: translateY(-4%);
  }

  100% {
    transform: none;
  }
}

/* Sombrita del pin: crece mientras el pin se acerca al piso */
@keyframes pin-shadow {
  0% {
    opacity: 0.01;
    transform: scale(0.3);
  }

  40%,
  100% {
    opacity: 1;
    transform: none;
  }
}

/* Anillo naranja: un solo pulso al aterrizar */
@keyframes pin-ring {
  0% {
    opacity: 0.01;
    transform: scale(0.2);
  }

  12% {
    opacity: 0.9;
  }

  100% {
    opacity: 0;
    transform: scale(1.9);
  }
}

/* Badges: entran desde la derecha girando de -90° a 0° */
@keyframes badge-in {
  from {
    opacity: 0.01;
    transform: translateX(3rem) rotate(-90deg);
  }
}

@media (prefers-reduced-motion: no-preference) {
  /* 0.0 s — Se prende el reflector (la viñeta ya está: es la oscuridad) */
  .js .hero__light {
    animation: fade-in var(--dur-light) var(--ease-enter) var(--delay-light) backwards var(--intro-play);
  }

  /* 0.3 s — Primera respiración fuerte y luego el ciclo sutil de siempre */
  .js .hero__glow {
    animation:
      glow-ignite var(--dur-glow) ease-in-out var(--delay-glow) backwards var(--intro-play),
      glow-breathe 9s ease-in-out calc(var(--delay-glow) + var(--dur-glow)) infinite var(--intro-play);
  }

  /* 0.5 s — Van, su reflejo, sombra de contacto y luz del piso */
  .js .hero__van-frame,
  .js .scene__gloss {
    animation: van-in var(--dur-van) var(--ease-enter) var(--delay-van) backwards var(--intro-play);
  }

  .js .scene__shadow {
    animation: shadow-grow var(--dur-van) var(--ease-enter) var(--delay-van) backwards var(--intro-play);
  }

  .js .scene__floor,
  .js .scene__reflection {
    animation: fade-in var(--dur-van) var(--ease-enter) var(--delay-van) backwards var(--intro-play);
  }

  /* 0.9 s — H1 línea por línea (--i en el HTML) */
  .js .hero__title-line,
  .js .hero__title-text {
    --line-delay: calc(var(--delay-title) + var(--i, 0) * var(--stagger-title));
  }

  /* El recorte dura 60%: para entonces el texto ya está en su lugar
     (quart out) */
  .js .hero__title-line {
    animation: line-mask calc(var(--dur-title) * 0.6) linear var(--line-delay) backwards var(--intro-play);
  }

  .js .hero__title-text {
    animation: line-up var(--dur-title) var(--ease-enter) var(--line-delay) backwards var(--intro-play);
  }

  /* 1.1 s — Header. Nunca se anima el <header>: un transform ahí
     recortaría el overlay fijo del menú móvil (ver layout.css). */
  .js .site-header__brand {
    animation: drop-in var(--dur-header) var(--ease-enter) var(--delay-header) backwards var(--intro-play);
  }

  /* 1.3 s — Ruta: se oculta hasta su turno; el trazo lo dibuja
     heroRoute (transición con --delay-route, arriba) */
  .js .scene__route {
    animation: fade-in 1ms linear var(--delay-route) backwards var(--intro-play);
  }

  /* 1.5 s / ~2.3 s — Pines: cae el ícono; la sombrita y el anillo se
     quedan en el piso */
  .scene__pin--start {
    --pin-delay: var(--delay-pin);
  }

  .scene__pin--end {
    --pin-delay: var(--delay-pin-end);
  }

  .js .scene__pin-icon {
    transform-origin: 50% 100%;
    animation: pin-drop var(--dur-pin) linear var(--pin-delay) backwards var(--intro-play);
  }

  .js .scene__pin::after {
    animation: pin-shadow var(--dur-pin) linear var(--pin-delay) backwards var(--intro-play);
  }

  .js .scene__pin::before {
    animation: pin-ring 0.9s var(--ease-enter) calc(var(--pin-delay) + var(--dur-pin) * 0.4) backwards var(--intro-play);
  }

  /* 1.6 s — Cajas de abajo hacia arriba (--i en el HTML) */
  .js .scene__box {
    --rise-y: 1.5rem;
    animation: rise-in var(--dur-rise) var(--ease-enter) calc(var(--delay-boxes) + var(--i, 0) * var(--stagger-boxes)) backwards var(--intro-play);
  }

  /* 1.8 s — Eyebrow, párrafo, CTA y beneficios */
  .hero__eyebrow { --i: 0; }
  .hero__lead { --i: 1; }
  .hero__cta { --i: 2; }
  .trust__item:nth-child(1) { --i: 3; }
  .trust__item:nth-child(2) { --i: 4; }
  .trust__item:nth-child(3) { --i: 5; }

  .js .hero__eyebrow,
  .js .hero__lead,
  .js .hero__cta,
  .js .trust__item {
    animation: rise-in var(--dur-rise) var(--ease-enter) calc(var(--delay-content) + var(--i, 0) * var(--stagger-content)) backwards var(--intro-play);
  }

  /* 2.0 s — Badges; su giro lento empieza al terminar la entrada */
  .badge:nth-child(2) {
    --i: 1;
  }

  .js .badge {
    --badge-delay: calc(var(--delay-badges) + var(--i, 0) * var(--stagger-badges));
    animation: badge-in var(--dur-badge) var(--ease-enter) var(--badge-delay) backwards var(--intro-play);
  }

  .js .badge__text {
    animation: badge-spin 40s linear calc(var(--badge-delay) + var(--dur-badge)) infinite var(--intro-play);
  }

  /* Header en desktop: la pastilla y las redes bajan junto con el logo
     y los links entran en cascada. En móvil viven en el overlay, que
     tiene su propia entrada. */
  @media (min-width: 56.25em) {
    .js .main-nav,
    .js .site-header__aside {
      animation: drop-in var(--dur-header) var(--ease-enter) var(--delay-header) backwards var(--intro-play);
    }

    .js .main-nav__list > li {
      --drop-y: -0.5rem;
      animation: drop-in 0.6s var(--ease-enter) calc(var(--delay-header) + 0.1s + var(--i, 0) * var(--stagger-nav)) backwards var(--intro-play);
    }
  }

  /* Desktop: terminada la entrada, la van flota 4px en ciclos de 6 s */
  @media (min-width: 64em) {
    .js .hero__van-frame {
      animation:
        van-in var(--dur-van) var(--ease-enter) var(--delay-van) backwards var(--intro-play),
        van-float 6s ease-in-out calc(var(--delay-van) + var(--dur-van)) infinite var(--intro-play);
    }

    .js .scene__gloss {
      animation:
        van-in var(--dur-van) var(--ease-enter) var(--delay-van) backwards var(--intro-play),
        gloss-float 6s ease-in-out calc(var(--delay-van) + var(--dur-van)) infinite var(--intro-play);
    }

    .js .scene__shadow {
      animation:
        shadow-grow var(--dur-van) var(--ease-enter) var(--delay-van) backwards var(--intro-play),
        shadow-float 6s ease-in-out calc(var(--delay-van) + var(--dur-van)) infinite var(--intro-play);
    }
  }

  /* < 1024px: la luz y el brillo de la escena se prenden como los del
     fondo en desktop (0.0 s y 0.3 s) */
  @media (max-width: 63.999em) {
    .js .hero__scene::before {
      animation: fade-in var(--dur-light) var(--ease-enter) var(--delay-light) backwards var(--intro-play);
    }

    .js .hero__scene::after {
      animation:
        glow-ignite var(--dur-glow) ease-in-out var(--delay-glow) backwards var(--intro-play),
        glow-breathe 9s ease-in-out calc(var(--delay-glow) + var(--dur-glow)) infinite var(--intro-play);
    }
  }

  /* ~2.5 s — El brillo cruza el CTA una vez, ya que terminó de subir */
  .js .hero__cta::before {
    animation: cta-shine 1.1s var(--ease-out) calc(var(--delay-content) + var(--stagger-content) * 2 + var(--dur-rise) * 0.7) backwards var(--intro-play);
  }

  /* 2.4 s — WhatsApp flotante: sube al final de la entrada */
  .js .whatsapp-float {
    --rise-y: 1.5rem;
    animation: rise-in var(--dur-badge) var(--ease-enter) var(--delay-float) backwards var(--intro-play);
  }

  /* Puntos de sección: aparecen junto con el WhatsApp */
  .js .page-dots {
    animation: fade-in var(--dur-badge) var(--ease-enter) var(--delay-float) backwards var(--intro-play);
  }
}

/* ---------- Preguntas frecuentes: cambio de tema ----------
   El panel que se muestra aparece y sube un poco. Al quitarle hidden
   pasa de display none a block y la animación vuelve a correr sola. */
@keyframes faq-panel-in {
  from {
    opacity: 0;
    transform: translateY(0.75rem);
  }
}

@media (prefers-reduced-motion: no-preference) {
  .js .faq__panel {
    animation: faq-panel-in 450ms var(--ease-out);
  }
}

/* ---------- Páginas internas (body.page--inner) ----------
   Sin hero no hay coreografía que esperar: el header entra de una vez,
   el contenido sube por bloques justo después y el WhatsApp flotante
   al final. También esperan a .is-loaded (fuentes listas). */
body.page--inner {
  --delay-header: 0s;
  --delay-float: 0.7s;
}

@media (prefers-reduced-motion: no-preference) {
  /* 404: la ruta, el texto, el CTA y los links suben en cascada */
  .js .not-found__inner > * {
    animation: rise-in 0.7s var(--ease-enter) calc(0.15s + var(--i, 0) * 0.08s) backwards var(--intro-play);
  }

  .js .not-found__eyebrow { --i: 1; }
  .js .not-found__title { --i: 2; }
  .js .not-found__lead { --i: 3; }
  .js .not-found__cta { --i: 4; }
  .js .not-found__links { --i: 5; }
}

/* ---------- Nosotros: bloques que suben al entrar en pantalla ----------
   main.js pone .has-reveal en el <html> y .is-revealed en cada bloque
   [data-reveal] al terminar de llegar a su pantalla (o, con scroll
   normal, cuando entra a la vista). Solo la primera vez. Se anima con rise-in (no con transition)
   para no pisar el hover de las tarjetas; las tarjetas se escalonan con
   --i. Si main.js no corre o hay movimiento reducido, todo se ve. */
@media (prefers-reduced-motion: no-preference) {
  /* 0.01 y no 0 (como en la entrada del hero): con 0 el navegador no
     pinta lo oculto y pintaba la galería de Servicios de golpe al llegar
     (tirón de ~300 ms). Así la pinta antes, mientras se ve la pantalla
     anterior. A simple vista sigue invisible. */
  .has-reveal [data-reveal]:not(.is-revealed) {
    opacity: 0.01;
  }

  .has-reveal [data-reveal].is-revealed {
    --rise-y: 1.5rem;
    animation: rise-in 0.9s var(--ease-enter) calc(var(--i, 0) * 0.1s) backwards;
  }
}

/* ---------- Servicios: nombre y raya del panel abierto ----------
   Al abrir o cerrar un panel, su nombre entra con un fundido. Son dos
   keyframes iguales con distinto nombre (cerrado / abierto) para que la
   animación se repita en cada cambio. La raya naranja crece desde la
   izquierda. */
@media (prefers-reduced-motion: no-preference) {
  .service-panel__name {
    animation: panel-name-closed 0.4s var(--ease-out) 0.2s backwards;
  }

  .service-panel.is-active .service-panel__name {
    animation: panel-name-open 0.5s var(--ease-enter) 0.2s backwards;
  }

  .service-panel.is-active .service-panel__heading::before {
    animation: panel-bar 0.5s var(--ease-enter) 0.3s backwards;
  }
}

@keyframes panel-name-closed {
  from {
    opacity: 0;
  }
}

@keyframes panel-name-open {
  from {
    opacity: 0;
    translate: 0 0.5rem;
  }
}

@keyframes panel-bar {
  from {
    scale: 0 1;
  }
}

/* ---------- Movimiento reducido: todo fijo y visible ---------- */
@media (prefers-reduced-motion: reduce) {
  .hero__glow,
  .hero__scene::after {
    will-change: auto;
    animation: none;
  }

  /* WhatsApp: sin escalar en hover ni al esconderse (solo se desvanece) */
  .whatsapp-float:hover,
  .whatsapp-float:focus-visible,
  .whatsapp-float.is-hidden {
    transform: none;
  }

  /* Ruta siempre completa; badges quietos (el hover solo enciende el anillo) */
  .scene__route.is-armed .scene__route-reveal {
    stroke-dashoffset: 0;
    transition: none;
  }

  .badge__text,
  .badge__spin {
    animation: none;
  }

  /* CTA: sin brillo ni desplazamientos; el hover solo intensifica el glow */
  .btn--primary::before {
    display: none;
  }

  .btn--primary:hover,
  .btn--primary:focus-visible,
  .btn--primary:hover .btn__arrow,
  .btn--primary:focus-visible .btn__arrow {
    transform: none;
  }

  .main-nav__indicator.is-ready {
    transition: none;
  }

  /* Servicios: los paneles cambian al instante */
  .service-panel,
  .service-panel__frame,
  .service-panel__frame::after,
  .service-panel__media,
  .service-panel__text,
  .service-panel.is-active .service-panel__text {
    transition: none;
  }

  .site-header__panel .main-nav__list > li,
  .site-header__panel .site-header__aside {
    transform: none;
    transition-delay: 0s !important;
  }
}
