/* ==========================================================================
   El Camino DT — Movimiento
   --------------------------------------------------------------------------
   Toda la vida del producto vive acá, en un archivo, para que se pueda leer
   entera y se pueda apagar entera. Son cuatro trabajos y ninguno decora:

   1. CONTINUIDAD. Trece documentos que se sentían trece recargas pasan a ser
      una aplicación: el chrome y el escudo NO se repintan al cambiar de
      pantalla —se quedan quietos, porque no cambiaron— y lo único que se
      cruza es la superficie de trabajo. La pantalla nueva llega desde abajo
      si vas hacia adelante y desde arriba si volvés: la dirección del viaje
      es un dato y se ve.

   2. LA ESCENA. Salir a la cancha no es cambiar de pantalla. `partido.html`
      no aparece: se PINTA, con una raya de cal que baja y va dejando el
      campo detrás. Es el único momento de autor del juego y por eso es el
      único que dura medio segundo. Ocurre unas cuarenta veces por temporada.

   3. EL ACUSE. Los controles contestan: la acción principal, que pinta su
      propia raya mientras la semana se simula; el marcador, que salta cuando
      entra un gol; y desde el 1/9/2026 los botones corrientes, que se hunden
      un punto cuando se los pulsa, y las tablas, que se presentan de nuevo
      cuando lo que muestran ya no es la misma lista.

   4. LO QUE SE VA. La mitad que faltaba. Hasta el 1/9/2026 el sistema sólo
      sabía llegar: los ocho modales y el panel del desplegable se apagaban
      de un fotograma a otro, así que de cada interacción estaba animada la
      mitad de abrir y era un corte la de cerrar. No le faltaba movimiento a
      esta interfaz —le faltaba la mitad del que ya tenía—.

   Nada de esto es imprescindible: sin JavaScript y sin transiciones de
   documento la interfaz queda exactamente donde estaba, seca pero entera.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. ENTRE PANTALLA Y PANTALLA
   --------------------------------------------------------------------------
   `navigation: auto` hace que el navegador tome una foto de la pantalla que
   se va y otra de la que llega, y anime entre las dos. No hace falta ni un
   router ni una sola línea de JavaScript: siguen siendo trece documentos con
   sus enlaces de verdad, su historial y su F5.
   -------------------------------------------------------------------------- */

@view-transition { navigation: auto; }

/* Y NO EN EL TELÉFONO, A PRUEBA. El 9/9/2026 Alberto tenía que tocar dos veces
   los botones en iOS, y después de arreglar tres causas de la familia del
   hover seguía. Lo que lo separó fue un dato suyo: escribiendo la dirección a
   mano el primer toque anda; llegando por un enlace desde adentro del juego,
   no. La diferencia entre esos dos caminos es exactamente esto — Safari trae
   las transiciones entre documentos desde 18.2 y él está en 18.7.

   Es una prueba, no una conclusión: si el doble toque se va, la transición era
   la causa y habrá que ver si se puede tener de otra forma; si no se va, esto
   se revierte y se busca en otro lado. Queda escrito para que el que lo lea
   sepa que está en observación y no lo dé por decidido.

   Se apaga por `(hover: none)` y no por el ancho: un teléfono no es una
   ventana chica, es un aparato sin puntero fino. */
@media (hover: none) {
  @view-transition { navigation: none; }
}

/* Lo que NO se recarga. Sacar un elemento del recorte de la raíz y darle
   nombre propio es decirle al navegador "este de acá es el mismo de antes":
   en vez de desvanecerse con el resto, se queda. El chrome es el estadio y
   el escudo es tu club — ninguno de los dos se fue a ningún lado. */
.chrome { view-transition-name: chrome; }
.crest[data-f='crest-club'] { view-transition-name: escudo-club; }

/* El chrome mide 74px con subnavegación y 44px sin ella (`carrera`, `club`,
   `nueva`, `sorteo`). Sin esto, el navegador estira la foto de uno hasta la
   altura del otro y la barra se ve aplastada medio segundo. Con `none` la
   foto conserva su tamaño y lo que sobra se recorta desde arriba, que es
   como crece una barra: hacia abajo. */
::view-transition-old(chrome),
::view-transition-new(chrome) {
  object-fit: none;
  object-position: top left;
}

/* El escudo es el mismo archivo en las dos pantallas. Cruzar dos imágenes
   idénticas al 50 % las apaga a las dos por 240ms; dejar la nueva encima y
   quietas no se nota, que es justo lo que se busca. */
::view-transition-old(escudo-club),
::view-transition-new(escudo-club) {
  animation: none;
  mix-blend-mode: normal;
}
::view-transition-old(escudo-club) { opacity: 0; }
::view-transition-new(escudo-club) { opacity: 1; }

/* Y LO QUE SÍ SE MUEVE: LA SOLAPA.

   El segundo piso del chrome son siete solapas y la que estás mirando lleva un
   subrayado de 2px. Como cada solapa es un DOCUMENTO, ese subrayado se apagaba
   debajo de «Jugadores» y se encendía debajo de «Entrenamiento» sin recorrer
   los ciento y pico de píxeles que hay entre las dos.

   Es exactamente el problema del punto 1 al revés, y por eso vale la pena
   nombrarlo acá: de la barra sabíamos decir que NO se había ido a ningún lado,
   y el único elemento que de verdad cambia de sitio al navegar era el que
   aparecía en su destino sin haber pasado por el camino. Con nombre propio, el
   navegador reconoce que el subrayado de la pantalla que se va y el de la que
   llega son EL MISMO, y lo lleva de uno al otro.

   Hay UNO por documento y por eso el nombre puede ser fijo: `aria-current`
   marca una solapa y sólo una, y dos elementos con el mismo
   `view-transition-name` en la misma pantalla cancelan la transición entera.
   `page` y `location` son las dos maneras de estar debajo de una solapa —ver
   `app.css`— y las dos pintan el mismo subrayado, así que nunca coinciden.

   Se pierde solo si no hay transición: sin foto que cruzar el subrayado
   aparece donde tiene que aparecer, que es lo que hacía hasta hoy. */
