/* Hoja de estilo de la web generada de LGM-OS (portada, novedades, instalación y docs).
 *
 * Es EL MISMO lenguaje que el portal (web/app/static/estilo.css): la píldora de cristal
 * flotante, el vidrio con canto degradado, las sombras en capas y un solo azul de acción.
 * Son dos hojas porque son dos cosas distintas —el portal lo sirve Jinja y esto lo genera
 * generar.py como HTML plano—, pero el visitante las ve como una sola web, así que las
 * recetas son las mismas traducidas a los tokens de aquí.
 *
 * Sin librerías a propósito: el panel usa Tailwind, pero una página estática que necesita
 * seis componentes no justifica arrastrar un compilador. Lo que sí se copia son los valores.
 *
 * Orden de la hoja: tokens → lienzo → cristal → barra → héroe → botones → componentes de
 * contenido → documentación → pie → movimiento → quietud. Nada de capas de «refinamiento»
 * que se pisan entre sí: cada cosa está una vez, en su sitio.
 */

/* ================================================================ tokens */

:root {
  color-scheme: light;

  /* Un solo azul para todo lo que es «acción»: botones, foco, enlaces y selección. */
  --acento: #2563eb;
  --acento-fuerte: #1d4ed8;
  /* El acento cuando es TEXTO. En claro coincide con el de relleno; en oscuro NO,
     y de ahí viene esta variable: un azul pensado para rellenar un botón es
     ilegible puesto sobre una tarjeta oscura. */
  --acento-texto: #1d4ed8;
  --acento-suave: rgba(37, 99, 235, 0.14);

  /* El morado es de UNA cosa: el grafo del código. No es un segundo color de marca ni un
     acento decorativo —dos azules y un morado repartidos por la web serían tres acentos y
     ninguno—: donde aparece, lleva al mapa del repositorio y a ningún otro sitio. */
  --morado: #7c3aed;
  --morado-fuerte: #6d28d9;
  --morado-texto: #6d28d9;
  --morado-suave: rgba(124, 58, 237, 0.13);
  --sombra-morado: 0 2px 6px rgba(109, 40, 217, .26), 0 12px 28px -8px rgba(109, 40, 217, .42);

  --texto: #1e293b;
  --texto-fuerte: #0f172a;
  --texto-suave: #475569;
  /* Medido: el gris de antes (#64748b) daba 4,02 : 1 sobre el fondo de la pagina y el
     minimo para leer es 4,5. Con este, 4,65; y a simple vista es el mismo gris. */
  --texto-tenue: #5b6a7f;
  --borde: rgba(15, 23, 42, 0.09);
  --codigo-fondo: #0f172a;

  --cristal: rgba(255, 255, 255, 0.72);
  --cristal-borde: rgba(255, 255, 255, 0.55);
  /* El cristal de las cosas que se ponen ENCIMA de contenido denso (la ficha del grafo).
     El de arriba está pensado para flotar sobre un fondo tranquilo; sobre un lienzo con
     doce mil puntos y sus nombres, deja que el dibujo se lea a través del texto. */
  --cristal-denso: rgba(255, 255, 255, 0.95);

  /* ---------------------------------------------------------- cristal de verdad
     Un `backdrop-filter` a secas no es cristal: es una mancha borrosa. El cristal se
     reconoce por tres cosas más, y son las que van aquí.

     · SATURACIÓN. Detrás del cristal los colores se avivan, no se apagan.
     · BRILLO DE CANTO. Un borde plano parece pegatina. El de verdad se enciende arriba
       —donde le da la luz— y se apaga abajo: un degradado, no un color.
     · LUZ INTERIOR. Una sombra interior clarísima en el borde superior da grosor de
       lámina en vez de recorte. */
  --vidrio-filtro: saturate(180%) blur(22px);
  --vidrio-canto: linear-gradient(150deg,
                    rgba(255, 255, 255, .85), rgba(255, 255, 255, .18) 42%,
                    rgba(255, 255, 255, .06));
  --vidrio-luz: inset 0 1px 0 rgba(255, 255, 255, .75);

  /* Sombras en capas: contacto, cuerpo y ambiente. Una sola sombra grande es una nube
     gris; tres cortas y una larga es como cae la luz de verdad. */
  --sombra-1: 0 1px 2px rgba(15, 23, 42, .06), 0 3px 8px -2px rgba(15, 23, 42, .08);
  --sombra-2: 0 2px 4px rgba(15, 23, 42, .05), 0 8px 20px -6px rgba(15, 23, 42, .12),
              0 24px 48px -20px rgba(15, 23, 42, .18);
  --sombra-3: 0 4px 8px rgba(15, 23, 42, .06), 0 16px 40px -12px rgba(15, 23, 42, .16),
              0 48px 96px -32px rgba(15, 23, 42, .28);
  /* Debajo de un botón azul, una sombra gris ensucia; una azul lo levanta. */
  --sombra-acento: 0 2px 6px rgba(37, 99, 235, .24), 0 12px 28px -8px rgba(37, 99, 235, .38);

  /* Escala tipográfica fluida: el titular crece con la ventana en vez de pegar un tirón
     al cruzar un breakpoint. Los grandes van apretados y con interlineado corto. */
  --t-h1: clamp(2.1rem, 1.1rem + 3.6vw, 3.6rem);
  --t-h2: clamp(1.45rem, 1rem + 1.8vw, 2.1rem);
  --t-h3: clamp(1.08rem, .95rem + .5vw, 1.3rem);
  --t-cuerpo: clamp(1rem, .96rem + .2vw, 1.08rem);

  /* Aire. Lo que más separa una página cara de una barata no es el color: es cuánto
     hueco se atreve a dejar. */
  --aire-seccion: clamp(3.2rem, 7vw, 6rem);
  --radio: 16px;
  --radio-g: 24px;

  /* Movimiento: una curva de salida suave tipo iOS y dos duraciones. Todo lo que se mueve
     en la hoja usa esto, y todo se apaga con una sola regla al final. */
  --curva: cubic-bezier(.22, 1, .36, 1);
  --rapido: .18s;
  --medio: .32s;
}

[data-theme="dark"] {
  color-scheme: dark;
  --acento: #3b82f6;
  --acento-fuerte: #2563eb;
  --acento-texto: #93c5fd;
  --acento-suave: rgba(59, 130, 246, 0.25);

  /* El relleno del botón no cambia (el morado oscuro con letra blanca da 5,7 : 1 en los dos
     temas); el morado como LETRA sí, igual que pasa con el azul: el de rellenar no se lee
     sobre una tarjeta oscura. */
  --morado-texto: #a78bfa;
  --morado-suave: rgba(139, 92, 246, 0.22);
  --sombra-morado: 0 2px 6px rgba(109, 40, 217, .4), 0 12px 28px -8px rgba(109, 40, 217, .55);

  --texto: #cbd5e1;
  --texto-fuerte: #e2e8f0;
  --texto-suave: #94a3b8;
  --texto-tenue: #7c8aa0;
  --borde: rgba(148, 163, 184, 0.18);
  --codigo-fondo: #060b16;

  --cristal: rgba(30, 41, 59, 0.6);
  --cristal-borde: rgba(148, 163, 184, 0.16);
  --cristal-denso: rgba(20, 29, 48, 0.95);

  /* En oscuro el cristal no se aclara: se ilumina el CANTO. Subir la opacidad del relleno
     da un plástico lechoso; dejarlo oscuro y encender el borde da vidrio ahumado. */
  --vidrio-canto: linear-gradient(150deg,
                    rgba(255, 255, 255, .28), rgba(255, 255, 255, .06) 42%,
                    rgba(255, 255, 255, .02));
  --vidrio-luz: inset 0 1px 0 rgba(255, 255, 255, .10);

  --sombra-1: 0 1px 2px rgba(0, 0, 0, .4), 0 3px 8px -2px rgba(0, 0, 0, .45);
  --sombra-2: 0 2px 4px rgba(0, 0, 0, .35), 0 8px 20px -6px rgba(0, 0, 0, .5),
              0 24px 48px -20px rgba(0, 0, 0, .6);
  --sombra-3: 0 4px 8px rgba(0, 0, 0, .4), 0 16px 40px -12px rgba(0, 0, 0, .55),
              0 48px 96px -32px rgba(0, 0, 0, .7);
  --sombra-acento: 0 2px 6px rgba(37, 99, 235, .35), 0 12px 28px -8px rgba(37, 99, 235, .5);
}

/* Sin JavaScript no hay quien ponga data-theme: que al menos el sistema mande. Son los
   mismos valores de arriba, y es duplicación a sabiendas —CSS no permite reutilizar un
   bloque en dos selectores sin preprocesador, y esta web no carga ninguno—. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --acento: #3b82f6;
    --acento-fuerte: #2563eb;
    --acento-texto: #93c5fd;
    --acento-suave: rgba(59, 130, 246, 0.25);
    --morado-texto: #a78bfa;
    --morado-suave: rgba(139, 92, 246, 0.22);
    --sombra-morado: 0 2px 6px rgba(109, 40, 217, .4), 0 12px 28px -8px rgba(109, 40, 217, .55);
    --texto: #cbd5e1;
    --texto-fuerte: #e2e8f0;
    --texto-suave: #94a3b8;
    --texto-tenue: #7c8aa0;
    --borde: rgba(148, 163, 184, 0.18);
    --codigo-fondo: #060b16;
    --cristal: rgba(30, 41, 59, 0.6);
    --cristal-borde: rgba(148, 163, 184, 0.16);
    --cristal-denso: rgba(20, 29, 48, 0.95);
    --vidrio-canto: linear-gradient(150deg,
                      rgba(255, 255, 255, .28), rgba(255, 255, 255, .06) 42%,
                      rgba(255, 255, 255, .02));
    --vidrio-luz: inset 0 1px 0 rgba(255, 255, 255, .10);
    --sombra-1: 0 1px 2px rgba(0, 0, 0, .4), 0 3px 8px -2px rgba(0, 0, 0, .45);
    --sombra-2: 0 2px 4px rgba(0, 0, 0, .35), 0 8px 20px -6px rgba(0, 0, 0, .5),
                0 24px 48px -20px rgba(0, 0, 0, .6);
    --sombra-3: 0 4px 8px rgba(0, 0, 0, .4), 0 16px 40px -12px rgba(0, 0, 0, .55),
                0 48px 96px -32px rgba(0, 0, 0, .7);
    --sombra-acento: 0 2px 6px rgba(37, 99, 235, .35), 0 12px 28px -8px rgba(37, 99, 235, .5);
  }
}

/* ================================================================ lienzo */

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  color: var(--texto);
  font-family: "SF Pro Display", Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: var(--t-cuerpo);
  line-height: 1.65;
  letter-spacing: -0.006em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  -webkit-text-size-adjust: 100%;
  /* Solo el color base: el degradado entero vive en el ::before de abajo. */
  background: #e8ecf5;
  position: relative;
}
[data-theme="dark"] body { background: #0d1424; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) body { background: #0d1424; }
}

/* Nada de desbordes laterales, pase lo que pase: una animación que asoma un píxel por el
   borde regala una barra de scroll horizontal en el móvil. `clip` y no `hidden`: `hidden`
   convierte el elemento en contenedor de scroll y ROMPE los position:sticky de dentro. */
html, body { overflow-x: clip; }

