/* ==========================================================================
   INVEXUS · motion.css
   INVEXUS MOTION SYSTEM · la capa de experiencia visual dinámica.

   SE CARGA DESPUÉS DE components.css Y ANTES DE responsive.css.

   QUÉ ES ESTO Y QUÉ NO ES
   No es una colección de efectos. Es un SISTEMA: tokens de intensidad,
   utilidades con nombre, y presets de ambiente. Cualquier efecto del sitio se
   sube o se baja desde aquí, en un sitio, sin tocar una sola plantilla.

   LA REGLA QUE GOBIERNA TODO EL FICHERO
   Sólo se animan transform, opacity y filter. Nada que fuerce al navegador a
   recalcular el layout —width, height, top, left, margin— porque eso es lo
   que convierte una animación bonita en una web lenta, y el encargo dice que
   el rendimiento manda.

   PRIORIDAD TÉCNICA, en este orden y por este motivo:
       CSS  >  SVG  >  JS vanilla  >  Canvas ligero
   Todo lo de este fichero es CSS puro. El JS sólo aporta tres cosas que el
   CSS no puede saber: dónde está el cursor, cuándo un elemento entra en
   pantalla y dónde se hizo clic.

   NADA DE ESTO ES NECESARIO PARA LEER EL SITIO. Si el CSS no carga, si el JS
   falla o si el visitante pidió menos movimiento, la web sigue completa.
   ========================================================================== */

/* ==========================================================================
   1 · TOKENS DE MOVIMIENTO
   Un solo sitio donde subir o bajar la intensidad de todo.
   ========================================================================== */
:root {
  /* --- INTENSIDAD GLOBAL ------------------------------------------------
     Multiplicador de 0 a 1. Bajarlo aquí atenúa el sistema entero sin tocar
     ninguna regla: es la palanca que pidió el encargo.                    */
  --inv-motion: 1;

  /* --- ELEVACIÓN EN HOVER · «discreto y controlado» ---------------------
     3px. Se probaron 6 y 8: a partir de 4px el movimiento deja de leerse
     como respuesta y empieza a leerse como salto.                         */
  --inv-lift:        calc(3px * var(--inv-motion));
  --inv-lift-strong: calc(5px * var(--inv-motion));

  /* --- MICROPARALLAX DEL CURSOR ----------------------------------------
     Desplazamiento MÁXIMO de las capas de luz del hero. 14px sobre un lienzo
     de 100vw es imperceptible como movimiento y perfectamente perceptible
     como profundidad. Es la diferencia entre premium y mareo.             */
  --inv-parallax:    calc(14px * var(--inv-motion));

  /* --- AMBIENTE LÍQUIDO -------------------------------------------------
     Duraciones deliberadamente largas. Una onda que se repite cada 8 s se
     percibe como un bucle; a 26-40 s el ojo deja de encontrarle el patrón y
     lo lee como agua.                                                      */
  --inv-liquid-a: 34s;
  --inv-liquid-b: 46s;
  --inv-liquid-c: 28s;

  /* --- VIDRIO -----------------------------------------------------------
     El desenfoque cuesta: cada superficie con backdrop-filter obliga al
     compositor a rasterizar lo que hay detrás. 14px es suficiente para leer
     «vidrio» y barato; 40px es un fundido caro que además tapa el fondo que
     justificaba el vidrio.                                                */
  --inv-glass-blur:   14px;
  --inv-glass-bg:     rgba(255,255,255,.66);
  --inv-glass-border: rgba(255,255,255,.55);
  --inv-glass-sheen:  rgba(255,255,255,.55);

  /* --- ONDA (ripple) ---------------------------------------------------- */
  --inv-ripple-color: rgba(255,255,255,.42);
  --inv-ripple-time:  620ms;

  /* --- POSICIÓN DEL CURSOR ---------------------------------------------
     El JS escribe aquí, en fracciones de 0 a 1, y el CSS lo consume. Es todo
     el contrato entre los dos: el JS no toca ni un estilo.                */
  --inv-mx: .5;
  --inv-my: .5;
}

/* Sobre superficies oscuras el vidrio es humo, no leche. */
.inv-hero, .inv-phero, .inv-section--navy, .inv-section--gradient,
:root[data-theme="dark"] {
  --inv-glass-bg:     rgba(12,32,52,.52);
  --inv-glass-border: rgba(255,255,255,.16);
  --inv-glass-sheen:  rgba(255,255,255,.22);
}

/* ==========================================================================
   2 · EL INTERRUPTOR GENERAL
   Tres formas de apagarlo, y las tres tienen que funcionar:
     · el visitante pidió menos movimiento
     · la pantalla es pequeña y el efecto no aporta
     · alguien puso data-inv-motion="off" en el <html>
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  :root { --inv-motion: 0; }
}
:root[data-inv-motion="off"] { --inv-motion: 0; }

/* Con la intensidad a cero, lo que se apaga NO es sólo el movimiento: son las
   capas decorativas enteras. Dejar una animación con duración 1ms sigue
   costando compositor y sigue parpadeando. Se retiran. */
@media (prefers-reduced-motion: reduce) {
  .inv-ambient,
  .inv-ambient__capa,
  .inv-ambient__brillo,
  .inv-ripple__onda { display: none !important; }
  .inv-reveal,
  .inv-reveal-blur { opacity: 1 !important; transform: none !important; filter: none !important; }
  * { animation-duration: .001ms !important; animation-iteration-count: 1 !important; }
}
:root[data-inv-motion="off"] .inv-ambient { display: none !important; }

/* ==========================================================================
   3 · AMBIENTE · el preset LIQUID
   ==========================================================================
   QUÉ ES
   Tres manchas de luz de gran tamaño, desenfocadas, que se desplazan muy
   despacio y en direcciones distintas. Al superponerse producen zonas de más
   y menos densidad: es lo que el ojo lee como agua atravesada por luz.

   POR QUÉ NO ES UN VÍDEO NI UN CANVAS
   Un vídeo de fondo son megabytes y un decodificador funcionando siempre. Un
   canvas son millones de píxeles recalculados por fotograma. Esto son tres
   gradientes radiales que el compositor mueve en la GPU: cuesta lo mismo que
   desplazar tres imágenes, y no hay imágenes.

   POR QUÉ NO USA filter: blur()
   Porque un blur de 80px sobre un elemento del tamaño de la pantalla es de
   las operaciones más caras que existen en un navegador. El desenfoque ya
   está DENTRO del gradiente: un radial-gradient con parada suave ES una
   mancha desenfocada, y no cuesta nada.
   ========================================================================== */