.nav-sub__item[aria-current='page']::after,
.nav-sub__item[aria-current='location']::after {
  view-transition-name: solapa;
}

/* Y LAS DOS FOTOS NO SE CRUZAN, por lo mismo que las del escudo: son dos barras
   macizas del mismo violeta y del mismo alto, así que fundir una en la otra al
   50 % las apaga a las dos a mitad de camino y el subrayado viaja pálido. Lo
   que anima es el GRUPO —el recorrido de un sitio al otro—, que es todo lo que
   hay que contar acá.

   Y de paso arregla el caso feo: cuatro pantallas del juego no tienen segundo
   piso (`carrera`, `club`, `nueva`, `sorteo`). Yendo del plantel a una de ésas
   el subrayado no tiene par que lo espere, y sin esto se quedaría apagándose
   solo durante 240ms sobre una barra que ya mide treinta píxeles menos. Con la
   foto vieja en cero, sale de la pantalla en el mismo instante que su
   pantalla. */
::view-transition-old(solapa) { animation: none; opacity: 0; }
::view-transition-new(solapa) { animation: none; opacity: 1; }

/* CON PASTILLA EN VEZ DE SUBRAYADO (8/10/2026). En el modo selección y en el
   club la solapa actual es una pastilla de acento pintada con el `::after`, y
   el rótulo vive en el enlace. Viajando sólo el `::after`, la pastilla pasaba
   POR ENCIMA del rótulo nuevo y se veía un óvalo amarillo vacío durante todo
   el cruce. En el modo viaja el enlace entero —pastilla y rótulo—, a su tamaño
   y sin estirarse. En el club no viaja: la pastilla de destino ya se enciende
   en el clic (`mov-destino`), y dos pastillas a la vez son una de más. */
.en-modo-seleccion .nav-sub__item:is([aria-current='page'], [aria-current='location'])::after,
.en-modo-club .nav-sub__item:is([aria-current='page'], [aria-current='location'])::after { view-transition-name: none; }
.en-modo-seleccion .nav-sub__item:is([aria-current='page'], [aria-current='location']),
.en-modo-club .nav-sub__item:is([aria-current='page'], [aria-current='location']) { view-transition-name: solapa; }
.en-modo-seleccion::view-transition-old(solapa),
.en-modo-seleccion::view-transition-new(solapa),
.en-modo-club::view-transition-old(solapa),
.en-modo-club::view-transition-new(solapa) { width: 100%; height: 100%; object-fit: none; }
/* EL CLUB VIAJA COMO EL MODO (8/10/2026, Alberto, con un video del modo
   selección: «quiero esa misma animación, y más fluidez»). Hasta acá el club
   encendía la pastilla de destino en el clic y apagaba la de origen; ahora
   la pastilla viaja entera de una a otra, como en el modo. Por eso el
   encendido en el clic (`mov-destino`) ya no se pinta en el club: dos
   pastillas a la vez son una de más. */
::view-transition-group(solapa) { animation-duration: 420ms; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }

/* La superficie de trabajo. Sale rápido y entra con calma: una salida lenta
   se lee como espera, una llegada lenta se lee como llegada.

   Son DOS animaciones sobre el mismo elemento y no una, porque el fundido y
   el recorrido no quieren la misma curva. Con la curva de llegada —que gasta
   el 90 % del camino en el primer tercio— un fundido se termina antes de que
   el ojo lo registre y el cambio vuelve a ser seco. El recorrido sí la
   quiere: es lo que hace que la pantalla aterrice en vez de deslizarse. */
::view-transition-old(root) {
  animation: mov-sale-funde var(--dur-fast) linear both,
             mov-sale-corre var(--dur-fast) var(--ease) both;
}
::view-transition-new(root) {
  animation: mov-entra-funde var(--dur-pantalla) ease-out both,
             mov-entra-corre var(--dur-pantalla) var(--ease-entrada) both;
}

/* MÁS LIVIANO (8/10/2026). La que se va ya no se corre: se apaga en 90 ms
   quieta, y la que llega —ya entera, ver `movimiento.js`— sube 6 px y frena,
   sin respiro: con un respiro de 40 ms se veía un instante sólo la foto de
   fondo, vacía (medido cuadro a cuadro). La foto de fondo de cada sección va adentro de las dos y se funde de una a
   la otra. El modo selección se queda con sus tiempos (`seleccion.css`): la
   pastilla que viaja va sin rótulo mientras dura el cruce, y uno más largo la
   dejaba vacía hasta los 600 ms. */
:root:not([data-lado]):not(.en-modo-seleccion)::view-transition-old(root) {
  animation: mov-sale-funde 160ms linear both;
}
:root:not([data-lado]):not(.en-modo-seleccion)::view-transition-new(root) {
  animation: mov-entra-funde 180ms ease-out both,
             mov-entra-corre 320ms var(--ease-entrada) both;
}
/* LA BARRA DE ARRIBA no se cruza consigo misma (visto cuadro a cuadro): el
   fundido de dos fotos casi iguales la aclaraba un instante, y cambiarla en
   seco mostraba la barra NUEVA a medio armar —la escriben los guiones—, con
   un «Continuar» suelto durante 80 ms. La vieja queda entera debajo hasta el
   final y la nueva se funde encima a partir de los 180 ms, ya escrita. */
::view-transition-old(chrome) { animation: none; opacity: 1; mix-blend-mode: normal; }
::view-transition-new(chrome) { animation: mov-entra-funde 140ms ease-out 180ms both; mix-blend-mode: normal; }
:root { --mov-viaje: 6px; }
:root[data-mov='atras'] { --mov-viaje: -6px; }

/* LA CASCADA. Las piezas grandes del `main` —el encabezado, las tarjetas,
   el riel— llegan una detrás de la otra, 35 ms de diferencia, subiendo 8 px.
   Es lo que hace que la pantalla se arme en vez de aparecer. Corre dentro del
   cruce (la foto nueva es viva) y sólo mientras dura (`mov-llega`). */