/* El lienzo entero —la luz del escritorio del panel— pintado en un pseudo-elemento con
   `position: fixed`, NO con `background-attachment: fixed`: los navegadores del móvil
   implementan `attachment` a medias y el fondo se corta al primer alto de pantalla, con
   toda la página en blanco de ahí para abajo. Un elemento fijo de verdad no tiene ese
   problema en ninguno. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(46vw 46vw at 12% 4%, color-mix(in srgb, var(--acento) 11%, transparent), transparent 70%),
    radial-gradient(38vw 38vw at 92% 18%, color-mix(in srgb, #8b5cf6 9%, transparent), transparent 70%),
    radial-gradient(ellipse 90% 70% at 12% 0%, rgba(99, 132, 199, 0.22), transparent 62%),
    radial-gradient(ellipse 80% 60% at 88% 8%, rgba(129, 140, 202, 0.18), transparent 62%),
    radial-gradient(ellipse 100% 60% at 50% 100%, rgba(100, 150, 170, 0.16), transparent 68%),
    linear-gradient(165deg, #eef2f8 0%, #e8ecf5 45%, #e6eef1 100%);
}
[data-theme="dark"] body::before {
  background:
    radial-gradient(46vw 46vw at 12% 4%, color-mix(in srgb, var(--acento) 13%, transparent), transparent 70%),
    radial-gradient(38vw 38vw at 92% 18%, color-mix(in srgb, #8b5cf6 10%, transparent), transparent 70%),
    radial-gradient(ellipse 90% 70% at 12% 0%, rgba(37, 99, 235, 0.18), transparent 62%),
    radial-gradient(ellipse 80% 60% at 88% 8%, rgba(99, 102, 241, 0.14), transparent 62%),
    radial-gradient(ellipse 100% 60% at 50% 100%, rgba(13, 148, 136, 0.12), transparent 68%),
    linear-gradient(165deg, #0b1220 0%, #111827 50%, #0f1a1f 100%);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) body::before {
    background:
      radial-gradient(46vw 46vw at 12% 4%, color-mix(in srgb, var(--acento) 13%, transparent), transparent 70%),
      radial-gradient(38vw 38vw at 92% 18%, color-mix(in srgb, #8b5cf6 10%, transparent), transparent 70%),
      radial-gradient(ellipse 90% 70% at 12% 0%, rgba(37, 99, 235, 0.18), transparent 62%),
      radial-gradient(ellipse 80% 60% at 88% 8%, rgba(99, 102, 241, 0.14), transparent 62%),
      radial-gradient(ellipse 100% 60% at 50% 100%, rgba(13, 148, 136, 0.12), transparent 68%),
      linear-gradient(165deg, #0b1220 0%, #111827 50%, #0f1a1f 100%);
  }
}

h1, h2, h3, h4 { color: var(--texto-fuerte); letter-spacing: -0.018em; }

/* Las cifras, en columna: en un NAS casi todo lo que se lee son tamaños y fechas. */
table, td, th, time, code, .tabular { font-variant-numeric: tabular-nums; }

.envoltorio { max-width: 1040px; margin: 0 auto; padding: 0 20px 72px; }

/* El color de los enlaces es `--acento-texto`, no `--acento`: el azul de acento (#2563eb) sobre el
   papel claro (#e8ecf5) da 4,37:1, y para texto normal hacen falta 4,5. `--acento-texto` (#1d4ed8)
   da 5,74:1; en oscuro es el azul claro de siempre. Al pasar el ratón se afirma el subrayado. */
a { color: var(--acento-texto); text-decoration-color: color-mix(in srgb, var(--acento-texto) 40%, transparent); }
a:hover { color: var(--acento-texto); text-decoration-color: currentColor; }

/* La selección, del azul de la casa y translúcida: no toca el color del texto, así que lo
   seleccionado se sigue leyendo igual en claro y en oscuro. */
::selection { background: color-mix(in srgb, var(--acento) 30%, transparent); }

/* El foco NUNCA se quita: es como se navega con teclado. Se dibuja bonito y se acabó.
   El anillo no toca el radio del elemento: sigue la forma que ya tenga. */
a[href]:focus-visible,
button:focus-visible,
input:focus-visible,
summary:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible {
  outline: 2px solid var(--acento);
  outline-offset: 2px;
}

/* `hidden` en un elemento con `display` propio no esconde nada: la propiedad gana al
   atributo. El filtro de guías se apoya en `hidden`, así que se dice explícitamente. */
[hidden] { display: none !important; }

/* Con la barra pegajosa delante, un enlace a #seccion aterrizaba con el título DEBAJO de
   ella. Todo lo que tiene ancla se frena antes. */
[id] { scroll-margin-top: 5.5rem; }

/* ================================================================ cristal */

.glass {
  background:
    linear-gradient(var(--cristal), var(--cristal)) padding-box,
    var(--vidrio-canto) border-box;
  border: 1px solid transparent;
  border-radius: var(--radio-g);
  -webkit-backdrop-filter: var(--vidrio-filtro);
  backdrop-filter: var(--vidrio-filtro);
  box-shadow: var(--sombra-2), var(--vidrio-luz);
}

.panel { padding: 20px 24px; margin-bottom: 14px; }
/* Los paneles llevan su hueco propio: lo primero y lo último no suman el suyo. */
.panel > :first-child { margin-top: 0; }
.panel > :last-child { margin-bottom: 0; }

/* ================================================================ la barra
   La misma píldora flotante del portal. Pegada al canto superior parece parte del
   navegador; flotando parece parte del producto. */

.barra {
  position: sticky; top: .75rem; z-index: 50;
  display: flex; align-items: center; gap: .5rem;
  /* Fuera del envoltorio (la abre generar.py antes que él) y con LA MISMA anchura que la
     barra del portal: si midieran distinto, al pasar de la landing a la documentación la
     barra «encogería» y delataría que son dos plantillas. */
  margin: .75rem auto 0; max-width: min(1180px, calc(100% - 1.5rem));
  padding: .55rem clamp(.9rem, 2vw, 1.3rem);
  border-radius: 999px;
  background:
    linear-gradient(var(--cristal), var(--cristal)) padding-box,
    var(--vidrio-canto) border-box;
  border: 1px solid transparent;
  -webkit-backdrop-filter: var(--vidrio-filtro);
  backdrop-filter: var(--vidrio-filtro);
  box-shadow: var(--sombra-2), var(--vidrio-luz);
  transition: box-shadow var(--medio) var(--curva), transform var(--medio) var(--curva);
  transform-origin: 50% 0;
}
/* Al bajar, la barra se encoge y gana sombra: la página «arranca». La clase la pone el script.
   Se encoge con `transform` y NO con el relleno: la barra es `sticky` —ocupa sitio—, y al cambiar
   de alto el navegador recolocaba el scroll, se volvía a cruzar el umbral y la barra parpadeaba
   al empezar a bajar (Android, 18-09-2026; la misma regla y el mismo porqué que en el portal). */
html.rodando .barra { transform: scale(.982); box-shadow: var(--sombra-3), var(--vidrio-luz); }

.marca {
  display: inline-flex; align-items: center; gap: .55rem;
  font-size: 1.1rem; font-weight: 800; letter-spacing: -.02em;
  color: var(--texto-fuerte); text-decoration: none; white-space: nowrap;
}
.marca:hover { text-decoration: none; }
.marca-tile { width: 26px; height: 26px; border-radius: 8px; box-shadow: var(--sombra-1); }
.marca-os { color: var(--acento); }

/* Tres pesos en la barra, no uno: los enlaces de sección son discretos, «Comprar» es un
   botón sólido y el tema es un icono. Si los siete pesan igual, la acción que paga la web
   se pierde entre «Inicio» y «Documentación». */
.barra nav { display: flex; align-items: center; gap: .15rem; margin-left: auto; }

.nav-enlace {
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: .42rem .7rem; border-radius: 9px;
  font-size: .9rem; font-weight: 500;
  color: var(--texto-suave); text-decoration: none;
  transition: background-color var(--rapido) var(--curva), color var(--rapido) var(--curva);
}
.nav-enlace:hover {
  text-decoration: none; color: var(--texto-fuerte);
  background: color-mix(in srgb, var(--texto-fuerte) 7%, transparent);
}
/* Dónde estás. Se cuelga de aria-current, que es el atributo que ya lo dice de verdad. */
.nav-enlace[aria-current="page"] {
  color: var(--texto-fuerte); font-weight: 650;
  background: color-mix(in srgb, var(--acento) 12%, transparent);
}

.nav-fijo { display: flex; align-items: center; gap: .5rem; margin-left: .6rem; }
/* El idioma y el tema, aparte: son PREFERENCIAS, no acciones de la web. Mismo envoltorio que
   el portal, para que la barra sea la misma en las cuatro páginas. */
.nav-preferencias { display: flex; align-items: center; gap: .5rem; margin-left: .5rem; }

/* Lo TUYO —tu cuenta, salir— detrás de tu inicial, y no como un enlace más en la fila del
   producto. Estas páginas son archivos estáticos y no saben quién eres: sale «Entrar» y un
   guion de la barra lo cambia por el círculo si la cookie `portal_sesion` trae tu letra.
   Antes ponía «Mi cuenta» como texto, y era la barra distinta que se veía al pasar de la
   portada a Novedades. */
.nav-entrar { font-weight: 600; }
.usuario { position: relative; display: flex; align-items: center; }
.usuario-btn {
  display: inline-flex; align-items: center; gap: .15rem;
  padding: 0; border: 0; background: none; color: var(--texto-suave); cursor: pointer;
  border-radius: 999px;
}
.usuario-btn:focus-visible { outline: 2px solid var(--acento); outline-offset: 3px; }
.usuario-punta { width: 16px; height: 16px; transition: rotate .15s ease; }
.usuario-btn[aria-expanded="true"] .usuario-punta { rotate: 180deg; }
.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 999px;
  background: linear-gradient(180deg, var(--acento), var(--acento-fuerte));
  color: #fff; font-weight: 700; font-size: .9rem; line-height: 1;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22);
}
.avatar.peq { width: 30px; height: 30px; font-size: .82rem; }
.usuario-menu {
  position: absolute; top: calc(100% + .45rem); right: 0; z-index: 60;
  min-width: 11rem; padding: .3rem; border-radius: 12px;
  background: var(--cristal-denso, var(--cristal));
  border: 1px solid color-mix(in srgb, var(--texto-fuerte) 12%, transparent);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--texto-fuerte) 22%, transparent);
  backdrop-filter: blur(14px);
}
.usuario-menu[hidden] { display: none; }
.usuario-opcion {
  display: block; width: 100%; box-sizing: border-box; text-align: left;
  padding: .45rem .55rem; border: 0; border-radius: 8px; background: none;
  color: var(--texto-fuerte); text-decoration: none; font: inherit; font-size: .85rem;
  cursor: pointer; white-space: nowrap;
}
.usuario-opcion:hover { background: color-mix(in srgb, var(--texto-fuerte) 8%, transparent); }
.usuario-opcion:focus-visible { outline: 2px solid var(--acento); outline-offset: -2px; }
.usuario-salir { color: var(--texto-suave); }


/* Quién eres, arriba del todo del menú: el correo y, si lo eres, el distintivo. Lo trae
   `/quien-soy` al abrir —en la cookie solo va la inicial, que una cookie legible por
   JavaScript no es sitio para el correo de nadie—, así que nace oculto y aparece al llegar. */
.usuario-quien {
  padding: .5rem .55rem .45rem; margin-bottom: .2rem;
  border-bottom: 1px solid color-mix(in srgb, var(--texto-fuerte) 10%, transparent);
  display: flex; flex-direction: column; gap: .3rem; align-items: flex-start;
}
.usuario-quien[hidden] { display: none; }
.usuario-correo { font-size: .82rem; color: var(--texto-suave); word-break: break-all; }
.marca-equipo {
  display: inline-block; padding: .1rem .45rem; border-radius: 999px;
  font-size: .68rem; font-weight: 700; letter-spacing: .02em;
  background: color-mix(in srgb, #8b5cf6 20%, transparent); color: #7c3aed;
}
:root[data-theme="dark"] .marca-equipo { color: #c4b5fd; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .marca-equipo { color: #c4b5fd; }
}

.nav-cta {
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: .45rem .95rem; border-radius: 999px;
  font-size: .9rem; font-weight: 650; color: #fff; text-decoration: none;
  background: linear-gradient(180deg, var(--acento), var(--acento-fuerte));
  box-shadow: 0 1px 2px rgba(15, 23, 42, .18), inset 0 1px 0 rgba(255, 255, 255, .22);
  transition: transform var(--rapido) var(--curva), box-shadow var(--rapido) var(--curva),
              filter var(--rapido) var(--curva);
}
.nav-cta:hover {
  text-decoration: none; color: #fff; filter: brightness(1.06);
  box-shadow: 0 4px 12px -2px color-mix(in srgb, var(--acento) 55%, transparent),
              inset 0 1px 0 rgba(255, 255, 255, .22);
}
.nav-cta:active { transform: translateY(1px); }

/* El interruptor de tema: un icono tranquilo. Cada tema enseña el icono del OTRO, que es
   a lo que se va a cambiar. */
.tema-btn {
  width: 34px; height: 34px; padding: 0;
  display: grid; place-items: center;
  border-radius: 10px; border: 0; background: none;
  color: var(--texto-suave); cursor: pointer;
  transition: background-color var(--rapido) var(--curva), color var(--rapido) var(--curva);
}
.tema-btn:hover { background: color-mix(in srgb, var(--texto-fuerte) 8%, transparent); color: var(--texto-fuerte); }
.tema-btn:active { transform: translateY(1px); }
.tema-btn svg { width: 17px; height: 17px; }
.tema-luna { display: block; }
.tema-sol { display: none; }
[data-theme="dark"] .tema-luna { display: none; }
[data-theme="dark"] .tema-sol { display: block; }

/* La bandera del idioma, hermana del botón de tema: misma altura, mismo hueco, mismo trato
   al pasar el ratón. Enseña la bandera del idioma AL QUE LLEVA (destino, no actual), que es
   lo que la hace pulsable sin pensarla, con su código de dos letras al lado —una bandera
   sola se lee como un país, no como un idioma—.
   Esta hoja es la de la web generada; la del portal (web/app/static/estilo.css) lleva las
   mismas reglas. Son dos archivos a propósito, así que lo que se toque aquí se toca allí. */
.idioma-btn {
  display: inline-flex; align-items: center; gap: .35rem;
  height: 34px; padding: 0 .5rem; border-radius: 10px;
  border: 0; background: none; color: var(--texto-suave); cursor: pointer;
  font-size: .74rem; font-weight: 700; letter-spacing: .04em; text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--rapido) var(--curva), color var(--rapido) var(--curva);
}
.idioma-btn:hover { background: color-mix(in srgb, var(--texto-fuerte) 8%, transparent); color: var(--texto-fuerte); }
.idioma-btn:active { transform: translateY(1px); }
/* La bandera con su bordecito: sobre fondo claro, el blanco de la británica se derretía con
   la barra y quedaba media bandera flotando. */