.inv-ambient {
  position: absolute; inset: 0; overflow: hidden;
  pointer-events: none;          /* jamás intercepta un clic */
  z-index: 0;
  border-radius: inherit;
  contain: layout paint size;    /* aísla al compositor del resto de la página */
}
.inv-ambient__capa {
  position: absolute;
  width: 78vmax; height: 78vmax;
  border-radius: 50%;
  will-change: transform;
  /* La capa se mueve con el cursor Y con su propia animación. Las dos cosas
     conviven porque el desplazamiento del cursor va en la variable y la
     animación en el keyframe: no compiten por la misma propiedad. */
  transform: translate3d(
      calc((var(--inv-mx) - .5) * var(--inv-parallax) * var(--f, 1)),
      calc((var(--inv-my) - .5) * var(--inv-parallax) * var(--f, 1)), 0);
}

/* Las tres manchas. Colores de la paleta, nunca inventados. */
.inv-ambient__capa--1 {
  --f: 1;
  top: -26%; left: -14%;
  background: radial-gradient(circle at 50% 50%,
      rgba(45,128,195,.30) 0%, rgba(45,128,195,.13) 38%, rgba(45,128,195,0) 68%);
  animation: inv-liquid-a var(--inv-liquid-a) ease-in-out infinite;
}
.inv-ambient__capa--2 {
  --f: -1.6;
  bottom: -34%; right: -18%;
  background: radial-gradient(circle at 50% 50%,
      rgba(18,168,199,.26) 0%, rgba(18,168,199,.11) 40%, rgba(18,168,199,0) 70%);
  animation: inv-liquid-b var(--inv-liquid-b) ease-in-out infinite;
}
.inv-ambient__capa--3 {
  --f: .7;
  top: 18%; right: 8%;
  width: 52vmax; height: 52vmax;
  background: radial-gradient(circle at 50% 50%,
      rgba(63,195,222,.20) 0%, rgba(63,195,222,.08) 44%, rgba(63,195,222,0) 72%);
  animation: inv-liquid-c var(--inv-liquid-c) ease-in-out infinite;
}

/* Los recorridos son distintos y de duración prima entre sí para que las tres
   manchas no vuelvan nunca a la misma posición relativa. Es lo que evita que
   se perciba el bucle. */
@keyframes inv-liquid-a {
  0%,100% { translate: 0 0;         scale: 1; }
  33%     { translate: 6% 4%;       scale: 1.08; }
  66%     { translate: -4% 7%;      scale: .96; }
}
@keyframes inv-liquid-b {
  0%,100% { translate: 0 0;         scale: 1; }
  40%     { translate: -7% -5%;     scale: 1.11; }
  70%     { translate: 5% -3%;      scale: .94; }
}
@keyframes inv-liquid-c {
  0%,100% { translate: 0 0;         scale: 1; opacity: .9; }
  50%     { translate: -5% 6%;      scale: 1.14; opacity: 1; }
}

/* --- LA LÁMINA DE AGUA ---------------------------------------------------
   Una banda diagonal de luz que cruza muy despacio. Es lo que separa «fondo
   con gradientes» de «superficie de agua»: el agua tiene un brillo que se
   desplaza, no sólo color que cambia.                                      */
.inv-ambient__brillo {
  position: absolute; inset: -20%;
  background: linear-gradient(104deg,
      transparent 38%,
      rgba(255,255,255,.05) 47%,
      rgba(255,255,255,.09) 50%,
      rgba(255,255,255,.05) 53%,
      transparent 62%);
  will-change: transform;
  animation: inv-lamina 22s linear infinite;
}
@keyframes inv-lamina {
  from { transform: translate3d(-28%, 0, 0); }
  to   { transform: translate3d( 28%, 0, 0); }
}

/* --- PRESETS ALTERNATIVOS · declarados, NO activos ------------------------
   El encargo pide que existan como presets y permanezcan desactivados. Están
   aquí, con su marcado descrito, y ninguna plantilla los emite hoy.
   Se activan cambiando la clase del contenedor: .inv-ambient--wind, etc.
   NO son dependencia de nada: si se borrara este bloque, el sitio no cambia.

   WIND    · deriva horizontal más rápida y más plana
   AUTUMN  · deriva descendente con rotación, tonos cálidos
   SNOW    · caída vertical lenta, partículas pequeñas y opacas
   -------------------------------------------------------------------------- */
.inv-ambient--wind .inv-ambient__capa { animation-duration: 18s, 18s; }
.inv-ambient--wind .inv-ambient__brillo { animation-duration: 11s; }

.inv-ambient--autumn .inv-ambient__capa--1 {
  background: radial-gradient(circle at 50% 50%,
      rgba(244,166,42,.24) 0%, rgba(244,166,42,.10) 40%, rgba(244,166,42,0) 70%);
}
.inv-ambient--autumn .inv-ambient__capa--2 {
  background: radial-gradient(circle at 50% 50%,
      rgba(214,69,69,.18) 0%, rgba(214,69,69,.07) 42%, rgba(214,69,69,0) 72%);
}

.inv-ambient--snow .inv-ambient__capa {
  background: radial-gradient(circle at 50% 50%,
      rgba(255,255,255,.16) 0%, rgba(255,255,255,.05) 42%, rgba(255,255,255,0) 70%);
}

/* En pantallas pequeñas el ambiente se simplifica: dos capas menos y sin la
   lámina. No es una degradación estética, es que en 360px de ancho las tres
   manchas se solapan tanto que se ven como una sola mancha plana. */
@media (max-width: 767px) {
  .inv-ambient__capa--3,
  .inv-ambient__brillo { display: none; }
  .inv-ambient__capa { width: 120vw; height: 120vw; }
}

/* ==========================================================================
   4 · VIDRIO · glassmorphism, aplicado con cuentagotas
   ==========================================================================
   DÓNDE SÍ: Funding, IA, Datos, CTA y elementos destacados. Ahí el vidrio
   comunica algo —hay una capa de análisis entre el dato y quien lo lee—.
   DÓNDE NO: en todo lo demás. Un sitio entero de vidrio no se lee como
   premium, se lee como una plantilla comprada.
   ========================================================================== */
