/* «La web viva»: la capa de luz y pulso común a TODAS las páginas. La cargan el portal (base.html)
   y las páginas generadas (generar.py), que tienen hojas distintas: por eso va aparte —como
   `movimiento.css` y `vinetas.css`— y no usa ninguna variable sin su respaldo. El guion es `viva.js`.

   Qué pone: los orbes de neón que respiran al fondo, los botones con su degradado que fluye y su
   barrido de luz (siempre con LETRA BLANCA), el trazo de luz que recorre el borde de las tarjetas,
   los pulsos de radar, el destello al tocar y el brillo que cruza los títulos con el scroll.

   Las reglas: solo `transform` y `opacity` en lo que no para de moverse; lo perpetuo se PARA fuera
   de pantalla (`.viva-a-la-vista` la pone el guion) y con «reducir movimiento» no existe; y todo
   tiene su versión en claro: en claro la luz es pastel, no neón. */

/* ── los orbes ─────────────────────────────────────────────────────────────────
   Tres manchas de luz difusa, cada una a su ritmo (9, 13 y 17 s, primos entre sí: no vuelven a
   coincidir en minutos). Van por DETRÁS del campo de partículas (que es z-index −1).

   SIN `filter: blur()`. Lo llevaban (72 px sobre tres cajas de media pantalla), y medido el
   18-09-2026 era lo más caro de toda la web en un equipo sin aceleración gráfica —media oficina, todo
   escritorio remoto—: ahí el navegador compone por software, y cada fotograma que toca la zona de un
   orbe (el polvo que cae, un botón que respira, el scroll) le obliga a recalcular ese desenfoque
   entero. La página iba a 17 fotogramas por segundo EN REPOSO, y a 45 solo con quitarlo. Una mancha
   difusa no necesita filtro: es un degradado radial con caída de campana (las seis paradas de abajo),
   en una caja 1,4 veces mayor para cubrir lo que el desenfoque desparramaba (`--viva-o` es el color y
   `--viva-a` la fuerza: con el prefijo de esta hoja, que la comparten las dos webs). Se ve igual, no cuesta
   nada en ningún equipo, y en los que tienen gráfica se ahorran tres texturas desenfocadas. */
.viva-orbes { position: fixed; inset: 0; z-index: -2; pointer-events: none; overflow: hidden; }
.viva-orbe {
  position: absolute; border-radius: 50%; will-change: transform, opacity; animation: viva-orbe 13s ease-in-out infinite;
  --viva-o: 59, 102, 255; --viva-a: .30;
  background: radial-gradient(closest-side,
    rgba(var(--viva-o), calc(var(--viva-a) * .86)) 0%, rgba(var(--viva-o), calc(var(--viva-a) * .70)) 20%, rgba(var(--viva-o), calc(var(--viva-a) * .42)) 42%,
    rgba(var(--viva-o), calc(var(--viva-a) * .18)) 64%, rgba(var(--viva-o), calc(var(--viva-a) * .05)) 84%, rgba(var(--viva-o), 0) 100%);
}
.viva-orbe.a { width: 64vmax; height: 64vmax; left: -23vmax; top: -21vmax; }
.viva-orbe.b { width: 53vmax; height: 53vmax; right: -19.5vmax; top: calc(22vh - 7.5vmax); --viva-o: 157, 92, 255; --viva-a: .26; animation-duration: 17s; animation-delay: -5s; }
.viva-orbe.c { width: 48vmax; height: 48vmax; left: calc(22vw - 7vmax); bottom: -25vmax; --viva-o: 34, 211, 238; --viva-a: .22; animation-duration: 9s; animation-delay: -2s; }
:root[data-theme="dark"] .viva-orbe.a { --viva-a: .50; }
:root[data-theme="dark"] .viva-orbe.b { --viva-a: .42; }
:root[data-theme="dark"] .viva-orbe.c { --viva-a: .30; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .viva-orbe.a { --viva-a: .50; }
  :root:not([data-theme="light"]) .viva-orbe.b { --viva-a: .42; }
  :root:not([data-theme="light"]) .viva-orbe.c { --viva-a: .30; }
}
@keyframes viva-orbe { 0%, 100% { transform: scale(.86) translate3d(0, 0, 0); opacity: .45; } 50% { transform: scale(1.12) translate3d(2vmax, -1.5vmax, 0); opacity: .9; } }