/* Mientras la pantalla se escribe, sus piezas esperan invisibles (sólo con
   cruce: sin él manda la entrada de respaldo de más abajo). */
:root.mov-llega.sin-pintar main[data-pinta-js] > * { opacity: 0; }
:root.mov-llega:not(.sin-pintar) main > :is(section, aside, article, div:not([hidden])) {
  animation: mov-pieza 380ms var(--ease-entrada) backwards;
  animation-delay: calc(var(--mov-i, 0) * 40ms);
}
:root.mov-llega main > :nth-child(2) { --mov-i: 1; }
:root.mov-llega main > :nth-child(3) { --mov-i: 2; }
:root.mov-llega main > :nth-child(4) { --mov-i: 3; }
:root.mov-llega main > :nth-child(5) { --mov-i: 4; }
:root.mov-llega main > :nth-child(n+6) { --mov-i: 5; }
@keyframes mov-pieza { from { opacity: 0; transform: translateY(8px); } }

/* LA LÍNEA DE PROGRESO. Al apretar un enlace la raíz queda `mov-yendo`; si la
   pantalla nueva tarda más de 150 ms, una línea fina del acento corre por el
   borde de arriba. Antes de eso no se ve: un paso rápido no necesita decir
   que está yendo. */
:root.mov-yendo body::after {
  content: '';
  position: fixed;
  top: 0; left: 0;
  z-index: 2147483000;
  width: 100%; height: 2px;
  background: linear-gradient(90deg, transparent, var(--accent) 30%, var(--accent) 70%, transparent);
  transform-origin: left;
  pointer-events: none;
  animation: mov-yendo 900ms var(--ease) 150ms both;
}
@keyframes mov-yendo {
  0% { transform: scaleX(0); opacity: 1; }
  70% { transform: scaleX(0.8); opacity: 1; }
  100% { transform: scaleX(0.92); opacity: 0.85; }
}

/* LA SOLAPA A LA QUE VAS SE ENCIENDE EN EL CLIC (`mov-destino`, la pone
   `movimiento.js`), y la de donde estás se apaga: el cambio de sección se ve
   en el instante del toque. En el club es la pastilla del acento. */
/* Con especificidad de sobra: `chrome.css` pinta de gris toda solapa que no
   es la actual (`.chrome__row--secondary .nav-sub__item:not([aria-current])`)
   y dejaba el rótulo gris sobre el acento. */
:root:not(.en-modo-seleccion) .chrome__row--secondary .nav-sub__item.mov-destino.mov-destino, .franja-club__sub.mov-destino { color: var(--nav-aqui, var(--accent)); }
html.en-modo-club .chrome__row--secondary .nav-sub__item.mov-destino.sin-viaje,
.en-modo-club .nav-sub__item.mov-destino.sin-viaje {
  transition-duration: 60ms;
  color: var(--text-on-accent); font-weight: 800;
  background: var(--accent); border-color: var(--accent);
  box-shadow: 0 0 18px color-mix(in srgb, var(--accent) 35%, transparent);
  transform: none;
}


/* Y LA PASTILLA CONTESTA AL APRETAR, sin esperar a la pantalla: el modo
   selección ya lo hacía; ahora también el club y el resto. */
.nav-sub__item:active, .franja-club__sub:active { color: var(--nav-aqui, var(--accent)); transition-duration: 0s; }

@keyframes mov-sale-funde  { to   { opacity: 0; } }
@keyframes mov-sale-corre  { to   { transform: translateY(calc(-1 * var(--mov-viaje, 10px))); } }
@keyframes mov-entra-funde { from { opacity: 0; } }
@keyframes mov-entra-corre { from { transform: translateY(var(--mov-viaje, 10px)); } }

/* Volver es el mismo viaje al revés. `movimiento.js` marca la raíz cuando la
   navegación es del historial —el botón atrás, el gesto, Alt+←— y el signo
   del recorrido se invierte: la pantalla vieja vuelve por donde vino. */
:root[data-mov='atras'] { --mov-viaje: -10px; }

/* --------------------------------------------------------------------------
   2. LA ESCENA — se encienden los reflectores
   --------------------------------------------------------------------------
   El día del partido llega como cualquier otra pantalla: la alineación, el
   marcador y la pizarra ya están puestos en el primer fotograma. Lo único
   que se toma su tiempo es el CAMPO, que sube desde el negro con un punto de
   escala, como cuando prenden las luces sobre el césped.

   **Nada tapa nada.** Una versión anterior bajaba una cortina opaca sobre la
   pantalla entera; ocultaba información durante medio segundo y se leía como
   un telón, no como un partido. La regla que quedó de ese intento: en una
   interfaz densa, el momento de autor se hace sobre UN elemento —el que es
   el motivo de la pantalla— y nunca escondiendo el resto.

   Es un fundido con escala sobre el lienzo y nada más: sin `filter`, que
   obligaría al compositor a repasar un canvas que se repinta sesenta veces
   por segundo durante los noventa minutos siguientes. El relleno es
   `backwards` y no `both` a propósito — sostiene el negro durante la espera
   y después devuelve el lienzo a su estilo natural, sin dejarle puesto un
   `transform` de por vida.
   -------------------------------------------------------------------------- */

:root[data-pantalla='partido'] .pitch__lienzo {
  animation: mov-reflectores var(--dur-escena) var(--ease-entrada) backwards;
}

/* Cuando SÍ hubo transición de documento, el campo espera a que termine. Sin
   esta espera, los reflectores se encienden por debajo de la foto de la
   pantalla anterior y no los ve nadie. */
:root:not(.sin-vt)[data-pantalla='partido'] .pitch__lienzo {
  animation-delay: var(--dur-pantalla);
}

@keyframes mov-reflectores {
  from { opacity: 0; transform: scale(0.985); }
}

/* --------------------------------------------------------------------------
   3. EL ACUSE
   -------------------------------------------------------------------------- */