.inv-glass {
  position: relative;
  background: var(--inv-glass-bg);
  border: 1px solid var(--inv-glass-border);
  border-radius: var(--inv-radius-lg);
}
/* El desenfoque va SÓLO donde el navegador lo soporta y sólo en pantallas
   que no son de gama baja. @supports evita el fondo semitransparente sin
   desenfocar, que se ve sucio. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  @media (min-width: 768px) {
    .inv-glass {
      -webkit-backdrop-filter: blur(var(--inv-glass-blur)) saturate(140%);
              backdrop-filter: blur(var(--inv-glass-blur)) saturate(140%);
    }
  }
}

/* El canto superior iluminado. Es el detalle que convierte un rectángulo
   translúcido en una lámina de vidrio: el vidrio real refleja la luz en su
   borde, no en su cara. */
.inv-glass::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(160deg,
      var(--inv-glass-sheen) 0%, transparent 42%);
  opacity: .5;
}

/* ==========================================================================
   5 · HOVER REACTIVO
   ==========================================================================
   Todo lo de aquí está bajo @media (hover: hover): en una pantalla táctil el
   estado :hover se queda PEGADO tras el toque, y una tarjeta que se queda
   elevada después de tocarla parece rota.
   ========================================================================== */
@media (hover: hover) and (pointer: fine) {

  /* --- ELEVACIÓN Y LUZ · tarjetas -------------------------------------- */
  .inv-lift {
    transition: transform var(--inv-duration-base) var(--inv-ease),
                box-shadow var(--inv-duration-base) var(--inv-ease),
                border-color var(--inv-duration-base) var(--inv-ease);
  }
  .inv-lift:hover {
    transform: translate3d(0, calc(-1 * var(--inv-lift)), 0);
    box-shadow: var(--inv-shadow-md);
    border-color: color-mix(in srgb, var(--inv-cyan-ink) 40%, var(--inv-border));
  }

  /* --- BORDE LUMINOSO QUE SIGUE AL CURSOR -------------------------------
     Un halo que aparece bajo el puntero, dentro de la tarjeta. El JS sólo
     escribe --inv-mx/--inv-my del elemento; el degradado lo hace el CSS.  */
  .inv-glow { position: relative; isolation: isolate; }
  .inv-glow::after {
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    pointer-events: none; z-index: -1;
    opacity: 0;
    transition: opacity var(--inv-duration-base) var(--inv-ease);
    background: radial-gradient(
        22rem circle at calc(var(--inv-mx) * 100%) calc(var(--inv-my) * 100%),
        color-mix(in srgb, var(--inv-cyan-ink) 16%, transparent) 0%,
        transparent 62%);
  }
  .inv-glow:hover::after { opacity: 1; }

  /* --- REFLEJO DE VIDRIO · un destello que cruza al entrar ------------- */
  .inv-sheen { position: relative; overflow: hidden; }
  .inv-sheen::after {
    content: ""; position: absolute; top: 0; bottom: 0; width: 45%;
    left: -60%;
    pointer-events: none;
    background: linear-gradient(100deg,
        transparent 0%, rgba(255,255,255,.16) 50%, transparent 100%);
    transition: transform 620ms var(--inv-ease);
    transform: translate3d(0,0,0);
  }
  .inv-sheen:hover::after { transform: translate3d(340%, 0, 0); }

  /* --- BOTONES · desplazamiento del gradiente -------------------------- */
  .inv-btn { transition: background-color var(--inv-duration-base) var(--inv-ease),
                         color var(--inv-duration-base) var(--inv-ease),
                         border-color var(--inv-duration-base) var(--inv-ease),
                         transform var(--inv-duration-micro) var(--inv-ease),
                         box-shadow var(--inv-duration-base) var(--inv-ease); }
  .inv-btn:hover  { transform: translate3d(0, -1px, 0); }
  .inv-btn:active { transform: translate3d(0,  0px, 0); }
  .inv-btn--primary:hover {
    box-shadow: 0 6px 20px color-mix(in srgb, var(--inv-btn-primary-bg) 38%, transparent);
  }

  /* --- ENLACES · subrayado que crece desde el inicio -------------------- */
  .inv-prose a:not(.inv-btn),
  .inv-nav__link {
    background-image: linear-gradient(currentColor, currentColor);
    background-size: 0% 1px;
    background-position: 0 100%;
    background-repeat: no-repeat;
    transition: background-size var(--inv-duration-base) var(--inv-ease),
                color var(--inv-duration-fast) var(--inv-ease);
  }
  .inv-prose a:not(.inv-btn):hover,
  .inv-nav__link:hover { background-size: 100% 1px; }

  /* --- ICONOS ----------------------------------------------------------- */
  .inv-icon-anim { transition: transform var(--inv-duration-base) var(--inv-ease); }
  :hover > .inv-icon-anim { transform: translate3d(2px, 0, 0); }
}

/* ==========================================================================
   6 · ONDA · el efecto agua, muy selectivo
   ==========================================================================
   NO se aplica a cada clic de la web. Sólo a las superficies marcadas con
   data-inv-ripple: los CTA principales y las superficies de vidrio.
   Una gota sobre agua: nace pequeña bajo el dedo, se expande y se disipa.
   ========================================================================== */
[data-inv-ripple] { position: relative; overflow: hidden; }

.inv-ripple__onda {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  transform: translate3d(-50%, -50%, 0) scale(0);
  background: radial-gradient(circle,
      var(--inv-ripple-color) 0%,
      color-mix(in srgb, var(--inv-ripple-color) 45%, transparent) 45%,
      transparent 70%);
  animation: inv-onda var(--inv-ripple-time) var(--inv-ease) forwards;
}
@keyframes inv-onda {
  from { transform: translate3d(-50%,-50%,0) scale(0);  opacity: .85; }
  to   { transform: translate3d(-50%,-50%,0) scale(1);  opacity: 0; }
}

/* Sobre botón claro la onda tiene que ser oscura o no se ve. */
.inv-btn--secondary[data-inv-ripple],
.inv-btn--ghost[data-inv-ripple],
.inv-glass[data-inv-ripple] {
  --inv-ripple-color: color-mix(in srgb, var(--inv-cyan-ink) 32%, transparent);
}

