/* ==========================================================================
   Enviamo — movimiento
   Requiere base.css (variables de marca y curva base de los botones).

   Criterio: el movimiento aqui es confirmacion, no espectaculo. Cada
   animacion responde a algo que ha hecho el usuario (scroll, hover, carga) y
   dura lo justo para que el ojo la registre sin llegar a esperarla. Si una
   transicion se percibe como "una animacion", se ha pasado.

   Todo lo que desplaza contenido se apoya en scroll-driven animations
   (animation-timeline), asi que no hay ni un IntersectionObserver detras.
   El fallback no es una version degradada: es el contenido quieto y visible.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Curvas y tiempos
   Un set corto y reutilizado. Mezclar diez easings distintos es exactamente
   lo que hace que una landing parezca ensamblada por piezas sueltas.
   -------------------------------------------------------------------------- */

:root {
  /* La misma curva que ya usan los botones en base.css: arranque rapido y
     frenada larga. Es la que da sensacion de "peso ligero". */
  --curva: cubic-bezier(.2, .8, .2, 1);
  /* Para salidas y repliegues: acelera al final, se va sin pedir atencion. */
  --curva-salida: cubic-bezier(.4, 0, 1, 1);
  /* Overshoot minusculo (1.02 de pico, no 1.15). Se nota en el cuerpo, no
     en la vista. Cualquier valor mas alto ya lee como rebote de plantilla. */
  --curva-firme: cubic-bezier(.34, 1.26, .64, 1);

  --t-rapido: .16s;   /* feedback directo: hover, press */
  --t-medio: .28s;    /* cambios de estado visibles */
  --t-lento: .62s;    /* entradas de bloque */
}

/* @property permite interpolar valores que normalmente saltan de golpe.
   Sin esto, un angulo dentro de conic-gradient() no anima: cambia a saltos. */
@property --angulo-brillo {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

/* Idem para el barrido del texto degradado: sin @property, background-position
   sobre un gradiente con background-clip:text parpadea en Safari. */
@property --pos-degradado {
  syntax: '<percentage>';
  initial-value: 0%;
  inherits: false;
}

/* --------------------------------------------------------------------------
   .revela — entrada al entrar en viewport
   Regla de oro: el estado por defecto es VISIBLE. La animacion solo existe
   dentro del @supports, de modo que un navegador sin soporte no esconde nada.
   Nunca al reves.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
  .revela {
    animation: revelar linear both;
    animation-timeline: view();
    /* Empieza cuando al elemento le faltan 100px para asomar y termina
       cuando lleva un 30% de altura dentro: acaba pronto, asi el usuario
       nunca lee un parrafo a medio opacar. */
    animation-range: entry 100px cover 30%;
  }

  /* El timeline es lineal por definicion (lo marca el scroll), asi que la
     curva se aplica con easing dentro del keyframe, no en animation-timing. */
  @keyframes revelar {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
  }

  /* --------------------------------------------------------------------------
     .revela-escalonado — el mismo gesto, desfasado por hijo
     El retardo NO es temporal (no hay reloj en un scroll timeline): se desplaza
     el rango de cada hijo. Asi el escalonado se mantiene aunque el usuario
     baje muy rapido o haga scroll hacia atras.
     -------------------------------------------------------------------------- */

  .revela-escalonado > * {
    animation: revelar linear both;
    animation-timeline: view();
    animation-range:
      entry calc(90px + var(--i, 0) * 34px)
      cover calc(28% + var(--i, 0) * 4%);
  }
}

/* Sin --i declarado en el HTML el escalonado se pierde pero no rompe nada:
   todos los hijos comparten rango y entran juntos. Degradar a "a la vez" es
   preferible a obligar a escribir style="--i:n" en cada <li>. */

/* --------------------------------------------------------------------------
   .brillo-borde — el borde capta la luz al pasar el raton
   Truco: el gradiente conico vive en un ::before detras del contenido y se
   recorta con una mascara de dos capas que deja ver solo 1px de perimetro.
   Animar un border-image seria mas simple pero no se puede interpolar.
   -------------------------------------------------------------------------- */

.brillo-borde {
  position: relative;
  border-radius: var(--radio);
  isolation: isolate; /* evita que el ::before se cuele sobre hermanos con z-index */
}