.idioma-bandera {
  width: 20px; height: 14px; border-radius: 3px; display: block; flex: 0 0 auto;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--texto-fuerte) 18%, transparent);
}

/* El menú de idiomas, desde que hay más de dos. Es un `<details>`: funciona sin JavaScript,
   con teclado y con lector de pantalla, y dentro van enlaces de verdad.

   Estas reglas se escribieron solo en la hoja del portal, así que Inicio y Descargas salían
   bien y Novedades y Documentación —que son la web GENERADA y tiran de ESTA hoja— enseñaban
   el menú desplegado, con sus topos, colgando fuera de la barra. Es justo lo que avisa el
   comentario de arriba: son dos archivos, y lo que se toca aquí se toca allí. */
.idioma-menu { position: relative; }
.idioma-menu > summary { list-style: none; }
.idioma-menu > summary::-webkit-details-marker { display: none; }
/* La flecha, dibujada con borde para no depender de ninguna fuente de iconos. */
.idioma-menu > summary::after {
  content: ''; width: 0; height: 0; margin-left: .1rem;
  border: 3px solid transparent; border-top-color: currentColor; translate: 0 1.5px;
  transition: rotate .15s ease;
}
.idioma-menu[open] > summary::after { rotate: 180deg; translate: 0 -1.5px; }
.idioma-lista {
  position: absolute; top: calc(100% + .35rem); right: 0; z-index: 60;
  margin: 0; padding: .3rem; list-style: none; min-width: 10.5rem;
  border-radius: 12px; background: var(--cristal-denso, var(--cristal));
  border: 1px solid color-mix(in srgb, var(--texto-fuerte) 12%, transparent);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--texto-fuerte) 22%, transparent);
  backdrop-filter: blur(14px);
}
.idioma-lista a {
  display: flex; align-items: center; gap: .5rem;
  padding: .45rem .55rem; border-radius: 8px;
  color: var(--texto-fuerte); text-decoration: none; font-size: .82rem; white-space: nowrap;
}
.idioma-lista a:hover { background: color-mix(in srgb, var(--texto-fuerte) 8%, transparent); }
.idioma-lista a:focus-visible { outline: 2px solid var(--acento); outline-offset: -2px; }
/* En el móvil la barra esconde el rótulo del botón (más abajo), pero DENTRO del menú el
   nombre es lo único que distingue un idioma de otro: ahí se queda. */
.idioma-lista span { display: inline !important; }

/* La rayita que separa las secciones del sitio de lo que es tu cuenta. Sin ella, cinco
   enlaces seguidos pesan lo mismo y «Entrar» parece una sección más. */
.nav-sep {
  width: 1px; height: 18px; margin: 0 .35rem;
  background: color-mix(in srgb, var(--texto-fuerte) 14%, transparent);
}

/* La línea de lectura: cuánto queda de página.

   Va DENTRO de la píldora, no pegada a su canto de abajo. Pegada al canto se salía por la
   esquina redonda —la barra tiene radio 999 y la línea era recta—, y lo que se veía era una
   raya azul suelta asomando por debajo de la barra, como si algo se hubiera roto.

   Y crece con `transform: scaleX()`, no con `width`: así puede tener los dos lados metidos
   hacia dentro (el ancho lo da el `left`/`right`) y además el navegador la anima en la capa
   de composición, sin rehacer la maqueta sesenta veces por segundo mientras se baja. */
.barra-progreso {
  position: absolute; bottom: 6px;
  left: clamp(.9rem, 2vw, 1.4rem); right: clamp(.9rem, 2vw, 1.4rem);
  height: 2px; border-radius: 999px;
  background: linear-gradient(90deg, var(--acento), #8b5cf6 60%, #06b6d4);
  transform: scaleX(0); transform-origin: left center;
  transition: transform .12s linear;
  pointer-events: none;
}

/* El botón de menú solo existe en pantallas estrechas. */
.nav-abrir { display: none; }

/* Por debajo de esto los enlaces ya no caben al lado de la marca, así que se pliegan.
   «Comprar» y el tema se quedan a la vista: son la venta y un ajuste de una pulsación. */
@media (max-width: 800px) {
  .barra { flex-wrap: wrap; border-radius: var(--radio-g); }
  /* La bandera sin su «EN» en pantalla estrecha: con las dos letras la fila no cabía y el
     botón de menú se caía a una segunda línea. Misma regla que en el portal. */
  .idioma-btn { padding: 0 .35rem; }
  .idioma-btn span { display: none; }
  .nav-abrir {
    display: inline-flex; align-items: center; justify-content: center;
    order: 3; margin-left: auto;
    width: 38px; height: 38px; padding: 0; border-radius: 10px;
    background: none; border: 1px solid var(--cristal-borde); color: var(--texto-fuerte);
    cursor: pointer;
  }
  .nav-abrir svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; }
  .nav-fijo { order: 2; margin-left: auto; }
  .barra nav {
    order: 4; display: none; width: 100%;
    flex-direction: column; align-items: stretch; gap: .1rem;
    margin: .35rem 0 .15rem; padding-top: .5rem;
    border-top: 1px solid var(--borde);
  }
  .barra.menu-abierto nav { display: flex; }
  .nav-enlace { width: 100%; justify-content: flex-start; padding: .6rem .7rem; font-size: .95rem; }
  .nav-sep { display: none; }
}

/* En un teléfono la fila no cabía: marca + «Comprar» + bandera + tema + menú pedían 327 px donde hay
   325 (a 380 px de pantalla), así que el botón de menú se caía solo a una segunda fila y la barra
   medía 104 px en vez de 58 en TODAS las páginas generadas, de 300 a 384 px. El portal ya tenía estas
   reglas; aquí faltaban (son dos hojas). Se le quita a huecos y rellenos, y por debajo de 350 la
   marca se queda en el dibujo: el nombre sigue ahí para quien lee la pantalla, escondido, no borrado. */
