/* ==========================================================================
   DRIFT — Animation system
   Vocabulario de motion por TIPO de elemento, no un fade-up universal:
     · títulos  → máscara (revelar)
     · texto    → fade corto + desplazamiento mínimo
     · líneas   → scaleX / scaleY (indicar)
     · filas    → stagger
   Los estados iniciales solo se aplican con JS activo (.js-ready), así la
   página nunca queda con contenido invisible si el script no corre.
   ========================================================================== */

/* ---------- Máscara: el verbo "revelar" ---------- */
.reveal-mask { display: block; overflow: hidden; }

/* display:block (no inline-block): un inline-block más ancho que su
   contenedor desbordaría y quedaría recortado por el overflow:hidden. */
.reveal-mask > span {
  display: block;
  transform: translateY(110%);
  transition: transform 1s var(--ease-premium);
}

.js-ready .reveal-mask > span { will-change: transform; }
.reveal-mask.in-view > span { transform: none; }

/* ---------- Fade corto ---------- */
.js-ready .reveal-up {
  opacity: 0;
  transform: translateY(14px);
}

.reveal-up {
  transition: opacity var(--dur-reveal) var(--ease-premium),
    transform var(--dur-reveal) var(--ease-premium);
}

.reveal-up.in-view { opacity: 1; transform: none; }

/* ---------- Stagger de filas y proyectos ---------- */
.js-ready .svc { opacity: 0; transform: translateY(20px); }

.svc {
  transition: opacity 700ms var(--ease-premium),
    transform 700ms var(--ease-premium),
    background-color var(--dur-ui) var(--ease-premium);
}

.svc.in-view { opacity: 1; transform: none; }

/* ---------- Por qué elegirnos ----------
   `nth-of-type` y no `nth-child`: el punto del cruce es el primer hijo de la
   grilla, así que con nth-child los cuatro retardos se corrían una posición y
   el último motivo se quedaba sin ninguno. Los motivos son los únicos <li>. */
.js-ready .reasons__grid .reason { opacity: 0; transform: translateY(14px); }

.reasons__grid .reason {
  transition: opacity 700ms var(--ease-premium),
    transform 700ms var(--ease-premium);
}

.reasons__grid.in-view .reason { opacity: 1; transform: none; }
.reasons__grid.in-view .reason:nth-of-type(1) { transition-delay: 140ms; }
.reasons__grid.in-view .reason:nth-of-type(2) { transition-delay: 230ms; }
.reasons__grid.in-view .reason:nth-of-type(3) { transition-delay: 320ms; }
.reasons__grid.in-view .reason:nth-of-type(4) { transition-delay: 410ms; }

/* La etiqueta abre la secuencia. */
.js-ready .reasons__eyebrow { opacity: 0; transform: translateY(10px); }

.reasons__eyebrow {
  transition: opacity 640ms var(--ease-premium), transform 640ms var(--ease-premium);
}

.reasons__eyebrow.in-view { opacity: 1; transform: none; }

.js-ready .project { opacity: 0; transform: translateY(24px); }

.project {
  transition: opacity 800ms var(--ease-premium),
    transform 800ms var(--ease-premium);
}

.project.in-view { opacity: 1; transform: none; }

/* ---------- Sin JS: todo visible ---------- */
html:not(.js-ready) .reveal-mask > span,
html:not(.js-ready) .reveal-up,
html:not(.js-ready) .svc,
html:not(.js-ready) .reason,
html:not(.js-ready) .reasons__eyebrow,
html:not(.js-ready) .project {
  opacity: 1;
  transform: none;
}

html:not(.js-ready) .label::before { transform: scaleX(1); }

/* ==========================================================================
   Entrada del Hero
   Secuencia escalonada al cargar, no al hacer scroll: el hero ya está en
   pantalla. Cada elemento tiene su propio retardo y su propia duración —el
   titular tarda más que el resto porque recorre más distancia— para que no
   se lea como un bloque único apareciendo de golpe.
   ========================================================================== */
.js-ready .hero__eyebrow,
.js-ready .hero__subtext,
.js-ready .hero__actions,
.js-ready .hero__scene {
  opacity: 0;
  transform: translateY(14px);
}

.hero__eyebrow,
.hero__subtext,
.hero__actions {
  transition: opacity 700ms var(--ease-premium), transform 700ms var(--ease-premium);
}

/* La escena entra sólo con opacidad y un desplazamiento mínimo: es una
   fotografía, no un elemento de interfaz. */