/* La punta de la acción principal se inclina hacia donde te lleva. El botón
   no se mueve —esa regla del sistema sigue en pie—; se mueve su flecha, que
   es la que promete el viaje. */
.action .i { transition: transform var(--dur-fast) var(--ease); }
@media (hover: hover) {
  .action:hover .i { transform: translateX(2px); }
}
.action:active .i { transform: translateX(0); }

/* Lo que NO lleva acuse: Continuar. Pulsarlo navega en el mismo latido, así
   que una barra de progreso en el botón sería una espera inventada — y la
   respuesta a ese clic ya es la pantalla entera, que cambia. Un botón que
   además se ilumina estaría contando dos veces la misma cosa. */

/* El gol. `data-gol` ya existía y ya teñía la cifra de cal; lo que faltaba
   era el salto. Entra una vez por gol —el atributo sólo cambia cuando cambia
   el marcador— y vuelve al tamaño exacto del que salió. */
.marcador__cifra[data-gol='true'] {
  animation: mov-gol 420ms var(--ease-entrada);
}

@keyframes mov-gol {
  0%   { transform: scale(1); }
  24%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* La repisa de clubes, cuando pasás a otra división o a otro país.

   Es el mismo trabajo que el punto 1 —contar que lo que estás mirando ya no es
   lo de antes— a la escala de un panel en vez de una pantalla. Sin esto, ir de
   la Primera a la Primera Nacional cambia treinta bloques de color de golpe y
   no se lee como una lista nueva: se lee como un parpadeo.

   ESCALONA POR FILA Y NO POR CARTA. Una grilla de siete columnas entra en
   cinco tiempos, no en treinta: escalonar carta por carta hace una ola que
   cruza la pantalla en diagonal y tarda más en terminar de lo que tarda el ojo
   en volver a la primera. La fila la calcula `nueva.js` con las columnas que
   de verdad haya, así que en una ventana angosta —tres columnas— siguen siendo
   pocos tiempos y no diez.

   EL TOPE ES LO QUE HACE QUE NO MOLESTE: pase lo que pase, la última carta
   arranca a 100ms de la primera. Un escalonado sin tope es una animación que
   crece con los datos, y el Federal A tiene treinta y siete clubes.

   Y NO ENTRA AL BUSCAR, que es la mitad del trabajo: `pintarClubes()` corre en
   cada tecla, y una repisa que se reconstruye con cada letra es un tartamudeo.
   La marca la pone `nueva.js` sólo cuando cambia el CONJUNTO. */
.clubes--entra .carta {
  animation: mov-carta-entra var(--dur) var(--ease-entrada) both;
  animation-delay: min(calc(var(--fila, 0) * 26ms), 100ms);
}

/* Seis píxeles y no veinte: la carta ya está en su sitio, sólo se presenta. */
@keyframes mov-carta-entra {
  from { opacity: 0; transform: translateY(6px); }
}

/* Los dos primeros pasos del selector, cuando pasás de uno a otro.

   Es el mismo trabajo que la repisa de clubes y con la misma cuenta de filas:
   contar que lo que estás mirando es una lista nueva. Vale la pena acá más que
   en ningún otro sitio porque los tres pasos son el MISMO documento —no hay
   transición de pantalla que los separe— y sin esto elegir Argentina cambia
   veintiuna cartas por cinco filas sin que nada diga que pasó algo.

   El escalonado y el tope son los de allá: la última fila arranca a 100ms de la
   primera, pase lo que pase. La marca la pone `escalonarEntrada()`, que es una
   sola función para las tres listas de esta pantalla. */
.paso--entra .nacion,
.paso--entra .liga {
  animation: mov-carta-entra var(--dur) var(--ease-entrada) both;
  animation-delay: min(calc(var(--fila, 0) * 26ms), 100ms);
}

/* El pie del selector, cuando elegís un club.

   Es el cierre del gesto y no un adorno: el clic pasa arriba, en la repisa, y
   la consecuencia —quién es, en qué puesto queda, y el botón de empezar—
   aparece abajo, lejos de donde estabas mirando. Sin acuse, la mitad de las
   veces no se ve que el pie cambió.

   Entra sólo lo de ADENTRO y no la banda: la banda del pie está fija y no se
   fue a ningún lado. Es la misma Regla de la Barra Quieta del punto 1, un piso
   más abajo. */
.pie--entra > * {
  animation: mov-pie-entra var(--dur) var(--ease-entrada) both;
}

@keyframes mov-pie-entra {
  from { opacity: 0; transform: translateY(4px); }
}

/* EL BOTÓN QUE SE PULSA, y por qué NO se mueve ni un píxel.

   `.action` contestaba al clic desde el primer día —se va a violeta hondo— y
   los otros cuatro botones del sistema no contestaban nada: `.btn`, el
   segmentado, la pastilla de filtro y el botón de icono tenían `:hover` y ahí
   se terminaba, así que apretar y no apretar se veían igual. En una pantalla
   donde casi todo lo que se toca es uno de esos cuatro, ésa es la mitad de la
   sensación de que la interfaz no contesta.

   LA REGLA DEL SISTEMA SIGUE EN PIE: el botón no se mueve. Un botón que se
   hunde 1px es el reflejo fácil y acá está mal, porque estos controles viven
   metidos en tablas de filas de 22px y en rieles de 26 — un píxel de viaje
   corre el texto de al lado y se lee como un salto de maquetación, no como un
   apretón. Lo que contesta es el TONO.

   LO QUE SÍ CAMBIA ES CUÁNDO. La bajada es instantánea y la vuelta es la
   transición de siempre, y ahí está todo el efecto: con los 120ms puestos en
   los dos sentidos, el color empieza a viajar cuando el dedo ya se está yendo
   y el botón parece contestar tarde. Apretar tiene que ocurrir en el
   fotograma del clic; soltar puede tomarse su tiempo. Es la misma asimetría
   que el punto 4 —salir rápido, llegar con calma— vista desde un botón.

   Y SE HUNDE CON `brightness` Y NO CON UN COLOR NUEVO, que fue el primer
   intento y estaba mal: `.btn:hover` YA se va a `--bg-active`, así que darle
   ese mismo tono al `:active` es escribir una regla que en escritorio no se ve
   nunca —al botón se lo aprieta con el puntero encima, siempre—. El sistema ya
   tenía la respuesta escrita dos veces, en el `:active` de la acción del chrome
   y en el del botón de peligro: se le baja el brillo a lo que haya. Sirve
   además para los tres que no tienen fondo en reposo, porque lo que oscurece es
   el fondo del `:hover` cuando lo hay y el rótulo cuando no.

   El 0,86 no es nuevo: es el que ya usa `chrome.css`. */
.btn:active:not([disabled]),
.icon-btn:active,
.segmented__btn:active,
.pais:active {
  filter: brightness(0.86);
  transition-duration: 0s;
}
/* El segmentado sin elegir no tiene fondo ni en reposo ni al pasar por encima
   —sólo cambia de color—, así que hace falta darle una superficie que hundir o
   apretarlo sería oscurecer cuatro letras. El elegido no lo necesita: ya está
   en violeta pleno y se hunde dentro de su propio color. */
.segmented__btn:active:not([aria-pressed='true']) { background: var(--bg-hover); }

/* LA TABLA QUE YA NO MUESTRA LO MISMO.

   Es el trabajo de la repisa de clubes —contar que lo que estás mirando es una
   lista nueva— en la pieza más usada del juego: la tabla. Tocar «Arqueros» en
   el plantel cambiaba treinta renglones de golpe, y treinta renglones que
   cambian de golpe no se leen como otra lista sino como un parpadeo; el ojo se
   queda en el renglón donde estaba, que ahora dice otra cosa.

   Y ACÁ NO SE ESCALONA, que es lo contrario de lo que hace la repisa. La
   repisa son cartas de 96px en cinco filas y el escalonado le sienta bien; una
   tabla son renglones de 22 y una ola que los recorra de a 26ms es un temblor,
   no una presentación — además de gastarse el tiempo de escaneo, que es el
   principio del producto. La tabla se presenta ENTERA y de una vez, con el
   mismo fundido de seis píxeles del sistema. Cero fotogramas nuevos.

   LA MARCA LA PONE EL GUION Y SÓLO CUANDO CAMBIA EL CONJUNTO, igual que en la
   repisa: ordenar por otra columna y filtrar por línea son listas nuevas,
   repintar la misma lista porque cambió un dato no lo es. Una tabla que se
   presenta cada vez que se repinta es un tartamudeo. */
/* SIN `both`, que es la única diferencia con la repisa y no es un descuido.
   La repisa escalona, así que sus cartas necesitan que el relleno las sostenga
   invisibles durante su retardo; acá no hay retardo que sostener. Y el relleno
   dejaría al `tbody` con un `transform` puesto para siempre, que es la trampa
   que `movimiento.js` documenta para el `main`: un transform, aunque sea de
   cero, convierte al elemento en el bloque contenedor de todo lo que tenga
   dentro en `position: fixed`. Una tabla no puede quedarse con eso armado. */
.data-table--entra tbody {
  animation: mov-carta-entra var(--dur) var(--ease-entrada);
}

/* LA PANTALLA QUE CONTESTA.

   Tres pantallas del juego son una conversación: la previa, el entretiempo y el
   portal. Elegís un tono y te dicen cómo cae; movés el planteo y te dicen cómo
   te va a ir; tocás un día del calendario y te dicen qué hay. Las respuestas ya
   estaban declaradas en el marcado con `role="status"` y se escribían en
   silencio: quien las escucha con voz se enteraba, quien las mira no.

   NACIÓ TRES VECES Y POR ESO ESTÁ ACÁ. Se escribió en `entretiempo.css`, se
   copió a `previa.css` y el portal iba a ser la tercera. Tres hojas con el
   mismo nombre y el mismo gesto son tres sitios donde cambiar una duración —el
   mismo camino que hicieron los modales esta misma tarde, que estuvieron
   escritos tres veces antes de consolidarse.

   Y ACÁ NO SE MIDE CADENCIA, que es la diferencia con el punto de arriba: en
   esas tres pantallas nada cambia si el DT no lo pide, así que el ritmo lo pone
   su mano y esto no puede volverse un estrobo. El día que una pantalla que se
   mueva sola quiera contestar, hay que medir antes cada cuánto cambia — la
   cuenta y el porqué están en `partido.css`, en «el dato que acaba de cambiar».

   El cuándo lo decide `contestar()`, en `mundo.js`: acusa sólo si el texto dice
   otra cosa, nunca por repintado. */
[data-contesta] {
  animation: mov-contesta var(--dur) var(--ease-entrada);
}

@keyframes mov-contesta {
  from { opacity: 0; transform: translateY(3px); }
}

/* --------------------------------------------------------------------------
   4. LO QUE SE VA
   --------------------------------------------------------------------------
   Los tres puntos de arriba son de agosto y los tres animan LLEGADAS. Mirado
   entero en septiembre, el sistema tenía un agujero con forma: sabía aparecer
   y no sabía irse. La pantalla que se va tiene su fotograma —punto 1—, pero
   los OCHO modales del juego y el panel del desplegable se apagaban de un
   cuadro al siguiente, y ésos son los dos que se abren y se cierran cincuenta
   veces por sesión.

   El síntoma es difícil de nombrar mirando una foto y obvio usándolo: cada
   cosa que tocás entra con calma y desaparece de un tirón, así que la interfaz
   se siente cuidada al abrir y brusca al cerrar. No le faltaban efectos —le
   faltaba la mitad de los que ya tenía—.
   -------------------------------------------------------------------------- */

/* LOS OCHO MODALES, EN UN SOLO GESTO Y EN UN SOLO SITIO.

   Estaba escrito TRES VECES —`mesa-llega` en `mesa.css`, `card-llega` en
   `card.css`, y otra vez para el parte del movimiento— con los mismos cuatro
   píxeles y el mismo 2 % en las tres, y los otros CINCO modales del juego no
   lo tenían: la pregunta de dos botones, `--ancho`, `--trato`, `--pre` y la
   llegada a un club nuevo aparecían y desaparecían secas. Tres copias de una
   regla y cinco sitios sin ella es la definición de una regla que tiene que
   vivir en el sistema. El gesto es EXACTAMENTE el que ya hacían esas tres; lo
   único que cambia es quién lo escribe y que ahora también se va.

   VA POR TRANSICIÓN Y NO POR FOTOGRAMAS, y eso es lo que compra la salida. Una
   animación sobre `[open]` sólo puede correr mientras el atributo está puesto,
   y al cerrar el atributo se va en el mismo instante que la caja: no hay
   ningún momento en el que exista un modal cerrándose. Con `allow-discrete`,
   `display` y `overlay` dejan de saltar y esperan a que la transición termine,
   así que el modal se queda en la capa superior los 120ms que tarda en irse.
   `@starting-style` es la otra mitad: sin él la llegada no tiene desde dónde
   salir —el modal nace ya con su estilo final— y sólo se vería la salida.

   Sin soporte, `display` salta como saltaba y el modal aparece y desaparece
   seco: se pierde el gesto, no se pierde el modal. Es la Regla del Respaldo
   Pesimista del punto 5, que acá sale gratis porque la escribe el navegador.

   EL VELO SE TOMA MÁS TIEMPO QUE LA CAJA, al llegar y al irse, y eso ya estaba
   decidido: si los dos duraran lo mismo, el fondo se apagaría de golpe y el
   modal se leería como un salto de corte en vez de como algo que se pone
   encima. Lo que es nuevo es que la asimetría también valga al revés — el velo
   sobreviviendo a la caja es lo que devuelve la pantalla de atrás en vez de
   destaparla.

   `:where()` para el estado de reposo, para que cualquier hoja que quiera
   otra cosa para su modal gane sin tener que enterarse de que esto existe. */
/* LA CAJA SE APAGA EN `--dur-fast` PERO SE QUEDA EN LA CAPA SUPERIOR
   `--dur-pantalla`, y las dos duraciones distintas de esta misma regla son
   deliberadas. El velo se toma el doble que la caja —está tres reglas más
   abajo— y el `::backdrop` es del elemento: si `overlay` y `display` se fueran
   a los 120ms con la caja, el modal saldría de la capa superior y se llevaría
   puesto el velo a mitad de su propio fundido. El elemento aguanta hasta que
   termina lo ÚLTIMO que se está yendo, que es el velo. */
:where(.dialogo) {
  opacity: 0;
  transform: translateY(4px) scale(0.98);
  transition:
    opacity var(--dur-fast) var(--ease-salida),
    transform var(--dur-fast) var(--ease-salida),
    overlay var(--dur-pantalla) allow-discrete,
    display var(--dur-pantalla) allow-discrete;
}

/* Y MIENTRAS SE VA NO RECIBE UN CLIC. Cerrado deja de ser modal en el acto,
   así que durante los 240ms que tarda en irse queda una caja visible y encima
   de todo que ya no atrapa el foco: sin esto, un clic apurado sobre lo que hay
   debajo se lo come un modal que el DT ya cerró. Es la misma línea que llevan
   el panel del desplegable y el menú del botón derecho. */
:where(.dialogo):not([open]) { pointer-events: none; }

:where(.dialogo)::backdrop {
  opacity: 0;
  transition:
    opacity var(--dur-pantalla) var(--ease-salida),
    overlay var(--dur-pantalla) allow-discrete,
    display var(--dur-pantalla) allow-discrete;
}

.dialogo[open] {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-pantalla) ease-out,
    transform var(--dur-pantalla) var(--ease-entrada),
    overlay var(--dur-pantalla) allow-discrete,
    display var(--dur-pantalla) allow-discrete;
}