@media (max-width: 430px) {
  .barra { gap: .25rem; padding-inline: .6rem; }
  .marca { font-size: 1rem; gap: .4rem; }
  .marca-tile { width: 22px; height: 22px; }
  .nav-cta { padding-left: .7rem; padding-right: .7rem; }
  .nav-fijo { gap: .25rem; }
}
@media (max-width: 349px) {
  .marca > span {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
}

/* ================================================================ héroe */

.hero {
  text-align: center;
  padding: clamp(3rem, 8vw, 5.5rem) 0 clamp(1.8rem, 4vw, 3rem);
  /* El aire de sección de abajo NO se suma: el héroe ya trae el suyo en el padding, y las
     dos cosas juntas eran nueve rem de página vacía antes del primer contenido. */
  margin-bottom: 0;
}
.hero h1 {
  margin: .9rem 0 .6rem;
  font-size: var(--t-h1); font-weight: 750; line-height: 1.04; letter-spacing: -.035em;
}
.hero .entradilla {
  margin: 0 auto;
  max-width: 62ch;
  font-size: clamp(1.02rem, .95rem + .5vw, 1.2rem);
  line-height: 1.55;
  color: var(--texto-suave);
}
.hero nav.principal {
  display: flex; flex-wrap: wrap; justify-content: center; gap: .7rem;
  margin-top: 1.5rem;
}
/* La entrada del héroe: la insignia, el titular, la entradilla y los botones suben uno
   detrás de otro al abrir la página. Es CSS puro y en `animation`, no en clases puestas por
   JavaScript: si el script no llega a correr, la página se ve entera igual —lo que se anima
   es el estado FINAL, no la visibilidad—. Y con `prefers-reduced-motion` la regla de abajo
   del todo la deja en nada, que es lo correcto para quien pide quietud.

   El escalonado es corto (70 ms) a propósito: por encima de 150 se nota la espera y una
   portada que tarda en montarse se siente lenta aunque haya cargado igual de rápido. */
@keyframes asomar {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
/* (La entrada escalonada del héroe vive más abajo, en «el héroe con escena»: su texto va ahora
   dentro de `.hero-texto`, y los selectores de hijo directo que había aquí ya no casaban con nada.) */

/* El héroe compacto de las guías: el título lo pone el propio documento. */
.hero.compacta { padding: clamp(1.6rem, 4vw, 2.6rem) 0 1.2rem; }
.hero.compacta .entradilla { font-size: 1.02rem; margin-top: .5rem; }
.hero.compacta nav.principal { margin-top: 1.1rem; }

/* ================================================================ pegatinas (chips) */

.chip {
  display: inline-flex; align-items: center; gap: 4px;
  border-radius: 999px; padding: 3px 10px;
  font-size: 11.5px; font-weight: 600; line-height: 1.15rem;
  letter-spacing: .01em; white-space: nowrap;
}
.chip-blue  { color: #1d4ed8; background: rgba(37, 99, 235, 0.12); }
.chip-amber { color: #b45309; background: rgba(217, 119, 6, 0.14); }
.chip-slate { color: #475569; background: rgba(71, 85, 105, 0.12); }
[data-theme="dark"] .chip-blue  { color: #93c5fd; background: rgba(59, 130, 246, 0.18); }
[data-theme="dark"] .chip-amber { color: #fcd34d; background: rgba(245, 158, 11, 0.18); }
[data-theme="dark"] .chip-slate { color: #cbd5e1; background: rgba(148, 163, 184, 0.18); }

/* ================================================================ botones
   Un botón se levanta al acercarse y se HUNDE al pulsarlo. Lo segundo es lo que casi nadie
   pone y lo que hace que se sienta físico: sin el hundido, pulsar no confirma nada. */

.btn-primary, .btn-secondary, .btn-morado {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border-radius: 999px; padding: 10px 20px;
  font-size: 14.5px; font-weight: 600; letter-spacing: -.01em; line-height: 1.25rem;
  text-decoration: none; border: 1px solid transparent; cursor: pointer;
  transition: transform var(--rapido) var(--curva), box-shadow var(--medio) var(--curva),
              background-color var(--rapido) var(--curva), border-color var(--rapido) var(--curva),
              color var(--rapido) var(--curva);
  will-change: transform;
}
.btn-primary svg, .btn-secondary svg, .btn-morado svg { width: 1.1em; height: 1.1em; }

.btn-primary {
  background: linear-gradient(180deg, var(--acento), var(--acento-fuerte));
  color: #fff;
  box-shadow: var(--sombra-acento), inset 0 1px 0 rgba(255, 255, 255, .22);
}
.btn-primary:hover {
  color: #fff; transform: translateY(-2px);
  box-shadow: var(--sombra-acento), 0 20px 40px -12px rgba(37, 99, 235, .45),
              inset 0 1px 0 rgba(255, 255, 255, .22);
}

.btn-secondary {
  color: var(--texto-suave);
  background: color-mix(in srgb, var(--cristal) 80%, transparent);
  border-color: var(--borde);
  box-shadow: var(--sombra-1);
}
.btn-secondary:hover {
  transform: translateY(-2px);
  color: var(--texto-fuerte); border-color: color-mix(in srgb, var(--texto-fuerte) 16%, transparent);
  box-shadow: var(--sombra-2);
}

/* El botón del grafo. Es el MISMO botón que el azul con otra tinta: mismo tamaño, misma
   sombra de color debajo y el mismo hundido al pulsar. Lo que cambia es adónde lleva, y el
   morado está para que se distinga de un vistazo entre las guías —es la única cosa de la
   documentación que no es un texto para leer, sino un mapa para mirar—. */
.btn-morado {
  background: linear-gradient(180deg, var(--morado), var(--morado-fuerte));
  color: #fff;
  box-shadow: var(--sombra-morado), inset 0 1px 0 rgba(255, 255, 255, .22);
}
.btn-morado:hover {
  color: #fff; transform: translateY(-2px);
  box-shadow: var(--sombra-morado), 0 20px 40px -12px rgba(109, 40, 217, .5),
              inset 0 1px 0 rgba(255, 255, 255, .22);
}
.btn-morado:focus-visible { outline: 2px solid var(--morado); outline-offset: 3px; }

.btn-primary:active, .btn-secondary:active, .btn-morado:active {
  transform: translateY(0) scale(.985); transition-duration: .06s;
}
/* Un botón apagado no promete nada: ni sombra, ni salto, ni cursor de acción. */
.btn-primary:disabled, .btn-secondary:disabled,
.btn-primary[aria-disabled="true"], .btn-secondary[aria-disabled="true"] {
  opacity: .5; cursor: not-allowed; transform: none;
  box-shadow: none; filter: none; pointer-events: none;
}
.btn-primary:focus-visible, .btn-secondary:focus-visible {
  outline: 2px solid var(--acento); outline-offset: 3px;
}

.acciones { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: 1.2rem; }

/* ================================================================ secciones */

section { margin-bottom: var(--aire-seccion); }

h2 { font-size: var(--t-h2); line-height: 1.12; letter-spacing: -.025em; margin: 0 0 4px; }
h3 { font-size: var(--t-h3); letter-spacing: -.015em; }
h2 + .sub { margin: 0 0 18px; color: var(--texto-tenue); font-size: 15.5px; }
.sub.aparte { margin-top: 1rem; color: var(--texto-tenue); font-size: 15.5px; }

.rejilla { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
/* «dos» son DOS columnas de verdad, no «las que quepan»: con auto-fit, cuatro tarjetas a
   ancho de escritorio salían 3+1, con la cuarta huérfana junto a un hueco doble. */
.rejilla.dos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.rejilla.tres { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
@media (max-width: 700px) {
  .rejilla.dos { grid-template-columns: minmax(0, 1fr); }
}

.dato { font-size: 15px; margin: 0; }
.dato dt { font-weight: 600; color: var(--texto-fuerte); margin-bottom: 2px; }
.dato dd { margin: 0; color: var(--texto-suave); }

.destacado h3 { margin: 0 0 6px; font-size: 16px; }
.destacado p { margin: 0; color: var(--texto-suave); font-size: 15px; }

/* ================================================================ código */

pre, code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

pre {
  background: var(--codigo-fondo);
  color: #e2e8f0;
  border: 1px solid rgba(148, 163, 184, 0.18);
  border-radius: 12px;
  padding: 14px 16px;
  overflow-x: auto;
  font-size: 13.5px;
  line-height: 1.6;
  margin: 12px 0;
  box-shadow: var(--sombra-1);
}

/* Dentro de un bloque, los saltos de línea son parte de la orden. */
pre code { background: none; padding: 0; color: inherit; font-size: inherit; white-space: pre; }

code {
  background: var(--acento-suave);
  color: var(--acento-texto);
  padding: 1px 6px;
  border-radius: 6px;
  font-size: 0.88em;
  font-variant-ligatures: none;
  /* SE PARTE si no cabe, y antes no.
     La idea de `nowrap` era buena —una orden cortada en dos renglones se lee como dos cosas
     distintas— pero el precio es desproporcionado: un código largo no tiene dónde partirse,
     así que empuja a su contenedor, y a la página entera, fuera de la pantalla. Pasó en las
     novedades con una línea que se comió medio párrafo por unas comillas mal cerradas: el
     texto se salía por la derecha y no había forma de leerlo.
     `anywhere` solo corta cuando no queda otra; mientras quepa, la orden sigue entera. */
  overflow-wrap: anywhere;
  /* Y por si acaso: dentro de su caja y nunca más ancho que ella. */
  max-width: 100%;
}
[data-theme="dark"] code { color: #93c5fd; }

/* ================================================================ avisos */

.aviso, .peligro {
  border-radius: 12px;
  padding: 13px 16px;
  margin: 16px 0;
  font-size: 15px;
  border: 1px solid;
}
.aviso {
  color: #92400e;   /* #b45309 sobre su propio fondo daba 3,87:1 a 15 px; éste pasa de 6:1 */
  background: rgba(217, 119, 6, 0.1);
  border-color: rgba(217, 119, 6, 0.28);
}
.peligro {
  color: #b91c1c;
  background: rgba(220, 38, 38, 0.09);
  border-color: rgba(220, 38, 38, 0.28);
}
[data-theme="dark"] .aviso {
  color: #fcd34d;
  background: rgba(245, 158, 11, 0.14);
  border-color: rgba(245, 158, 11, 0.3);
}
[data-theme="dark"] .peligro {
  color: #fecaca;
  background: rgba(248, 113, 113, 0.14);
  border-color: rgba(248, 113, 113, 0.32);
}

/* ================================================================ novedades */

.version { padding: 18px 22px; margin-bottom: 14px; transition: box-shadow var(--medio) var(--curva); }
.version:hover { box-shadow: var(--sombra-3), var(--vidrio-luz); }

.version h3 {
  display: inline-flex; align-items: baseline; gap: 10px;
  margin: 0; font-size: 18px;
}
.version .num { font-variant-numeric: tabular-nums; }
.version .fecha { font-size: 12px; font-weight: 400; color: var(--texto-tenue); }

.version h4 { margin: 18px 0 6px; font-size: 14px; font-weight: 600; color: var(--acento-texto); }
[data-theme="dark"] .version h4 { color: #93c5fd; }

.version .entradilla-version { margin: 10px 0 4px; color: var(--texto-suave); font-size: 15px; }
.version ul { margin: 0; padding-left: 20px; }
.version li { margin-bottom: 8px; color: var(--texto-suave); }
.version li strong { color: var(--texto-fuerte); font-weight: 600; }

details.version summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 8px; padding: 2px 0;
}
details.version summary::-webkit-details-marker { display: none; }
/* El signo gira al abrir: la animación más barata que existe y la que más se nota. */
details.version summary::before {
  content: "▸"; color: var(--texto-tenue); font-size: 12px;
  transition: transform var(--rapido) var(--curva);
}
details.version[open] summary::before { transform: rotate(90deg); }
details.version summary:hover { color: var(--acento-texto); }

/* El resumen de una versión: sus titulares en una línea, DENTRO del pliegue cerrado.
   Ciento y pico renglones que solo dicen «1.28.4 · 12 de julio» obligan a abrir a ciegas
   hasta dar con la versión que se busca; con los titulares delante se encuentra a la
   primera. En una línea y cortado con puntos suspensivos: si envuelve, la lista se
   convierte en un muro y deja de servir para recorrerla con la vista. */
.version-resumen {
  margin: .3rem 0 0; min-width: 0;
  font-size: 13.5px; line-height: 1.45; color: var(--texto-tenue);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
details.version summary .version-resumen { margin: 0; flex: 1 1 auto; }
/* En un teléfono el resumen compartía renglón con el número y la fecha y le quedaban 100 px de
   caja: se leía «Correccion…» —el 11 % de la frase— y la fecha partía en dos. Ahí baja a su propio
   renglón, con dos líneas como mucho; la lista sigue recorriéndose de un vistazo. */
@media (max-width: 560px) {
  details.version summary { flex-wrap: wrap; row-gap: 2px; }
  details.version summary h3 { white-space: nowrap; }
  details.version summary .version-resumen {
    flex: 1 0 100%; white-space: normal; padding-left: 20px;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
  }
}
details.version[open] summary .version-resumen { display: none; }   /* ya está el contenido */
article.version .version-resumen { display: none; }                 /* abierta, sobra */

/* La almohadilla que enlaza a ESTA versión. Aparece al acercarse: una lista con ciento y
   pico almohadillas siempre visibles es ruido, pero sin ellas no hay forma de mandarle a
   alguien «lo que trae la 1.30». */
.version-ancla {
  margin-left: .1rem; padding: 0 .25rem;
  color: var(--texto-tenue); text-decoration: none; font-weight: 600;
  opacity: 0; transition: opacity var(--rapido) var(--curva), color var(--rapido) var(--curva);
}
.version:hover .version-ancla, .version-ancla:focus-visible { opacity: 1; }
.version-ancla:hover { color: var(--acento-texto); }
/* Al llegar por un enlace con almohadilla, la versión se enciende un momento: sin eso, la
   página salta a mitad de una lista larga y no se sabe cuál de todas es la que se buscaba. */
.version:target { box-shadow: var(--sombra-3), 0 0 0 2px var(--acento-suave), var(--vidrio-luz); }

/* El buscador de versiones, con el aire de la sección y no pegado al título. */
#novedades .doc-buscador { margin-block: 1rem 1.4rem; }

/* ── El hilo de las versiones ──────────────────────────────────────────────────
   Una línea a la izquierda de la lista, con un punto por versión: la lista de novedades es
   una línea de tiempo y se lee como tal. El hilo se DIBUJA conforme se baja —lo lleva el
   navegador con `animation-timeline`, sin un solo guion— y cada punto se enciende cuando su
   versión entra en pantalla. Donde el navegador no sepa (Safari viejo), el hilo sale entero y
   los puntos encendidos: la misma línea de tiempo, quieta. Solo con sitio a la izquierda. */
#novedades { --nov-papel: #e8ecf5; }
[data-theme="dark"] #novedades { --nov-papel: #0d1424; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #novedades { --nov-papel: #0d1424; }
}
/* (El hilo y los puntos son decoración quieta; lo único que se MUEVE —dibujarse con el scroll— va
   colgado del modo de movimiento, como todo lo demás: ver «movimiento» en la cabecera.) */
@media (min-width: 64rem) {
  #novedades { position: relative; }
  #novedades::before {
    content: ''; position: absolute; left: -2.1rem; top: 9.5rem; bottom: 1rem; width: 2px;
    border-radius: 2px;
    background: linear-gradient(180deg, var(--acento), color-mix(in srgb, var(--acento) 35%, transparent));
    transform-origin: 50% 0; transform: scaleY(1);
  }
  @supports (animation-timeline: view()) {
    :where(html:not([data-movimiento="reducido"])) #novedades::before { animation: hilo-que-se-dibuja linear both; animation-timeline: view(); animation-range: cover 4% cover 70%; }
  }
  .version { position: relative; }
  /* El punto va en `::after`, NO en `::before`: el `::before` de una versión ya es el reflejo que sigue
     al puntero (más abajo, en «El reflejo que sigue al puntero»), y como esa regla va DESPUÉS y pesa lo
     mismo, se llevaba por delante el punto entero —`inset: 0`, `z-index: -1`, `opacity: 0`—: el hilo
     salía, y sus puntos no salieron nunca, en ningún modo. Medido el 18-09-2026 en la página generada. */
  /* De base, ENCENDIDO: es como sale donde nada lo anima (un navegador sin `animation-timeline`, el
     movimiento reducido). Donde sí, la animación de abajo lo tiene apagado hasta que su versión entra
     en pantalla (`both`: antes de empezar manda su primer fotograma). */
  .version::after {
    content: ''; position: absolute; left: calc(-2.1rem - 5px); top: 26px; width: 12px; height: 12px;
    border-radius: 50%; background: var(--acento); border: 2px solid var(--acento);
    box-shadow: 0 0 0 4px var(--nov-papel);
  }
  @supports (animation-timeline: view()) {
    :where(html:not([data-movimiento="reducido"])) .version::after { animation: punto-que-se-enciende linear both; animation-timeline: view(); animation-range: entry 0% entry 60%; }
  }
}
@keyframes hilo-que-se-dibuja { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes punto-que-se-enciende {
  from { border-color: var(--texto-tenue); background: var(--nov-papel); }
  to { border-color: var(--acento); background: var(--acento); }
}

/* ================================================================ pasos */

ol.pasos { counter-reset: paso; list-style: none; padding: 0; margin: 0; }

ol.pasos > li {
  counter-increment: paso;
  position: relative;
  padding: 0 0 18px 40px;
  border-left: 2px solid var(--borde);
  margin-left: 13px;
}
ol.pasos > li:last-child { border-left-color: transparent; padding-bottom: 0; }

ol.pasos > li::before {
  content: counter(paso);
  position: absolute; left: -14px; top: -1px;
  width: 26px; height: 26px; border-radius: 50%;
  background: linear-gradient(160deg, #60a5fa, var(--acento));
  color: #fff; font-size: 12px; font-weight: 700;
  display: grid; place-items: center;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), var(--sombra-1);
}

ol.pasos h3 { margin: 0 0 4px; font-size: 16px; }
ol.pasos p { margin: 0 0 8px; color: var(--texto-suave); font-size: 15px; }

/* ================================================================ tablas */

.tabla-envuelta { overflow-x: auto; }

table { border-collapse: collapse; width: 100%; font-size: 15px; min-width: 460px; }

th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--borde); vertical-align: top; }
tr:last-child td { border-bottom: 0; }

th {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--texto-tenue); font-weight: 600;
}
td { color: var(--texto-suave); }
td:first-child { font-weight: 600; color: var(--texto-fuerte); white-space: nowrap; }

/* ================================================================ capturas */

.captura { margin: 0 0 16px; padding: 12px; }
.captura img {
  display: block; width: 100%; height: auto;
  border-radius: 12px; border: 1px solid var(--borde);
}
.captura figcaption {
  margin-top: 12px; font-size: 14.5px; color: var(--texto-suave); line-height: 1.6;
}
.captura figcaption strong { color: var(--texto-fuerte); }

/* Dos capturas por app, una por tema: se enseña la del tema puesto. */
[data-theme="light"] .captura .oscuro,
[data-theme="light"] .captura-movil .oscuro,
[data-theme="dark"] .captura .claro,
[data-theme="dark"] .captura-movil .claro { display: none; }

.moviles { display: flex; flex-wrap: wrap; gap: 22px; justify-content: center; }

.captura-movil { margin: 0; max-width: 240px; }
.captura-movil img {
  display: block; width: 100%; height: auto;
  /* El radio y el borde bastan para que se lea como un móvil sin dibujar una carcasa. */
  border-radius: 26px;
  border: 1px solid var(--borde);
  box-shadow: var(--sombra-3);
}
.captura-movil figcaption {
  margin-top: 10px; text-align: center; font-size: 13.5px; color: var(--texto-tenue);
}
.pie-movil { margin: 20px 0 0; font-size: 14.5px; color: var(--texto-suave); }

/* ================================================================ áreas (qué sabe hacer) */

.area { padding: 18px 22px; margin-bottom: 14px; }
.area h3 { margin: 0 0 10px; font-size: 17px; }
.area ul { margin: 0; padding-left: 20px; }
.area li { margin-bottom: 7px; color: var(--texto-suave); font-size: 15px; }
.area li strong { color: var(--texto-fuerte); }

/* ================================================================ documentación: portada
   Quien llega a una documentación trae UNA pregunta y necesita saber cuál de las ocho
   guías la responde, sin abrir tres para averiguarlo. De ahí el buscador, los grupos y la
   línea de «cuándo» de cada tarjeta. */

.doc-buscar { margin: 0 0 clamp(2rem, 4.5vw, 2.8rem); }
.doc-buscador {
  display: flex; align-items: center; gap: .7rem;
  max-width: 34rem; margin: 0 auto; padding: .1rem .3rem .1rem 1.1rem;
  border-radius: 999px;
  background:
    linear-gradient(var(--cristal), var(--cristal)) padding-box,
    var(--vidrio-canto) border-box;
  border: 1px solid transparent;
  -webkit-backdrop-filter: var(--vidrio-filtro);
  backdrop-filter: var(--vidrio-filtro);
  box-shadow: var(--sombra-2), var(--vidrio-luz);
  transition: box-shadow var(--rapido) var(--curva);
}
/* El anillo de foco va en el contenedor, no en el campo: si no, salen dos. */
.doc-buscador:focus-within { box-shadow: var(--sombra-3), 0 0 0 3px var(--acento-suave); }
.doc-lupa { width: 17px; height: 17px; flex: 0 0 auto; color: var(--texto-tenue); }
.doc-buscador input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent;
  color: inherit; font: inherit; padding: .8rem .4rem .8rem 0;
}
.doc-buscador input::placeholder { color: var(--texto-tenue); }
.doc-nada { text-align: center; margin-top: 1.2rem; color: var(--texto-tenue); font-size: .95rem; }

.doc-grupo { margin-bottom: clamp(2rem, 4.5vw, 2.8rem); }
/* Los títulos de grupo son rótulos de un índice, no titulares: a tamaño de titular, cuatro
   grupos gritando convertían la página en una escalera de letras enormes. */
.doc-grupo > h2 { font-size: 1.28rem; letter-spacing: -.015em; margin-bottom: .15rem; }
.doc-grupo > .sub { margin-top: 0; margin-bottom: 12px; font-size: 14.5px; }

/* La guía es una FILA: peana con el icono, texto y un chevrón. Compacta a propósito —esto
   es un índice, y en el móvil ocho bloques verticales eran seis pantallas de scroll—. */
.guia {
  position: relative; display: flex; align-items: flex-start; gap: .95rem;
  padding: 1.05rem 1.1rem;
  border-radius: 18px;
  text-decoration: none;
  transition: transform var(--medio) var(--curva), box-shadow var(--medio) var(--curva);
}
/* Un grupo con una sola guía ocupa el ancho entero: media rejilla vacía parece un fallo. */
.rejilla.dos > .guia:only-child { grid-column: 1 / -1; }

.guia-ico {
  flex: 0 0 auto; width: 42px; height: 42px;
  display: grid; place-items: center;
  border-radius: 12px;
}
.guia-ico svg { width: 22px; height: 22px; }
/* El tinte de la peana lo decide el grupo (lo pone generar.py): azul para leer con calma,
   ámbar para los apuros, pizarra para la referencia. */
.t-azul    { background: color-mix(in srgb, var(--acento) 12%, transparent); color: var(--acento); }
.t-ambar   { background: rgba(217, 119, 6, 0.14); color: #b45309; }
.t-pizarra { background: rgba(71, 85, 105, 0.12); color: #475569; }
[data-theme="dark"] .t-azul    { color: #93c5fd; }
[data-theme="dark"] .t-ambar   { background: rgba(245, 158, 11, 0.16); color: #fcd34d; }
[data-theme="dark"] .t-pizarra { background: rgba(148, 163, 184, 0.16); color: #cbd5e1; }

.guia-cuerpo { flex: 1 1 auto; min-width: 0; }
.guia h3 { margin: 0 0 .15rem; font-size: 1.02rem; letter-spacing: -.012em; color: var(--texto-fuerte); }
.guia p { margin: 0; font-size: .9rem; line-height: 1.5; color: var(--texto-suave); }
/* El «cuándo» es la línea que responde «¿esta es la mía?». En texto quieto, no en píldora:
   ocho píldoras azules parecían ocho botones desactivados. */
.guia-cuando {
  display: block; margin-top: .4rem;
  font-size: .8rem; line-height: 1.4; font-weight: 550;
  color: var(--acento-texto);   /* a 12,8 px el azul de acento se quedaba en 4,37:1 sobre el papel claro */
}
[data-theme="dark"] .guia-cuando { color: #93c5fd; }

/* El chevrón promete que la fila lleva a algún sitio; al acercarse, empuja. */
.guia-flecha {
  flex: 0 0 auto; align-self: center;
  width: 17px; height: 17px;
  color: var(--texto-tenue); opacity: .55;
  transition: transform var(--rapido) var(--curva), opacity var(--rapido) var(--curva),
              color var(--rapido) var(--curva);
}
.guia:hover, .guia:focus-visible { transform: translateY(-2px); box-shadow: var(--sombra-3), var(--vidrio-luz); }
.guia:hover h3 { color: var(--acento-texto); }
.guia:hover .guia-flecha, .guia:focus-visible .guia-flecha {
  color: var(--acento); opacity: 1; transform: translateX(3px);
}

/* ================================================================ documentación: guías */

.con-lateral {
  display: grid; gap: 18px;
  grid-template-columns: 230px minmax(0, 1fr);
  align-items: start;
}

/* El índice se queda a la vista mientras se baja: en un documento de dos mil palabras,
   perder de vista dónde estás es perder el hilo. */
.lateral { position: sticky; top: 5.2rem; align-self: start; max-height: calc(100vh - 6.5rem); overflow-y: auto; }
.lateral h4 { margin: 0 0 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--texto-tenue); }
.lateral ul { margin: 0; padding: 0; list-style: none; }
.lateral li { margin-bottom: 2px; }
.lateral a {
  display: block; padding: 6px 10px; border-radius: 10px;
  font-size: 14px; text-decoration: none; color: var(--texto-suave);
  transition: background-color var(--rapido) var(--curva), color var(--rapido) var(--curva);
}
.lateral a:hover { background: rgba(37, 99, 235, 0.1); color: var(--acento-texto); }
[data-theme="dark"] .lateral a:hover { background: rgba(59, 130, 246, 0.22); color: #dbeafe; }
/* La guía que estás leyendo, marcada. Nueve enlaces iguales no dicen en cuál de los nueve
   estás, que es la mitad de para lo que sirve un índice al lado. */
.lateral a[aria-current="page"] {
  color: var(--texto-fuerte); font-weight: 650;
  background: color-mix(in srgb, var(--acento) 12%, transparent);
}

/* El índice de la PROPIA guía, debajo del de guías. Una guía de dos mil palabras tiene diez
   apartados y hasta ahora la única forma de saber cuáles era bajando hasta el final.
   Va con una guía a la izquierda —la línea vertical— para que se lea como lo que es: el
   mapa de esta página, no otra lista de enlaces a otras partes. */
.lateral h4 + .doc-indice { margin-top: 0; }
.doc-indice { border-left: 2px solid var(--borde); padding-left: .35rem; }
.doc-indice a {
  font-size: 13px; line-height: 1.35; padding: .34rem .55rem;
  color: var(--texto-tenue);
}
.doc-indice a.activo {
  color: var(--acento-texto); font-weight: 600;
  background: color-mix(in srgb, var(--acento) 10%, transparent);
}
[data-theme="dark"] .doc-indice a.activo { color: #93c5fd; }

/* Al terminar una guía, la siguiente. Las guías tienen un orden pensado y el final de una
   era un callejón sin salida: o volvías al índice o cerrabas la pestaña. */
.doc-pasos {
  display: flex; flex-wrap: wrap; gap: 12px;
  margin-top: 2.4rem; padding-top: 1.4rem; border-top: 1px solid var(--borde);
}
.doc-paso {
  flex: 1 1 12rem; min-width: 0;
  padding: .7rem .9rem; border-radius: 14px;
  border: 1px solid var(--borde); text-decoration: none;
  transition: transform var(--rapido) var(--curva), border-color var(--rapido) var(--curva),
              box-shadow var(--rapido) var(--curva);
}
.doc-paso:hover {
  text-decoration: none; transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--acento) 40%, transparent);
  box-shadow: var(--sombra-2);
}
.doc-paso span { display: block; font-size: .74rem; color: var(--texto-tenue); }
.doc-paso b { display: block; margin-top: .15rem; color: var(--texto-fuerte); font-size: .95rem; }
.doc-paso.siguiente { text-align: right; }

/* La guía en sí se lee como un documento, no como una interfaz. La medida de línea es LO
   que decide si se lee o se abandona: por encima de ~75 caracteres el ojo pierde el
   renglón al volver, y en una guía técnica eso es cerrar la pestaña. */
.documento { padding: 26px 30px; --medida: 72ch; }
.documento p, .documento li { max-width: var(--medida); line-height: 1.7; color: var(--texto-suave); }
.documento h1 { font-size: clamp(1.9rem, 1.1rem + 2.6vw, 2.9rem); line-height: 1.06; letter-spacing: -.035em; margin: 0 0 14px; }
.documento h2 {
  font-size: clamp(1.35rem, 1rem + 1.4vw, 1.9rem); line-height: 1.15; letter-spacing: -.025em;
  margin: 2.4em 0 8px; padding-top: .9em; border-top: 1px solid var(--borde);
}
/* El primer h2 no lleva raya: una línea justo debajo del título no separa nada. */
.documento h2:first-of-type { border-top: 0; padding-top: 0; margin-top: 1.4em; }
.documento h3 { font-size: 16.5px; margin: 1.9em 0 6px; letter-spacing: -.015em; }
.documento h4 { font-size: 15px; margin: 18px 0 6px; color: var(--acento-texto); }
[data-theme="dark"] .documento h4 { color: #93c5fd; }
.documento li { margin-bottom: 6px; }
.documento li strong, .documento p strong { color: var(--texto-fuerte); }
.documento ul, .documento ol { padding-left: 22px; }
.documento blockquote {
  margin: 14px 0; padding: 10px 16px;
  border-left: 3px solid var(--acento);
  background: var(--acento-suave);
  border-radius: 0 10px 10px 0;
  color: var(--texto-suave);
}
.documento hr { border: 0; border-top: 1px solid var(--borde); margin: 26px 0; }
/* El código no se parte por estética: si no cabe, se desliza él. */
.documento pre { max-width: none; }

/* Los enlaces del cuerpo se subrayan, con el subrayado despegado de las colas de las
   letras: un enlace que solo cambia de color no se distingue si eres daltónico. */
.documento a {
  text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: .18em;
  text-decoration-color: color-mix(in srgb, var(--acento) 45%, transparent);
  transition: text-decoration-color var(--rapido) var(--curva);
}
.documento a:hover { text-decoration-color: var(--acento); }

/* Ancla junto a cada título: la forma de mandarle a alguien EL punto exacto de la guía.
   Aparece al pasar por encima y no estorba al leer. */
.documento h2, .documento h3 { position: relative; }
.documento h2:hover::after, .documento h3:hover::after {
  content: '#'; position: absolute; left: -.85em; top: 0;
  color: color-mix(in srgb, var(--acento) 55%, transparent); font-weight: 400;
}

/* ================================================================ documentación: el grafo
   Dos cosas: la BANDA que lo anuncia en la portada de la documentación y la PÁGINA donde se
   dibuja. La banda va arriba del todo y con el botón morado porque es lo único de esta
   sección que no se lee, se mira: quien llega buscando «cómo funciona esto por dentro» no
   está buscando una guía, y entre ocho tarjetas de texto iguales no lo iba a encontrar. */

.doc-grafo {
  display: flex; align-items: center; gap: clamp(1rem, 3vw, 2rem);
  padding: clamp(1.1rem, 2.6vw, 1.6rem) clamp(1.2rem, 3vw, 1.8rem);
  margin: 0 0 clamp(2rem, 4.5vw, 2.8rem);
  border-radius: var(--radio-g);
  /* El velo morado es lo que hace que la banda no parezca una tarjeta más: el cristal es el
     mismo de siempre y el color entra por encima, no sustituyéndolo. */
  background-image: radial-gradient(28rem 14rem at 8% 0%, var(--morado-suave), transparent 72%);
}
.doc-grafo {
  transition: transform var(--medio) var(--curva), box-shadow var(--medio) var(--curva);
}
.doc-grafo:hover { transform: translateY(-2px); box-shadow: var(--sombra-3), var(--vidrio-luz); }
.doc-grafo-cuerpo { flex: 1 1 auto; min-width: 0; }
.doc-grafo h2 { font-size: 1.28rem; letter-spacing: -.015em; margin: 0 0 .3rem; }
.doc-grafo p { margin: 0; font-size: .95rem; line-height: 1.55; color: var(--texto-suave); }
.doc-grafo .doc-grafo-cifras {
  margin-top: .5rem; font-size: .82rem; font-weight: 550; color: var(--morado-texto);
}
.doc-grafo-ico {
  flex: 0 0 auto; width: 52px; height: 52px;
  display: grid; place-items: center; border-radius: 15px;
  background: var(--morado-suave); color: var(--morado-texto);
}
.doc-grafo-ico svg { width: 27px; height: 27px; }
.doc-grafo .btn-morado { flex: 0 0 auto; }

/* --- la página. El lienzo manda: todo lo demás flota encima de él. */
.grafo-marco { position: relative; padding: 0; overflow: hidden; }
.grafo-barra {
  display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
  padding: .8rem .9rem; border-bottom: 1px solid var(--borde);
}
.grafo-buscar {
  display: flex; align-items: center; gap: .55rem;
  flex: 1 1 16rem; min-width: 0;
  padding: .1rem .3rem .1rem .9rem; border-radius: 999px;
  border: 1px solid var(--borde);
  background: color-mix(in srgb, var(--cristal) 70%, transparent);
}
.grafo-buscar:focus-within { box-shadow: 0 0 0 3px var(--morado-suave); }
.grafo-buscar svg { width: 16px; height: 16px; flex: 0 0 auto; color: var(--texto-tenue); }
.grafo-buscar input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent;
  color: inherit; font: inherit; padding: .55rem .4rem .55rem 0;
}
.grafo-buscar input::placeholder { color: var(--texto-tenue); }
.grafo-contador { font-size: .82rem; color: var(--texto-tenue); font-variant-numeric: tabular-nums; }

.grafo-lienzo {
  position: relative;
  height: clamp(26rem, 68vh, 46rem);
  /* Sin esto, arrastrar el grafo en el móvil arrastra la PÁGINA: el gesto se lo queda el
     navegador y el mapa no se mueve. */
  touch-action: none;
}
.grafo-lienzo canvas { display: block; width: 100%; height: 100%; outline: 0; }
.grafo-lienzo canvas:focus-visible { box-shadow: inset 0 0 0 2px var(--morado); }

.grafo-estado {
  position: absolute; inset: 0; display: grid; place-items: center; text-align: center;
  padding: 2rem; color: var(--texto-suave); font-size: .95rem;
}

.grafo-globo {
  position: absolute; top: 0; left: 0; pointer-events: none; z-index: 3;
  max-width: 20rem; padding: .5rem .7rem; border-radius: 12px;
  background: var(--cristal-solido, rgba(15, 23, 42, .92)); color: #f1f5f9;
  box-shadow: var(--sombra-2);
  font-size: .8rem; line-height: 1.35;
}
.grafo-globo b { display: block; font-weight: 650; overflow-wrap: anywhere; }
.grafo-globo span { display: block; margin-top: .15rem; opacity: .78; overflow-wrap: anywhere; }

/* La ficha de la pieza elegida. Flota DENTRO del lienzo y no debajo: mirar un grafo es
   señalar y comparar, y si la explicación sale fuera de la vista hay que apartar los ojos
   del sitio que acabas de señalar. */
.grafo-ficha {
  position: absolute; top: .8rem; right: .8rem; z-index: 4;
  width: min(21rem, calc(100% - 1.6rem)); max-height: calc(100% - 1.6rem);
  overflow-y: auto; padding: .9rem 1rem 1rem;
  border-radius: 16px;
  background:
    linear-gradient(var(--cristal-denso), var(--cristal-denso)) padding-box,
    var(--vidrio-canto) border-box;
  border: 1px solid transparent;
  -webkit-backdrop-filter: var(--vidrio-filtro);
  backdrop-filter: var(--vidrio-filtro);
  box-shadow: var(--sombra-3), var(--vidrio-luz);
}
.grafo-ficha h3 { margin: 0 1.4rem .35rem 0; font-size: 1rem; overflow-wrap: anywhere; }
.grafo-meta { margin: 0 0 .3rem; font-size: .8rem; color: var(--texto-suave); }
.grafo-ruta {
  margin: 0 0 .5rem; font-size: .78rem; color: var(--texto-tenue);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  overflow-wrap: anywhere;
}
.grafo-cifra { margin: 0 0 .6rem; font-size: .8rem; font-weight: 600; color: var(--morado-texto); }
.grafo-punto {
  display: inline-block; width: .55rem; height: .55rem; border-radius: 50%;
  margin-right: .35rem; vertical-align: baseline;
}
.grafo-rel { margin-top: .7rem; }
.grafo-rel h4 {
  margin: 0 0 .3rem; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--texto-tenue);
}
.grafo-vecinos { display: flex; flex-wrap: wrap; gap: .3rem; }
.grafo-vecinos button {
  max-width: 100%; padding: .22rem .55rem; border-radius: 999px;
  border: 1px solid var(--borde); background: transparent;
  color: var(--texto-suave); font: inherit; font-size: .78rem; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color var(--rapido) var(--curva), border-color var(--rapido) var(--curva);
}
.grafo-vecinos button:hover { color: var(--morado-texto); border-color: var(--morado); }
.grafo-mas { align-self: center; font-size: .75rem; color: var(--texto-tenue); }
.grafo-cerrar {
  position: absolute; top: .5rem; right: .55rem;
  width: 1.7rem; height: 1.7rem; border-radius: 50%;
  border: 0; background: transparent; color: var(--texto-tenue);
  font-size: 1.2rem; line-height: 1; cursor: pointer;
}
.grafo-cerrar:hover { color: var(--texto-fuerte); }

.grafo-leyenda {
  display: flex; flex-wrap: wrap; gap: .4rem;
  padding: .8rem .9rem; border-top: 1px solid var(--borde);
}
.grafo-chip {
  display: inline-flex; align-items: center; gap: .1rem;
  padding: .25rem .65rem; border-radius: 999px;
  border: 1px solid var(--borde); background: transparent;
  color: var(--texto-suave); font: inherit; font-size: .78rem; cursor: pointer;
  transition: color var(--rapido) var(--curva), border-color var(--rapido) var(--curva),
              background-color var(--rapido) var(--curva);
}
.grafo-chip:hover { color: var(--texto-fuerte); border-color: color-mix(in srgb, var(--texto-fuerte) 20%, transparent); }
.grafo-chip.activo { background: var(--morado-suave); border-color: var(--morado); color: var(--morado-texto); }

.grafo-ayuda { margin: 1rem 0 0; font-size: .85rem; color: var(--texto-tenue); }
.grafo-numeros { display: flex; flex-wrap: wrap; gap: 1.6rem; margin: 1.2rem 0 0; }
.grafo-numeros div { min-width: 6rem; }
.grafo-numeros b {
  display: block; font-size: 1.5rem; letter-spacing: -.02em; color: var(--texto-fuerte);
  font-variant-numeric: tabular-nums;
}
.grafo-numeros span { font-size: .82rem; color: var(--texto-tenue); }

/* ================================================================ pie */

footer.pie {
  border-top: 1px solid var(--borde);
  padding-top: 22px;
  text-align: center;
  color: var(--texto-tenue);
  font-size: 14px;
}
.pie-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 18px; margin-bottom: .5rem; }
.pie-links a { color: var(--texto-tenue); text-decoration: none; }
.pie-links a:hover { color: var(--texto-fuerte); text-decoration: underline; }
footer.pie p { margin: 0; }
/* El mando de las animaciones: el mismo botón que lleva el pie del portal (ver la cabecera que
   escribe `generar.py`, «CUÁNTO SE MUEVE LA WEB»). */
footer.pie p.pie-movimiento-fila { margin: 0 0 .5rem; }
.pie-movimiento {
  font: inherit; font-size: .78rem; color: var(--texto-tenue); background: none; cursor: pointer;
  border: 1px solid var(--borde, rgba(148, 163, 184, .35)); border-radius: 999px; padding: .3rem .8rem;
}
.pie-movimiento:hover { color: var(--texto-fuerte); }
@media (pointer: coarse) { .pie-movimiento { min-height: 44px; padding-inline: 1.1rem; } }
/* El aviso de una vez para quien su equipo pide menos animaciones (ver `generar.py` y, para el porqué,
   la cabecera de `base.html` del portal). Abajo a la izquierda: a la derecha vive la taza. La «voz» es
   la región viva que lo anuncia a un lector de pantalla: siempre en la página, vacía y sin verse. */
.aviso-movimiento-voz {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
/* `z-index` por ENCIMA de la pantalla de arranque del Inicio (1000): a quien su equipo pide menos
   movimiento la web le llega completa —cortina incluida—, y con el aviso debajo se pasaba dos segundos
   anunciado pero tapado, justo lo primero que esa persona necesita poder pulsar. */
.aviso-movimiento {
  position: fixed; z-index: 1001; left: clamp(1rem, 2.5vw, 2rem); bottom: clamp(1rem, 2.5vw, 2rem);
  max-width: 27rem; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .8rem;
  /* A la derecha, el hueco de la ×, que va en la esquina: en la fila, en un teléfono caía a una línea
     propia y el aviso medía 145 px de alto, con el de cookies debajo. */
  padding: .8rem 2.9rem .8rem 1rem; border-radius: var(--radio-g); border: 1px solid var(--borde);
  background: var(--cristal); -webkit-backdrop-filter: var(--vidrio-filtro); backdrop-filter: var(--vidrio-filtro);
  box-shadow: var(--sombra-2); font-size: .88rem; color: var(--texto-fuerte);
}
.aviso-movimiento[hidden] { display: none; }
.aviso-movimiento span { flex: 1 1 11rem; }
.aviso-movimiento-si {
  font: inherit; font-weight: 600; font-size: .85rem; cursor: pointer; color: #fff; border: 0;
  border-radius: 999px; padding: .45rem 1rem; background: linear-gradient(115deg, #0e7490, #2b50e6 55%, #7a3df0);
}
.aviso-movimiento-no {
  font: inherit; font-size: 1.2rem; line-height: 1; cursor: pointer; color: var(--texto-tenue); background: none; border: 0;
  padding: .2rem .5rem; border-radius: 8px; position: absolute; top: .25rem; right: .25rem;
}
.aviso-movimiento-no:hover { color: var(--texto-fuerte); }
@media (pointer: coarse) { .aviso-movimiento-si, .aviso-movimiento-no { min-height: 44px; min-width: 44px; } }
@media (max-width: 430px) { .aviso-movimiento { left: .75rem; right: .75rem; bottom: .75rem; max-width: none; } }

/* En un teléfono, el pie es la segunda navegación —y en una guía larga, la que se tiene a
   mano al terminar de leer—. Sus enlaces miden 17 px de alto: se apunta a uno y se da al de
   al lado. Se les da alto y aire de verdad, y la MISMA regla que en el portal, que si no las
   dos mitades de la web se pulsan distinto. */
@media (pointer: coarse) {
  .pie-links { gap: .15rem .35rem; }
  .pie-links a {
    display: inline-flex; align-items: center;
    min-height: 44px; padding-inline: .5rem; border-radius: 10px;
  }
}

/* ================================================================ movimiento
   Pocas piezas y bien cronometradas; todas se apagan con `prefers-reduced-motion`. Y NADA
   de esto oculta contenido si el script no corre: la clase que esconde («armada») la pone
   él, nunca el HTML. Ocultar desde el CSS y confiar en JavaScript para revelar es como se
   acaba con una web en blanco. */

/* --- 1. Aparecer al llegar. Solo en vertical y con poco recorrido: los deslizamientos
   laterales asomaban por los bordes de la pantalla del móvil. El ritmo lo pone el
   escalonado (lo cronometra el script), no el recorrido. */
.aparece { transition: opacity .55s var(--curva), transform .55s var(--curva); }
.aparece.armada { opacity: 0; transform: translateY(14px); }
.aparece.armada.visible { opacity: 1; transform: none; }

/* --- 2. El reflejo que sigue al puntero. Es LO que hace que una superficie parezca
   vidrio y no una caja con desenfoque: la luz se mueve con quien mira. Las coordenadas
   las pone el script en --mx/--my; sin él, se queda centrado y no pasa nada. */
.guia, .version, .doc-grafo {
  --mx: 50%; --my: 0%;
  isolation: isolate;
}
.version, .doc-grafo { position: relative; }
.guia::before, .version::before, .doc-grafo::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  /* En claro, un velo del color de marca: sobre fondo blanco el azul ACLARA y el texto,
     que es oscuro, se sigue leyendo igual de bien. */
  background: radial-gradient(16rem 16rem at var(--mx) var(--my),
              color-mix(in srgb, var(--acento) 13%, transparent), transparent 70%);
  opacity: 0; transition: opacity .35s var(--curva);
}
/* En OSCURO no se tiñe de azul: se ilumina con blanco.
   Estaba al 22 % de azul de marca, y sobre un fondo ya oscuro eso no ilumina, ENSUCIA: la
   tarjeta se volvía azul marino y el texto gris de la descripción se quedaba encima con la
   mitad del contraste. En un cristal, pasar el ratón por encima tiene que hacer que la
   superficie coja luz —como cuando le da un reflejo— y eso es blanco, no color. */
[data-theme="dark"] .guia::before, [data-theme="dark"] .version::before,
[data-theme="dark"] .doc-grafo::before {
  background: radial-gradient(16rem 16rem at var(--mx) var(--my),
              rgba(255, 255, 255, .07), transparent 70%);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .guia::before,
  :root:not([data-theme="light"]) .version::before,
  :root:not([data-theme="light"]) .doc-grafo::before {
    background: radial-gradient(16rem 16rem at var(--mx) var(--my),
                rgba(255, 255, 255, .07), transparent 70%);
  }
}
.guia:hover::before, .version:hover::before, .doc-grafo:hover::before { opacity: 1; }

/* ================================================================ estrecho */

@media (max-width: 900px) {
  /* El índice de la guía, de columna pegada a TIRA horizontal: en un teléfono robaba 659
     de los 812 px de pantalla antes de la primera línea de texto. */
  .con-lateral { grid-template-columns: minmax(0, 1fr); }
  .lateral {
    position: static; max-height: none; overflow: visible;
    padding: .8rem .9rem;
  }
  .lateral h4 { margin: 0 0 .55rem; font-size: .72rem; letter-spacing: .06em; opacity: .7; }
  .lateral ul {
    display: flex; gap: .45rem; margin: 0; padding: 0 .2rem .3rem;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none;                 /* la barra estorba en una tira de fichas */
    scroll-snap-type: x proximity;
  }
  .lateral ul::-webkit-scrollbar { display: none; }
  .lateral li { flex: 0 0 auto; scroll-snap-align: start; margin: 0; }
  .lateral a {
    white-space: nowrap; padding: .42rem .8rem; border-radius: 999px;
    border: 1px solid var(--borde); font-size: .85rem;
  }
  .documento { padding-left: 1rem; padding-right: 1rem; }
  .documento h2 { margin-top: 1.8em; }
}

@media (max-width: 700px) {
  /* Las tablas de las guías, a lo ancho del teléfono. La alternativa era un scroll lateral
     que nada anunciaba: la columna derecha quedaba cortada a media palabra y parecía un
     fallo, no un gesto.

     `table-layout: fixed` porque con el reparto automático las columnas se las reparte el
     contenido: una ruta larga engordaba la suya, aplastaba la primera a 60 px y las
     palabras salían guillotinadas letra a letra. Fijo, cada columna sabe lo que mide, el
     texto envuelve por palabras y los guiones (`hyphens`, el HTML ya declara lang="es")
     parten con criterio lo que no quepa. */
  table { min-width: 0; width: 100%; table-layout: fixed; }
  td, th { overflow-wrap: break-word; hyphens: auto; }
  td:first-child { white-space: normal; width: 28%; }

  /* La banda del grafo, en columna: en 375 px el icono, el texto y el botón en fila dejaban
     al botón un renglón de 90 px y al título dos palabras por línea. */
  .doc-grafo { flex-wrap: wrap; }
  .doc-grafo-cuerpo { flex: 1 1 12rem; }
  .doc-grafo .btn-morado { width: 100%; }

  /* La ficha, de tarjeta flotante a cajón de abajo: flotando tapaba justo la mitad del
     grafo que acabas de tocar. */
  .grafo-ficha {
    top: auto; left: .6rem; right: .6rem; bottom: .6rem;
    width: auto; max-height: 52%;
  }
  .grafo-lienzo { height: clamp(22rem, 62vh, 34rem); }
  .grafo-globo { max-width: 14rem; }

  /* La barra de arriba se partía en TRES renglones (buscador, cuenta, botón) y se comía
     164 px de teléfono antes del grafo. El buscador ocupa su fila y la cuenta comparte la
     siguiente con el botón. */
  .grafo-buscar { flex: 1 1 100%; }
  .grafo-contador { flex: 1 1 auto; font-size: .74rem; }
  .grafo-barra .btn-secondary { padding: 8px 14px; font-size: 13.5px; }

  /* Y la leyenda, de doce píldoras apiladas (347 px, casi media pantalla) a una TIRA que se
     desliza, como el índice de las guías. Se ve que hay más porque la última asoma cortada
     por el borde, que es lo que invita a empujarla. */
  .grafo-leyenda {
    flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; scroll-snap-type: x proximity;
  }
  .grafo-leyenda::-webkit-scrollbar { display: none; }
  .grafo-chip { flex: 0 0 auto; scroll-snap-align: start; }
}

@media (max-width: 560px) {
  .panel, .version { padding: 16px; }
  .hero { padding-top: 2.2rem; }
  .hero nav.principal .btn-primary, .hero nav.principal .btn-secondary { flex: 1 1 auto; }
  /* El índice de guías, pegado y al grano: en 375 px cada aire de más es medio codo de
     scroll. Las filas se estrechan, la rejilla junta y la peana un punto más pequeña. */
  .rejilla.dos { gap: 12px; }
  .guia { padding: .9rem .95rem; gap: .8rem; }
  .guia-ico { width: 38px; height: 38px; border-radius: 11px; }
  .guia-ico svg { width: 20px; height: 20px; }
  .doc-buscador input { padding-block: .72rem; }
  /* Un `código` EN LÍNEA largo —una ruta, una orden— lleva `white-space: nowrap`, y en una
     pantalla estrecha eso empuja la página fuera. Se parte antes que desbordar; los
     bloques (`pre code`) se quedan: ahí los saltos SÍ importan y el `pre` ya se desliza. */
  code { white-space: normal; overflow-wrap: anywhere; }
  pre code { white-space: pre; overflow-wrap: normal; }
}

/* ================================================================ quietud
   Una sola regla apaga TODO el movimiento de la hoja. Va la última para que nada se le
   escape por especificidad: hay gente a la que una animación de entrada le provoca mareo
   de verdad, y ahí lo correcto es que no exista, no que sea más corta. */
/* [movimiento: reducido] */
:where(html[data-movimiento="reducido"]) *, :where(html[data-movimiento="reducido"]) *::before, :where(html[data-movimiento="reducido"]) *::after {
  animation-duration: .01ms !important;
  animation-iteration-count: 1 !important;
  scroll-behavior: auto !important;
}
:where(html[data-movimiento="reducido"]) .aparece.armada { opacity: 1 !important; transform: none !important; }
:where(html[data-movimiento="reducido"]) .guia::before, :where(html[data-movimiento="reducido"]) .version::before, :where(html[data-movimiento="reducido"]) .doc-grafo::before { display: none; }
:where(html[data-movimiento="reducido"]) .guia:hover, :where(html[data-movimiento="reducido"]) .doc-grafo:hover, :where(html[data-movimiento="reducido"]) .btn-primary:hover, :where(html[data-movimiento="reducido"]) .btn-secondary:hover,
:where(html[data-movimiento="reducido"]) .btn-morado:hover, :where(html[data-movimiento="reducido"]) .nav-cta:active,
:where(html[data-movimiento="reducido"]) .btn-primary:active, :where(html[data-movimiento="reducido"]) .btn-secondary:active, :where(html[data-movimiento="reducido"]) .btn-morado:active { transform: none !important; }

/* ---------------------------------------------------------------- saltar al contenido
   Escondido hasta que se le da el foco con el tabulador: es el primer enlace de la página y
   la única forma de no recorrer la barra entera —y en la documentación, el índice— en cada
   página cuando se navega sin ratón. */
.saltar {
  position: absolute; left: .75rem; top: 0; z-index: 100;
  padding: .55rem 1rem; border-radius: 0 0 var(--radio) var(--radio);
  background: var(--azul); color: #fff; font-weight: 600; text-decoration: none;
  /* Fuera de la pantalla POR TRANSFORMACIÓN y no con `top` negativo: con `top` había que
     acertar un número mayor que su alto, y como el alto depende del tipo de letra y del
     idioma, en algún tamaño asomaba una franja azul en la esquina. Con -200% nunca asoma. */
  transform: translateY(-200%);
  transition: transform .18s var(--curva, ease);
}
.saltar:focus { transform: translateY(0); }

/* ---------------------------------------------------------------- impresión
   La documentación se imprime y se guarda en PDF: es una guía. En papel sobran la barra, el
   pie, el buscador y los fondos de cristal —que salen como manchas grises—, y hacen falta
   las direcciones de los enlaces, que si no se pierden. */
/* ---------------------------------------------------------------- la taza de Ko-fi
   Flota en la esquina de todas las páginas generadas para que se pueda apoyar el proyecto sin
   ir a buscar el enlace al final del pie. El rojo es el de Ko-fi bajado hasta 4,5:1 con texto
   blanco encima: el suyo (#FF5E5B) se queda en 2,9 y no se lee. */
.kofi-flota {
  position: fixed; right: clamp(1rem, 2.5vw, 2rem); bottom: clamp(1rem, 2.5vw, 2rem); z-index: 60;
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .7rem 1.05rem; border-radius: 999px; text-decoration: none;
  font-weight: 600; font-size: .95rem; color: #fff;
  background: #d1413f; border: 1px solid #b5322f; box-shadow: var(--sombra-2, 0 10px 30px -12px rgba(0,0,0,.4));
}
.kofi-flota:hover { background: #b5322f; }
.kofi-flota svg { width: 1.3em; height: 1.3em; }
@media (max-width: 60rem) {
  .kofi-flota-txt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .kofi-flota { padding: .7rem .8rem; }
}

/* ---------------------------------------------------------------- lo que se puede pulsar
   Dos objetivos de la barra se quedaban en 31 px en el móvil —la bandera del idioma y el
   nombre del producto—, por debajo de los 44 con los que se acierta con el pulgar sin mirar.
   Se agranda el ÁREA, no el dibujo: un rectángulo invisible centrado sobre cada uno. Hacerlos
   más grandes de verdad descuadraría la barra, y lo que falla aquí no es cómo se ven, es
   dónde hay que dar. Solo en pantallas táctiles: con ratón, 31 px sobran. */
@media (pointer: coarse) {
  .marca, .idioma-btn, .version-ancla { position: relative; }
  .marca::after, .idioma-btn::after, .version-ancla::after {
    content: ''; position: absolute; left: 50%; top: 50%;
    width: max(100%, 44px); height: max(100%, 44px);
    transform: translate(-50%, -50%);
  }

  /* Y el resto de lo que se pulsa, que se había quedado fuera.
     Medido en un teléfono de 375 px: el botón de claro/oscuro daba **18 × 34 px** de zona
     pulsable —dieciocho—, el de menú 38 y «Entrar» y «Comprar» unos 35 de alto. Son justo
     los que más se pulsan: el menú es la ÚNICA puerta a las secciones en pantalla estrecha, y
     «Comprar» es la acción que paga la web.
     Con `::after` y no con `min-height` porque así la zona crece SIN mover el diseño: la
     barra sigue midiendo lo mismo y el dedo acierta igual. */
  .tema-btn, .nav-abrir, .nav-cta, .nav-entrar { position: relative; }
  .tema-btn::after, .nav-abrir::after, .nav-cta::after, .nav-entrar::after {
    content: ''; position: absolute; left: 50%; top: 50%;
    width: max(100%, 44px); height: max(100%, 44px);
    transform: translate(-50%, -50%);
  }
  /* Estos dos ya usan `::after` para su icono —el «+» de un desplegable, la flecha de un
     botón—, así que se les da el alto de verdad: son de bloque y crecer no descoloca nada. */
  summary, .btn { min-height: 44px; }
}

@media print {
  /* Fuera todo lo que en papel no se puede pulsar. `.lateral` es el índice de guías: son
     diecinueve enlaces que ocupan un tercio de CADA hoja y no llevan a ninguna parte cuando
     lo tienes impreso. Igual la barra de progreso y el botón flotante de la comunidad. */
  .barra, .pie, .saltar, .motas, .lp-cta, .doc-nav, .buscador, nav,
  .lateral, .barra-progreso, .comunidad, .comunidad-btn, .kofi-flota { display: none !important; }
  /* Y el contenido recupera el ancho que dejaba el índice: `.con-lateral` es la rejilla de
     dos columnas de las guías; sin deshacerla, el texto se quedaba en su columna estrecha y
     la tabla salía cortada por el borde de la hoja. */
  .con-lateral { display: block !important; }
  .con-lateral > article { max-width: none !important; width: auto !important; }
  * { background: #fff !important; color: #000 !important; box-shadow: none !important; }
  a { text-decoration: underline; }
  main a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .85em; word-break: break-all; }
  details { display: block !important; }
  details > summary { display: none; }
  h1, h2, h3 { page-break-after: avoid; }
  pre, table, figure { page-break-inside: avoid; }
}

/* ================================================================ pulido
   Lo de aquí abajo no añade ninguna función: hace que lo que ya hay se note bien hecho. Todo
   degrada solo —un navegador que no entienda una regla se queda como estaba— y todo se apaga
   con `prefers-reduced-motion`, que en esta web no es una casilla: hay gente a la que el
   movimiento le sienta mal de verdad. */

/* ── Transición entre páginas ──────────────────────────────────────────────────
   Ir de la portada a «Comprar» o a la documentación deja de ser un corte en seco: el
   navegador funde una página con la siguiente. Son dos líneas y las lleva él en su hilo de
   composición; donde no exista, la navegación es la de siempre y no falta nada.

   La barra y el pie se declaran con nombre propio para que NO parpadeen: son los mismos en
   las dos páginas, así que el navegador los mantiene quietos y funde solo lo que cambia. Eso
   es lo que hace que parezca una aplicación y no una web recargándose. */
@view-transition { navigation: auto; }

/* [movimiento: completo] */
:where(html:not([data-movimiento="reducido"])) .barra { view-transition-name: barra; }
:where(html:not([data-movimiento="reducido"])) .pie { view-transition-name: pie; }
:where(html:not([data-movimiento="reducido"]))::view-transition-old(root) { animation-duration: .18s; }
:where(html:not([data-movimiento="reducido"]))::view-transition-new(root) { animation-duration: .26s; }
/* [movimiento: reducido] */
/* Sin cruce: se cambia y ya. */
:where(html[data-movimiento="reducido"])::view-transition-group(*), :where(html[data-movimiento="reducido"])::view-transition-old(*), :where(html[data-movimiento="reducido"])::view-transition-new(*) {
  animation: none !important;
}

/* ── Tipografía ────────────────────────────────────────────────────────────────
   `balance` reparte las líneas de un titular para que no quede una palabra sola abajo, y
   `pretty` hace lo propio con el final de cada párrafo. Es lo que separa un texto compuesto
   de un texto volcado, y no cuesta ni un byte de JavaScript. */
h1, h2, h3, .lp-h2, .lp-ficha-txt h3, summary { text-wrap: balance; }
p, li, figcaption, .lp-sub { text-wrap: pretty; }

/* Los enlaces del cuerpo: subrayado fino y separado de las letras, como en un libro. El de
   por defecto corta las jotas y las ges por la mitad. */
main p a:not(.btn):not(.btn-primary):not(.btn-secondary),
main li a:not(.btn):not(.btn-primary):not(.btn-secondary) {
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
  transition: text-decoration-color .18s var(--curva, ease);
}
main p a:hover, main li a:hover { text-decoration-color: currentColor; }

/* Las casillas y los deslizadores, del color de la casa en vez del azul del sistema. */
:root { accent-color: var(--azul); }

/* ── Al pulsar ─────────────────────────────────────────────────────────────────
   Un botón que no se hunde al pulsarlo parece una imagen. Dos píxeles bastan, y en táctil es
   la única confirmación que hay de que el dedo ha acertado. */
/* [movimiento: completo] */
:where(html:not([data-movimiento="reducido"])) .btn:active, :where(html:not([data-movimiento="reducido"])) .btn-primary:active, :where(html:not([data-movimiento="reducido"])) .btn-secondary:active {
  transform: translateY(1px) scale(.985);
}

/* ── El foco ───────────────────────────────────────────────────────────────────
   Un anillo del color de la marca y con aire alrededor: se ve sobre el cristal, sobre el
   azul y sobre las capturas, que es donde los anillos de fábrica se pierden. */
:where(a, button, summary, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--azul);
  outline-offset: 3px;
  border-radius: var(--radio, 10px);
}

/* ── Selección de texto ────────────────────────────────────────────────────────
   Del azul de la casa, no del azul del navegador. */
::selection { background: color-mix(in srgb, var(--azul) 28%, transparent); }

/* Las capturas dentro de una guía. A su tamaño y centradas, NUNCA estiradas: con
   `width: 100%` una captura de un cuadro de acceso —que es pequeño— se ampliaba hasta el
   ancho de la columna, salía borrosa y ocupaba una pantalla entera para enseñar cuatro
   campos. El tope de 560 px es el ancho al que una captura de consola se sigue leyendo sin
   comerse el texto que la explica. El fondo oscuro es el de las propias capturas: sin él, en
   tema claro, la imagen parece flotar en un agujero. */
.captura-guia {
  display: block;
  max-width: min(100%, 560px);
  height: auto;
  margin: 1.4rem auto;
  border: 1px solid var(--borde, rgba(148, 163, 184, .3));
  border-radius: 14px;
  background: #0f172a;
  box-shadow: 0 6px 24px rgba(2, 6, 23, .12);
}


/* ================================================================ el héroe con escena
   Las páginas con nombre propio (el mapa, Novedades, Descargar e instalar, Documentación) dejan
   de abrir todas con el mismo bloque centrado: el texto va a un lado y, al otro, la escena
   ilustrada de la página (`vinetas.css` + `app/templates/_vinetas.html`), que se construye una
   vez al cargar. Por debajo de 900 px el héroe vuelve a ser una columna y el dibujo va debajo. */
.hero.con-escena .vineta { margin: clamp(1.6rem, 4vw, 2.4rem) auto 0; max-width: 34rem; text-align: left; }
@media (min-width: 900px) {
  .hero.con-escena {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center;
    column-gap: clamp(2rem, 5vw, 5rem); text-align: left;
    --medio-heroe: min(36rem, 50vw - 1.25rem); margin-inline: calc(50% - var(--medio-heroe));
  }
  .hero.con-escena h1 { font-size: clamp(2.4rem, 1.2rem + 3.6vw, 4.2rem); line-height: 1.02; letter-spacing: -.04em; }
  .hero.con-escena .entradilla { margin: 0; max-width: 46ch; }
  .hero.con-escena nav.principal { justify-content: flex-start; }
  .hero.con-escena .vineta { margin: 0; max-width: none; }
}
/* La entrada escalonada del héroe iba sobre sus hijos directos, y ahora el texto va envuelto. */
.hero > .hero-texto > .chip { animation: asomar .5s var(--curva) both; }
.hero > .hero-texto > h1 { animation: asomar .55s var(--curva) .07s both; }
.hero > .hero-texto > .entradilla { animation: asomar .55s var(--curva) .14s both; }
.hero > .hero-texto > nav.principal { animation: asomar .55s var(--curva) .21s both; }
.hero > .vineta { animation: vineta-construir 2.4s cubic-bezier(.3, .6, .25, 1) .3s both, asomar .7s var(--curva) .2s both; }
/* [movimiento: reducido] */
:where(html[data-movimiento="reducido"]) .hero > .hero-texto > *, :where(html[data-movimiento="reducido"]) .hero > .vineta { animation: none !important; }

/* En pantalla estrecha el botón queda a la IZQUIERDA de la barra, y un menú anclado por su
   derecha se sale de la pantalla: medido en un móvil de 375 px, la lista empezaba en x=-12 y
   no había forma de elegir idioma. Anclado por la izquierda cabe, y en pantalla ancha no
   cambia nada porque esta regla solo existe aquí.

   Va al FINAL de la hoja, no junto a lo que corrige: puesta arriba, el `right: 0` de las
   reglas de más abajo la tapaba y no se aplicaba nunca. Lo caza
   `test_ninguna_regla_de_movil_queda_tapada_por_una_posterior`. */
@media (max-width: 860px) {
  .idioma-lista, .usuario-menu { right: auto; left: 0; }
}