.hero__scene {
  transition: opacity 1100ms var(--ease-premium), transform 1100ms var(--ease-premium);
}

.hero.is-ready .hero__eyebrow,
.hero.is-ready .hero__subtext,
.hero.is-ready .hero__actions,
.hero.is-ready .hero__scene {
  opacity: 1;
  transform: none;
}

.hero.is-ready .hero__eyebrow { transition-delay: 0ms; }
.hero.is-ready .hero__title .reveal-mask:nth-child(1) > span { transition-delay: 120ms; }
.hero.is-ready .hero__title .reveal-mask:nth-child(2) > span { transition-delay: 260ms; }
.hero.is-ready .hero__subtext { transition-delay: 460ms; }
.hero.is-ready .hero__actions { transition-delay: 580ms; }

/* La escena cierra la secuencia y una vez colocada NO se mueve más: nada de
   flotar en bucle, ni tilt con el mouse, ni parallax. */
.hero.is-ready .hero__scene { transition-delay: 200ms; }

.hero.is-ready .reveal-mask > span { transform: none; }

/* El indicador de scroll sólo funde en opacidad —nunca en transform—: ese
   eje ya lo ocupa el rebote infinito propio (`hero-scroll-cue` en
   styles.css) y una `transition` y una `animation` sobre la misma propiedad
   se pisarían entre sí. */
.js-ready .hero__scroll-cue { opacity: 0; }
.hero__scroll-cue { transition: opacity 700ms var(--ease-premium); }
.hero.is-ready .hero__scroll-cue { opacity: 0.75; transition-delay: 700ms; }

/* ==========================================================================
   Íconos del Proceso — microinteracciones
   Cada animación representa lo que ocurre en su paso, no adorna:
     01 la idea se enciende · 02 los elementos se conectan ·
     03 el proyecto se arma · 04 el resultado se valida · 05 se pone en marcha

   Disparadores: `.is-active`, que el JS pone en el paso alcanzado por el
   recorrido, y hover/focus. La clase es transitoria: el JS la quita al avanzar,
   así el hover puede volver a reproducir la animación después. Una animación
   CSS no se reinicia si su nombre nunca deja de estar aplicado.

   Las partes que "aparecen" quedan en su estado final con animation-fill-mode
   y vuelven al reposo por transition cuando el disparador se retira, sin corte.
   ========================================================================== */

/* Las transformaciones se calculan sobre la caja del propio trazo, no sobre
   el sistema de coordenadas del SVG completo. */
.process-icon :where(g, path, circle, rect) {
  transform-box: fill-box;
  transform-origin: center;
}

/* ---------- Estados de reposo ---------- */
/* 01 · los puntos arrancan apagados y se encienden en orden. */
.talk-dot { fill: var(--icon-stroke, var(--color-text-faint)); stroke: none; opacity: 0.25; transition: opacity 300ms var(--ease-out-soft); }

/* 03 · la barra se traza; los ángulos entran desde sus lados. */
.code-slash { stroke-dasharray: 1; stroke-dashoffset: 0; }
.code-lt, .code-gt { opacity: 0.32; transition: opacity 300ms var(--ease-out-soft); }

.solution-link {
  stroke-dasharray: 1;
  stroke-dashoffset: 0;
  opacity: 0.28;
  transition: opacity 300ms var(--ease-out-soft);
}



.review-content { opacity: 0.3; transition: opacity 300ms var(--ease-out-soft); }

.review-check {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  opacity: 0;
  transition: opacity 300ms var(--ease-out-soft);
}

.launch-flame,
.launch-sparks { opacity: 0; }

/* ---------- 01 · alguien está escribiendo ---------- */
@keyframes icon-talk-bubble {
  0%, 100% { transform: scale(1); }
  44% { transform: scale(1.03); }
}