.dialogo[open]::backdrop {
  opacity: 1;
  transition:
    opacity var(--dur-escena) var(--ease),
    overlay var(--dur-escena) allow-discrete,
    display var(--dur-escena) allow-discrete;
}

@starting-style {
  .dialogo[open] { opacity: 0; transform: translateY(4px) scale(0.98); }
  .dialogo[open]::backdrop { opacity: 0; }
}

/* El panel del desplegable se va por su cuenta y su regla vive en
   `controles.css`, al lado de la geometría que la necesita: de qué lado del
   botón se abrió es una cuenta de `colocarPanel()`, y el gesto de entrada y el
   de salida salen los dos de esa respuesta. Separarlos dejaría media regla acá
   sin el dato que la explica. */

/* --------------------------------------------------------------------------
   5. EL RESPALDO
   --------------------------------------------------------------------------
   Hay más motivos para que no haya transición que navegadores que no la
   soporten: una recarga, entrar desde fuera del sitio, o —el que más cuesta
   descubrir— que la pestaña esté oculta, porque el navegador saltea toda
   transición que nadie está mirando. En cualquiera de esos casos no hay foto
   que cruzar y la pantalla entra sola, con el mismo gesto y la misma
   duración: se pierde la continuidad del chrome, no se pierde la vida.

   Por eso `movimiento.js` da por sentado que NO hay transición y sólo retira
   esta marca cuando el evento le confirma que sí la hay, antes del primer
   pintado. Al revés —detectar soporte y confiar— el respaldo no entraría
   nunca en los tres casos de arriba y el juego se quedaría seco.
   -------------------------------------------------------------------------- */