/* ==========================================================================
   7 · APARICIÓN AL DESPLAZAR
   ==========================================================================
   .inv-reveal ya existía y sigue igual: es la que usan las plantillas. Aquí
   sólo se añaden variantes y el escalonado, y se le suma un desenfoque muy
   leve que da sensación de profundidad al aparecer, como algo que emerge del
   agua en vez de deslizarse.
   ========================================================================== */
.inv-reveal-blur {
  opacity: 0;
  transform: translate3d(0, calc(16px * var(--inv-motion)), 0);
  filter: blur(calc(6px * var(--inv-motion)));
  transition: opacity var(--inv-duration-slow) var(--inv-ease),
              transform var(--inv-duration-slow) var(--inv-ease),
              filter var(--inv-duration-slow) var(--inv-ease);
  transition-delay: var(--inv-reveal-delay, 0ms);
}
.inv-reveal-blur.is-visible { opacity: 1; transform: none; filter: none; }

/* Escalonado. El JS pone --inv-reveal-delay según la posición del elemento
   dentro de su grupo: las tarjetas de una fila no aparecen a la vez, sino
   una detrás de otra, con 70 ms de diferencia. Es lo que hace que se lea
   como una secuencia y no como un parpadeo colectivo. */
.inv-stagger > * { --inv-reveal-delay: 0ms; }

/* ==========================================================================
   8 · TEXTO · animación selectiva, nunca párrafos enteros
   ==========================================================================
   Animar un párrafo entero retrasa la lectura, que es lo contrario de lo que
   se busca. Aquí sólo hay dos cosas: el titular del hero, que aparece por
   líneas, y el resaltado de una palabra clave.
   ========================================================================== */

/* --- TITULAR DEL HERO ----------------------------------------------------
   Aparece de una pieza pero con un desplazamiento y un desenfoque mínimos.
   NO se anima palabra por palabra: eso obliga a partir el texto en <span>,
   lo que rompe la selección, complica al lector de pantalla y no aporta.  */
.inv-display--anim {
  animation: inv-entrada-titular 900ms var(--inv-ease) both;
}
@keyframes inv-entrada-titular {
  from { opacity: 0; transform: translate3d(0, calc(18px * var(--inv-motion)), 0);
         filter: blur(calc(8px * var(--inv-motion))); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* --- PALABRA CLAVE RESALTADA --------------------------------------------
   Un trazo de luz que crece bajo la palabra, una sola vez, al aparecer.   */
.inv-hl { position: relative; white-space: nowrap; }
.inv-hl::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: .04em;
  height: .30em; z-index: -1; border-radius: 2px;
  background: linear-gradient(90deg,
      color-mix(in srgb, var(--inv-cyan-ink) 34%, transparent),
      color-mix(in srgb, var(--inv-brand-ink) 22%, transparent));
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform 720ms var(--inv-ease) 260ms;
}
.inv-hl.is-visible::after,
.is-visible .inv-hl::after { transform: scaleX(1); }

/* --- KPI ------------------------------------------------------------------
   El contador ya existe en motion.js. Aquí sólo se le da el acabado: cifras
   tabulares para que no bailen los dígitos mientras cuentan, que es el
   defecto que delata a un count-up mal hecho.                             */
.inv-metric__value,
[data-count-to] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ==========================================================================
   9 · SUPERFICIES DEL HERO
   El hero recibe el ambiente. Su contenido tiene que quedar POR ENCIMA y su
   fondo no debe desbordar.
   ========================================================================== */
.inv-hero, .inv-phero { position: relative; overflow: hidden; isolation: isolate; }
.inv-hero > .inv-container,
.inv-phero > .inv-container { position: relative; z-index: 2; }
.inv-hero .inv-ambient,
.inv-phero .inv-ambient { z-index: 0; }

/* La retícula de puntos queda entre el ambiente y el contenido: da textura
   sin competir con las manchas de luz. */
.inv-hero.inv-dotgrid::before,
.inv-phero.inv-dotgrid::before { z-index: 1; }

/* ==========================================================================
   10 · MÓVIL
   Lo que se apaga y por qué. Todo lo dependiente del cursor deja de tener
   sentido, y lo caro deja de compensar en un procesador de teléfono.
   ========================================================================== */
@media (max-width: 767px) {
  :root {
    --inv-parallax: 0px;     /* no hay cursor que seguir */
    --inv-lift:     0px;     /* no hay hover que responder */
    --inv-glass-blur: 0px;   /* el desenfoque es lo más caro de todo */
  }
  .inv-glass { background: var(--inv-glass-bg); }
  .inv-sheen::after { display: none; }
}

/* ==========================================================================
   11 · IMPRESIÓN
   Nada decorativo se imprime. Ahorra tinta y evita bandas de color sobre el
   texto.
   ========================================================================== */
@media print {
  .inv-ambient, .inv-ripple__onda { display: none !important; }
  .inv-reveal, .inv-reveal-blur { opacity: 1 !important; transform: none !important; filter: none !important; }
  .inv-hl::after { display: none; }
}

/* ==========================================================================
   12 · EL BOTÓN «VOLVER ARRIBA» DE ASTRA
   ==========================================================================
   NO es nuestro: lo pinta el tema padre con el identificador #ast-scroll-top.
   Se veía como un cuadrado azul plano en la esquina, con un azul que no está
   en la paleta, y era lo único de la pantalla que no pertenecía al sistema.

   NO SE DESACTIVA, SE VISTE. Desactivarlo quitaría una ayuda de navegación
   real en páginas largas -el archivo del radar, un artículo de 1500 palabras-
   y habría que reimplementarla. Vestirlo cuesta doce líneas.

   Se usa el mismo lenguaje que el resto: píldora, vidrio, elevación al pasar
   por encima y colores de token, así que sigue al modo día/noche solo.
   ========================================================================== */
#ast-scroll-top {
  background: var(--inv-glass-bg) !important;
  border: 1px solid var(--inv-glass-border);
  color: var(--inv-brand-ink) !important;
  border-radius: var(--inv-radius-pill) !important;
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--inv-shadow-sm);
  transition: transform var(--inv-duration-base) var(--inv-ease),
              box-shadow var(--inv-duration-base) var(--inv-ease),
              color var(--inv-duration-fast) var(--inv-ease);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  #ast-scroll-top {
    -webkit-backdrop-filter: blur(10px) saturate(140%);
            backdrop-filter: blur(10px) saturate(140%);
  }
}
#ast-scroll-top:hover {
  transform: translate3d(0, calc(-1 * var(--inv-lift)), 0);
  box-shadow: var(--inv-shadow-md);
  color: var(--inv-cyan-ink) !important;
}
#ast-scroll-top:focus-visible {
  outline: 3px solid var(--inv-color-focus);
  outline-offset: 2px;
}
/* El icono de Astra hereda el color y se centra: por defecto venía con un
   tamaño de fuente que lo dejaba descentrado dentro de la píldora. */