@keyframes icon-talk-dot {
  0% { opacity: 0.25; transform: scale(0.55) translateY(1px); }
  50% { opacity: 1; transform: scale(1) translateY(-0.6px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}

/* ---------- 02 · los elementos se conectan ---------- */
@keyframes icon-solution-draw {
  from { stroke-dashoffset: 1; opacity: 0.28; }
  to { stroke-dashoffset: 0; opacity: 1; }
}

@keyframes icon-solution-node {
  from { transform: scale(0.82); }
  to { transform: scale(1); }
}

@keyframes icon-solution-core {
  0%, 100% { transform: scale(1); }
  62% { transform: scale(1.16); }
}

/* ---------- 03 · el signo se arma ---------- */
@keyframes icon-code-lt {
  from { opacity: 0.32; transform: translateX(-3.5px); }
  to { opacity: 1; transform: none; }
}

@keyframes icon-code-gt {
  from { opacity: 0.32; transform: translateX(3.5px); }
  to { opacity: 1; transform: none; }
}

@keyframes icon-code-slash {
  0% { stroke-dashoffset: 1; opacity: 0; }
  22% { opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 1; }
}

/* ---------- 04 · el resultado se valida ---------- */
@keyframes icon-review-screen {
  0%, 100% { transform: scale(1); }
  38% { transform: scale(1.03); }
}

@keyframes icon-review-content {
  from { opacity: 0.3; }
  to { opacity: 1; }
}

@keyframes icon-review-check {
  0% { stroke-dashoffset: 1; opacity: 0; }
  18% { opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 1; }
}

/* ---------- 05 · se pone en marcha ---------- */
@keyframes icon-launch-craft {
  0% { transform: translateY(0); }
  14% { transform: translateY(1.4px); }
  58% { transform: translateY(-4.5px); }
  100% { transform: translateY(0); }
}

@keyframes icon-launch-flame {
  0% { opacity: 0; transform: scaleY(0.45); }
  24% { opacity: 1; transform: scaleY(1); }
  66% { opacity: 1; transform: scaleY(1.1); }
  100% { opacity: 0; transform: scaleY(0.55); }
}

@keyframes icon-launch-spark {
  0% { opacity: 0; transform: translateY(-2px); }
  35% { opacity: 0.85; }
  100% { opacity: 0; transform: translateY(5px); }
}

/* ==========================================================================
   Disparadores de las microinteracciones
   ========================================================================== */
.timeline__step.is-active .talk-bubble,
.timeline__step:hover .talk-bubble,
.timeline__step:focus-within .talk-bubble { animation: icon-talk-bubble 640ms var(--ease-out-soft) both; }

/* Los tres puntos se encienden en orden: es el gesto de alguien escribiendo. */

.timeline__step.is-active .talk-dot--a,
.timeline__step:hover .talk-dot--a,
.timeline__step:focus-within .talk-dot--a { animation: icon-talk-dot 420ms var(--ease-out-soft) 60ms both; }

.timeline__step.is-active .talk-dot--b,
.timeline__step:hover .talk-dot--b,
.timeline__step:focus-within .talk-dot--b { animation: icon-talk-dot 420ms var(--ease-out-soft) 200ms both; }

.timeline__step.is-active .talk-dot--c,
.timeline__step:hover .talk-dot--c,
.timeline__step:focus-within .talk-dot--c { animation: icon-talk-dot 420ms var(--ease-out-soft) 340ms both; }

.timeline__step.is-active .solution-node,
.timeline__step:hover .solution-node,
.timeline__step:focus-within .solution-node { animation: icon-solution-node 420ms var(--ease-out-soft) both; }

.timeline__step.is-active .solution-link,
.timeline__step:hover .solution-link,
.timeline__step:focus-within .solution-link { animation: icon-solution-draw 560ms var(--ease-out-soft) 140ms both; }

.timeline__step.is-active .solution-core,
.timeline__step:hover .solution-core,
.timeline__step:focus-within .solution-core { animation: icon-solution-core 460ms var(--ease-out-soft) 380ms both; }

/* El signo se arma en orden: entra el angulo izquierdo, se traza la barra y

   cierra el derecho. */

.timeline__step.is-active .code-lt,
.timeline__step:hover .code-lt,
.timeline__step:focus-within .code-lt { animation: icon-code-lt 380ms var(--ease-out-soft) both; }

.timeline__step.is-active .code-slash,
.timeline__step:hover .code-slash,
.timeline__step:focus-within .code-slash { animation: icon-code-slash 520ms var(--ease-out-soft) 180ms both; }

.timeline__step.is-active .code-gt,
.timeline__step:hover .code-gt,
.timeline__step:focus-within .code-gt { animation: icon-code-gt 380ms var(--ease-out-soft) 380ms both; }

.timeline__step.is-active .review-screen,
.timeline__step:hover .review-screen,
.timeline__step:focus-within .review-screen { animation: icon-review-screen 620ms var(--ease-out-soft) both; }

.timeline__step.is-active .review-content,
.timeline__step:hover .review-content,
.timeline__step:focus-within .review-content { animation: icon-review-content 380ms var(--ease-out-soft) 100ms both; }

.timeline__step.is-active .review-check,
.timeline__step:hover .review-check,
.timeline__step:focus-within .review-check { animation: icon-review-check 520ms var(--ease-out-soft) 260ms both; }

.timeline__step.is-active .launch-craft,
.timeline__step:hover .launch-craft,
.timeline__step:focus-within .launch-craft { animation: icon-launch-craft 860ms var(--ease-in-out-soft) both; }

.timeline__step.is-active .launch-flame,
.timeline__step:hover .launch-flame,
.timeline__step:focus-within .launch-flame { animation: icon-launch-flame 760ms var(--ease-out-soft) 120ms both; }

.timeline__step.is-active .launch-spark,
.timeline__step:hover .launch-spark,
.timeline__step:focus-within .launch-spark { animation: icon-launch-spark 620ms var(--ease-out-soft) both; }

.timeline__step.is-active .launch-spark:nth-child(2),
.timeline__step:hover .launch-spark:nth-child(2),
.timeline__step:focus-within .launch-spark:nth-child(2) { animation-delay: 160ms; }

.timeline__step.is-active .launch-spark:nth-child(3),
.timeline__step:hover .launch-spark:nth-child(3),
.timeline__step:focus-within .launch-spark:nth-child(3) { animation-delay: 300ms; }

/* En táctil el :hover queda "pegado" tras el tap: ahí no dispara nada. */
@media (hover: none), (pointer: coarse) {
  .timeline__step:hover:not(.is-active) .process-icon :where(g, path, circle, rect) { animation: none; }
}

/* ---------- Preloader ---------- */
.preloader {
  position: fixed;
  inset: 0;
  z-index: var(--z-preloader);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg);
  transition: opacity 0.6s var(--ease-premium), visibility 0.6s var(--ease-premium);
}

.preloader[data-hidden="true"] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.preloader__mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
}