.brillo-borde::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px; /* el grosor real del borde luminoso */
  background: conic-gradient(
    from var(--angulo-brillo),
    transparent 0turn,
    var(--violeta) .12turn,
    var(--verde) .2turn,
    transparent .34turn,
    transparent 1turn
  );
  /* Dos fondos + xor: el relleno se resta y queda solo el anillo del padding.
     -webkit-mask va duplicado porque Safari aun no acepta el sin prefijo. */
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  transition: opacity var(--t-medio) var(--curva);
  pointer-events: none;
}

.brillo-borde:hover::before,
/* :focus-within y no :focus: la tarjeta suele contener el enlace real, y
   quien navega con teclado merece la misma pista visual que con raton. */
.brillo-borde:focus-within::before {
  opacity: 1;
  /* La rotacion solo corre mientras hay hover. Un conic girando en bucle en
     seis tarjetas a la vez es ruido y consume GPU sin que nadie lo mire. */
  animation: girar-brillo 3.2s linear infinite;
}

@keyframes girar-brillo {
  to { --angulo-brillo: 1turn; }
}

/* --------------------------------------------------------------------------
   .elevacion — hover que levanta el elemento
   -4px es el limite util: a partir de ahi el elemento "flota" y se nota el
   salto de la sombra al reordenarse el resto de la rejilla.
   -------------------------------------------------------------------------- */

.elevacion {
  transition:
    transform var(--t-medio) var(--curva),
    box-shadow var(--t-medio) var(--curva),
    border-color var(--t-medio) var(--curva);
}

.elevacion:hover,
.elevacion:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--sombra-alta);
}

/* La vuelta al reposo va mas rapida y con otra curva. Si sube y baja con el
   mismo tiempo, el elemento parece pegajoso al retirar el raton. */
.elevacion:not(:hover) {
  transition-duration: var(--t-rapido);
  transition-timing-function: var(--curva-salida);
}

/* El press cancela la elevacion en el acto: confirma la pulsacion. */
.elevacion:active {
  transform: translateY(-1px);
  transition-duration: .08s;
}

/* --------------------------------------------------------------------------
   .barra-progreso-scroll — progreso de lectura de la pagina
   scaleX en vez de width: width recalcula layout en cada frame de scroll y
   es la causa numero uno de que una barra de progreso vaya a tirones.
   -------------------------------------------------------------------------- */

.barra-progreso-scroll {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px; /* mas de 3px y deja de ser un detalle para ser un elemento */
  z-index: 100;
  background: linear-gradient(90deg, var(--violeta), var(--verde));
  transform: scaleX(0);
  transform-origin: 0 50%;
  /* Sin soporte la barra queda a 0 y simplemente no se ve: no tapa nada ni
     deja una franja de color fija en lo alto de la pagina. */
}

@supports (animation-timeline: scroll()) {
  .barra-progreso-scroll {
    animation: llenar-barra linear both;
    animation-timeline: scroll(root block);
    will-change: transform; /* justificado: se repinta en cada frame de scroll */
  }

  @keyframes llenar-barra {
    to { transform: scaleX(1); }
  }
}

/* --------------------------------------------------------------------------
   .contador — cifras grandes de metrica
   La escala sube desde .94 (no desde .5): la cifra tiene que ser legible
   durante toda la animacion, no materializarse. Escalar texto grande es de
   los pocos sitios donde el overshoot suma, porque imita el peso del numero.
   -------------------------------------------------------------------------- */

.contador {
  display: inline-block; /* transform no aplica a cajas inline puras */
  font-variant-numeric: tabular-nums; /* si el JS incrementa, no baila el ancho */
}

@supports (animation-timeline: view()) {
  .contador {
    animation: entrar-cifra linear both;
    animation-timeline: view();
    animation-range: entry 80px cover 25%;
  }

  @keyframes entrar-cifra {
    from { opacity: 0; transform: scale(.94) translateY(8px); }
    60%  { opacity: 1; transform: scale(1.012) translateY(0); }
    to   { opacity: 1; transform: scale(1); }
  }
}

/* --------------------------------------------------------------------------
   .marquesina — cinta de logos
   Lineal y sin pausa por diseño: una cinta con easing parece averiada.
   Estructura esperada:
     <div class="marquesina"><div class="marquesina__pista">…logos…</div></div>
   La pista debe contener la lista de logos DUPLICADA (los del duplicado, con
   aria-hidden="true"): el bucle es perfecto solo si se recorre exactamente
   el 50% del ancho total.
   -------------------------------------------------------------------------- */