#ast-scroll-top .ast-icon,
#ast-scroll-top svg { fill: currentColor; width: 15px; height: 15px; }

/* ==========================================================================
   13 · DÓNDE VA EL VIDRIO, Y POR QUÉ SÓLO AHÍ
   ==========================================================================
   DEFECTO CORREGIDO, y se vio mirando la página, no leyendo el CSS.
   Se puso vidrio en la ficha lateral de una convocatoria y desaparecio: esa
   ficha vive sobre una seccion BLANCA, y un panel translucido sobre blanco no
   es vidrio, es nada. El borde blanco sobre blanco tampoco se veia.

   LA REGLA QUE SALE DE AHI: el vidrio necesita algo DETRAS que refractar.
   Sobre un fondo plano no comunica profundidad, sólo quita contraste.

   ASI QUE VA SOLO EN DOS SITIOS, los dos sobre el gradiente de marca:
     · el mockup del hero, que flota sobre el ambiente líquido
     · el bloque «volver arriba», que flota sobre lo que haya debajo

   Y las secciones de CTA reciben el ambiente pero NO un panel de vidrio: su
   contenido es un titular centrado, y encerrar un titular en una caja
   translucida lo empequeñece en vez de destacarlo.
   ========================================================================== */
.inv-mock.inv-glass {
  /* El mockup ya tenía su fondo y su borde. El vidrio los sustituye, pero se
     conserva la sombra: sobre el ambiente en movimiento, sin sombra el panel
     se despega y parece recortado. */
  box-shadow: var(--inv-shadow-lg);
}
.inv-cta .inv-ambient { z-index: 0; }
.inv-cta > .inv-container { position: relative; z-index: 2; }
.inv-cta { overflow: hidden; isolation: isolate; }

/* ==========================================================================
   14 · LA ESCENA DEL HERO · vidrio líquido en WebGL
   ==========================================================================
   El lienzo se dibuja ENCIMA del ambiente CSS, no en su lugar. Esa es la
   diferencia entre tener un respaldo y tener un plan B: el respaldo está
   pintado desde el primer fotograma y no hay que activarlo nunca. Si el
   JavaScript no llega, si no hay WebGL o si el visitante pidió menos
   movimiento, lo que se ve es exactamente lo que ya se veía.

   Razonamiento completo de la decisión técnica en
   01_ARQUITECTURA/DECISION_TECNICA_HERO_3D_REV01.txt
   ========================================================================== */
.inv-liquid {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  z-index: 1;                /* sobre el ambiente (0), bajo el contenido (2) */
  pointer-events: none;      /* jamás intercepta un clic */
  /* Aparece con un desvanecido en lugar de dar un salto: el JS pone .is-on en
     el fotograma siguiente al de la primera pintada. */
  opacity: 0;
  transition: opacity 900ms var(--inv-ease);
  /* El navegador puede tratarlo como una capa propia y no repintar el resto
     del hero en cada fotograma. */
  contain: strict;
}
.inv-liquid.is-on { opacity: 1; }

/* CUANDO LA ESCENA ARRANCA, EL AMBIENTE CSS SE ATENÚA.
   Las dos capas a plena intensidad compiten: el ambiente aporta manchas
   grandes y la escena aporta detalle, y juntas ensucian. Atenuado al 35% el
   ambiente sigue dando la base de color y deja el protagonismo a la escena. */
.inv-liquid-on .inv-ambient { opacity: .35; transition: opacity 900ms var(--inv-ease); }

/* El halo cian del hero también estorba a la escena: es un degradado sobre
   otro degradado. */
.inv-liquid-on::after { opacity: .35; }

/* EN MÓVIL la escena existe pero se recorta a la mitad inferior: en 390 px de
   ancho no hay «mitad derecha» donde ponerla sin pisar el titular, y el texto
   manda siempre. */
@media (max-width: 767px) {
  .inv-liquid { top: auto; height: 62%; }
}

/* Movimiento reducido: el lienzo no llega a crearse -el JS ni arranca-, pero
   se declara igualmente por si un cambio de preferencia lo pilla en pantalla. */
@media (prefers-reduced-motion: reduce) {
  .inv-liquid { display: none !important; }
  .inv-liquid-on .inv-ambient { opacity: 1; }
}
:root[data-inv-motion="off"] .inv-liquid { display: none !important; }

@media print { .inv-liquid { display: none !important; } }

/* ==========================================================================
   15 · PÁGINA NO ENCONTRADA
   ==========================================================================
   Antes la servía Astra con el diseño antiguo y en inglés. Ahora es una
   página del sitio: misma cabecera, mismo pie, mismos tokens, mismo modo
   día/noche y el mismo Motion System.

   Usa el ambiente CSS y NO la escena WebGL de la portada: quien llega a una
   404 llega por accidente y quiere irse cuanto antes. Compilar un shader para
   una página de paso es la clase de decoración cara que aquí no se hace.
   ========================================================================== */
.inv-404 { position: relative; overflow: hidden; }

/* El «404» de fondo. Grande, muy atenuado y detrás de todo: es una textura,
   no un mensaje. El mensaje es el titular. */
.inv-404__cifra {
  position: absolute;
  top: 50%; right: 4vw;
  transform: translateY(-50%);
  z-index: 0;
  font-family: var(--inv-font-heading);
  font-weight: var(--inv-fw-bold);
  font-size: clamp(9rem, 30vw, 26rem);
  line-height: .8;
  letter-spacing: -.06em;
  color: var(--inv-on-dark);
  opacity: .05;
  pointer-events: none;
  user-select: none;
}
.inv-404__cuerpo { position: relative; z-index: 2; max-width: 42rem; }
.inv-404__acciones { margin-top: var(--inv-space-5); }