/* ── los botones ───────────────────────────────────────────────────────────────
   El degradado es hondo A PROPÓSITO: lleva letra blanca en los dos temas, y el blanco sobre un
   cian claro no llega ni a 2:1. Con estos tres tonos el peor punto da 5,4:1. Los botones
   «fantasma», el de Ko-fi y el de Discord conservan su color: solo ganan el barrido y el toque. */
.btn:not(.ghost):not(.kofi):not(.discord):not(.peligro), .btn-primary, .nav-cta {
  color: #fff; border-color: transparent; text-shadow: 0 1px 1px rgba(3, 6, 15, .3);
  background-image: linear-gradient(115deg, #0e7490, #2b50e6 34%, #7a3df0 66%, #0e7490);
  background-size: 300% 100%; background-position: 0% 50%;
  transition: transform .16s ease-out, background-position .8s cubic-bezier(.22, 1, .36, 1), box-shadow .3s ease;
  box-shadow: 0 10px 28px -14px rgba(59, 102, 255, .55), inset 0 1px 0 rgba(255, 255, 255, .34);
}
/* La sombra que «respira» es un REPINTADO por fotograma (una sombra no se compone, se pinta), y
   sin parar. Con ratón y gráfica de sobremesa no se nota; en un teléfono, sumada a todo lo demás,
   es de lo que hace que la web vaya a tirones. Así que solo respira donde hay ratón, y el
   «Comprar» de la barra NUNCA: vive dentro de una barra de cristal que está siempre en pantalla,
   y cada fotograma suyo obligaba a recomponer la barra entera con su desenfoque. */
@media (hover: hover) and (pointer: fine) {
  .btn:not(.ghost):not(.kofi):not(.discord):not(.peligro), .btn-primary { animation: viva-respira 3.6s ease-in-out infinite; }
}
.btn:not(.ghost):not(.kofi):not(.discord):not(.peligro):hover, .btn-primary:hover, .nav-cta:hover {
  color: #fff; background-position: 100% 50%; animation-play-state: paused;
}
@keyframes viva-respira {
  0%, 100% { box-shadow: 0 10px 28px -14px rgba(59, 102, 255, .55), inset 0 1px 0 rgba(255, 255, 255, .34); }
  50% { box-shadow: 0 14px 46px -10px rgba(122, 61, 240, .85), inset 0 1px 0 rgba(255, 255, 255, .34); }
}
/* (El «Comprar» de la barra NO lleva barrido: su `::after` es, en el móvil, la zona de toque de
   44 px que lo hace pulsable con el dedo, y pisarlo aquí lo dejaría otra vez diminuto.) */
.btn, .btn-primary, .btn-secondary, .btn-morado { position: relative; overflow: hidden; isolation: isolate; }
.btn::after, .btn-primary::after, .btn-secondary::after, .btn-morado::after {
  content: ''; position: absolute; z-index: -1; top: -30%; bottom: -30%; left: 0; width: 38%; pointer-events: none;
  background: linear-gradient(105deg, transparent 25%, rgba(255, 255, 255, .34) 50%, transparent 75%);
  transform: translateX(-180%) skewX(-16deg); transition: transform 0s;
}
.btn:hover::after, .btn:focus-visible::after, .btn-primary:hover::after, .btn-secondary:hover::after,
.btn-morado:hover::after { opacity: 1; animation: none; left: 0; transform: translateX(420%) skewX(-16deg); transition: transform .85s cubic-bezier(.22, 1, .36, 1); }
.btn svg, .btn-primary svg { transition: transform .35s cubic-bezier(.22, 1, .36, 1); }
.btn:hover svg, .btn-primary:hover svg { transform: translateX(3px); }

/* ── el destello al tocar (el nodo lo pone el guion donde cae el dedo o el clic) ── */
.viva-toque {
  position: absolute; z-index: 3; left: var(--tx); top: var(--ty); width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: 50%; pointer-events: none; animation: viva-toque .7s cubic-bezier(.22, 1, .36, 1) forwards;
  background: radial-gradient(closest-side, rgba(255, 255, 255, .75), rgba(34, 211, 238, .35) 55%, transparent);
}
@keyframes viva-toque { from { transform: scale(.4); opacity: 1; } to { transform: scale(24); opacity: 0; } }

/* ── el trazo de luz que recorre el borde de las tarjetas ─────────────────────────
   Un degradado cónico que gira (`--viva-giro`, registrada como ángulo para poder animarla) y una
   máscara que deja ver solo el canto. El nodo lo pone el guion y solo gira con la tarjeta a la
   vista: una docena de degradados girando a ciegas son una docena de repintados que nadie ve. */
@property --viva-giro { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
.viva-trazo {
  position: absolute; inset: 0; z-index: 2; border-radius: inherit; padding: 1.5px; pointer-events: none;
  background: conic-gradient(from var(--viva-giro), transparent 0 58%, #22d3ee 72%, #7a9bff 80%, #9d5cff 88%, transparent 97%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude;
  animation: viva-recorre 7s linear infinite paused; opacity: .85;
}
.viva-a-la-vista > .viva-trazo { animation-play-state: running; }
@keyframes viva-recorre { to { --viva-giro: 360deg; } }
/* Las doce tarjetas de «Todo lo que hace» lo encienden solo al señalarlas: doce degradados
   cónicos girando a la vez son doce repintados por fotograma para algo que se mira de uno en uno. */
.lp-ficha > .viva-trazo { opacity: 0; animation-play-state: paused; transition: opacity .35s ease; }
@media (hover: hover) and (pointer: fine) {
  .lp-ficha.viva-a-la-vista:hover > .viva-trazo { opacity: .85; animation-play-state: running; }
}
/* ── el canto de NEÓN de las tarjetas de paso ────────────────────────────────────
   Entrar, el código de dos pasos, registrarse, comprar: «faltan efectos LED neón en movimiento
   continuo, por ejemplo en doble factor o login o lugares muy visitados» (Luis, 18-09). Aquí el
   trazo son DOS cabezas de luz enfrentadas, más gruesas y a plena luz, y detrás va su halo —el
   mismo anillo, más ancho y desenfocado, por debajo del contenido—. La clase y el nodo del halo
   los pone el guion. Son páginas donde se está segundos: por eso aquí la luz no se para nunca,
   tampoco en un teléfono, cuando el trazo de las demás tarjetas da una vuelta y se queda quieto. */
.viva-trazo.viva-neon, .viva-halo {
  background: conic-gradient(from var(--viva-giro),
    transparent 0 7%, #22d3ee 16%, #3b82f6 24%, #a78bfa 31%, transparent 41% 57%,
    #a78bfa 66%, #3b82f6 74%, #22d3ee 81%, transparent 91% 100%);
}
.viva-trazo.viva-neon { inset: -1px; padding: 2px; opacity: 1; animation-duration: 6s; }
.viva-halo {
  position: absolute; inset: -5px; z-index: -1; border-radius: inherit; padding: 7px; pointer-events: none;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude;
  filter: blur(11px); opacity: .78; animation: viva-recorre 6s linear infinite paused;
}
.viva-a-la-vista > .viva-halo { animation-play-state: running; }
@keyframes viva-late { 50% { opacity: .95; } }

/* En táctil: UNA vuelta al aparecer y se queda quieto, y los orbes no se mueven. Un fondo que
   respira por debajo de una barra de cristal obliga a desenfocarla de nuevo en cada fotograma,
   se esté tocando la pantalla o no.
   El neón de las tarjetas de paso es la excepción —ver arriba—: sigue girando, pero su halo
   cambia de técnica. Lo caro es un desenfoque recalculado en cada fotograma, así que en un
   teléfono el halo es una sombra de color FIJA que late solo con la opacidad: eso lo lleva la
   gráfica sin repintar nada. */
@media (pointer: coarse) {
  .viva-trazo { animation-iteration-count: 1; animation-fill-mode: forwards; }
  .viva-trazo.viva-neon { animation-iteration-count: infinite; }
  .viva-halo, .viva-a-la-vista > .viva-halo {
    inset: 0; padding: 0; background: none; -webkit-mask: none; mask: none; filter: none;
    box-shadow: 0 0 24px 1px rgba(59, 130, 246, .55), 0 0 56px 6px rgba(167, 139, 250, .3);
    opacity: .4; animation: viva-late 3.4s ease-in-out infinite;
  }
  .viva-orbe { animation: none; opacity: .7; will-change: auto; }
}

/* ── EL EQUIPO JUSTO ───────────────────────────────────────────────────────────
   `html.equipo-justo` lo pone el motor de partículas (`escena.js`) cuando el navegador no le da un
   contexto gráfico ACELERADO —o `campo.js` en un ordenador con menos de 4 GB—: ese equipo compone la
   página por software, y ahí cada fotograma cuesta lo que mida la zona que cambia. Medido el
   18-09-2026 (Chromium sin gráfica, 1440×900): con las animaciones de AMBIENTE en marcha —las que no
   paran nunca y casi no se ven: los orbes que respiran, el fondo que deriva en 92 s, la sombra de los
   botones— la página entera iba a 17 fotogramas por segundo, en reposo y haciendo scroll. Quietas,
   a 46. Así que en esos equipos el ambiente se queda QUIETO (se ve igual en una foto) y todo el
   movimiento que sí se mira —la coreografía del scroll, el polvo que cae, los cantos de luz, los
   contadores— corre casi al triple. `will-change` fuera: sin gráfica, una capa aparte es solo otra
   superficie que componer. */
html.equipo-justo .viva-orbe { animation: none !important; opacity: .7; will-change: auto; }
html.equipo-justo .btn, html.equipo-justo .btn-primary { animation: none !important; }

/* ── el pulso de radar ─────────────────────────────────────────────────────── */
.viva-pulso { position: relative; }
.viva-pulso::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; border: 1px solid var(--azul, var(--acento, #2563eb));
  opacity: 0; pointer-events: none; animation: viva-radar 2.8s cubic-bezier(.22, 1, .36, 1) infinite;
}
@keyframes viva-radar { 0% { transform: scale(1); opacity: .7; } 100% { transform: scale(1.8); opacity: 0; } }

/* ── el brillo que cruza los títulos, ligado al scroll ─────────────────────────
   CSS puro (`animation-timeline: view()`): donde el navegador no lo tiene, el título es el de
   siempre. Solo títulos de TEXTO LLANO —los que el guion marca `.viva-brillo`—: con el color
   transparente, un enlace o un énfasis de dentro dejarían de verse. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) and (animation-timeline: view()) {
  .viva-brillo {
    background-image: linear-gradient(100deg, currentColor 0 42%, #22d3ee 48%, #fff 50%, #9d5cff 52%, currentColor 58% 100%);
    background-size: 320% 100%; background-position: 100% 0; -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: viva-brillo linear both; animation-timeline: view(); animation-range: entry 20% exit 80%;
  }
}
@keyframes viva-brillo { from { background-position: 100% 0; } to { background-position: 0% 0; } }

/* [movimiento: reducido] */
:where(html[data-movimiento="reducido"]) .viva-orbe, :where(html[data-movimiento="reducido"]) .viva-trazo:not(.viva-neon), :where(html[data-movimiento="reducido"]) .viva-pulso::before, :where(html[data-movimiento="reducido"]) .viva-brillo,
:where(html[data-movimiento="reducido"]) .btn, :where(html[data-movimiento="reducido"]) .btn-primary, :where(html[data-movimiento="reducido"]) .nav-cta { animation: none !important; }
:where(html[data-movimiento="reducido"]) .btn::after, :where(html[data-movimiento="reducido"]) .btn-primary::after, :where(html[data-movimiento="reducido"]) .btn-secondary::after, :where(html[data-movimiento="reducido"]) .btn-morado::after { display: none; }
:where(html[data-movimiento="reducido"]) .viva-brillo { -webkit-text-fill-color: currentColor; background-image: none; }

/* ── Movimiento reducido: lo que se queda (ver «MOVIMIENTO REDUCIDO» en estilo.css) ──
   El neón de las tarjetas de paso y su halo: una luz que da la vuelta a un borde no cruza la
   pantalla ni viaja con el scroll. Más despacio que en el modo completo. Los orbes se ven, quietos. */
html[data-movimiento="reducido"] .viva-trazo.viva-neon, html[data-movimiento="reducido"] .viva-halo { animation-duration: 10s !important; animation-iteration-count: infinite !important; }