/* `mov-hecho` la retira en cuanto termina, para no dejarle al `main` un
   `transform` puesto de por vida (ver `movimiento.js`).

   Y ESPERA AL DATO CUANDO LA PANTALLA SE ESCRIBE SOLA. Las veintidós que
   llevan `data-pinta-js` traen el `main` vacío y lo llenan sus guiones cientos
   de milisegundos después; sin la espera, la bienvenida corría sobre el
   cascarón y el contenido llegaba después, seco (los números, en
   `movimiento.js`). `movimiento.js` retira `sin-pintar` en
   `DOMContentLoaded`, que es el momento en que las veinticuatro ya están
   escritas — comprobado una por una, no supuesto.

   Las que traen su `main` en el marcado —`home` y `espera`— no llevan el
   atributo y entran en el acto, como siempre: la puerta del juego no puede
   esperar a ciento cuarenta guiones para verse. */
:root.sin-vt:not(.mov-hecho) main:not([data-pinta-js]),
:root.sin-vt:not(.mov-hecho):not(.sin-pintar) main[data-pinta-js] {
  animation: mov-entra-funde var(--dur-pantalla) ease-out both,
             mov-entra-corre var(--dur-pantalla) var(--ease-entrada) both;
}

/* MIENTRAS TANTO, EL CASCARÓN NO SE MUESTRA. No es por estética: una tabla con
   sus cabeceras y ninguna fila, o un contador en cero, no es «cargando» — es
   una respuesta, y es falsa.

   VA SÓLO CUANDO NO HUBO TRANSICIÓN DE DOCUMENTO. Con transición, el navegador
   retiene el pintado de la pantalla nueva hasta que está lista, así que la
   foto que cruza YA tiene el contenido: esconder el `main` ahí lo único que
   haría es fotografiarlo vacío. `sin-vt` es exactamente esa pregunta y ya está
   contestada antes del primer pintado. */
