﻿/* =============================================================================
   intro.css -- la entrada 3D de la portada del Congreso Internacional PIV
   Fecha: 2026-09-08

   QUE ES. Un velo a pantalla completa que sale UNA VEZ POR SESION al entrar a
   la portada. Las dos figuras del logo -- la esfera de orbitas y el nodo de
   circuito -- llegan desde el fondo girando en 3D, chocan en el centro y de
   ese golpe sale la frase del evento. Al final las dos se apartan y quedan
   flanqueandola: la intro termina RECONSTRUYENDO EL LOGO.

   Carlos, 2026-09-08: "algo que impresione... es para deslumbrar al visitante,
   que vean que le echamos muchas ganas al evento".

   POR QUE NO SE USA EL PNG DEL LOGO. Una imagen solo se puede mover en bloque.
   Aqui cada orbita, cada arco y cada punto es un elemento con su propio plano
   en el espacio, asi que giran de verdad -- no es el dibujo de algo girando.
   De paso: esto pesa unos 4 KB de texto contra los 109 KB del PNG, y una intro
   que sale MIENTRAS carga la pagina no puede depender de una descarga que
   todavia no termino.

   SOLO SE ANIMAN transform Y opacity. Las dos las resuelve el compositor sin
   volver a maquetar la pagina, que es lo que permite que esto corra a 60
   cuadros en un telefono. Ni un width, ni un top, ni un gap animado: cualquiera
   de esos obliga al navegador a recalcular la maqueta 60 veces por segundo.

   POR ESO LAS PIEZAS VAN EN DOS CAPAS. Una capa lleva el viaje (viene de lejos,
   se separa, choca, se aparta) y la de dentro lleva el giro continuo. Dos
   animaciones sobre el transform del MISMO elemento no se suman -- gana la
   ultima --, asi que la unica forma de que algo gire mientras se desplaza es
   que sean dos elementos, uno dentro del otro.

   SI EL JAVASCRIPT NO CARGA, ESTA INTRO NO EXISTE. Todo cuelga de
   'html.intro-va', clase que pone un script corto en el <head> de la portada.
   Sin ella el velo esta en display:none y la portada se ve directa. Es la
   leccion que ya costo una pagina en blanco entera con '.por-revelar' (ver
   sitio.css): nunca se esconde con CSS algo que necesita JavaScript para
   volver a aparecer.

   LOS COLORES SALEN DE LOS TOKENS DE sitio.css, que se carga antes. Ni un hex
   copiado a mano: el dia que cambie la marca, cambia con ella.

   LA LINEA DE TIEMPO, DE UN VISTAZO (segundos desde que abre la pagina):
      0.00  entra la rejilla del piso
      0.00  EL VUELO: la camara atraviesa un campo de atomos que se le acercan
      0.00  las dos piezas llegan desde -900px, girando
      1.15  frenan, separadas, girando en su sitio
      1.35  el campo de atomos se apaga
      1.84  arrancan la una contra la otra
      2.21  CHOQUE en el centro: destello y onda
      2.52  cae la frase, palabra por palabra
      2.85  las piezas quedan a los lados del titulo, a su misma altura
      3.59  (78% de 4.6s: 1.01 s para 1000 px de acercamiento, ~990 px/s)
      3.59  EL CIERRE: se vienen encima CONVERGIENDO al centro y creciendo;
            el titulo se va con ellas
      4.60  han pasado de largo; el velo se retira y aparece la portada

   CUIDADO AL AFINAR: los porcentajes de intro-viaje-* son de una animacion de
   4.6s, pero el destello, la onda, las cinco palabras, la salida del titulo y
   la barra llevan su retraso en SEGUNDOS ABSOLUTOS y no se enteran de que la
   animacion cambio. Si se toca la duracion del viaje hay que recorrerlos a
   mano -- y tambien DURACION en intro.js, que es lo que decide cuando se
   retira el velo.
   ============================================================================= */

/* ------------------------------------------------------------- el escenario -- */

/* Mientras la intro esta puesta, la pagina de atras no se desplaza. Sin esto
   la rueda del raton mueve la portada por debajo del velo y al terminar se
   aterriza a media pagina, sin haberla visto empezar. */
html.intro-va,
html.intro-va body { overflow: hidden; }

.intro {
    position: fixed; inset: 0; z-index: 9000;
    display: none;
    align-items: center; justify-content: center;
    overflow: hidden;

    /* Radial y no color plano: el centro mas claro hace de foco y manda la
       mirada justo donde van a aparecer las figuras. */
    background:
        radial-gradient(120% 90% at 50% 45%,
            var(--panel-alto) 0%,
            var(--panel-fondo) 45%,
            var(--panel-hondo) 100%);

    /* LA PROFUNDIDAD SALE DE AQUI. Sin perspective, rotateY solo achata las
       cosas; con ella, lo que se aleja se ve mas chico y el giro es un giro de
       verdad. 1100px es un punto de vista natural: mas corto deforma como ojo
       de pez, mas largo aplana el efecto hasta que no se nota. */
    perspective: 1100px;
    perspective-origin: 50% 45%;

    transition: opacity .6s ease, transform .6s ease;
}

html.intro-va .intro { display: flex; }