.inv-404__ruta {
  margin-top: var(--inv-space-4);
  padding-top: var(--inv-space-3);
  border-top: 1px solid rgba(255,255,255,.14);
  font-size: var(--inv-fs-small);
  color: var(--inv-on-dark-muted);
  /* Una URL larga no puede desbordar la caja ni empujar la composición. */
  overflow-wrap: anywhere;
}
.inv-404__ruta code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .95em;
  color: var(--inv-cyan-bright);
}

/* --- buscador ------------------------------------------------------------- */
.inv-404__form { margin-top: var(--inv-space-3); }
.inv-404__form-fila { display: flex; flex-wrap: wrap; gap: var(--inv-space-2); }
.inv-404__form-fila input[type="search"] {
  flex: 1 1 16rem;
  min-height: 48px;
  padding: 0 var(--inv-space-2);
  border: 1px solid var(--inv-border);
  border-radius: var(--inv-radius-btn);
  background: var(--inv-surface);
  color: var(--inv-text);
  font-size: var(--inv-fs-body);
  font-family: var(--inv-font-body);
}
.inv-404__form-fila input[type="search"]:focus-visible {
  outline: 3px solid var(--inv-color-focus);
  outline-offset: 2px;
}

/* --- atajos --------------------------------------------------------------- */
.inv-404__atajos {
  display: flex; flex-wrap: wrap; gap: var(--inv-space-2);
  list-style: none; margin: var(--inv-space-4) 0 0; padding: 0;
}
.inv-404__atajos a {
  display: inline-flex; align-items: center;
  min-height: 44px; padding: 8px 18px;
  border: 1px solid var(--inv-border);
  border-radius: var(--inv-radius-pill);
  color: var(--inv-ink-strong);
  text-decoration: none;
  font-size: .95rem;
  transition: border-color var(--inv-duration-fast) var(--inv-ease),
              color var(--inv-duration-fast) var(--inv-ease),
              transform var(--inv-duration-base) var(--inv-ease);
}
@media (hover: hover) and (pointer: fine) {
  .inv-404__atajos a:hover {
    border-color: var(--inv-cyan-ink);
    color: var(--inv-cyan-ink);
    transform: translate3d(0, calc(-1 * var(--inv-lift)), 0);
  }
}

/* En pantallas estrechas la cifra estorba: pasa a estar centrada y aún más
   tenue, detrás del texto, en lugar de competir por el lado derecho. */
@media (max-width: 767px) {
  .inv-404__cifra { right: auto; left: 50%; transform: translate(-50%, -50%); opacity: .04; }
}


/* ==========================================================================
   INVEXUS · ESCENAS
   ==========================================================================
   TRES PRIMITIVAS, NO DIEZ EFECTOS.

   El encargo pedía un efecto protagonista distinto en nueve páginas. Escribir
   nueve efectos habría dado un catálogo de trucos: nueve maneras de fallar,
   nueve cosas que mantener y ninguna coherencia entre páginas. Al mirarlos
   juntos, casi todos son la misma idea contada de otra forma:

     CONEXIÓN   cosas separadas que resultan estar unidas
                → sistema, rutas, flujo, workflow, progreso
     NUBE       desorden que se ordena
                → investigación, datos
     PULSO      algo que emite y algo que se detecta
                → radar, convergencia humana

   Y una cuarta que no es un dibujo sino una manera de mirar:

     PROFUNDIDAD  capas que se acercan mientras se baja

   Cada página escoge UNA. El tono cambia el color y el ritmo; la mecánica es
   la misma. Eso es lo que hace que el sitio se lea como un sistema y no como
   una colección de páginas hechas por personas distintas.

   CÓMO SE USA
     <section data-inv-scene="conexion" data-inv-scene-tono="sistema">
     <section data-inv-scene="nube"     data-inv-scene-tono="datos">
     <section data-inv-scene="pulso"    data-inv-scene-tono="radar">
     <section data-inv-scene="profundidad">

   LA INTENSIDAD SE REGULA DESDE UN SOLO SITIO: --inv-scene-fuerza. Ponerlo a
   0 apaga todas las escenas del sitio sin tocar una sola plantilla. Es la
   misma idea que --inv-motion, y existe por el mismo motivo: cuando algo se
   sube de tono, hace falta poder bajarlo entero y de una vez.

   LO QUE NINGUNA ESCENA HACE
     · no captura el desplazamiento: la rueda del ratón nunca se toca
     · no intercepta clics: todas las capas son pointer-events:none
     · no ocupa espacio: van en position:absolute sobre su sección, así que
       no pueden provocar un salto de maquetación
     · no aparece con prefers-reduced-motion
     · no usa WebGL: el Hero de la portada es el único, y sigue siéndolo
   ========================================================================== */

:root {
  /* El mando global. 1 = como se diseñó; 0 = todas apagadas. */
  --inv-scene-fuerza: 1;

  /* Colores de escena. Se derivan de la paleta, no se inventan. */
  --inv-scene-track: color-mix(in srgb, var(--inv-border) 90%, transparent);
  --inv-scene-line:  color-mix(in srgb, var(--inv-brand-ink) 55%, transparent);
  --inv-scene-node:  var(--inv-cyan-ink);
  --inv-scene-dot:   color-mix(in srgb, var(--inv-brand-ink) 42%, transparent);
  --inv-scene-halo:  color-mix(in srgb, var(--inv-cyan-ink) 16%, transparent);

  /* Progreso de la sección dentro de la ventana, de 0 a 1. Lo escribe el
     Motion System. El valor por defecto es 1 y no 0 A PROPÓSITO: si el
     JavaScript no llegara a cargarse, las escenas se ven completas en vez de
     vacías. Un adorno que no carga debe verse terminado, nunca a medias. */
  --inv-progress: 1;
}

/* La capa de escena. Común a las cuatro primitivas. */
.inv-scene {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 0;
  /* contain evita que el navegador tenga que repintar la sección entera
     cuando cambia --inv-progress sesenta veces por segundo.
     Se pide layout, paint y style, NO strict: strict incluye size, y en un
     elemento con inset:0 eso invita a que el tamaño se resuelva de formas
     sutilmente distintas entre navegadores. Lo que hace falta aquí es aislar
     el repintado, y eso lo dan estos tres. */
  contain: layout paint style;
}
[data-inv-scene] { position: relative; }
/* El contenido SIEMPRE por encima de la escena. Sin esto, una capa decorativa
   puede acabar tapando un enlace y nadie entiende por qué no se puede pulsar. */