.marquesina {
  overflow: hidden;
  /* Los logos no se cortan de golpe en el borde: se apagan. Sin esto se ve
     el corte del overflow y delata el truco del duplicado. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8rem, #000 calc(100% - 8rem), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8rem, #000 calc(100% - 8rem), transparent);
  padding-block: 1.2rem;
}

.marquesina__pista {
  display: flex;
  align-items: center;
  gap: clamp(2.5rem, 1.5rem + 3vw, 5rem);
  width: max-content;
  animation: desfilar 42s linear infinite;
  /* Muy lenta a proposito: la cinta es contexto, no un carrusel que haya que
     seguir. Si el usuario percibe velocidad, le esta robando la atencion al
     titular que tiene al lado. */
  will-change: transform; /* justificado: animacion continua sobre transform */
}

/* Pausa al pasar el raton y al enfocar con teclado: si los logos son enlaces,
   perseguir un objetivo en movimiento es un fallo de accesibilidad. */
.marquesina:hover .marquesina__pista,
.marquesina:focus-within .marquesina__pista {
  animation-play-state: paused;
}

@keyframes desfilar {
  /* -50% y no -100%: el contenido esta duplicado, asi que a mitad de recorrido
     la posicion es visualmente identica al inicio y el salto es invisible. */
  to { transform: translateX(-50%); }
}

/* --------------------------------------------------------------------------
   .texto-degradado — barrido de luz sobre el titular
   Violeta a indigo, un rango cromatico estrecho a proposito: el degradado
   tiene que leerse como una variacion de tinta, no como un arcoiris. El verde
   de marca no entra aqui — se reserva para estados de exito.
   -------------------------------------------------------------------------- */

.texto-degradado {
  background-image: linear-gradient(
    100deg,
    var(--violeta-tinta) 0%,
    var(--violeta) 38%,
    #5b54ff 50%,
    var(--violeta) 62%,
    var(--violeta-tinta) 100%
  );
  background-size: 250% 100%;
  background-position: var(--pos-degradado) 0;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* Si el navegador no recorta el fondo al texto, "color: transparent" dejaria
     el titular invisible. Esta es la red de seguridad. */
  -webkit-text-fill-color: transparent;
  animation: barrer-degradado 9s var(--curva) infinite alternate;
}

@supports not ((background-clip: text) or (-webkit-background-clip: text)) {
  .texto-degradado {
    color: var(--violeta);
    -webkit-text-fill-color: currentColor;
    animation: none;
  }
}

@keyframes barrer-degradado {
  /* Recorrido corto (0 -> 100% de 250%) y con alternate: el degradado respira
     en lugar de desfilar. Un bucle completo en un titular cansa a la segunda
     vuelta. */
  to { --pos-degradado: 100%; }
}

/* --------------------------------------------------------------------------
   .magnetico — soporte CSS para el efecto iman
   El JS solo escribe dos numeros: --iman-x / --iman-y en px. Todo lo demas
   (composicion del transform, curvas, capa interna) se decide aqui, para que
   el script no tenga que conocer ni el layout ni el estado de hover.
   -------------------------------------------------------------------------- */

.magnetico {
  --iman-x: 0px;
  --iman-y: 0px;
  position: relative;
  transform: translate3d(var(--iman-x), var(--iman-y), 0);
  /* Tiempo largo (.45s) en reposo para que el retorno al centro sea suave.
     Mientras hay puntero encima se acorta: si no, el boton persigue al raton
     con retraso y se siente elastico en lugar de magnetico. */
  transition: transform .45s var(--curva);
  will-change: transform;
}

.magnetico:hover {
  transition-duration: .1s;
}

/* El JS no deberia tener que resetear nada al salir: basta con poner las dos
   variables a 0 y esta regla devuelve el boton a su sitio con la curva larga. */
.magnetico:not(:hover) {
  --iman-x: 0px;
  --iman-y: 0px;
}

/* Capa interna opcional (<span class="magnetico__nucleo">): el texto se
   desplaza menos que la caja. Ese desfase es lo que vende el efecto — sin el,
   el boton entero se limita a deslizarse. */
.magnetico__nucleo {
  display: inline-flex;
  align-items: center;
  gap: inherit;
  transform: translate3d(
    calc(var(--iman-x) * -.35),
    calc(var(--iman-y) * -.35),
    0
  );
  transition: transform .45s var(--curva);
}