/* El adios: el velo se va HACIA el visitante mientras se desvanece, asi que la
   portada no "aparece" sino que se descubre detras. */
.intro--sale { opacity: 0; transform: scale(1.12); pointer-events: none; }

/* -------------------------------------------------------------- el piso 3D -- */
/*  Una rejilla tumbada 76 grados que se pierde en el horizonte. Es lo que
    convierte el fondo en un ESPACIO y no en un color: sin una referencia de
    suelo, el ojo no tiene contra que medir la profundidad de las figuras.

    Con mascara para que se disuelva antes del borde: una rejilla que llega
    cortada al filo de la pantalla delata el truco. */
.intro__piso {
    position: absolute; left: 50%; bottom: -8%;
    width: 260vw; height: 90vh;
    margin-left: -130vw;
    transform: rotateX(76deg);
    transform-origin: 50% 100%;
    background-image:
        repeating-linear-gradient(90deg,
            rgba(var(--piv-verde-rgb), .32) 0 1px, transparent 1px 84px),
        repeating-linear-gradient(0deg,
            rgba(var(--piv-verde-rgb), .32) 0 1px, transparent 1px 84px);
    -webkit-mask-image: radial-gradient(60% 70% at 50% 100%, #000 0%, transparent 78%);
    mask-image: radial-gradient(60% 70% at 50% 100%, #000 0%, transparent 78%);
    opacity: 0;
    animation: intro-piso 1.5s cubic-bezier(.16,.84,.35,1) .1s forwards;
}

@keyframes intro-piso {
    from { opacity: 0; transform: rotateX(76deg) translateY(160px); }
    to   { opacity: 1; transform: rotateX(76deg) translateY(0); }
}

/* ================================================== EL VUELO ENTRE ATOMOS ===

   Carlos, 2026-09-08: "has visto el efecto de la teoria del big bang de la
   serie, cuando los atomos parece que se te acercan super rapido".

   Eso es lo que pasa durante el primer segundo y medio: la camara atraviesa un
   campo de atomos que vienen de muy lejos, pasan zumbando al lado y salen por
   detras del hombro. De ese vuelo llegan las dos figuras del logo.

   NO ALARGA LA INTRO NI UN CUADRO. El vuelo ocurre MIENTRAS las dos piezas
   hacen su entrada, no antes: refuerza la llegada en vez de retrasarla.

   EL TRUCO ES QUE NO HAY TRUCO: solo se anima translateZ. Como el velo tiene
   perspective con el origen en el centro, un elemento que NO esta en el centro
   y avanza en Z se abre hacia afuera solo -- igual que en la vida, donde lo que
   se te acerca se sale del encuadre por el lado mas cercano. Calcular a mano
   esa apertura habria sido trigonometria por cada mota; el navegador ya la
   hace, gratis y bien.

   Y POR ESO CADA ATOMO ES DOS ELEMENTOS. El de fuera vuela (translateZ) y el de
   dentro gira. La misma razon que las piezas grandes: dos animaciones sobre el
   transform del mismo elemento no se suman, gana la ultima.
   =========================================================================== */
.intro__polvo,
.intro__vuelo {
    position: absolute; inset: 0;
    transform-style: preserve-3d;
    pointer-events: none;
}

/*  El campo se apaga cuando las figuras ya frenaron. Si siguiera, la frase
    tendria que leerse con cosas cruzandose por delante. */
.intro__vuelo { animation: intro-apaga-vuelo .7s ease-out 1.35s both; }

@keyframes intro-apaga-vuelo {
    from { opacity: 1; }
    to   { opacity: 0; }
}

/* --------------------------------------------------------------- los atomos -- */
.intro__atomo {
    position: absolute;
    width: 46px; height: 46px;
    margin: -23px 0 0 -23px;
    transform-style: preserve-3d;
    opacity: 0;
    will-change: transform, opacity;
    animation: intro-vuela 1.5s cubic-bezier(.45,0,.75,.55) infinite;
}

/*  LA CURVA ES ACELERADA A PROPOSITO (ease-in). Un atomo que se acerca a
    velocidad constante se lee como un objeto flotando; acelerando al final se
    lee como algo que SE TE VIENE ENCIMA, que es justo lo que se pidio. */
@keyframes intro-vuela {
    0%   { opacity: 0;   transform: translateZ(-1500px) scale(.5); filter: blur(0); }
    12%  { opacity: .9; }
    72%  { opacity: .9;  filter: blur(0); }
    /* al pasar junto a la camara se desenfoca: es lo que vende la velocidad */
    100% { opacity: 0;   transform: translateZ(560px) scale(1.3); filter: blur(3px); }
}

/*  SIN drop-shadow, Y ES A PROPOSITO. Un halo por aro serian 42 sombras
    desenfocadas repintandose 60 veces por segundo, y el campo entero existe
    para durar segundo y medio. El brillo lo pone el nucleo, que son 14
    box-shadow en vez de 42, y el trazo se hace ver subiendo su opacidad. */
.intro__atomo-aro {
    position: absolute; inset: 0;
    border: 1.5px solid var(--piv-verde-claro);
    border-radius: 50%;
    opacity: .95;
}

.intro__atomo-aro:nth-child(1) { transform: rotateY(0deg); }
.intro__atomo-aro:nth-child(2) { transform: rotateY(60deg); }
.intro__atomo-aro:nth-child(3) { transform: rotateY(120deg); }

.intro__atomo-nucleo {
    position: absolute; left: 50%; top: 50%;
    width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px;
    border-radius: 50%;
    background: var(--piv-verde-claro);
    box-shadow: 0 0 9px 2px rgba(var(--piv-verde-rgb), .8);
}

/*  El giro va en la capa de DENTRO, que es la unica forma de que un atomo gire
    mientras vuela. Cada uno lleva su propia duracion, puesta por intro.js: con
    la misma para todos, catorce atomos girando al unisono se leen como una
    pieza repetida catorce veces. */
.intro__atomo-giro {
    position: absolute; inset: 0;
    transform-style: preserve-3d;
    animation: intro-gira-atomo 2.2s linear infinite;
}

@keyframes intro-gira-atomo {
    from { transform: rotateX(-15deg) rotateY(0deg); }
    to   { transform: rotateX(-15deg) rotateY(360deg); }
}

/* ---------------------------------------------------------------- el polvo -- */
/*  Las motas del fondo, que ahora son ESTELAS y no puntos: alargadas y
    verticales, para que el campo tenga sensacion de velocidad incluso donde no
    hay ningun atomo. Las crea intro.js, que les reparte posicion, tamanio y
    retraso -- 24 elementos escritos a mano serian 24 renglones que nadie va a
    mantener.

    Si el JS no llega a crearlas, el contenedor se queda vacio y la intro corre
    igual. Es el adorno del adorno. */
.intro__mota {
    position: absolute;
    width: 2.5px; height: 26px;
    border-radius: 2px;
    background: linear-gradient(180deg,
        transparent 0%,
        var(--piv-verde-claro) 45%,
        transparent 100%);
    box-shadow: 0 0 6px rgba(var(--piv-verde-rgb), .55);
    opacity: 0;
    animation: intro-mota 1.6s cubic-bezier(.45,0,.75,.55) infinite;
}

@keyframes intro-mota {
    0%   { opacity: 0;   transform: translateZ(-1500px) scaleY(.3); }
    14%  { opacity: .9; }
    70%  { opacity: .75; }
    100% { opacity: 0;   transform: translateZ(620px) scaleY(2.6); }
}

/* --------------------------------------------------------------- la escena -- */
/*  EL BLOQUE VA UN POCO POR ENCIMA DEL CENTRO, no centrado exacto. Carlos,
    2026-09-08: "el texto subelo poquito mas arriba".

    Y tiene su razon: centrado matematicamente se ve BAJO. La barra de progreso
    ocupa la parte de abajo y el ojo la cuenta como parte del conjunto, asi que
    el centro optico queda mas arriba que el geometrico. Es el mismo motivo por
    el que el perspective-origin de la intro esta en 45% y no en 50%. */
.intro__escena {
    position: relative;
    display: flex;
    align-items: center; justify-content: center;
    transform-style: preserve-3d;
    padding: 0 24px;
    width: 100%;
    transform: translateY(-30px);
}

/*  LAS FIGURAS Y EL TITULO COMPARTEN LINEA, no van una encima del otro.

    Carlos, 2026-09-08: "el texto deberia subirse un poco y quedar EN MEDIO de
    las 2 figuras, para que resalte". Antes el titulo caia debajo del par de
    figuras y las tres cosas se leian como una lista; en la misma linea, las
    figuras dejan de ser un adorno colocado arriba y pasan a enmarcar el texto
    -- que es como esta armado el logo de verdad.

    Por eso la caja de las figuras es ABSOLUTA y cubre la escena entera: su
    centro es el mismo punto que el centro del titulo. Ademas de encuadrarlo,
    eso es lo que permite que el choque del principio ocurra justo ahi, en el
    sitio del que despues brota la frase. */
.intro__figuras {
    position: absolute; inset: 0;
    transform-style: preserve-3d;
    pointer-events: none;
}

.intro__pieza {
    position: absolute; left: 50%; top: 50%;
    transform-style: preserve-3d;
    will-change: transform, opacity;
}

.intro__pieza--izq { width: 172px; height: 172px; margin: -86px 0 0 -86px; }
.intro__pieza--der { width: 152px; height: 152px; margin: -76px 0 0 -76px; }

/* EL VIAJE COMPLETO EN UNA SOLA ANIMACION, por pieza. Va asi y no en cuatro
   animaciones encadenadas porque todas mueven el mismo transform: encadenarlas
   obligaria a que cada una empezara donde termino la anterior, escrito a mano,
   y un solo numero mal puesto daria un salto a media coreografia. */
.intro__pieza--izq { animation: intro-viaje-izq 4.6s linear both; }
.intro__pieza--der { animation: intro-viaje-der 4.6s linear both; }

/*  EL VIAJE COMPLETO DE CADA PIEZA, EN UNA SOLA ANIMACION.

    Va asi y no en cinco animaciones encadenadas porque todas mueven el mismo
    transform: encadenarlas obligaria a que cada una empezara donde termino la
    anterior, escrito a mano, y un solo numero mal puesto daria un salto a media
    coreografia.

    EL ORDEN DE LOS ACTOS LO CORRIGIO CARLOS, y la correccion es la buena. En un
    primer intento las figuras pasaban rozando la camara AL PRINCIPIO, y el
    efecto se leia al reves de lo que se buscaba:

      "estas haciendo que se alejen de mi las figuras grandes, y lo que yo
       quiero es CERRAR con el efecto de que se acerquen bien rapido, vayan
       creciendo y desaparecen, e inicia la pagina".

    Tiene razon por una cuestion de narrativa, no de gusto: una pieza que se te
    acerca y despues retrocede a su sitio esta, netamente, ALEJANDOSE -- el
    acercamiento es solo el camino de ida. Para que el gesto se lea como
    acercamiento tiene que ser lo ULTIMO que hace. Asi que la entrada volvio a
    ser la de siempre y la pasada se mudo al final, donde ya no vuelve: crece,
    se sale del encuadre y detras esta la portada.

    CADA TRAMO LLEVA SU PROPIA CURVA. Con una sola cubic-bezier el choque no se
    leia: esa curva gasta el 70% del recorrido en el primer 20% del tiempo, asi
    que las piezas se separaban de golpe al tocarse. Ahora:
      llegada    frenazo fuerte -- vienen lanzadas y clavan
      espera     quietas
      al centro  ease-IN: arrancan lentas y llegan rapido, o sea un impacto
      a los lados  suave, que es el desenlace
      el titulo  quietas, para que se lea
      EL CIERRE  acelerando hasta encima de la camara

    Los porcentajes son de 4.6s: 25% son 1.15s, 40% son 1.84s, 48% son 2.21s,
    62% son 2.85s y 80% son 3.68s.  */
@keyframes intro-viaje-izq {
    /* llega desde fuera de cuadro y desde muy atras, girando de mas */
    0%   { opacity: 0; transform: translate3d(-46vw, 0, -900px) rotateY(-540deg) scale(.35);
           animation-timing-function: cubic-bezier(.16,.9,.3,1); }
    16%  { opacity: 1; animation-timing-function: cubic-bezier(.16,.9,.3,1); }
    /* frena en su sitio, separada */
    25%  { opacity: 1; transform: translate3d(-118px, 0, 0) rotateY(0deg) scale(1);
           animation-timing-function: linear; }
    /* aguanta: sin esta pausa el ojo no alcanza a ver las dos figuras */
    40%  { opacity: 1; transform: translate3d(-118px, 0, 0) rotateY(0deg) scale(1);
           animation-timing-function: cubic-bezier(.55,0,.85,.4); }
    /* al centro, al choque */
    48%  { opacity: 1; transform: translate3d(0, 0, 60px) rotateY(0deg) scale(1.1);
           animation-timing-function: cubic-bezier(.25,.7,.3,1); }
    /* se aparta a un lado del titulo, a su misma altura */
    62%  { opacity: 1; transform: translate3d(calc(-1 * min(30vw, 430px)), 0, 0) rotateY(0deg) scale(.6);
           animation-timing-function: linear; }
    /* quieta, flanqueando la frase, mientras se lee */
    78%  { opacity: 1; transform: translate3d(calc(-1 * min(30vw, 430px)), 0, 0) rotateY(0deg) scale(.6);
           animation-timing-function: cubic-bezier(.4,0,.62,.88); }
    /*  EL CIERRE: se vienen encima CONVERGIENDO, creciendo, y se desvanecen.

        LA CONVERGENCIA NO ES UN ADORNO, ES LO QUE HACE QUE SE LEA. Antes
        terminaban en 150 px de su lado y la perspectiva multiplica ese
        desplazamiento igual que el tamanio: a 900 px de acercamiento eso son
        casi 900 px aparentes, o sea que salian DE FRENTE Y HACIA AFUERA, cada
        una por su esquina. Carlos: "se ve que se van derecho pero alejados".

        Terminando cerca del eje (26 px) el recorrido lateral es de vuelta al
        centro, y lo que se ve es lo que se pidio: dos cosas que se te vienen
        encima juntandose, hasta que tapan el cuadro y detras esta la portada.

        LA VELOCIDAD SALE DE ACORTAR EL TRAMO, NO DE TORCER LA CURVA. Se probo
        lo segundo primero -- una cubic-bezier con el control en (.96,.42) -- y
        no funciona: una curva asi deja el movimiento casi parado hasta el final
        y despues lo resuelve en 30 ms, o sea que **no se ve rapido, se ve como
        un salto**. Medido: a falta del 2% de la animacion la pieza solo habia
        recorrido la mitad del acercamiento.

        Ahora el tramo entero cabe en 1.01 s (del 78% al 100%) con una curva que
        acelera al entrar y **mantiene la velocidad alta hasta el final**
        (`.4,0,.62,.88`). Son ~990 px por segundo contra los ~650 de la version
        anterior: la mitad mas rapido, y sin saltos.

        LOS NUMEROS DE ESTE TRAMO, Y POR QUE SON ESOS. El acercamiento llega a
        1000 px con perspective 1100, o sea **11 aumentos**: la pieza de 172 px
        acaba midiendo mas de mil y llena la pantalla. Mas cerca no se puede ir
        sin rozar el plano de la camara -- a Z = 1100 el factor se va a infinito
        y el navegador deja de rasterizar bien.

        Y NO SE DESVANECEN HASTA EL 97%. Carlos: "que desaparezcan hasta que
        esten mas cerca las 2 figuras". Antes empezaban a irse en el 96 y se
        perdian todavia a media pantalla; ahora aguantan opacas hasta que estan
        practicamente encima, y el fundido dura los ultimos 138 ms -- que a esa
        escala se lee como un destello, no como un desvanecido. */
    97%  { opacity: 1; }
    100% { opacity: 0; transform: translate3d(-26px, 0, 1000px) rotateY(0deg) scale(.6); }
}

@keyframes intro-viaje-der {
    0%   { opacity: 0; transform: translate3d(46vw, 0, -900px) rotateY(540deg) scale(.35);
           animation-timing-function: cubic-bezier(.16,.9,.3,1); }
    16%  { opacity: 1; animation-timing-function: cubic-bezier(.16,.9,.3,1); }
    25%  { opacity: 1; transform: translate3d(118px, 0, 0) rotateY(0deg) scale(1);
           animation-timing-function: linear; }
    40%  { opacity: 1; transform: translate3d(118px, 0, 0) rotateY(0deg) scale(1);
           animation-timing-function: cubic-bezier(.55,0,.85,.4); }
    48%  { opacity: 1; transform: translate3d(0, 0, 60px) rotateY(0deg) scale(1.1);
           animation-timing-function: cubic-bezier(.25,.7,.3,1); }
    62%  { opacity: 1; transform: translate3d(min(30vw, 430px), 0, 0) rotateY(0deg) scale(.6);
           animation-timing-function: linear; }
    78%  { opacity: 1; transform: translate3d(min(30vw, 430px), 0, 0) rotateY(0deg) scale(.6);
           animation-timing-function: cubic-bezier(.4,0,.62,.88); }
    97%  { opacity: 1; }
    100% { opacity: 0; transform: translate3d(26px, 0, 1000px) rotateY(0deg) scale(.6); }
}

/* ------------------------------------------------- figura 1: el orbe -------- */
/*  Cinco anillos en cinco planos, girando como un solo cuerpo: la esfera de
    orbitas cruzadas del logo, armada en el espacio. Cada anillo va rotado 36
    grados mas que el anterior sobre el eje Y.

    36 = 180/5. Con el reparto exacto los cinco nunca se solapan del todo y a
    cualquier altura del giro se ve la trama completa. */
.intro__orbe,
.intro__nodo {
    position: absolute; inset: 0;
    transform-style: preserve-3d;
    will-change: transform;
}

.intro__orbe { animation: intro-gira-orbe 7s linear infinite; }

@keyframes intro-gira-orbe {
    from { transform: rotateX(-18deg) rotateY(0deg); }
    to   { transform: rotateX(-18deg) rotateY(360deg); }
}

.intro__anillo {
    position: absolute; inset: 0;
    border: 2px solid var(--piv-verde-claro);
    border-radius: 50%;

    /* El halo. drop-shadow y no box-shadow: box-shadow dibuja la sombra del
       CAJON cuadrado, y lo que tiene que brillar aqui es el trazo del aro. */
    filter: drop-shadow(0 0 7px rgba(var(--piv-verde-rgb), .85));
    opacity: .92;
}

.intro__anillo:nth-child(1) { transform: rotateY(0deg); }
.intro__anillo:nth-child(2) { transform: rotateY(36deg); }
.intro__anillo:nth-child(3) { transform: rotateY(72deg); }
.intro__anillo:nth-child(4) { transform: rotateY(108deg); }
.intro__anillo:nth-child(5) { transform: rotateY(144deg); }

/* El corazon del orbe. Sin el, la esfera se ve hueca y el ojo no encuentra el
   centro de giro. */
.intro__nucleo {
    position: absolute; left: 50%; top: 50%;
    width: 26px; height: 26px; margin: -13px 0 0 -13px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 32%, #fff 0%, var(--piv-verde-claro) 40%, var(--piv-verde) 100%);
    box-shadow: 0 0 26px 6px rgba(var(--piv-verde-rgb), .75);
}

/* ------------------------------------------------ figura 2: el nodo --------- */
/*  El circulo con anillos y tres puntos de la derecha del logo. Gira en
    SENTIDO CONTRARIO al orbe: dos cosas girando igual se leen como una pieza
    repetida; girando al reves se leen como dos piezas que se responden. */
.intro__nodo { animation: intro-gira-nodo 6s linear infinite; }

/*  GIRA SOBRE Z, NO SOBRE Y, y esa es la diferencia entre que se lea o no.
    Girando en Y, cuatro veces por vuelta el nodo quedaba DE CANTO: sus arcos y
    sus puntos son figuras planas y de perfil desaparecian hasta dejar una
    raya. El orbe si aguanta el giro en Y porque sus cinco anillos estan en
    cinco planos distintos y siempre hay alguno de frente.

    Asi que el nodo gira de frente, inclinado en X y en Y para que siga siendo
    un objeto en el espacio y no una calcomania pegada a la pantalla. */
@keyframes intro-gira-nodo {
    from { transform: rotateX(26deg) rotateY(-16deg) rotateZ(0deg); }
    to   { transform: rotateX(26deg) rotateY(-16deg) rotateZ(-360deg); }
}

.intro__nodo-anillo,
.intro__nodo-arco {
    position: absolute;
    border-radius: 50%;
    filter: drop-shadow(0 0 6px rgba(var(--piv-verde-rgb), .8));
}

/* El anillo cerrado del centro */
.intro__nodo-anillo {
    left: 50%; top: 50%;
    width: 58px; height: 58px; margin: -29px 0 0 -29px;
    border: 3px solid var(--piv-verde-claro);
}

/* Los dos arcos abiertos. El truco de siempre: se dejan transparentes los
   lados que no se quieren y el circulo deja de ser un aro para ser un tramo. */
.intro__nodo-arco { border: 3px solid transparent; }

.intro__nodo-arco--uno {
    inset: 4px;
    border-top-color: var(--piv-verde-claro);
    border-right-color: var(--piv-verde-claro);
    animation: intro-arco 3.2s linear infinite;
}

.intro__nodo-arco--dos {
    inset: 26px;
    border-bottom-color: var(--piv-verde);
    border-left-color: var(--piv-verde);
    animation: intro-arco 2.4s linear infinite reverse;
}

/*  El aro punteado exterior. Se aniadio porque al lado del orbe -- que son
    cinco trazos cruzados -- el nodo se veia flaco, y porque el punteado es lo
    que lo hace leerse como CIRCUITO y no como un segundo planeta. */
.intro__nodo-halo {
    position: absolute; inset: -10px;
    border: 2px dashed rgba(var(--piv-verde-rgb), .75);
    border-radius: 50%;
    animation: intro-arco 9s linear infinite;
}

@keyframes intro-arco {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* Los tres puntos del circuito */
.intro__nodo-punto {
    position: absolute;
    width: 13px; height: 13px;
    border-radius: 50%;
    background: var(--piv-verde-claro);
    box-shadow: 0 0 14px 3px rgba(var(--piv-verde-rgb), .8);
}

.intro__nodo-punto--a { top: 0;    right: 26px; }
.intro__nodo-punto--b { bottom: 6px; left: 12px; }
.intro__nodo-punto--c { top: 44%;  right: -4px; }

/* El centro macizo */
.intro__nodo-centro {
    position: absolute; left: 50%; top: 50%;
    width: 20px; height: 20px; margin: -10px 0 0 -10px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 32%, #fff 0%, var(--piv-verde-claro) 45%, var(--piv-verde) 100%);
    box-shadow: 0 0 22px 5px rgba(var(--piv-verde-rgb), .7);
}

/* -------------------------------------------------------------- el impacto -- */
/*  Cuando las dos se cruzan en el centro salen un destello y una onda. Los dos
    duran menos de un segundo: un golpe es corto, y estirarlo lo convierte en
    una pantalla de carga barata. */
.intro__destello,
.intro__onda {
    position: absolute; left: 50%; top: 50%;
    border-radius: 50%;
    pointer-events: none;
    opacity: 0;
}

.intro__destello {
    width: 280px; height: 280px; margin: -140px 0 0 -140px;
    background: radial-gradient(circle,
        rgba(255,255,255,.95) 0%,
        rgba(var(--piv-verde-rgb), .55) 35%,
        transparent 70%);
    animation: intro-destello .55s ease-out 2.18s both;
}

@keyframes intro-destello {
    0%   { opacity: 0; transform: scale(.2); }
    30%  { opacity: 1; transform: scale(1.05); }
    100% { opacity: 0; transform: scale(2); }
}

.intro__onda {
    width: 180px; height: 180px; margin: -90px 0 0 -90px;
    border: 2px solid var(--piv-verde-claro);
    animation: intro-onda .9s cubic-bezier(.12,.75,.3,1) 2.22s both;
}

/*  EL 0% VA EN opacity 0 Y NO EN .95. Con fill-mode both, lo que el navegador
    pinta durante el retraso es el PRIMER fotograma -- asi que un 0% opaco
    dejaba un aro parado en mitad de la pantalla desde que abria la intro hasta
    el segundo 2.22, como si fuera parte del decorado. */
@keyframes intro-onda {
    0%   { opacity: 0;   transform: scale(.25); }
    6%   { opacity: .95; transform: scale(.3); }
    100% { opacity: 0;   transform: scale(4.4); }
}

/* ----------------------------------------------------------------- la frase -- */
/*  Cada palabra cae desde atras girando sobre su eje horizontal, escalonada.
    El escalonado es lo que hace que se lea como una frase que SE ARMA y no
    como un bloque de texto que aparece de golpe.

    La tipografia es la misma del encabezado -- Barlow Condensed en
    mayusculas -- para que lo primero que se ve del sitio y lo que queda
    despues en la barra de arriba sean la misma cosa. */
.intro__frase {
    position: relative;
    margin: 0;
    text-align: center;
    font-family: var(--f-titulo);
    text-transform: uppercase;
    line-height: 1.08;
    color: #fff;
    transform-style: preserve-3d;
}

.intro__renglon { display: block; }

/*  LOS MINIMOS DEL clamp ESTAN MEDIDOS, NO ELEGIDOS A OJO. "PROYECTOS
    INSTITUCIONALES" en Barlow Condensed 700 mide 314 px a 1.65rem, y un
    telefono de 360 px deja 312 utiles con el padding: el renglon se partia en
    tres. A 1.3rem mide 248 y entra hasta en una pantalla de 320. */
.intro__renglon--uno {
    font-size: clamp(1.3rem, 5.4vw, 3.4rem);
    font-weight: 700; letter-spacing: .05em;
}

.intro__renglon--dos {
    font-size: clamp(1.05rem, 4.4vw, 2.8rem);
    font-weight: 700; letter-spacing: .17em;
    color: var(--piv-verde-claro);
}

.intro__palabra {
    display: inline-block;
    margin: 0 .16em;
    opacity: 0;
    transform-origin: 50% 100%;
    animation: intro-palabra .75s cubic-bezier(.2,.85,.25,1.05) both;
}

/* El retraso de cada palabra, escrito uno por uno y no calculado en el JS: son
   cinco, se leen de un vistazo, y asi la coreografia entera vive en el CSS y se
   afina sin tocar codigo. */
.intro__renglon--uno .intro__palabra:nth-child(1) { animation-delay: 2.52s; }
.intro__renglon--uno .intro__palabra:nth-child(2) { animation-delay: 2.64s; }
.intro__renglon--dos .intro__palabra:nth-child(1) { animation-delay: 2.80s; }
.intro__renglon--dos .intro__palabra:nth-child(2) { animation-delay: 2.88s; }
.intro__renglon--dos .intro__palabra:nth-child(3) { animation-delay: 2.96s; }

/*  El titulo se desvanece cuando las figuras arrancan hacia la camara. Si se
    quedara quieto mientras ellas se vienen encima, el cierre se partiria en
    dos: una cosa que sale disparada y otra que espera. Asi el cuadro entero se
    va junto. */
.intro__frase { animation: intro-frase-sale .62s ease-in 3.45s both; }

@keyframes intro-frase-sale {
    from { opacity: 1; transform: translateZ(0); }
    to   { opacity: 0; transform: translateZ(180px); }
}

@keyframes intro-palabra {
    from {
        opacity: 0;
        transform: translate3d(0, 26px, -320px) rotateX(-92deg);
        filter: blur(6px);
    }
    to {
        opacity: 1;
        transform: translate3d(0, 0, 0) rotateX(0deg);
        filter: blur(0);
    }
}

/* ---------------------------------------------------------- barra y saltar -- */
/*  La barra dice "esto se acaba": una pantalla que tapa el sitio sin avisar
    cuanto falta se siente como un cuelgue. Y el boton de saltar existe porque,
    aunque sea una vez por sesion, quien viene a inscribirse tiene derecho a no
    ver la funcion. */
.intro__barra {
    position: absolute; left: 50%; bottom: 46px;
    width: 190px; height: 2px; margin-left: -95px;
    background: rgba(255,255,255,.16);
    border-radius: 2px; overflow: hidden;
}

.intro__barra span {
    display: block; height: 100%; width: 100%;
    background: linear-gradient(90deg, var(--piv-verde) 0%, var(--piv-verde-claro) 100%);
    transform: scaleX(0); transform-origin: 0 50%;
    animation: intro-barra 4.5s cubic-bezier(.35,.1,.25,1) both;
}

@keyframes intro-barra {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}

.intro__saltar {
    position: absolute; top: 22px; right: 22px;
    background: none;
    border: 1px solid rgba(255,255,255,.28);
    border-radius: var(--radio-sm);
    color: rgba(255,255,255,.82);
    font-family: var(--f-titulo); font-size: .98rem; letter-spacing: .06em;
    text-transform: uppercase;
    padding: 7px 15px;
    cursor: pointer;
    transition: background .2s ease, color .2s ease, border-color .2s ease;
}

.intro__saltar:hover,
.intro__saltar:focus-visible {
    background: rgba(255,255,255,.1);
    border-color: var(--piv-verde-claro);
    color: #fff;
}

/* ------------------------------------------------------------------- movil -- */
/*  En el telefono se reduce el reparto, no la idea: piezas mas chicas, menos
    recorrido y sin piso. La rejilla en perspectiva a 380 px de ancho no se lee
    como suelo, solo como ruido detras del texto. */
@media (max-width: 700px) {
    .intro__piso { display: none; }

    .intro__pieza--izq { width: 112px; height: 112px; margin: -56px 0 0 -56px; }
    .intro__pieza--der { width: 100px; height: 100px; margin: -50px 0 0 -50px; }

    .intro__nodo-anillo { width: 40px; height: 40px; margin: -20px 0 0 -20px; }
    .intro__nodo-arco--dos { inset: 18px; }
    .intro__nodo-punto { width: 10px; height: 10px; }
    .intro__nucleo { width: 18px; height: 18px; margin: -9px 0 0 -9px; }

    .intro__barra { bottom: 34px; }
    .intro__escena { padding: 0 14px; }

    /* Atomos mas chicos: en una pantalla de telefono, a 46 px, tres de ellos
       tapan la mitad del cuadro cuando pasan cerca. */
    .intro__atomo { width: 32px; height: 32px; margin: -16px 0 0 -16px; }
    .intro__mota { height: 16px; }
    .intro__saltar { top: 14px; right: 14px; padding: 6px 12px; font-size: .9rem; }

    /*  EN EL TELEFONO LAS FIGURAS SE VAN ARRIBA, NO A LOS LADOS.

        En pantalla ancha el titulo queda ENTRE las dos, que es como esta armado
        el logo. Aqui no cabe y no es cuestion de apretarlo: en una pantalla de
        360 px el titulo ya ocupa 248 px, o sea 124 a cada lado del centro, y
        cada figura pide 34 mas de radio -- las dos se saldrian por el borde.

        Asi que en vertical se apilan: las figuras arriba y el titulo debajo,
        que es la version de siempre. El gesto no cambia, cambia el encuadre. */
    @keyframes intro-viaje-izq {
        0%   { opacity: 0; transform: translate3d(-52vw, 0, -900px) rotateY(-540deg) scale(.35);
               animation-timing-function: cubic-bezier(.16,.9,.3,1); }
        16%  { opacity: 1; animation-timing-function: cubic-bezier(.16,.9,.3,1); }
        25%  { opacity: 1; transform: translate3d(-62px, -62px, 0) rotateY(0deg) scale(.85);
               animation-timing-function: linear; }
        40%  { opacity: 1; transform: translate3d(-62px, -62px, 0) rotateY(0deg) scale(.85);
               animation-timing-function: cubic-bezier(.55,0,.85,.4); }
        48%  { opacity: 1; transform: translate3d(0, -62px, 40px) rotateY(0deg) scale(.95);
               animation-timing-function: cubic-bezier(.25,.7,.3,1); }
        62%  { opacity: 1; transform: translate3d(-86px, -72px, 0) rotateY(0deg) scale(.5);
               animation-timing-function: linear; }
        78%  { opacity: 1; transform: translate3d(-86px, -72px, 0) rotateY(0deg) scale(.5);
               animation-timing-function: cubic-bezier(.4,0,.62,.88); }
        97%  { opacity: 1; }
        100% { opacity: 0; transform: translate3d(-16px, -10px, 985px) rotateY(0deg) scale(.5); }
    }

    @keyframes intro-viaje-der {
        0%   { opacity: 0; transform: translate3d(52vw, 0, -900px) rotateY(540deg) scale(.35);
               animation-timing-function: cubic-bezier(.16,.9,.3,1); }
        16%  { opacity: 1; animation-timing-function: cubic-bezier(.16,.9,.3,1); }
        25%  { opacity: 1; transform: translate3d(62px, -62px, 0) rotateY(0deg) scale(.85);
               animation-timing-function: linear; }
        40%  { opacity: 1; transform: translate3d(62px, -62px, 0) rotateY(0deg) scale(.85);
               animation-timing-function: cubic-bezier(.55,0,.85,.4); }
        48%  { opacity: 1; transform: translate3d(0, -62px, 40px) rotateY(0deg) scale(.95);
               animation-timing-function: cubic-bezier(.25,.7,.3,1); }
        62%  { opacity: 1; transform: translate3d(86px, -72px, 0) rotateY(0deg) scale(.5);
               animation-timing-function: linear; }
        78%  { opacity: 1; transform: translate3d(86px, -72px, 0) rotateY(0deg) scale(.5);
               animation-timing-function: cubic-bezier(.4,0,.62,.88); }
        97%  { opacity: 1; }
        100% { opacity: 0; transform: translate3d(16px, -10px, 985px) rotateY(0deg) scale(.5); }
    }

    /*  El destello y la onda se corren con ellas: el choque ya no ocurre en el
        centro de la pantalla sino 62 px mas arriba, donde estan las figuras.

        Se suma a su margen, no se sustituye: ese margen negativo es lo que los
        centra sobre su propio punto, y pisarlo con -62px los descolocaria. */
    .intro__destello { margin-top: -202px; }   /* -140 de centrado, -62 de subida */
    .intro__onda     { margin-top: -152px; }   /*  -90 de centrado, -62 de subida */

    /*  Y el titulo baja lo que suben las figuras, para que no se pisen.

        VA POR MARGEN Y NO POR transform. La frase ya tiene una animacion
        (intro-frase-sale) que mueve su transform con fill-mode both, o sea que
        se aplica desde el primer cuadro: un translateY escrito aqui no llegaria
        a verse nunca. */
    .intro__frase { margin-top: 46px; }
}

/* ------------------------------------------------------ menos movimiento ---- */
/*  A quien tiene prefers-reduced-motion activo el movimiento le provoca mareo
    de verdad, y una pantalla completa girando es el peor caso posible. Aqui NO
    se apagan las animaciones dejando el velo puesto -- eso lo dejaria clavado
    para siempre --: intro.js ni siquiera enciende la intro en ese caso, y esta
    regla es el segundo cerrojo por si alguna vez se encendiera igual. */
@media (prefers-reduced-motion: reduce) {
    html.intro-va .intro { display: none; }
    html.intro-va, html.intro-va body { overflow: auto; }
}