:root.sin-vt.sin-pintar main[data-pinta-js] { opacity: 0; }

/* --------------------------------------------------------------------------
   Y EL CHROME TIENE SUS PROPIOS HUECOS
   --------------------------------------------------------------------------
   Acá decía que el chrome se ve desde el primer fotograma y que por eso
   quedaba afuera. La barra sí se ve; lo que NO está en el primer fotograma es
   lo que la barra dice. Medido con `herramientas/tira-del-refresh.mjs` sobre
   `plantel` recargada en localhost, un fotograma cada ~18 ms — los
   milisegundos son los de UNA corrida y bailan veinte o treinta entre una y
   otra; lo que no baila es el orden:

      91 ms  primer pintado: el escudo es una CAJA BLANCA VACÍA de 64 px —el
             `<img>` no tiene `src` todavía y el navegador le dibuja el
             contorno—, el botón dice «Continuar», la píldora del día está
             vacía, no hay chevrones, ni logo de liga, ni tinte del club
     120 ms  aparece el escudo dentro de la caja
     141 ms  el botón pasa a «Te esperan 2 tareas» y CRECE 72 px, que se los
             come la píldora del día: se corre entera hacia la izquierda
     157 ms  aparecen los seis chevrones, el logo de la liga, las utilidades,
             los marcadores y el tinte del club
     169 ms  aparece el día
     195 ms  recién acá arranca la bienvenida del `main`

   O sea que el `main` se pone en suspenso para no contestar en falso y la
   barra contesta en falso cuatro veces mientras tanto, con un salto de 72 px
   en la esquina que el ojo está mirando —es la esquina del botón que uno
   viene a apretar— y todo eso sobre una superficie de trabajo vacía, que es
   lo peor: no hay nada más donde mirar.

   LA REGLA ES LA MISMA Y POR EL MISMO MOTIVO: un escudo que todavía no es
   ningún club, un botón que dice «Continuar» cuando hay dos tareas y una
   píldora sin día no son «cargando», son respuestas falsas. Se ponen en
   suspenso hasta que el guion las escriba y entran todas juntas con el
   `main`, en un solo gesto.

   CON `opacity` Y NO CON `visibility`, que es lo que arregla el salto de los
   72 px: la caja sigue midiendo, así que el botón crece y la píldora se corre
   ANTES de que se vea nada. Cuando aparece, ya está en su sitio definitivo.

   NO VIAJA. `mov-entra-corre` es para la superficie de trabajo; la barra se
   funde y no se mueve, que es la Regla de la Barra Quieta dicha para el caso
   en que la barra sí cambia (ver la sección 1).

   SON SIETE PIEZAS Y EL TINTE, Y SE NOMBRAN UNA POR UNA, porque no todo el
   chrome es falso al arrancar: los botones de historial, los rótulos de las
   secciones y las solapas del segundo piso vienen escritos en el marcado y son
   ciertos desde el primer fotograma. Esconder la barra entera sería inventar
   una espera que no existe — se probó, y con la barra primaria en blanco y las
   solapas del segundo piso encendidas la pantalla queda peor que con el
   defecto. Cinco piezas las LLENA el guion —escudo, logo de liga, píldora del
   día, rótulo del botón, chevrones—, dos las CREA de cero —las utilidades y
   los marcadores— y el tinte del club sale del `--club-fondo` que `chrome.js`
   le escribe a la barra.

   LO QUE DIO, medido con la misma tira sobre tres pantallas: el chrome pasa de
   moverse en 4 fotogramas ANTES de que llegue la superficie de trabajo —42 %
   de píxeles acumulados, pico 26 %— a moverse en 2, con 4,8 % acumulado. Y ese
   4,8 % que queda no es ninguna pieza del chrome: es la tipografía, que entra
   con `font-display: swap` y reacomoda los rótulos. Es otro asunto y está en
   `fonts.css`.

   **Y la lista se queda vieja sola**, igual que la de las barras de
   desplazamiento en `app.css`: la próxima pieza que `chrome.js` agregue va a
   entrar tarde y sin avisar. Lo que la vuelve a contar es la tira de
   fotogramas, que no mira selectores sino píxeles.

   `mov-hecho` la retira, y acá no es prolijidad: `.nav-primary__chev` tiene su
   propia transición de `opacity` para el hover, y una animación que rellena
   hacia adelante se la comería para siempre. -------------------------------- */
:root.sin-vt.sin-pintar :is(
  .chrome .crest,
  .chrome .comp-marca,
  .chrome .clock,
  .chrome .action,
  .chrome__utiles,
  .marcadores) { opacity: 0; }

:root.sin-vt.sin-pintar .chrome__row--primary::before { opacity: 0; }

