@charset "UTF-8";
/* =========================================================================
   DescargasWindows — hoja de estilo única
   Identidad: blanco dominante + azul claro + azul marino, con degradados
   morados de apoyo. Todo el color pasa por tokens: cambiar la marca es
   cambiar :root, no perseguir valores por el fichero.
   ========================================================================= */

/* ------------------------------------------------------------- tipografía */
/* Todas autoalojadas: cero peticiones a terceros (y por tanto nada que
   consentir en materia de cookies).
   DM Sans viene recortada al eje de peso 500-700, que es el único que usa el
   sitio: de 36 KB a 23 KB sin perder un solo glifo. Si algún día hace falta
   un peso fuera de ese rango, hay que regenerarla (ver CLAUDE.md). */
@font-face {
  font-family: 'DM Sans'; font-style: normal; font-weight: 500 700;
  font-display: swap; src: url('../fonts/dm-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Sans'; font-style: normal; font-weight: 500 700;
  font-display: swap; src: url('../fonts/dm-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono'; font-style: normal; font-weight: 100 800;
  font-display: swap; src: url('../fonts/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Source Sans 3 — la fuente del TEXTO. DM Sans es geométrica: distingue bien
   en titulares y en interfaz, pero cansa en párrafos largos. Source Sans es
   humanista y está diseñada para leer. El par geométrica/humanista es un
   contraste real, no dos sans que se parecen por accidente.
   Para español solo se descarga el subconjunto "latin" (~29 KB): las tildes y
   la eñe viven en U+00C0-U+00FF, dentro de ese rango. */
@font-face {
  font-family: 'Source Sans 3'; font-style: normal; font-weight: 300 700;
  font-display: swap; src: url('../fonts/source-sans-3-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Sans 3'; font-style: normal; font-weight: 300 700;
  font-display: swap; src: url('../fonts/source-sans-3-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ----------------------------------------------------------------- tokens
   Dos temas, una sola definición de cada uno. El script de arranque resuelve
   siempre data-tema a "claro" u "oscuro" ANTES de pintar (leyendo la
   preferencia guardada o, si no hay, la del sistema), así que aquí no hace
   falta repetir la paleta oscura dentro de un @media. Sin JS se queda en
   claro, que es la identidad de la marca.                                   */
:root,
:root[data-tema="claro"] {
  color-scheme: light;
  /* Superficies: el blanco manda. */
  --blanco:        #ffffff;
  --fondo:         #ffffff;
  --fondo-suave:   #f5f9ff;
  --fondo-tarjeta: #ffffff;
  --borde:         #dfe8f6;
  --borde-fuerte:  #c5d5ee;

  /* Azules de marca */
  --marino:        #0f2557;   /* titulares */
  --marino-hondo:  #0a1a3f;
  --azul:          #0369a1;   /* texto de enlace: 5,8:1 sobre blanco */
  --azul-claro:    #0ea5e9;   /* acento gráfico */
  --azul-cielo:    #38bdf8;
  --azul-bruma:    #e8f4fe;

  /* Morado de apoyo, solo en degradados y detalles */
  --morado:        #7c3aed;
  --morado-claro:  #a78bfa;
  --morado-bruma:  #f1ebfe;

  /* Texto */
  /* Contrastes medidos sobre #ffffff. "tenue" se usa en fechas y metadatos a
     0,75rem: es texto normal para la WCAG, así que necesita 4,5:1 — por eso
     no es más claro de lo que parece que podría ser. */
  --texto:         #16233d;   /* 15,65:1 */
  --texto-suave:   #4a5a75;   /*  6,98:1 */
  --texto-tenue:   #65738b;   /*  4,80:1 */

  --degradado:     linear-gradient(120deg, var(--azul-claro), var(--morado));
  --degradado-sutil: linear-gradient(120deg, var(--azul-bruma), var(--morado-bruma));

  --sombra-s: 0 1px 2px rgba(15, 37, 87, .06), 0 2px 8px rgba(15, 37, 87, .04);
  --sombra-m: 0 4px 14px rgba(15, 37, 87, .08), 0 12px 32px rgba(15, 37, 87, .06);
  --sombra-l: 0 10px 40px rgba(15, 37, 87, .12);

  /* Proporción de TODAS las portadas del sitio: 1200x628, la misma que pide
     Open Graph. Las miniaturas usan este mismo valor, así que la tarjeta
     enseña exactamente el mismo encuadre que la vista previa de WhatsApp o
     Facebook y no se recorta nada. Una portada con otra proporción SÍ se
     recortaría: el estándar del sitio es 1200x628. */
  --proporcion-portada: 1200 / 628;

  --radio-s: 8px;
  --radio:   14px;
  --radio-l: 22px;

  --ancho:        1160px;
  /* Aire a los lados. Único sitio donde se decide: lo usan el contenedor, el
     menú móvil y el aviso de borrador, así que TODO alinea por el mismo canto.
     Crece con la pantalla en vez de saltar en un breakpoint: 22 px en un móvil
     de 360 (antes 20, y el texto se sentía pegado al filo) y 40 en escritorio.
     Si se toca, hay que recalcular los `sizes` de lib/imagen.php.             */
  --margen-lateral: clamp(1.25rem, 1rem + 2vw, 2.5rem);
  /* Medida de lectura definida en CARACTERES, no en píxeles.
     32,3 veces el cuerpo = 72 caracteres por línea. El número NO está estimado:
     se cuenta carácter a carácter en el navegador (herramientas/medir-medida.py)
     detectando el salto de renglón por el cambio de `top`. Estimarlo con "el
     ancho medio de un carácter es medio em" daba 65 y eran 80: el error que
     llevaba el comentario viejo de este mismo token.
     72 está dentro del rango cómodo (45-75) y aprovecha pantalla sin llegar al
     tope. Al derivarse del cuerpo, la medida NO PUEDE desajustarse: si el
     cuerpo crece en una pantalla grande, la columna crece con él y el recuento
     de caracteres se queda clavado.
     OJO: esto es el ancho del TEXTO. Donde el bloque además lleva el relleno
     del contenedor, se usa --caja-lectura, que se lo suma. */
  --ancho-lectura: calc(var(--t-2) * 32.3);
  --caja-lectura:  calc(var(--ancho-lectura) + 2 * var(--margen-lateral));

  --fuente:        'Source Sans 3', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --fuente-titulo: 'DM Sans', var(--fuente);
  --mono:          'JetBrains Mono', ui-monospace, 'Cascadia Code', monospace;

  /* Escala tipográfica modular (razón ~1,2), fluida entre móvil y escritorio.
     Tener la escala en tokens evita el goteo de tamaños inventados sobre la
     marcha, que es lo que hace que un sitio parezca descuidado. */
  --t--1: .8125rem;                          /* metadatos, cintillos          */
  --t-0:  .9375rem;                          /* interfaz, textos secundarios  */
  --t-1:  1.0625rem;                         /* base de interfaz              */
  /* CUERPO de artículo. Crece de 18 px a 20 px entre un portátil pequeño y una
     pantalla grande: no es capricho, una pantalla de 1920 se mira desde más
     lejos. Es también la mitad de la respuesta a "se ve muy fino en el PC" —
     como la medida se calcula de aquí, la columna pasa de 672 a 747 px sin
     tocar los 65 caracteres por línea. La otra mitad es el índice lateral. */
  --t-2:  clamp(1.125rem, 1.05rem + .22vw, 1.25rem);
  --t-3:  clamp(1.15rem, 1.05rem + .5vw, 1.3rem);
  --t-4:  clamp(1.35rem, 1.15rem + 1vw, 1.6rem);
  --t-5:  clamp(1.6rem, 1.25rem + 1.7vw, 2.1rem);
  --t-6:  clamp(1.9rem, 1.35rem + 2.7vw, 2.9rem);
  --t-7:  clamp(2.2rem, 1.4rem + 4vw, 3.6rem);

  --interlineado-texto: 1.7;
  --interlineado-corto: 1.28;                /* titulares y títulos de tarjeta */

  --transicion: .18s ease;
}

/* Tema oscuro. Solo se redefinen tokens: ninguna regla de más abajo sabe que
   existe. Contrastes medidos sobre #0b1220 — texto 16,04:1, suave 9,30:1,
   tenue 6,24:1, enlaces 11,23:1, titulares 15,11:1. Todos AAA. */
:root[data-tema="oscuro"] {
  color-scheme: dark;

  --blanco:        #0d1526;
  --fondo:         #0b1220;
  --fondo-suave:   #101a2e;
  --fondo-tarjeta: #101a2e;
  --borde:         #1e2c47;
  --borde-fuerte:  #2c3d5e;

  --marino:        #dbe8fb;
  --marino-hondo:  #f0f6ff;
  --azul:          #7dd3fc;
  --azul-claro:    #38bdf8;
  --azul-cielo:    #7dd3fc;
  --azul-bruma:    #12243d;

  --morado:        #c4b5fd;
  --morado-claro:  #ddd6fe;
  --morado-bruma:  #1d1934;

  --texto:         #e6eefb;
  --texto-suave:   #a8b8d0;
  --texto-tenue:   #8496b3;

  --degradado:       linear-gradient(120deg, #38bdf8, #a78bfa);
  --degradado-sutil: linear-gradient(120deg, #12243d, #1d1934);

  --sombra-s: 0 1px 2px rgba(0, 0, 0, .4);
  --sombra-m: 0 4px 14px rgba(0, 0, 0, .45);
  --sombra-l: 0 10px 40px rgba(0, 0, 0, .5);
}

/* El pie es azul marino en claro; en oscuro se separa del fondo con un borde
   porque si no, se funde con la página. */
:root[data-tema="oscuro"] .pie { background: var(--fondo-suave); border-top: 1px solid var(--borde); }
:root[data-tema="oscuro"] .pie__lista a,
:root[data-tema="oscuro"] .pie__claim { color: var(--texto-suave); }
:root[data-tema="oscuro"] .pie__legal { color: var(--texto-tenue); }
:root[data-tema="oscuro"] .pie .marca--pie .marca__texto { color: var(--marino); }
:root[data-tema="oscuro"] .tabla-scroll thead th { background: var(--fondo-suave); color: var(--marino); }
:root[data-tema="oscuro"] .boton--primario { color: #0b1220; }
:root[data-tema="oscuro"] .etiqueta-afiliado { background: #3a2a12; color: #fbd38d; border-color: #6b4d1c; }

/* ------------------------------------------------------------------ reset */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
body {
  font-family: var(--fuente);
  font-size: var(--t-1);
  color: var(--texto);
  background: var(--fondo);
  line-height: var(--interlineado-texto);
  font-optical-sizing: auto;
  /* Ligaduras y kerning explícitos: Source Sans trae buen kerning para las
     combinaciones que abundan en español (Vo, Ta, "¿A"). */
  font-feature-settings: 'kern' 1, 'liga' 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img, svg, video { display: block; max-width: 100%; height: auto; }
/* <picture> es solo el envoltorio que elige el formato: no debe generar caja
   propia ni interponerse entre la <img> y su contenedor (si no, reglas como
   ".tarjeta__imagen img { height: 100% }" dejan de tener a quién referirse). */
picture { display: contents; }
/* …pero eso tiene una consecuencia que no se ve venir: al no generar caja, los
   <source> del <picture> pasan a ser hijos directos del contenedor de verdad, y
   en un flex o un grid CUENTAN como elemento aunque no pinten nada — cada uno
   se lleva su hueco (gap). Es lo que empujaba el logotipo 9,6 px a la derecha
   del canto de la página, y el motivo de que la cabecera no alineara con nada.
   El navegador no le da `display: none` de serie: hay que ponérselo. */
source { display: none; }
/* Rutas del registro (HKEY_LOCAL_MACHINE\SYSTEM\...), nombres de fichero y
   URLs largas: sin esto empujan el ancho de toda la página en el móvil. */
p, li, td, th, h1, h2, h3, h4, dd, dt, blockquote, figcaption { overflow-wrap: break-word; }
code { overflow-wrap: anywhere; }
a { color: var(--azul); text-underline-offset: 3px; }
h1, h2, h3, h4 {
  font-family: var(--fuente-titulo);
  color: var(--marino);
  line-height: var(--interlineado-corto);
  letter-spacing: -.021em;      /* DM Sans va suelta de serie en tamaños grandes */
  text-wrap: balance;
  font-weight: 700;
}
:focus-visible { outline: 3px solid var(--azul-claro); outline-offset: 3px; border-radius: 4px; }

.contenedor { width: 100%; max-width: var(--ancho); margin-inline: auto; padding-inline: var(--margen-lateral); }

.saltar {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--marino); color: #fff; padding: .75rem 1.25rem; border-radius: 0 0 var(--radio-s) 0;
}
.saltar:focus { left: 0; }

/* ------------------------------------------------------- aviso de borrador */
.aviso-borrador {
  background: var(--degradado); color: #fff;
  font-size: .8125rem; text-align: center; padding: .5rem var(--margen-lateral);
}
.aviso-borrador a { color: #fff; font-weight: 600; }
/* El aviso de conflicto de URL usa rojo plano y no el degradado de marca: es un
   error que hay que arreglar, no un cintillo informativo más. */
.aviso-borrador--alerta { background: #b91c1c; }

/* --------------------------------------------------------------- cabecera */
.cabecera {
  position: sticky; top: 0; z-index: 100;
  background: color-mix(in srgb, var(--fondo) 88%, transparent);
  backdrop-filter: saturate(1.6) blur(12px);
  border-bottom: 1px solid var(--borde);
}
.cabecera__fila { display: flex; align-items: center; gap: 1.5rem; min-height: 68px; }

.marca { display: inline-flex; align-items: center; gap: .6rem; text-decoration: none; flex: 0 0 auto; }
/* Logotipo del sitio. Tamaño fijo en píxeles y no en rem a propósito: no debe
   crecer si el visitante agranda el texto del navegador, o desplaza el menú.
   El width/height del HTML es el del fichero (512), así que aquí hacen falta
   los dos ejes para que no reserve un hueco enorme antes de aplicar el CSS. */
.marca__logo {
  width: 40px; height: 40px; flex: 0 0 auto;
  border-radius: 9px;
}
.marca__texto {
  font-family: var(--fuente-titulo); font-size: 1.1875rem; font-weight: 500;
  color: var(--marino); letter-spacing: -.025em;
}
.marca__texto strong {
  font-weight: 700;
  background: var(--degradado); -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* Los ajustes de móvil van DESPUÉS de las reglas base, no antes: tienen la
   misma especificidad, así que gana la última que aparece en el fichero. Antes
   estaban arriba y no llegaban a aplicarse nunca — la cabecera se salía 15 px
   a 320 px y el botón de menú quedaba cortado. */
@media (max-width: 30rem) {
  .cabecera__fila { gap: .5rem; }
  .marca { min-width: 0; }                      /* puede encogerse dentro del flex */
  .marca__texto { font-size: 1.0625rem; }
  .marca__logo { width: 32px; height: 32px; }
}
/* A 320 px (el móvil pequeño que todavía existe) la fila de la cabecera —logo,
   nombre y dos botones— no cabe: se pasaba 15 px y el botón de menú quedaba
   cortado. Aquí se encoge lo justo, sin esconder el nombre del sitio. */
@media (max-width: 22.5rem) {
  .marca { gap: .4rem; }
  .marca__texto { font-size: .9375rem; }
  .marca__logo { width: 27px; height: 27px; border-radius: 7px; }
}


.menu { margin-left: auto; }
.menu__lista { display: flex; gap: .35rem; list-style: none; padding: 0; }
.menu__lista a {
  display: block; padding: .45rem .8rem; border-radius: 999px;
  color: var(--texto-suave); text-decoration: none; font-size: var(--t-0); font-weight: 600;
  transition: color var(--transicion), background var(--transicion);
}
.menu__lista a:hover { color: var(--azul); background: var(--azul-bruma); }

/* Interruptor de tema. Se muestran sol y luna en el mismo hueco y solo se ve
   el que toca, así el botón nunca cambia de tamaño al pulsarlo. */
.tema {
  display: grid; place-items: center; width: 44px; height: 44px; flex: 0 0 auto;
  padding: 0; background: none; border: 1px solid var(--borde);
  border-radius: var(--radio-s); cursor: pointer; color: var(--texto-suave);
  transition: color var(--transicion), border-color var(--transicion), background var(--transicion);
}
.tema:hover { color: var(--azul); border-color: var(--azul-claro); background: var(--azul-bruma); }
.tema[hidden] { display: none; }
.tema svg {
  grid-area: 1 / 1; width: 20px; height: 20px;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}
.tema__luna { fill: currentColor; stroke: none; }
:root[data-tema="claro"] .tema__sol   { display: none; }   /* en claro ofrecemos ir a oscuro */
:root[data-tema="oscuro"] .tema__luna { display: none; }

.menu__boton {
  display: none; width: 44px; height: 44px; padding: 10px;
  background: none; border: 1px solid var(--borde); border-radius: var(--radio-s); cursor: pointer;
}
.menu__boton span { display: block; height: 2px; background: var(--marino); border-radius: 2px; margin: 4px 0; transition: var(--transicion); }
.menu__boton[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.menu__boton[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.menu__boton[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.menu-movil { display: none; flex-direction: column; border-top: 1px solid var(--borde); background: var(--fondo); }
.menu-movil a {
  padding: .9rem var(--margen-lateral); color: var(--texto); text-decoration: none;
  font-weight: 500; border-bottom: 1px solid var(--borde);
}
.menu-movil a:last-child { border-bottom: none; }

@media (max-width: 62rem) {
  .menu { display: none; }
  /* Sin el nav, es el interruptor quien empuja el grupo a la derecha. */
  .tema:not([hidden]) { margin-left: auto; }
  .menu__boton { display: block; }
  .menu-movil:not([hidden]) { display: flex; }
}

/* ------------------------------------------------------------------- hero */
.hero { position: relative; overflow: hidden; background: var(--degradado-sutil); border-bottom: 1px solid var(--borde); }
.hero::after {
  content: ''; position: absolute; inset: auto -10% -60% 40%; height: 22rem;
  background: radial-gradient(closest-side, rgba(124, 58, 237, .18), transparent 70%);
  pointer-events: none;
}
/* La caja del hero NO lleva max-width propio: es un .contenedor normal, así
   que su canto izquierdo cae en la misma vertical que el logotipo, que
   "Explora por versión" y que las tarjetas. Antes eran 54rem centradas y el
   hero aparecía metido 148 px respecto de todo lo demás — el desajuste que
   más se notaba en escritorio. Quien limita la medida es el TEXTO, no la caja. */
.hero__caja { position: relative; padding-block: clamp(3rem, 8vw, 5.5rem); }
.hero__titulo { font-size: var(--t-7); letter-spacing: -.032em; margin-bottom: 1rem; max-width: 20ch; }
.degradado { background: var(--degradado); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero__texto {
  font-size: var(--t-2); line-height: 1.65; color: var(--texto-suave);
  max-width: 40rem; text-wrap: pretty;
}
.hero__acciones { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 2rem; }

/* ---------------------------------------------------------------- botones */
.boton {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: .8rem 1.6rem; border-radius: 999px;
  font-weight: 600; font-size: 1rem; text-decoration: none;
  transition: transform .12s ease, box-shadow var(--transicion), background var(--transicion);
}
.boton--primario { background: var(--degradado); color: #fff; box-shadow: var(--sombra-m); }
.boton--primario:hover { transform: translateY(-2px); box-shadow: var(--sombra-l); }
.boton--secundario { background: var(--fondo-tarjeta); color: var(--marino); border: 1.5px solid var(--borde-fuerte); }
.boton--secundario:hover { border-color: var(--azul-claro); color: var(--azul); }
@media (prefers-reduced-motion: reduce) { .boton:hover { transform: none; } }

/* -------------------------------------------------------------- secciones */
.seccion { padding-block: clamp(2.5rem, 6vw, 4rem); }
.seccion__titulo { font-size: var(--t-5); margin-bottom: 1.75rem; }

.cintillo {
  display: inline-flex; align-items: center; gap: .5rem; margin-bottom: .75rem;
  font-size: .75rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--morado);
}
.cintillo__punto { width: 6px; height: 6px; border-radius: 50%; background: var(--degradado); flex: 0 0 auto; }
.cintillo--hero { color: var(--azul); }

/* ----------------------------------------------------- tarjetas categoría */
/* Misma pista mínima que .rejilla-posts a propósito: las dos rejillas de la
   portada rompen en el mismo sitio y comparten columna, así que la página
   tiene UNA retícula y no dos. Con 15rem salían cuatro columnas y la quinta
   categoría se quedaba sola en una fila con tres huecos. */
.rejilla-cats {
  list-style: none; padding: 0; display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
}
.tarjeta-cat {
  display: flex; flex-direction: column; gap: .35rem; height: 100%;
  padding: 1.35rem; border: 1px solid var(--borde); border-radius: var(--radio);
  background: var(--fondo-tarjeta); text-decoration: none; box-shadow: var(--sombra-s);
  transition: border-color var(--transicion), transform .12s ease, box-shadow var(--transicion);
}
.tarjeta-cat:hover { border-color: var(--azul-claro); transform: translateY(-3px); box-shadow: var(--sombra-m); }
.tarjeta-cat__nombre {
  font-family: var(--fuente-titulo); font-weight: 700; font-size: var(--t-3);
  color: var(--marino); letter-spacing: -.018em;
}
.tarjeta-cat__desc { font-size: var(--t-0); line-height: 1.55; color: var(--texto-suave); flex: 1; }
.tarjeta-cat__n {
  font-size: var(--t--1); color: var(--morado); font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------- tarjetas posts */
.rejilla-posts {
  list-style: none; padding: 0; display: grid; gap: 1.5rem;
  grid-template-columns: repeat(auto-fill, minmax(min(19rem, 100%), 1fr));
}
.tarjeta { display: flex; }
.tarjeta__enlace {
  display: flex; flex-direction: column; width: 100%;
  background: var(--fondo-tarjeta); border: 1px solid var(--borde); border-radius: var(--radio);
  overflow: hidden; text-decoration: none; box-shadow: var(--sombra-s);
  transition: border-color var(--transicion), transform .12s ease, box-shadow var(--transicion);
}
.tarjeta__enlace:hover { border-color: var(--azul-claro); transform: translateY(-3px); box-shadow: var(--sombra-m); }
/* El hueco tiene la proporción EXACTA de las portadas (1200x628), no 16/9: con
   16/9 el `cover` se comía un 7 % por los lados y en la portada de "Descargar e
   instalar Windows 11" cortaba el "DVD" del título. El object-fit se queda como
   red de seguridad para una portada futura que no cumpla el estándar. */
.tarjeta__imagen {
  display: block; aspect-ratio: var(--proporcion-portada);
  overflow: hidden; background: var(--fondo-suave);
}
.tarjeta__imagen img { width: 100%; height: 100%; object-fit: cover; }
.tarjeta__cuerpo { display: flex; flex-direction: column; gap: .5rem; padding: 1.25rem; flex: 1; }
.tarjeta__cat {
  font-size: .6875rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--morado);
}
.tarjeta__titulo {
  font-family: var(--fuente-titulo); font-size: var(--t-3); font-weight: 700;
  color: var(--marino); line-height: var(--interlineado-corto);
  letter-spacing: -.018em; text-wrap: balance;
}
.tarjeta__resumen { font-size: var(--t-0); line-height: 1.6; color: var(--texto-suave); flex: 1; text-wrap: pretty; }
.tarjeta__meta {
  font-size: var(--t--1); color: var(--texto-tenue); font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.tarjeta__enlace:hover .tarjeta__titulo { color: var(--azul); }

/* ------------------------------------------------------- listados/cabecera */
.cabecera-listado { padding-block: 2.5rem 0; }
/* Cabecera de categoría a dos columnas en pantalla ancha: el texto queda con su
   medida de lectura y la imagen ocupa el hueco de la derecha, que si no se
   queda vacío. Por debajo de 60rem se apilan y la imagen va debajo del texto:
   arriba empujaría los artículos fuera de la primera pantalla, que es
   justamente lo que se quiere evitar en una página de categoría. */
.cabecera-listado--con-imagen { padding-bottom: 1.5rem; }
.cabecera-listado__texto { max-width: var(--ancho-lectura); }
.cabecera-listado__imagen { margin-top: 1.5rem; }
.cabecera-listado__imagen img {
  width: 100%; border-radius: var(--radio); border: 1px solid var(--borde);
  box-shadow: var(--sombra-s);
}
@media (min-width: 60rem) {
  .cabecera-listado--con-imagen {
    display: grid; align-items: start;
    grid-template-columns: minmax(0, var(--ancho-lectura)) minmax(14rem, 26rem);
    justify-content: space-between; column-gap: clamp(2rem, 3vw, 3.5rem);
  }
  .cabecera-listado__imagen { margin-top: .5rem; }
}
.categoria__entradilla { margin-top: 1.25rem; font-size: var(--t-2); }
.categoria__entradilla p { margin-bottom: 0; color: var(--texto-suave); text-wrap: pretty; }
/* Entradilla de categoría: hereda la tipografía de lectura de .articulo__cuerpo
   pero sin su relleno inferior de 3rem, que aquí dejaba un socavón entre el
   texto y las tarjetas. */
/* La entradilla hereda de .articulo__cuerpo la tipografía de lectura, pero NO
   su caja: en una página de categoría el canto lo marca el contenedor, igual
   que el H1 y las tarjetas. Así que la caja es la del contenedor y quien
   limita la medida es el TEXTO —los hijos—, no el bloque. Con la caja de
   lectura centrada, la entradilla flotaba en medio de la página; con el
   bloque pegado a la izquierda sin contenedor, se iba al filo. */
/* Selector compuesto y no `.categoria__intro` a secas: las dos clases pesan
   igual (0,1,0) y `.articulo__cuerpo` está más abajo en el fichero, así que
   ganaba ella y la entradilla seguía centrada. Con las dos clases juntas
   (0,2,0) el orden deja de importar. */
.articulo__cuerpo.categoria__intro {
  padding-block: 1.25rem 0;
  max-width: var(--ancho);
}
.categoria__intro > * { max-width: var(--ancho-lectura); }
/* Con una sola tarjeta, el aire de .seccion (hasta 4rem arriba) más el de la
   cabecera del listado dejaban media pantalla vacía antes del primer artículo.
   La sección de un listado arranca antes que una sección de la portada. */
.seccion--listado { padding-block: 1.75rem clamp(2.5rem, 6vw, 4rem); }
.titulo-listado { font-size: var(--t-6); margin-block: .5rem; }
.subtitulo-listado { color: var(--texto-suave); font-size: var(--t-2); max-width: 40rem; text-wrap: pretty; }
.vacio { color: var(--texto-suave); }

/* ------------------------------------------------------------------ migas */
.migas ol { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; padding: 0; font-size: .8125rem; }
.migas li + li::before { content: '/'; margin-right: .4rem; color: var(--texto-tenue); }
.migas a { color: var(--texto-suave); text-decoration: none; }
.migas a:hover { color: var(--azul); text-decoration: underline; }
.migas li[aria-current] span { color: var(--texto-tenue); }

/* -------------------------------------------------- barra de progreso */
.progreso { position: fixed; inset: 0 0 auto 0; height: 3px; z-index: 200; pointer-events: none; }
.progreso span { display: block; height: 100%; width: 0; background: var(--degradado); transition: width .08s linear; }

/* --------------------------------------------------------------- artículo */
.articulo__cabecera { padding-top: 2rem; max-width: var(--caja-lectura); }
.articulo__titulo { font-size: var(--t-6); margin-bottom: .85rem; }
.articulo__entradilla {
  font-size: var(--t-4); font-weight: 300; line-height: 1.5;
  color: var(--texto-suave); margin-bottom: 1.5rem; text-wrap: pretty;
}

.firma { display: flex; align-items: center; gap: .8rem; padding-block: 1.25rem; border-block: 1px solid var(--borde); }
/* El avatar es un ENVOLTORIO de la foto, no la foto: <picture> es
   display:contents y no puede recortar nada, así que el círculo y el recorte
   viven aquí y la <img> solo rellena. */
.firma__avatar {
  display: block; width: 42px; height: 42px; border-radius: 50%; overflow: hidden;
  flex: 0 0 auto; background: var(--degradado-sutil);
}
.retrato { width: 100%; height: 100%; object-fit: cover; display: block; }
.firma__datos { display: flex; flex-direction: column; min-width: 0; }
.firma__nombre { color: var(--marino); font-weight: 600; text-decoration: none; }
.firma__nombre:hover { color: var(--azul); }
.firma__meta { font-size: .8125rem; color: var(--texto-tenue); }

.articulo__portada { max-width: var(--caja-lectura); margin-block: 2rem; }
.articulo__portada img { width: 100%; border-radius: var(--radio); border: 1px solid var(--borde); box-shadow: var(--sombra-m); }

.articulo__cuerpo {
  max-width: var(--caja-lectura); padding-block: 1.5rem 3rem;
  font-size: var(--t-2);                    /* el cuerpo manda: 1,125rem */
}

/* CONVENCIÓN — el contenedor de prosa va SIEMPRE dentro de :where().
   Las reglas de abajo dan estilo a etiquetas sueltas (p, ul, li, a, img…) que
   salen del Markdown y no llevan clase. Escritas como `.articulo__cuerpo li`
   pesarían (0,1,1) y le ganarían a CUALQUIER componente de una sola clase que
   se meta dentro: pasó con la lista de grupos de Telegram —las tarjetas
   quedaban 24 px metidas por la izquierda y el texto pegado al borde— y con el
   color del texto de los botones. Con :where() el contenedor aporta CERO
   especificidad, el selector queda en (0,0,1) y basta una clase para mandar.
   Regla práctica: dentro de este bloque, nunca subir la especificidad para
   ganar una pelea; si hace falta, es que el selector está mal escrito.        */
:where(.articulo__cuerpo) p {
  margin-bottom: 1.35rem; color: var(--texto);
  /* Evita que un párrafo termine en una palabra suelta colgando. */
  text-wrap: pretty;
}
/* Ritmo vertical: el espacio ANTES de un titular es grande y el de después
   pequeño, para que la cabecera se agrupe con su texto y no flote en medio. */
:where(.articulo__cuerpo) h2 {
  font-size: var(--t-5); margin: 3rem 0 1rem; padding-bottom: .5rem;
  border-bottom: 2px solid var(--borde); scroll-margin-top: 5.5rem;
}
:where(.articulo__cuerpo) h3 {
  font-size: var(--t-3); margin: 2.25rem 0 .6rem; scroll-margin-top: 5.5rem;
  color: var(--marino-hondo); letter-spacing: -.015em;
}
:where(.articulo__cuerpo) h2 + p, :where(.articulo__cuerpo) h3 + p,
:where(.articulo__cuerpo) h2 + ul, :where(.articulo__cuerpo) h2 + ol,
:where(.articulo__cuerpo) h3 + ul, :where(.articulo__cuerpo) h3 + ol { margin-top: 0; }
:where(.articulo__cuerpo) ul, :where(.articulo__cuerpo) ol:not(:where(.toc__lista)) { margin: 0 0 1.35rem; padding-left: 1.5rem; }
:where(.articulo__cuerpo) li { margin-bottom: .55rem; padding-left: .25rem; color: var(--texto); text-wrap: pretty; }
:where(.articulo__cuerpo) ol:not(:where(.toc__lista)) { counter-reset: paso; list-style: none; padding-left: 0; }
/* Pasos numerados: el número en su propia columna, alineado, para que un paso
   de tres líneas no rompa la sangría. Es lo que más se lee en este sitio. */
:where(.articulo__cuerpo) ol:not(:where(.toc__lista)) > li {
  counter-increment: paso; position: relative; padding-left: 2.4rem; margin-bottom: .85rem;
}
:where(.articulo__cuerpo) ol:not(:where(.toc__lista)) > li::before {
  content: counter(paso); position: absolute; left: 0; top: .1em;
  display: grid; place-items: center; width: 1.65rem; height: 1.65rem;
  background: var(--azul-bruma); color: var(--azul); border-radius: 50%;
  font-family: var(--fuente-titulo); font-size: .8125rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
:where(.articulo__cuerpo) ol:not(:where(.toc__lista)) ol { margin-top: .7rem; }
:where(.articulo__cuerpo) li::marker { color: var(--azul-claro); font-weight: 700; }
:where(.articulo__cuerpo) strong { color: var(--marino-hondo); font-weight: 700; }
:where(.articulo__cuerpo) a { color: var(--azul); font-weight: 500; text-decoration: underline; text-decoration-thickness: 1.5px; text-decoration-color: color-mix(in srgb, var(--azul) 40%, transparent); }
:where(.articulo__cuerpo) a:hover { text-decoration-color: var(--azul); }
:where(.articulo__cuerpo) img { margin: 1.5rem auto; border-radius: var(--radio-s); border: 1px solid var(--borde); box-shadow: var(--sombra-s); }
:where(.articulo__cuerpo) li img { margin: .85rem 0; }

/* Imagen CON pie. Solo la llevan las que aportan algo por sí mismas: en el
   .md se escribe ![alt](ruta "pie"). El margen pasa a la <figure> para que el
   pie quede pegado a su imagen y no flotando entre las dos. */
:where(.articulo__cuerpo) .figura { margin: 1.75rem 0; }
:where(.articulo__cuerpo) .figura img { margin: 0; }
/* A ras del canto izquierdo de su imagen. Antes llevaba un filete de 2 px y
   0,7rem de sangría: el filete casi no se veía en claro y a cambio el pie
   quedaba descolgado respecto de la foto y del texto. Lo que lo distingue del
   cuerpo es que es más pequeño y más tenue, que ya basta. */
:where(.articulo__cuerpo) .figura figcaption {
  margin-top: .6rem;
  font-size: var(--t--1); line-height: 1.5; color: var(--texto-tenue);
}
:where(.articulo__cuerpo) li .figura { margin: .85rem 0; }

:where(.articulo__cuerpo) blockquote {
  margin: 1.75rem 0; padding: 1.1rem 1.35rem;
  background: var(--azul-bruma); border: 1px solid var(--borde);
  border-left: 4px solid var(--azul-claro); border-radius: 0 var(--radio-s) var(--radio-s) 0;
}
:where(.articulo__cuerpo) blockquote p:last-child { margin-bottom: 0; }
:where(.articulo__cuerpo) blockquote p { color: var(--texto-suave); }

:where(.articulo__cuerpo) code {
  font-family: var(--mono); font-size: .875em; background: var(--fondo-suave);
  border: 1px solid var(--borde); padding: .12em .4em; border-radius: 5px; color: var(--morado);
}
:where(.articulo__cuerpo) hr { border: none; height: 1px; background: var(--borde); margin-block: 2.5rem; }

/* Divulgación de afiliación. Ámbar a propósito: tiene que verse, no camuflarse
   con el texto. Contraste 6,4:1 sobre su fondo. */
.etiqueta-afiliado {
  display: inline-block; vertical-align: baseline; white-space: nowrap;
  font-size: .6875rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: .08em .45em; border-radius: 5px;
  background: #fdf3d8; color: #7a5410; border: 1px solid #e8cf94;
}

/* ------------------------------------------------------------------ tablas */
.tabla-scroll {
  overflow-x: auto; margin-block: 1.75rem; border: 1px solid var(--borde);
  border-radius: var(--radio-s); -webkit-overflow-scrolling: touch;
}
.tabla-scroll table { width: 100%; border-collapse: collapse; font-size: .9375rem; }
.tabla-scroll th, .tabla-scroll td { padding: .7rem 1rem; text-align: left; border-bottom: 1px solid var(--borde); }
.tabla-scroll thead th {
  background: var(--marino); color: #fff; font-weight: 600; white-space: nowrap;
  font-size: .75rem; letter-spacing: .05em; text-transform: uppercase;
}
.tabla-scroll tbody th { color: var(--marino); font-weight: 600; background: var(--fondo-suave); }
.tabla-scroll tbody tr:last-child th, .tabla-scroll tbody tr:last-child td { border-bottom: none; }
.tabla-scroll tbody tr:hover th, .tabla-scroll tbody tr:hover td { background: var(--azul-bruma); }

/* En móvil cada fila se convierte en una tarjeta con su etiqueta delante
   (data-label lo pone el renderizador desde el <thead>). Una tabla de 3
   columnas es ilegible a 360px por mucho scroll horizontal que le pongas. */
@media (max-width: 40rem) {
  .tabla-scroll { border: none; overflow: visible; }
  .tabla-scroll table, .tabla-scroll tbody, .tabla-scroll tr, .tabla-scroll td, .tabla-scroll tbody th { display: block; width: auto; }
  .tabla-scroll thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .tabla-scroll tbody tr {
    background: var(--fondo-tarjeta); border: 1px solid var(--borde);
    border-radius: var(--radio-s); padding: .3rem 1rem .8rem; margin-bottom: 1rem; box-shadow: var(--sombra-s);
  }
  .tabla-scroll tbody th, .tabla-scroll td { border: none; padding: .5rem 0 0; background: none; }
  .tabla-scroll tbody th { font-size: 1.05rem; color: var(--azul); padding-top: .6rem; }
  .tabla-scroll td::before {
    content: attr(data-label); display: block; font-size: .6875rem; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase; color: var(--morado); margin-bottom: .1rem;
  }
  /* La primera celda es el asunto de la fila: hace de título de la tarjeta y no
     necesita que le repitan encima la cabecera de su columna. */
  .tabla-scroll td:first-child {
    font-family: var(--fuente-titulo); font-size: 1.05rem; font-weight: 700;
    color: var(--marino); padding: .2rem 0 .7rem; margin-bottom: .2rem;
    border-bottom: 1px solid var(--borde);
  }
  .tabla-scroll td:first-child::before { content: none; }
  .tabla-scroll td + td { border-top: 1px solid var(--borde); margin-top: .5rem; padding-top: .6rem; }
  .tabla-scroll td:nth-child(2) { border-top: none; margin-top: 0; }
}

/* --------------------------------------- artículo a dos columnas en pantalla
   El problema medido: la columna de lectura son 65-66 caracteres —la medida
   correcta, no se toca— pero a 1920 px eso deja el 65 % de la pantalla vacío.
   La solución no es ensanchar el texto (a 100 caracteres se lee peor), sino
   darle al hueco un trabajo: el índice se muda ahí y se queda pegado mientras
   se baja. En un sitio de tutoriales por pasos es justo lo que hace falta, y
   además quita del principio del artículo un bloque que retrasaba el texto.

   El umbral son 72rem porque es donde caben las dos columnas: a 1152 px la
   medida son 722 px y al índice le quedan 240 mínimos con hueco de sobra.
   `space-between` y no `center`: así el canto izquierdo del artículo cae en la
   misma vertical que el logotipo y el pie, y el derecho también.               */
@media (min-width: 72rem) {
  /* :has() evita la columna fantasma en los artículos cortos, que no llevan
     índice (html_indice() lo omite con menos de 3 secciones). Si el navegador
     no lo soporta, se queda una columna vacía a la derecha: se degrada feo,
     no roto. */
  .articulo:has(.articulo__indice) {
    display: grid;
    grid-template-columns: minmax(0, var(--ancho-lectura)) minmax(15rem, 20rem);
    grid-template-rows: auto 1fr;      /* explícitas: sin ellas el índice no
                                          puede abarcar en alto su columna */
    justify-content: space-between;
    column-gap: clamp(2rem, 3vw, 3.5rem);
    width: 100%; max-width: var(--ancho);
    margin-inline: auto; padding-inline: var(--margen-lateral);
  }
  /* La rejilla asume el papel de contenedor: los hijos sueltan el suyo o
     sumarían dos rellenos y dos anchos máximos. */
  /* La cabecera y la portada ocupan las DOS columnas. En la columna 2 no hay
     índice hasta la fila 2 —va al lado del cuerpo, no del título—, así que con
     el previo en la columna 1 la portada quedaba arrinconada a la izquierda
     con medio ancho de página vacío a su derecha. Ocupando las dos, la portada
     es la banda de entrada del artículo y no una isla. */
  .articulo:has(.articulo__indice) .articulo__previo,
  .articulo:has(.articulo__indice) .articulo__resto { grid-column: 1; min-width: 0; }
  .articulo:has(.articulo__indice) .articulo__previo > *,
  .articulo:has(.articulo__indice) .articulo__resto > * {
    max-width: none; margin-inline: 0; padding-inline: 0;
  }
  .articulo:has(.articulo__indice) .articulo__cabecera { max-width: var(--ancho-lectura); }
  .articulo:has(.articulo__indice) .articulo__indice {
    grid-area: 1 / 2 / -1 / 3;         /* desde arriba del todo: sin esto, la
                                          portada quedaba sola a la izquierda
                                          con media página vacía a su derecha */
    align-self: start;
    position: sticky; top: 5.5rem;     /* justo debajo de la cabecera pegajosa */
    padding-inline: 0; max-width: none; margin: 0;
    /* Un índice de quince entradas no puede tapar la pantalla: se desplaza él. */
    max-height: calc(100vh - 7rem); overflow-y: auto;
  }
  .articulo:has(.articulo__indice) .articulo__indice .toc { margin-bottom: 0; }
}

/* -------------------------------------------------------- bloques ::: ---
   Desplegable. <details> nativo: sin JS, sin depender de nada, y el navegador
   ya sabe abrirlo con teclado. El contenido de dentro SÍ lo indexa Google
   (lleva años tratando lo plegable como contenido normal), así que aquí se
   pliega lo que estorba al que ya sabe, no lo que hay que esconder.          */
.desplegable {
  margin: 1.25rem 0; border: 1px solid var(--borde); border-radius: var(--radio);
  background: var(--fondo-tarjeta); overflow: hidden;
}
.desplegable__titulo {
  display: flex; align-items: center; gap: .6rem; cursor: pointer;
  padding: .85rem 1.1rem; list-style: none;
  font-family: var(--fuente-titulo); font-weight: 700; font-size: var(--t-1);
  color: var(--marino); line-height: var(--interlineado-corto);
}
.desplegable__titulo::-webkit-details-marker { display: none; }
/* La flecha se dibuja aquí y no con un carácter: así gira al abrir y no
   depende de qué emoji tenga instalado el sistema. */
.desplegable__titulo::before {
  content: ''; flex: 0 0 auto; width: .5rem; height: .5rem;
  border-right: 2px solid var(--azul-claro); border-bottom: 2px solid var(--azul-claro);
  transform: rotate(-45deg); transition: transform var(--transicion);
}
.desplegable[open] > .desplegable__titulo::before { transform: rotate(45deg); }
.desplegable__titulo:hover { color: var(--azul); background: var(--fondo-suave); }
.desplegable__cuerpo { padding: 0 1.1rem 1rem; font-size: var(--t-1); }
.desplegable__cuerpo > *:last-child { margin-bottom: 0; }

/* Gráfico comparativo. La rejilla de marcas se queda RESTRINGIDA en anchura a
   propósito: las columnas de "sí/no" no necesitan más de 2,75rem, y con eso
   una comparativa de cuatro ediciones entra en un móvil de 360 px. Antes esto
   era una tabla normal y en el móvil se convertía en una tarjeta por fila:
   cuarenta tarjetas seguidas para comparar dos ediciones. */
.comparativa { margin: 1.75rem 0; }
.comparativa__titulo {
  font-family: var(--fuente-titulo); font-weight: 700; font-size: var(--t-1);
  color: var(--marino); margin-bottom: .6rem;
}
.comparativa__caja {
  overflow-x: auto; border: 1px solid var(--borde); border-radius: var(--radio);
  background: var(--fondo-tarjeta); -webkit-overflow-scrolling: touch;
}
.comparativa table { width: 100%; border-collapse: collapse; font-size: .9375rem; }
.comparativa thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--fondo-suave); color: var(--marino);
  font-size: .75rem; letter-spacing: .04em; text-transform: uppercase;
  padding: .6rem .35rem; text-align: center;
  /* Sin `nowrap`: un rótulo de dos palabras como "IoT LTSC" forzaba su columna
     a ser más ancha de la cuenta. Y sin `overflow-wrap: anywhere`, que se probó
     y parte por donde le da la gana: "LibreOffice" salía como "LI-BREO-FFICE".
     Cuando un rótulo es una palabra larga, el corte se marca EN EL TEXTO con un
     guion suave (U+00AD) y así rompe por donde tiene sentido. */
  border-bottom: 1px solid var(--borde-fuerte);
}
/* padding-inline y no solo padding-left: si se toca un lado, el otro se queda
   con el valor de la regla de arriba y la celda sale descuadrada (lo cazó
   comprobar-alineacion.py con 14,4 px a la izquierda y 8 a la derecha). */
.comparativa thead th:first-child { text-align: left; padding-inline: .9rem; }
.comparativa tbody th {
  text-align: left; font-weight: 500; color: var(--texto);
  padding: .6rem .9rem; line-height: 1.35;
  /* El mínimo del rótulo es fluido: 8rem en cuanto hay sitio, pero nunca más
     del 30 % de la ventana. Con 8rem fijos, una comparativa de cuatro columnas
     empujaba la PÁGINA 4 px a lo ancho en un móvil de 320 —la caja no absorbía
     ese desbordamiento pese a tener overflow— y lo cazó el comprobador de
     móvil. Medido, no supuesto. */
  min-width: min(8rem, 30vw);
  /* Pegada al borde al desplazar: sin esto, al mirar la cuarta columna en un
     móvil ya no sabes de qué función estás leyendo el dato. */
  position: sticky; left: 0; background: var(--fondo-tarjeta);
}
/* La anchura estrecha vale solo para las celdas de marca: si se le mete texto
   a una columna, esa celda tiene que poder crecer. Antes `width` iba en todos
   los `td` y `--valor` llevaba además `nowrap`, así que una tabla de dos
   columnas con frases dentro se salía de la pantalla. El componente es para
   ✓/✗ y valores cortos; para texto, tabla normal. */
.comparativa td { padding: .6rem .25rem; text-align: center; }
.comparativa__celda--si, .comparativa__celda--no { width: 2.75rem; }
.comparativa tbody tr + tr th, .comparativa tbody tr + tr td { border-top: 1px solid var(--borde); }
.comparativa__celda--si  { color: #15803d; font-weight: 700; }
.comparativa__celda--no  { color: var(--texto-tenue); }
.comparativa__celda--valor { font-size: .8125rem; color: var(--texto-suave); }
:root[data-tema="oscuro"] .comparativa__celda--si { color: #4ade80; }
/* El rótulo de la fila viaja sobre las marcas al desplazar, así que necesita
   fondo opaco propio en los dos temas. */
:root[data-tema="oscuro"] .comparativa tbody th { background: var(--fondo-tarjeta); }

/* ------------------------------------------------------------------- índice */
.toc {
  background: var(--fondo-suave); border: 1px solid var(--borde);
  border-radius: var(--radio); padding: 1.15rem 1.4rem; margin-bottom: 2.25rem;
}
.toc__titulo {
  font-size: .6875rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--morado); margin-bottom: .5rem; border: none; padding: 0;
}
.toc__lista { list-style: none; margin: 0; padding: 0; counter-reset: toc; }
/* Carril de progreso a la izquierda: el filete se enciende en las secciones por
   las que ya has pasado, así que la parte encendida ES cuánto llevas leído. Va
   en el <li> y no en un ::after medido con JS, porque así no hay que calcular
   píxeles ni se descuadra al cambiar el tamaño de la ventana. */
.toc__lista li {
  counter-increment: toc; margin: 0;
  border-left: 2px solid var(--borde); padding-left: .7rem;
  transition: border-color var(--transicion);
}
/* Tres estados, y el porqué de que sean tres: con uno solo el índice contesta
   "dónde estoy" pero no "cuánto me queda", que es la pregunta de quien llega
   por primera vez. Lo leído se apaga, lo actual se marca, lo pendiente espera. */
.toc__lista li[data-estado="leido"],
.toc__lista li[data-estado="actual"] { border-left-color: var(--azul-claro); }
.toc__lista li[data-estado="leido"] a { color: var(--texto-tenue); }
.toc__lista li[data-estado="leido"] a::before { color: var(--texto-tenue); opacity: .7; }
.toc__lista li[data-estado="actual"] a { color: var(--azul); font-weight: 600; }
.toc__lista li[data-estado="actual"] a::before { color: var(--azul); }
/* El punto de "estás aquí": un marcador sólido sobre el carril, que es lo que
   hace que la posición se lea sin comparar colores entre sí. */
.toc__lista li[data-estado="actual"] {
  border-left-color: var(--azul); position: relative;
}
.toc__lista li[data-estado="actual"]::before {
  content: ''; position: absolute; left: -5px; top: 50%; width: 8px; height: 8px;
  margin-top: -4px; border-radius: 50%; background: var(--azul);
  box-shadow: 0 0 0 3px var(--fondo-suave);
}
/* Flex y no `display: block`: con el número como ::before en línea, un título
   de dos renglones devolvía el segundo al margen izquierdo y quedaba debajo de
   la cifra. En columnas, el texto que envuelve cae bajo el texto — la misma
   sangría francesa que ya usan los pasos numerados del artículo. */
.toc__lista a {
  display: flex; gap: .6rem; align-items: baseline;
  padding: .42rem 0; font-size: .9375rem; color: var(--texto-suave);
  text-decoration: none;
  transition: color var(--transicion);
}
.toc__lista a::before {
  content: counter(toc, decimal-leading-zero); font-family: var(--mono); font-size: .8em;
  font-weight: 700; color: var(--azul-claro); flex: 0 0 auto;
}
.toc__lista a:hover { color: var(--azul); }
.toc__n3 a { padding-left: 1.5rem; font-size: .875rem; }

/* -------------------------------------------------------------- relacionados */
.relacionados { max-width: var(--caja-lectura); padding-bottom: 4rem; }
.relacionados h2 { font-size: var(--t-4); margin-bottom: 1.15rem; padding-top: 2rem; border-top: 1px solid var(--borde); }
.relacionados ul { list-style: none; padding: 0; display: grid; gap: .75rem; }
.relacionados a {
  display: flex; flex-direction: column; gap: .25rem; padding: 1rem 1.2rem;
  background: var(--fondo-tarjeta); border: 1px solid var(--borde); border-radius: var(--radio);
  text-decoration: none; transition: border-color var(--transicion), transform .12s ease;
}
.relacionados a:hover { border-color: var(--azul-claro); transform: translateY(-2px); }
.relacionados__cat { font-size: .6875rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--morado); }
.relacionados__titulo {
  font-family: var(--fuente-titulo); color: var(--marino); font-weight: 700;
  font-size: var(--t-1); line-height: var(--interlineado-corto); letter-spacing: -.015em;
}
.relacionados__meta {
  font-size: var(--t--1); color: var(--texto-tenue); font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.relacionados a:hover .relacionados__titulo { color: var(--azul); }

/* ------------------------------------------------------------ ficha autor */
.ficha-autor {
  display: flex; align-items: center; gap: 1.25rem; margin-bottom: 2rem; padding: 1.25rem;
  background: var(--degradado-sutil); border: 1px solid var(--borde); border-radius: var(--radio);
  /* Envuelve en móvil: a 320 px la foto y el bloque de texto no caben en una
     fila sin que el nombre se parta letra a letra. */
  flex-wrap: wrap;
}
.ficha-autor__foto {
  display: block; width: 112px; height: 112px; border-radius: 50%; overflow: hidden;
  flex: 0 0 auto; box-shadow: 0 0 0 3px var(--fondo), 0 0 0 5px var(--azul);
}
.ficha-autor__datos { min-width: 0; }
.ficha-autor__nombre { font-weight: 700; font-size: 1.15rem; color: var(--marino); }
.ficha-autor__rol { font-size: .9375rem; color: var(--texto-suave); }
.ficha-autor__perfiles { display: flex; flex-wrap: wrap; gap: .25rem 1.1rem; margin-top: .5rem;
  font-size: .9375rem; font-weight: 600; }

/* ------------------------------------------------ caja de autor del artículo */
.caja-autor {
  display: flex; align-items: flex-start; gap: 1.25rem; flex-wrap: wrap;
  max-width: var(--caja-lectura); margin-block: 1rem 0; padding: 1.25rem;
  background: var(--degradado-sutil); border: 1px solid var(--borde); border-radius: var(--radio);
}
.caja-autor__foto {
  display: block; width: 72px; height: 72px; border-radius: 50%; overflow: hidden; flex: 0 0 auto;
}
.caja-autor__datos { flex: 1 1 16rem; min-width: 0; }
.caja-autor__nombre { font-weight: 700; color: var(--marino); margin-bottom: .3rem; }
.caja-autor__texto { font-size: .9375rem; color: var(--texto-suave); line-height: 1.6; }
.caja-autor__perfiles { display: flex; flex-wrap: wrap; gap: .25rem 1.1rem; margin-top: .6rem;
  font-size: .9375rem; font-weight: 600; }

/* Título que solo existe para los lectores de pantalla: la caja se entiende
   sola mirándola, pero un <aside> sin nombre accesible no se anuncia. */
.visualmente-oculto {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ------------------------------------------------------- grupos de Telegram */
/* min() en el minmax: sin la guarda, la pista no baja de 18rem y la página
   desborda a lo ancho en un móvil de 360 px. Misma trampa que le pasó al pie. */
.lista-grupos {
  display: grid; gap: 1rem; margin-block: 1.5rem 2rem; padding: 0; list-style: none;
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
}
.grupo {
  display: flex; flex-direction: column; gap: .6rem; padding: 1.25rem;
  background: var(--fondo-tarjeta); border: 1px solid var(--borde); border-radius: var(--radio);
}
/* Sin prefijo de contenedor: la prosa vive en :where() y no le gana a una
   clase, así que el componente se describe una vez y vale en cualquier página
   (el `text-decoration: none` ya lo trae .boton de serie). */
.grupo__nombre { font-size: var(--t-3); margin: 0; padding-bottom: 0; border-bottom: 0; }
.grupo__texto { font-size: .9375rem; color: var(--texto-suave); flex: 1 1 auto; margin: 0; }
.grupo__acciones { margin: 0; }
.grupo__acciones .boton { width: 100%; justify-content: center; }

/* -------------------------------------------------------------------- 404 */
.error404 { text-align: center; max-width: 42rem; }
.error404__codigo {
  font-family: var(--mono); font-size: clamp(4rem, 14vw, 7rem); font-weight: 800; line-height: 1;
  background: var(--degradado); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.error404__titulo { font-size: clamp(1.5rem, 4vw, 2rem); margin-block: .5rem 1rem; }
.error404__texto { color: var(--texto-suave); }
.error404 .hero__acciones { justify-content: center; }
.error404__lista { margin-top: 1.5rem; text-align: left; }
.error404__titulo-lista { margin-top: 3rem; text-align: left; font-size: var(--t-4); }
.error404__pista { margin-top: 2rem; color: var(--texto-suave); font-weight: 600; }
/* Las sugerencias son la parte útil de un 404: van con peso de botón, no de
   lista suelta, porque son la acción que se espera que haga el visitante. */
.error404__sugerencias {
  list-style: none; padding: 0; margin: .75rem auto 0; display: grid; gap: .5rem;
  max-width: 34rem; text-align: left;
}
.error404__sugerencias a {
  display: block; padding: .8rem 1.1rem; border-radius: var(--radio);
  background: var(--fondo-tarjeta); border: 1px solid var(--borde-fuerte);
  color: var(--marino); font-weight: 600; text-decoration: none;
  transition: border-color var(--transicion), color var(--transicion);
}
.error404__sugerencias a:hover { border-color: var(--azul-claro); color: var(--azul); }

/* ---------------------------------------------------------- páginas legales */
.aviso-pendiente {
  margin-bottom: 2rem; padding: 1rem 1.25rem; border-radius: var(--radio-s);
  background: #fdf3d8; color: #5c3f0c; border: 1px solid #e8cf94; border-left: 4px solid #c9922a;
}
.falta-dato {
  background: #ffe9a8; color: #6b4a0a; padding: .1em .4em; border-radius: 4px;
  font-weight: 700; font-size: .875em; letter-spacing: .02em;
}
.nota-actualizacion { margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--borde);
  font-size: .875rem; color: var(--texto-tenue); }
:root[data-tema="oscuro"] .aviso-pendiente { background: #3a2a12; color: #f3d9a0; border-color: #6b4d1c; }
:root[data-tema="oscuro"] .falta-dato { background: #6b4d1c; color: #ffe9a8; }

/* -------------------------------------------------------------------- pie */
.pie { background: var(--marino-hondo); color: #c9d7ef; margin-top: 2rem; }
/* Móvil primero: una columna. La rejilla anterior declaraba
   `minmax(16rem, 2fr)` como pista FIJA (fuera del auto-fit), así que no bajaba
   de 256 px, y sumada a la siguiente daba 416 px mínimos: en un móvil de 360
   la página entera desbordaba a lo ancho y el fondo del pie se cortaba. */
.pie__rejilla { display: grid; gap: 2rem; padding-block: 3rem 2rem; }
@media (min-width: 48rem) {
  .pie__rejilla { grid-template-columns: 2fr 1fr 1fr; gap: 2.5rem; }
}
.pie .marca--pie .marca__texto { color: #fff; }
.pie .marca--pie .marca__texto strong {
  background: linear-gradient(120deg, var(--azul-cielo), var(--morado-claro));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.pie__claim { margin-top: .75rem; font-size: .9375rem; color: #9fb3d4; max-width: 28rem; }
.pie__titulo { font-size: .75rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--azul-cielo); margin-bottom: .75rem; }
.pie__lista { list-style: none; padding: 0; display: grid; gap: .5rem; }
.pie__lista a { color: #c9d7ef; text-decoration: none; font-size: .9375rem; }
.pie__lista a:hover { color: #fff; text-decoration: underline; }
.pie__legal { padding-block: 1.5rem; border-top: 1px solid rgba(255, 255, 255, .1); font-size: .8125rem; color: #8fa5c8; }
.pie__nota { margin-top: .4rem; }
.pie a:focus-visible { outline-color: var(--azul-cielo); }