/* :not(.inv-scene) es imprescindible. Sin el, esta regla le pone
   position:relative a la propia capa de escena y le quita el absolute que la
   mantiene fuera del flujo: la escena pasaria a ocupar sitio y empujaria el
   contenido hacia abajo. */
[data-inv-scene] > *:not(.inv-scene) { position: relative; z-index: 1; }


/* ==========================================================================
   PRIMITIVA 1 · CONEXIÓN
   ==========================================================================
   Una línea que atraviesa la rejilla POR DETRÁS de las tarjetas y sólo se ve
   en los huecos. Por eso no colisiona nunca con el contenido: las tarjetas
   son opacas y la línea aparece entre ellas, exactamente como un cable que
   pasa por detrás de unos objetos sobre una mesa.

   LOS NODOS NO SE MIDEN, SE CALCULAN. La rejilla tiene N columnas iguales, así
   que el centro de cada hueco está en una fracción exacta del ancho. Un
   degradado repetido cada 100%/N los coloca solos, y se recolocan al cambiar
   el ancho sin que ningún JavaScript tenga que escuchar el resize. Medir el
   DOM habría costado un reflow por cada cambio de tamaño y habría fallado
   justo cuando la rejilla cambia de número de columnas.
   ========================================================================== */
.inv-scene--conexion { --inv-scene-grosor: 1.5px; }

/* TRAMOS MEDIDOS, NO UNA LINEA QUE CRUCE LA REJILLA.
   Hubo dos intentos antes de este, y los dos fallaron por lo mismo: dar por
   supuesta la forma de la rejilla.

     1. Una linea a 56 px del borde de la SECCION. Esos 56 px caian en mitad
        del titular, no a la altura de los iconos.
     2. Una linea repetida por fila, dentro de la rejilla. Mejor, pero la
        rejilla de la portada resulto ser de SEIS columnas con tarjetas
        escalonadas que ocupan dos cada una: la linea se salia por la derecha y
        cruzaba espacio vacio. En la captura no parecia una conexion, parecia
        una raya suelta.

   Ahora no se supone nada: se miden las tarjetas, se agrupan por fila y se
   dibuja UN TRAMO por fila, del borde izquierdo de la primera al derecho de la
   ultima. Donde no hay tarjeta no hay linea. Una tarjeta sola en su fila se
   queda con su nodo y sin tramo, que es lo correcto: no esta unida a nada a su
   lado.

   La medida se hace UNA vez, al construir, y se repite solo al cambiar el
   tamano. El bucle de desplazamiento no toca el DOM. */
/* Posicion y opacidad comunes a tramos y nodos.
   ESTA REGLA SE PARTIO POR LA MITAD una vez, al insertar el bloque del tramo
   vertical entre su lista de selectores y su cuerpo. El resultado fue que
   .inv-scene__tramo se quedo SIN position:absolute, su ::after -que lleva
   inset:0- dejo de referirse al tramo y paso a referirse a la capa entera, y
   cada tramo se dibujaba como un bloque azul del alto de toda la fila.
   No dio ningun error: dio un dibujo distinto. Se vio en la captura. */
.inv-scene--conexion .inv-scene__tramo,
.inv-scene--conexion .inv-scene__nodo {
  position: absolute;
  opacity: calc(var(--inv-scene-fuerza) * .85);
}

/* TRAMO VERTICAL. En una lista de pasos cada elemento ocupa su propia fila,
   asi que no hay nada a los lados con lo que conectarlo: la secuencia se lee
   hacia abajo. La primera version dibujaba un nodo por fila en el centro
   horizontal y salia un punto suelto flotando a la derecha del texto, que no
   significaba nada.
   Aqui el tramo baja por el eje de los numeros y son los propios numeros los
   que hacen de nodos. No se anade ni un punto: ya estaban ahi. */
.inv-scene--conexion .inv-scene__tramo--v {
  width: var(--inv-scene-grosor);
  height: auto;
}
.inv-scene--conexion .inv-scene__tramo--v::after {
  clip-path: inset(0 0 calc((1 - var(--inv-progress) * var(--inv-scene-fuerza)) * 100%) 0);
}

.inv-scene--conexion .inv-scene__tramo {
  height: var(--inv-scene-grosor);
  border-radius: var(--inv-scene-grosor);
  background: var(--inv-scene-track);
}
/* El relleno del tramo: la misma barra en color de acento, recortada segun el
   avance. clip-path en vez de width, porque recortar no provoca reflow y esto
   se recalcula mientras la seccion esta en pantalla. */
.inv-scene--conexion .inv-scene__tramo::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--inv-scene-line);
  clip-path: inset(0 calc((1 - var(--inv-progress) * var(--inv-scene-fuerza)) * 100%) 0 0);
}

.inv-scene--conexion .inv-scene__nodo {
  width: 7px; height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--inv-scene-dot);
  transition: background-color var(--inv-duration-state) var(--inv-ease),
              transform var(--inv-duration-state) var(--inv-ease);
}

/* Al senalar una tarjeta se enciende la red entera. El gesto dice "esto que
   tocas forma parte de esto otro", que es lo que la seccion afirma con
   palabras y aqui se ve sin anadir ninguna. */
[data-inv-scene="conexion"]:has(.inv-card:hover) .inv-scene__nodo,
[data-inv-scene="conexion"]:has(.inv-card:focus-within) .inv-scene__nodo {
  background: var(--inv-scene-node);
  transform: scale(1.25);
}
[data-inv-scene="conexion"]:has(.inv-card:hover) .inv-scene__tramo::after,
[data-inv-scene="conexion"]:has(.inv-card:focus-within) .inv-scene__tramo::after {
  background: var(--inv-scene-node);
}


/* ==========================================================================
   PRIMITIVA 2 · NUBE
   ==========================================================================
   Puntos que empiezan dispersos y se ordenan al bajar. Es la idea de las dos
   páginas que hablan de convertir lo disperso en algo con forma —datos e
   investigación—, y se cuenta sin una sola palabra añadida.

   CADA PUNTO ES UN SPAN con cuatro variables que escribe el Motion System una
   sola vez: de dónde parte y a dónde va. El movimiento lo hace el CSS
   interpolando con --inv-progress. Así el JavaScript no toca nada en cada
   fotograma: escribe al principio y se aparta.

   NO SON UNA VISUALIZACIÓN. No representan ningún dato real y no deben
   parecerlo: son abstractos y decorativos, y por eso van en aria-hidden.
   ========================================================================== */