.preloader__icon { width: 38px; height: auto; }

.preloader__line {
  width: 160px;
  height: 1px;
  background: var(--color-border-strong);
  position: relative;
  overflow: hidden;
}

.preloader__line::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 40%;
  background: var(--color-accent);
  animation: preloader-sweep 1.1s var(--ease-in-out-soft) infinite;
}

@keyframes preloader-sweep {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}

/* ---------- Selección y scrollbar ---------- */
::selection { background: var(--color-accent); color: var(--color-bg); }

html {
  scrollbar-width: thin;
  scrollbar-color: var(--color-border-strong) transparent;
}

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--color-border-strong); border-radius: var(--radius-full); }
::-webkit-scrollbar-thumb:hover { background: var(--color-text-faint); }


/* ==========================================================================
   Reduced motion
   No basta con frenar: cada elemento resuelve a su estado final legible.
   El barrido ambiental se retira por completo (movimiento continuo sin
   función informativa) y el riel del Proceso queda recorrido entero.
   El smooth scroll de los anchors NO se apaga acá a propósito (ver
   css/reset.css): pedido explícito de que funcione igual en cualquier
   navegador, sin depender de la preferencia de movimiento del sistema.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .reveal-mask > span,
  .reveal-up,
  .svc,
  .reason,
  .reasons__eyebrow,
  .project,
  .hero__eyebrow,
  .hero__subtext,
  .hero__actions,
  .hero__scene,
  .hero__scroll-cue {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }


  /* Qué hacemos: el ícono no se desplaza y la regla de cierre no crece. */
  .svc:hover .svc__icon svg { transform: none !important; }
  .svc:hover .svc__rule { width: 72px !important; }

  /* Por qué elegirnos: los íconos conservan su encendido —color y opacidad—
     y pierden todo desplazamiento. La regla bajo el ícono no crece. */
  .reason:hover :where(.tg-arrow, .tk-dot, .tm-side, .id-rays) { transform: none !important; }
  .reason:hover .reason__rule { width: 34px !important; }

  .label::before { transform: scaleX(1) !important; }

  /* Proceso queda directamente en su estado final. La línea completa y los
     cinco pasos permanecen visibles, pero no se ejecutan microanimaciones. */
  .timeline__step .process-icon :where(g, path, circle, rect),
  .timeline__step .timeline__node,
  .timeline__step .timeline__ring {
    animation: none !important;
    transform: none !important;
    transition: none !important;
  }

  .preloader__line::after { animation: none; width: 100%; }
}