/* EL CHEVRÓN ES EL ÚNICO QUE SE VA DEL TODO, y es el que enseñó por qué la
   diferencia importa. Con `opacity` seguía ocupando sus 13 px, así que al
   insertarlo `chrome.js` los seis rótulos de las secciones se corrían 35 px
   —medido a 1280— treinta y siete milisegundos ANTES de que entrara nada más:
   un renglón de texto que se mueve solo, en una pantalla por lo demás quieta.
   Con `display: none` el sitio no se reserva y el corrimiento ocurre en el
   mismo fotograma en que entra todo lo demás, o sea adentro de la llegada y
   no como un episodio aparte.

   ES LA EXCEPCIÓN Y NO LA REGLA: acá se puede porque el chevrón no dice nada
   —promete un menú— y su hueco no significa nada. El botón NO puede irse así:
   sacarlo del flujo movería la píldora del día y la fila entera, que es
   exactamente el salto de 72 px que esto vino a arreglar.

   LO QUE NO ARREGLA, dicho para el que lo mida: el corrimiento sigue
   existiendo, sólo que ya no es un evento suelto. Sacárselo de encima pide que
   el chevrón venga escrito en el marcado de las veintitrés pantallas, y eso es
   otro cambio y de otra zona. */
:root.sin-vt.sin-pintar .chrome .nav-primary__chev { display: none; }

:root.sin-vt:not(.mov-hecho):not(.sin-pintar) :is(
  .chrome .crest,
  .chrome .comp-marca,
  .chrome .clock,
  .chrome .action,
  .chrome .nav-primary__chev,
  .chrome__utiles,
  .marcadores),
:root.sin-vt:not(.mov-hecho):not(.sin-pintar) .chrome__row--primary::before {
  animation: mov-entra-funde var(--dur-pantalla) ease-out both;
}

/* --------------------------------------------------------------------------
   6. QUIEN PIDE QUIETO
   --------------------------------------------------------------------------
   El `*` de `app.css` no alcanza a llegar al árbol de la transición: sus
   pseudoelementos no son elementos del documento y hay que apagarlos por
   nombre. Sin animaciones, la transición termina en el primer fotograma y el
   cambio de pantalla vuelve a ser instantáneo, que es lo que se pidió.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  :root.mov-llega main > * { animation: none !important; }
  :root.mov-yendo body::after { animation: none; opacity: 0; }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }

  :root.sin-vt:not(.mov-hecho) main:not([data-pinta-js]),
  :root.sin-vt:not(.mov-hecho):not(.sin-pintar) main[data-pinta-js],
  /* Las siete del chrome. Se les apaga la ENTRADA y no la espera: el que pide
     quieto no pide que le contesten en falso, así que el suspenso —que es
     `opacity` y no una animación— sigue puesto y las piezas aparecen igual
     todas juntas, sin fundido. */
  :root.sin-vt:not(.mov-hecho):not(.sin-pintar) :is(
    .chrome .crest, .chrome .comp-marca, .chrome .clock, .chrome .action,
    .chrome .nav-primary__chev, .chrome__utiles, .marcadores),
  :root.sin-vt:not(.mov-hecho):not(.sin-pintar) .chrome__row--primary::before,
  :root[data-pantalla='partido'] .pitch__lienzo,
  .marcador__cifra[data-gol='true'],
  .clubes--entra .carta,
  .paso--entra .nacion,
  .paso--entra .liga,
  .pie--entra > * { animation: none !important; }

  @media (hover: hover) {
    .action:hover .i { transform: none; }
  }
}

/* LA CABECERA DE LA SECCIÓN NO PESTAÑEA (8/10/2026). Es la misma pieza en
   todas las pantallas del club: con nombre propio en la transición se queda
   en su sitio y sólo cambian sus textos, en vez de irse con la pantalla vieja
   y volver a entrar con la nueva. Y llegando por una transición no repite su
   entrada. */
html.en-modo-club .franja-club { view-transition-name: franja; }
::view-transition-group(franja) { animation-duration: 320ms; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
::view-transition-old(franja) { animation: mov-sale-funde 180ms linear both; }
::view-transition-new(franja) { animation: none; }
html:not(.sin-vt) .franja-club { animation: none; }

/* La pastilla del submenú de la sección viaja como la del menú. */
html.en-modo-club .franja-club__sub[aria-current='page'] { view-transition-name: subsolapa; }
::view-transition-group(subsolapa) { animation-duration: 420ms; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
::view-transition-old(subsolapa), ::view-transition-new(subsolapa) { width: 100%; height: 100%; object-fit: none; }
/* Mientras viaja la copia, el submenú propio queda debajo, tapado por ella y
   sin nombre, para que la pastilla tenga un solo punto de llegada. Al
   retirarse la copia no hay nada que aparezca: ya estaba. */
html.subs-de-viaje .franja-club__subs:not(.subs-viajeras *) .franja-club__sub { view-transition-name: none !important; }
/* La copia que viaja, fuera de su cabecera, se arma igual que el original:
   en una fila y sin partirse. */
.subs-viajeras > .franja-club__subs { display: flex; flex-wrap: nowrap; width: max-content; max-width: none; }
.subs-viajeras .franja-club__sub { white-space: nowrap; flex: none; }
/* LA CABECERA DE LA SECCIÓN NO ENTRA DE NUEVO (8/10/2026). En las pantallas
   del club con cabecera propia (`*__hero`), esa tarjeta hacía su fundido de
   entrada cuando la pantalla terminaba de armarse —medio segundo después del
   cruce— y se veía apagarse y volver: el pestañeo. Llegando por una
   transición, la cabecera ya está; no entra. */
html.en-modo-club:not(.sin-vt) main .tarjeta[class*="__hero"] { animation: none; }

/* La pastilla de las vistas del Portal (transición del mismo documento). */
.ipad__vista[aria-current='page'] { view-transition-name: vista-portal; }
::view-transition-group(vista-portal) { animation-duration: 420ms; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
::view-transition-old(vista-portal), ::view-transition-new(vista-portal) { width: 100%; height: 100%; object-fit: none; }