.inv-scene--nube .inv-scene__punto {
  position: absolute;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--inv-scene-dot);
  /* De la posición dispersa a la ordenada. La mezcla la hace calc() con el
     progreso; no hay ni una línea de JavaScript por fotograma. */
  left: calc((var(--x0) + (var(--x1) - var(--x0)) * var(--inv-progress)) * 1%);
  top:  calc((var(--y0) + (var(--y1) - var(--y0)) * var(--inv-progress)) * 1%);
  opacity: calc((.28 + .5 * var(--inv-progress)) * var(--inv-scene-fuerza));
  transform: scale(calc(.8 + .45 * var(--inv-progress)));
  will-change: left, top;
}
/* Uno de cada cinco puntos es de acento: da profundidad sin añadir elementos. */
.inv-scene--nube .inv-scene__punto:nth-child(5n) {
  background: var(--inv-scene-node);
  width: 5px; height: 5px;
}


/* ==========================================================================
   PRIMITIVA 3 · PULSO
   ==========================================================================
   Anillos concéntricos muy tenues y un barrido lento. La lectura buscada es
   «vigilancia y detección», no un radar militar: no hay retícula, no hay
   grados, no hay verde de fósforo. Sólo la sensación de que algo está
   mirando.

   TODO ES CSS Y NO CUESTA NADA: dos degradados y una rotación compuesta. Una
   rotación no provoca reflow ni repintado del contenido, sólo composición.
   ========================================================================== */
.inv-scene--pulso .inv-scene__anillos {
  position: absolute;
  top: 50%; left: 50%;
  width: min(120vw, 1100px);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  opacity: calc(var(--inv-scene-fuerza) * .55);
  background:
    radial-gradient(circle,
      transparent 0 18%,
      var(--inv-scene-halo) 18.2% 18.6%, transparent 18.8% 33%,
      var(--inv-scene-halo) 33.2% 33.6%, transparent 33.8% 50%,
      var(--inv-scene-halo) 50.2% 50.6%, transparent 50.8% 68%,
      var(--inv-scene-halo) 68.2% 68.6%, transparent 68.8%);
}
/* El barrido. 18 s por vuelta: a esa velocidad no se percibe como una
   animación sino como algo que está ocurriendo. */
.inv-scene--pulso .inv-scene__barrido {
  position: absolute;
  top: 50%; left: 50%;
  width: min(120vw, 1100px);
  aspect-ratio: 1;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  opacity: calc(var(--inv-scene-fuerza) * .5);
  background: conic-gradient(from 0deg,
              var(--inv-scene-halo) 0deg 26deg, transparent 26deg 360deg);
  animation: inv-barrido 18s linear infinite;
  will-change: transform;
}
@keyframes inv-barrido {
  to { transform: translate(-50%, -50%) rotate(360deg); }
}

/* Variante humana: la misma mecánica, sin barrido y con la luz convergiendo
   hacia el centro. En Nosotros la tecnología tiene que bajar el tono. */
.inv-scene--pulso[data-inv-scene-tono="humano"] .inv-scene__barrido { display: none; }
.inv-scene--pulso[data-inv-scene-tono="humano"] .inv-scene__anillos {
  background: radial-gradient(circle,
              var(--inv-scene-halo) 0 22%, transparent 62%);
  opacity: calc(var(--inv-scene-fuerza) * (.35 + .35 * var(--inv-progress)));
  transform: translate(-50%, -50%) scale(calc(1.25 - .25 * var(--inv-progress)));
}


/* ==========================================================================
   PRIMITIVA 4 · PROFUNDIDAD
   ==========================================================================
   No dibuja nada: cambia cómo se miran las capas que ya existen. Al bajar, lo
   que está detrás se acerca y lo que está delante se aparta.

   ES PERSPECTIVA, NO 3D. No hay geometría, no hay escena, no hay bucle de
   render: hay dos transformaciones compuestas. El encargo pedía que se
   sintiera tridimensional sin pagar el precio de serlo, y ésta es la manera
   honesta de conseguirlo.

   SIN SCROLL HIJACKING. La página se desplaza como cualquier otra; lo único
   que ocurre es que unos elementos leen cuánto se ha desplazado.
   ========================================================================== */
[data-inv-scene="profundidad"] { perspective: 1200px; }

.inv-capa {
  transform: translate3d(0,
             calc((1 - var(--inv-progress)) * var(--inv-capa-z, 18px) * var(--inv-scene-fuerza)),
             0)
             scale(calc(1 - (1 - var(--inv-progress)) * .02 * var(--inv-scene-fuerza)));
  opacity: calc(.55 + .45 * var(--inv-progress));
  will-change: transform, opacity;
}
.inv-capa--fondo   { --inv-capa-z: 34px; }
.inv-capa--media   { --inv-capa-z: 18px; }
.inv-capa--frente  { --inv-capa-z: 6px;  }


/* ==========================================================================
   APAGADO
   ==========================================================================
   Con prefers-reduced-motion no se atenúan las escenas: DESAPARECEN. Una
   escena a medio gas sigue moviéndose, y quien pide menos movimiento no está
   pidiendo movimiento más suave.

   El contenido no depende de ninguna de ellas: son todas position:absolute y
   aria-hidden, así que quitarlas no cambia ni una línea de texto de sitio.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .inv-scene { display: none; }
  .inv-capa {
    transform: none;
    opacity: 1;
    will-change: auto;
  }
}
[data-inv-motion="off"] .inv-scene { display: none; }
[data-inv-motion="off"] .inv-capa { transform: none; opacity: 1; }

/* En móvil las escenas bajan de intensidad en vez de desaparecer: el móvil no
   tiene que imitar al escritorio, pero tampoco quedarse sin identidad. */
@media (max-width: 767px) {
  :root { --inv-scene-fuerza: .72; }
  .inv-scene--pulso .inv-scene__barrido { animation-duration: 26s; }
}

@media print { .inv-scene { display: none; } }