.magnetico:hover .magnetico__nucleo { transition-duration: .1s; }

/* El press gana al iman: escala sobre el translate que haya en ese momento. */
.magnetico:active {
  transform: translate3d(var(--iman-x), var(--iman-y), 0) scale(.97);
  transition-duration: .08s;
}

/* --------------------------------------------------------------------------
   .aparece-al-cargar — secuencia de entrada del hero
   Con @starting-style el navegador anima desde el primer render, sin clases
   que añadir por JS ni el parpadeo tipico del patron ".cargado" que llega un
   frame tarde.
   -------------------------------------------------------------------------- */

.aparece-al-cargar {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--t-lento) var(--curva),
    transform var(--t-lento) var(--curva);
  /* El retardo lo marca el orden de lectura, no el diseño: primero titular,
     luego entradilla, luego botones. --i lo fija el HTML. */
  transition-delay: calc(var(--i, 0) * 90ms);
}

@starting-style {
  .aparece-al-cargar {
    opacity: 0;
    transform: translateY(14px);
  }
}

/* Sin @starting-style la regla de arriba ya deja el elemento en su estado
   final: el hero aparece de golpe, que es exactamente el comportamiento
   correcto cuando no hay soporte. */

/* Para elementos que ademas se muestran/ocultan (display:none), la transicion
   de opacidad se perderia: allow-discrete hace que display espere a que la
   animacion acabe en lugar de cortarla. */
.aparece-al-cargar[hidden],
.aparece-al-cargar.oculto {
  display: none;
  transition-behavior: allow-discrete;
}

/* --------------------------------------------------------------------------
   Preferencia de movimiento reducido
   base.css ya neutraliza duraciones globalmente. Aqui hace falta ir mas alla:
   una duracion de .001ms deja el elemento en su estado FINAL, pero una
   animacion scroll-driven no tiene "final" temporal — se quedaria congelada
   en el frame que marque el scroll, es decir, potencialmente invisible.
   Por eso aqui se retira el timeline entero.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .revela,
  .revela-escalonado > *,
  .contador,
  .texto-degradado,
  .marquesina__pista,
  .brillo-borde::before,
  .barra-progreso-scroll {
    animation: none !important;
    animation-timeline: none !important;
    opacity: 1 !important;
    transform: none !important;
  }

  /* La barra de progreso si conserva su funcion: solo deja de animarse.
     Se muestra completa porque sin timeline no hay valor de scroll que leer. */
  .barra-progreso-scroll { transform: scaleX(1) !important; }

  /* El borde luminoso se vuelve un borde fijo y discreto: la pista visual de
     "esto es interactivo" se mantiene sin movimiento. */
  .brillo-borde::before {
    opacity: .5 !important;
    background: var(--linea) !important;
  }

  .magnetico,
  .magnetico__nucleo {
    --iman-x: 0px;
    --iman-y: 0px;
    will-change: auto;
  }

  .marquesina {
    /* Sin desplazamiento la cinta se convierte en una lista: que se pueda
       recorrer a mano en lugar de quedar cortada. */
    overflow-x: auto;
    scrollbar-width: none;
  }
}

/* --------------------------------------------------------------------------
   Higiene de rendimiento
   will-change es un presupuesto, no un adorno: cada capa promocionada ocupa
   memoria de GPU. Se retira en cuanto el elemento deja de moverse.
   -------------------------------------------------------------------------- */

.elevacion:hover { will-change: transform, box-shadow; }
.elevacion:not(:hover) { will-change: auto; }

/* --------------------------------------------------------------------------
   Respaldo para navegadores sin scroll-driven animations (Safari, Firefox).
   El JS marca `.revelado` con IntersectionObserver. Va al final del fichero
   para ganar en cascada a cualquier regla anterior.
   -------------------------------------------------------------------------- */

@supports not (animation-timeline: view()) {
  .revela,
  .revela-escalonado > * {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity .6s cubic-bezier(.2, .8, .2, 1),
                transform .6s cubic-bezier(.2, .8, .2, 1);
  }

  .revela.revelado,
  .revela-escalonado.revelado > * {
    opacity: 1;
    transform: none;
  }

  /* El escalonado usa el indice que ya declara el HTML */
  .revela-escalonado.revelado > * {
    transition-delay: calc(var(--i, 0) * 80ms);
  }
}
