/*
 Theme Name:   Jellyfin en Español — hijo de Bricks
 Template:     bricks
 Version:      2.2
 Description:  Tema hijo de jellyfinapp.com. v1.0 = migración fiel desde Divi (2026-08-11).
               v2.0 = REDISEÑO INTEGRAL aprobado por Miguel, mockup
               `entregables/mockups/jellyfinapp-rediseno/index.html`.
               v2.2 (2026-08-13) = fila 380 (el índice del artículo, por delante del cuerpo en
               móvil) + el ancla de la sección «¿de qué va esto?» de la portada.
               🔴 OJO: esta cabecera es documentación, NO el cache-buster. La cadena que viaja en
               `?ver=` está escrita a mano en `functions.php` (hoy 2.7.0) y hay que subirla ALLÍ
               en el mismo commit que el CSS. Subir solo esta línea no invalida ninguna caché:
               comprobado el 13-08 gastando dos purgas para nada.
               v2.1 (2026-08-12) = las clases que pide la reescritura F6 de
               /descargar-jellyfin/. NO es un rediseño: el esqueleto aprobado no se toca, se
               estila lo que la página nueva estrena. Cinco cosas, cada una medida donde se
               escribe:
                 · color de enlace en `.jf-bajada`, `.jf-tabla` y `.jf-aviso`, que hasta hoy no
                   llevaban enlaces y se servían en el azul del navegador (1,83:1 sobre el fondo
                   del aviso) — lo único de esta tanda que arregla un fallo de contraste y no una
                   carencia estética. El tercero no venía en el encargo: salió de preguntarle a
                   la página servida el color computado de TODOS sus enlaces;
                 · `.jf-aviso--version`, el aviso de versión a una columna y con acento frío;
                 · los tres acentos que faltaban (`--linux`, `--docker`, `--escritorio`);
                 · `<code>` sobre fondo oscuro (el texto F6 lo usa 30 veces);
                 · la tabla de TV en fichas por debajo de 600 px.
               🔴 NI UNA media query nueva: las fichas se escriben DENTRO del
               `@media (max-width: 600px)` que ya cierra este fichero. Los dos puntos de corte
               de la casa siguen siendo 900 (retículas) y 600 (aprietes de móvil).

 ─────────────────────────────────────────────────────────────────────────────────────────
 DE DÓNDE SALE CADA VALOR

 Los tokens (paleta, tipografía, escala, radios, sombras) son LOS DEL MOCKUP APROBADO, copiados
 uno a uno. La norma de la casa es que el diseño aprobado se RECUPERA, no se reinventa: si algo
 hay que cambiarlo, se cambia en el mockup y se trae, para que las dos fuentes no diverjan.

 Tres desviaciones DELIBERADAS respecto del mockup, todas medidas y anotadas donde ocurren:
   1. `--morado-tinta` (#7E3D96) para enlaces y acentos SOBRE FONDO CLARO. El morado de marca
      (#AA5CC3) da 3,87:1 sobre `--papel` y NO llega al AA de 4,5. El de marca se conserva
      intacto en todo lo que va sobre fondo oscuro (4,53:1) y en los degradados.
   2. Las franjas `.pantalla` del mockup (separadores de las 3 pantallas) NO existen aquí: eran
      andamio del mockup, no diseño de la web.
   3. Se conservan las clases heredadas de Divi (`.blog-content`, `.avance`, `.wp-caption`)
      re-tematizadas: el cuerpo de las 10 entradas las usa DENTRO del HTML del post, así que
      borrarlas cambiaría 10 artículos sin tocar una palabra.

 POR QUÉ ESTE FICHERO NO ES OPCIONAL (se conserva de v1.0, sigue vigente)

 El cuerpo de las 10 entradas y de 3 páginas usa clases CSS que NO están en el contenido:
 estaban en `themes/teky/style.css`, en `wp_options → et_divi → divi_custom_css` y en el post
 `custom_css` del Customizer. Las TRES fuentes mueren o quedan huérfanas al cambiar de tema, y
 ninguna de las tres se ve revisando páginas.

 POR QUÉ EL LAYOUT VIVE AQUÍ Y NO EN LOS AJUSTES DE BRICKS

 🔴 El fallo que motivó este encargo: 5 contenedores quedaron en `flex-direction: row` poniendo
 EN FILA secciones que Divi apilaba — en /descargar-jellyfin/ cada sistema operativo salía en
 una columna con el anuncio encima del texto. Causa de fondo: el conversor traducía los anchos
 de Divi a `_width` de Bricks, y Bricks emite `#brxe-id{width:33.333%}`, un selector de ID que
 vale para TODOS los tamaños de pantalla y que ninguna hoja de estilos puede desbancar.

 La cura estructural es no darle a Bricks ninguna propiedad de layout: las retículas se declaran
 aquí, con `grid-template-columns` y su media query, así que **apilar en móvil es el
 comportamiento por defecto, no algo que haya que acordarse de añadir**. Bricks pone `@layer
 bricks` a sus reglas de base, y una regla sin capa (como todas las de este fichero) gana a
 cualquier regla dentro de una capa sea cual sea su especificidad: por eso `.jf-env` puede
 desbancar el `width:1100px` de `.brxe-container` sin un solo `!important`.
 ─────────────────────────────────────────────────────────────────────────────────────────
*/

/* ══════════════════════════════════ TOKENS (del mockup) ══════════════════════════════════ */
:root {
  --noche:        #0B1020;   /* fondo base, azul de sala a oscuras (no negro puro: cansa) */
  --noche-2:      #131A2E;   /* superficie elevada */
  --noche-3:      #1C2540;   /* bordes y superficies terciarias */
  --tinta:        #EEF1FA;   /* texto principal sobre oscuro   → 16,8:1 */
  --tinta-2:      #A6AFC9;   /* texto secundario, azulado      →  8,7:1 */
  --morado:       #AA5CC3;   /* marca Jellyfin */
  --cian:         #00A4DC;   /* marca Jellyfin */
  --morado-tinta: #7E3D96;   /* ↑ desviación 1: morado legible sobre claro → 6,5:1 */
  --lima:         #3DDC97;   /* semántico: gratis / correcto / disponible */
  --ambar:        #FFB454;   /* semántico: ojo con esto */
  --marca:        linear-gradient(115deg, var(--morado), var(--cian));
  --papel:        #F4F6FC;   /* la única zona clara: la lectura larga */
  --papel-2:      #ffffff;
  --papel-borde:  #DDE3F2;
  --papel-tinta:  #151B2E;   /* → 15,8:1 sobre --papel */
  --papel-tinta-2:#5C688C;   /* → 5,1:1  sobre --papel */
  --radio:        14px;
  --sombra:       0 18px 50px -12px rgb(0 0 0 / .55);
  --ancho:        1200px;
  --lectura:      70ch;
}

/* Base de 10 px (patrón 62,5 % que usa Bricks y sobre el que están escritos todos los `rem`
   de este fichero). Se MIDE en el navegador tras desplegar: en filezilla salió 10 px y con los
   16 px teóricos los títulos habrían salido a la mitad. Cada web tiene el suyo. */
html { font-size: 62.5%; }

body {
  background: var(--noche);
  color: var(--tinta);
  font: 400 1.7rem/1.65 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  line-height: 1.15;
  text-wrap: balance;
  letter-spacing: -.02em;
  font-weight: 800;
}
h1 { font-size: clamp(3.4rem, 1.6rem + 4.4vw, 6.4rem); }
h2 { font-size: clamp(2.8rem, 1.6rem + 2.6vw, 4.2rem); }
h3 { font-size: 2.2rem; }

/* Toda imagen, venga de donde venga, se queda dentro de su caja. El desbordamiento horizontal
   a 375 px fue el fallo nº1 de la auditoría de diseño del piloto. */
img { max-width: 100%; height: auto; }

/* 🔴 Y una palabra larga desborda igual que una imagen. Medido a 375 px en
   /descargar-jellyfin/: el enlace «http://tu_ip_local:8096» de los pasos de Windows llegaba a
   459 px y desplazaba la PÁGINA ENTERA en horizontal — una URL escrita dentro del contenido,
   nada que ver con la maqueta. Se pone como regla general porque el contenido lo escribe un
   humano y las URLs largas van a seguir apareciendo. */
body { overflow-wrap: break-word; }
a, code, kbd { overflow-wrap: anywhere; }

/* ══════════════════════════════════ retícula base ══════════════════════════════════ */

/* `.jf-env` sustituye al ancho fijo de `.brxe-container` (1100 px dentro de @layer bricks). */
.jf-env {
  width: 100%;
  max-width: var(--ancho);
  margin-inline: auto;
  padding-inline: 2.4rem;
  align-items: stretch;
  /* Aire entre piezas hermanas de una sección. Bricks no pone ninguno y los elementos salían
     pegados (las migas contra el antetítulo, el antetítulo contra el H1). Las retículas que
     declaran su propio `gap` más abajo lo pisan. */
  row-gap: 1.6rem;
}

/* 🔴 EN UNA FILA FLEX, TODO HIJO DIRECTO VUELVE A SU ANCHO NATURAL.
   Los `.brxe-*` de Bricks nacen con `width: 100%` dentro de `@layer bricks`. En una retícula da
   igual (el ancho lo manda la retícula), pero en un `display:flex; flex-direction:row` cada hijo
   reclama la línea entera y la fila se APILA. Es el mismo fallo TRES veces en esta web: la línea
   de meta de las tarjetas (se partía en tres y dejaba el «·» solo), las columnas del pie (1.152
   px cada una, pie de 727 de alto) y la franja legal (el crédito y los enlaces en dos líneas).
   Por eso está aquí arriba y en grupo: la lección no es «arregla esta fila», es «toda fila flex
   de este sistema resetea a sus hijos». Las reglas de más abajo que dan un ancho concreto a un
   hijo ganan por especificidad. */
.jf-cta > *, .jf-pie__legal > *, .jf-so > *, .jf-sistema__cab > *,
.jf-tele__pie > * { width: auto; }

.jf-seccion  { padding: 8rem 0; width: 100%; align-items: stretch; position: relative; }

/* ── separación de secciones (2ª pasada de diseño, 2026-08-11) ──────────────────────────────
   El encargo: «gráficos SVG que den color y ayuden a diferenciar las secciones». La frontera
   entre secciones se resuelve con DOS gestos que no cuestan una sola petición ni un píxel de
   layout (y por tanto no pueden mover nada: CLS 0):
     · un filete que va de morado a cian y se apaga en los extremos — es el degradado de marca
       convertido en regla, así que la web se «cose» con su propio color;
     · dos resplandores muy suaves en esquinas opuestas, el mismo gesto que ya hacía el hero,
       que dan profundidad a un fondo que si no es plano.
   Se hacen con `background`/pseudoelementos y NO con un `<img>`: un SVG en gradientes puros es
   exactamente lo mismo que un `radial-gradient` y el gradiente no viaja por la red. */
.jf-seccion {
  background:
    radial-gradient(70rem 34rem at 88% 0%, rgb(0 164 220 / .07), transparent 70%),
    radial-gradient(56rem 28rem at 6% 100%, rgb(170 92 195 / .07), transparent 70%);
}
.jf-seccion--alt {
  background:
    radial-gradient(70rem 34rem at 12% 0%, rgb(170 92 195 / .10), transparent 70%),
    radial-gradient(56rem 28rem at 92% 100%, rgb(0 164 220 / .10), transparent 70%),
    var(--noche-2);
  border-block: 1px solid var(--noche-3);
}
.jf-seccion--alt::before, .jf-seccion--alt::after {
  content: ""; position: absolute; left: 0; right: 0; height: 1px; pointer-events: none;
  background: linear-gradient(90deg, transparent, rgb(170 92 195 / .6) 28%,
                              rgb(0 164 220 / .6) 72%, transparent);
}
.jf-seccion--alt::before { top: -1px; }
.jf-seccion--alt::after  { bottom: -1px; }
.jf-seccion--fina { padding: 0 0 4rem; background: none; }

.jf-et {
  font: 700 1.25rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--tinta-2);
}
/* Guion de marca delante del antetítulo: marca el arranque de cada sección con color, y como es
   un pseudoelemento inline no puede desplazar nada de lo que ya estaba. */
.jf-et p::before {
  content: ""; display: inline-block; vertical-align: middle;
  width: 2.8rem; height: 2px; margin-right: 1.2rem; border-radius: 2px;
  background: var(--marca);
}
.jf-deg {
  background: var(--marca);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.jf-bajada { color: var(--tinta-2); max-width: 62ch; font-size: 1.8rem; }
.jf-mini   { font-size: 1.4rem; color: var(--tinta-2); }
.jf-mini b { color: var(--lima); }

/* 🔴 UN CONTENEDOR DE TEXTO QUE NUNCA HABÍA LLEVADO ENLACES SE SIRVE EN EL AZUL DEL NAVEGADOR.
   Medido el 2026-08-12 sobre la reescritura F6 de /descargar-jellyfin/: la bajada de sección, la
   tabla y el aviso estrenan enlaces (jellyfin.org, repo.jellyfin.org, Google Play, la Channel
   Store de Roku, la Microsoft Store, la documentación de Jellyfin) y ninguna de las tres clases
   estaba estilada. Sin regla propia manda el `-webkit-link` del navegador, **#0000EE, que da
   2,01:1 sobre `--noche` y 1,83:1 sobre el fondo del aviso de versión** — ni siquiera llega al
   3:1 que se le pide a algo que NO es texto. Este fichero estila `.jf-prosa a`, `.jf-sistema a`,
   `.jf-guia a`, `.jf-pie a`, `.jf-art a`, `.jf-so a`, `.jf-tags a`… y estas tres no, porque
   hasta hoy no habían hecho falta.

   La lección no es «píntame estos enlaces»: en este sistema **cada contenedor de texto declara su
   propio color de enlace**, y el que no lo declara hereda el del navegador sin dar ningún error,
   sin desbordar nada y sin que se note en una captura pequeña. Por eso van agrupadas aquí, con la
   familia, y no cada una escondida en su sección: la próxima clase de texto que se invente tiene
   que pasar por este renglón.

   🔴 Y `.jf-aviso a` NO estaba en el encargo: lo canta el navegador, no la lista. Las dos
   primeras venían anotadas; la del aviso apareció al pedirle a la página el color COMPUTADO de
   TODOS sus `<a>` y agruparlos por color — un grupo de un solo enlace en `rgb(0,0,238)`. Es el
   mismo método que hay que repetir cada vez que se toca esta página: la comprobación no es
   «¿están las reglas que me pidieron?», es «¿queda algún enlace del color del navegador?».

   ⚠️ `--cian` y no el morado de marca, y no es indiferente: `--cian` da **6,62:1 sobre
   `--noche`**, **6,04:1 sobre `--noche-2`** (el fondo real de las celdas de `.jf-tabla`, no el de
   la sección), **5,63:1** sobre el aviso ámbar y **6,02:1** sobre el de versión; `--morado`
   (#AA5CC3) da 4,53 sobre `--noche` pero 4,14 sobre `--noche-2` y 3,85 sobre el aviso ámbar, o
   sea que en los tres sitios nuevos se caería del AA.
   ⚠️ Y las tres clases viven SIEMPRE sobre oscuro — comprobado en el generador: las 8
   apariciones de `.jf-bajada` (portada, archivo, 404 y las cuatro de /descargar-jellyfin/)
   cuelgan de `.jf-seccion`, ninguna de `.jf-art`. Es lo que hace legítimo el cian aquí: sobre
   `--papel` daría 2,65:1, y si algún día una de estas cajas baja a la zona clara hay que darle
   `--morado-tinta`, no arrastrar esta regla. */
.jf-bajada a, .jf-tabla a, .jf-aviso a { color: var(--cian); }

/* ══════════════════════════════════ cabecera ══════════════════════════════════ */
/* 🔴 EL `backdrop-filter` DE LA CABECERA SE COMÍA EL MENÚ MÓVIL ENTERO (medido el 2026-08-11).
   Un elemento con `backdrop-filter` distinto de `none` **es bloque contenedor de sus
   descendientes `position: fixed`**. El panel deslizante de Bricks y su velo son fixed y viven
   DENTRO de la cabecera, así que dejaban de referirse a la ventana y pasaban a referirse a la
   cabecera. Grupo de control, a 375 px y con la página desplazada 600 px:

     con blur en `.jf-top`  → panel `top: -600`, velo `top: -600` y **72 px de alto** (recortado
                              a la caja de la cabecera), aspa en `-572`: al tocar la hamburguesa
                              NO PASA NADA VISIBLE y, abierto, **no hay forma de cerrarlo** salvo
                              la tecla Esc, que en un móvil no existe.
     sin blur               → panel `top: 0`, velo `top: 0` y 812 px de alto. Correcto.

   El efecto se conserva: se muda al `::before`, que no tiene descendientes y por tanto no atrapa
   a nadie. Es la misma cura de siempre —quitar la propiedad del elemento que estorba, no el
   efecto— y deja el aspecto idéntico. Falla F2.5 del protocolo: «el menú abierto SE PUEDE
   CERRAR», que hasta ahora se comprobaba sin desplazar la página. */
/* 🔴 Y EL `position: sticky` DE ESTA MISMA REGLA NO PEGABA NADA (medido el 2026-08-12).
   Un `sticky` se mueve DENTRO de la caja de su padre y no un píxel más. El padre aquí es el
   `<header id="brx-header">` que pinta Bricks, y mide **exactamente lo que la cabecera**: 73 px
   de cabecera dentro de 73 px de contenedor = **cero recorrido**. Medido a 1280: con la página
   desplazada 1.500 px, `#brx-header.getBoundingClientRect().top` = **-1500**, o sea la cabecera
   se iba con el resto del documento. Era una intención escrita que no se cumplía: código muerto
   que el siguiente que lo lea dará por bueno.

   La cura es subir el `sticky` al elemento que SÍ tiene recorrido —el `<header>`, hijo directo
   del `<body>`, cuyo bloque contenedor es el documento entero— y dejar aquí solo el `relative`
   que el `::before` del desenfoque necesita como referencia.

   Y se enciende SOLO a partir de 900 px, que es la decisión: en escritorio sobra alto y tener la
   navegación siempre a mano ayuda a saltar de guía en guía (más páginas por sesión = más
   impresiones); en móvil la cabecera come **73 px de 812**, casi un 9 % de pantalla, y ese
   espacio en la mitad superior es justo donde se ven los anuncios. Por debajo de 900 px la
   cabecera es una cabecera normal que se va con el scroll.

   ⚠️ `position: sticky` NO es de la familia de `transform`/`filter`/`backdrop-filter`: **no**
   crea bloque contenedor para los descendientes `position: fixed`, así que no puede repetir el
   fallo del menú móvil (que además vive por debajo de 768 px, donde esto ni se aplica). Sí crea
   contexto de apilamiento, y por eso el `z-index` se muda con él. */
.jf-top {
  position: relative; z-index: 50;
  background: transparent;
  border-bottom: 1px solid var(--noche-3);
  padding: 0;
}
@media (min-width: 900px) {
  #brx-header { position: sticky; top: 0; z-index: 50; }
  /* Con la cabecera fija, un salto a un ancla (`#macos` de /descargar-jellyfin/, los del índice
     lateral) deja el destino DEBAJO de ella. `scroll-padding-top` es la única forma de arreglarlo
     sin tocar ni un `id`, y no ocupa layout: no puede mover nada (CLS 0). 73 px de cabecera + un
     respiro. */
  html { scroll-padding-top: 9rem; }
}
.jf-top::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: rgb(11 16 32 / .82);
  backdrop-filter: blur(12px);
}
.jf-top > * { position: relative; }
.jf-top__in {
  display: flex; flex-direction: row; align-items: center;
  gap: 3.2rem; min-height: 7.2rem; flex-wrap: nowrap;
}
.jf-logo {
  display: inline-flex; align-items: center; gap: 1rem;
  font-weight: 800; font-size: 2rem; letter-spacing: -.03em;
  color: var(--tinta); text-decoration: none;
}
.jf-logo svg  { width: 3.2rem; height: 3.2rem; flex: none; }
.jf-logo span { color: var(--tinta-2); font-weight: 600; }
.jf-top .brxe-nav-menu { margin-left: auto; }
/* El panel deslizante no llevaba fondo propio (todo transparente): lo que se veía era el desenfoque
   del velo. En una entrada —cuerpo sobre `--papel`— eso deja los rótulos BLANCOS sobre claro en
   cuanto el desenfoque no tapa del todo. Se le da el fondo de marca, y sitio arriba para que el
   primer enlace («Descargar Jellyfin», el CTA que más vale de la web) deje de compartir píxeles
   con el aspa de cerrar: medido, el aspa cae en y=28-44 y el enlace ocupaba y=0-60. */
.jf-top .bricks-mobile-menu-wrapper {
  background: var(--noche-2); border-right: 1px solid var(--noche-3);
}
.jf-top .bricks-mobile-menu { padding-top: 8rem; }
.jf-top .bricks-mobile-menu > li > a { color: var(--tinta); }
.jf-top .bricks-mobile-menu-overlay { background: rgb(11 16 32 / .6); }
/* Los rótulos del menú de esta web son largos («Preguntas frecuentes», «¿Qué es Jellyfin?») y
   sin `nowrap` cada uno se partía en dos líneas y la cabecera crecía al doble. El mockup los
   dibuja cortos; aquí manda el menú real, así que se aprieta el hueco en vez de recortar textos
   (los rótulos son los que rankean y no se tocan sin medir). */
.jf-top .bricks-nav-menu { gap: 2.4rem; }
.jf-top .bricks-nav-menu > li > a {
  font-weight: 600; font-size: 1.5rem; color: var(--tinta-2);
  text-decoration: none; white-space: nowrap;
}
.jf-top .bricks-nav-menu > li > a:hover { color: var(--tinta); }
.jf-top .brxe-button { white-space: nowrap; flex: none; }
@media (max-width: 1100px) and (min-width: 768px) {
  .jf-top__in { gap: 1.6rem; }
  .jf-top .bricks-nav-menu { gap: 1.6rem; }
  .jf-top .bricks-nav-menu > li > a { font-size: 1.4rem; }
}

/* botón de marca */
.jf-btn, .jf-top .brxe-button {
  display: inline-flex; align-items: center; gap: .8rem;
  padding: 1.2rem 2.2rem; border-radius: 99px;
  font-weight: 700; text-decoration: none; border: 0;
  background: var(--marca); color: #0B1020; font-size: 1.6rem;
}
.jf-btn--fant { background: transparent; color: var(--tinta); border: 1px solid var(--noche-3); }
.jf-btn--g    { padding: 1.6rem 3rem; font-size: 1.8rem; }
.jf-btn:hover { filter: brightness(1.08); color: #0B1020; }
.jf-btn--fant:hover { color: var(--tinta); border-color: var(--morado); }

/* ══════════════════════════════════ hero ══════════════════════════════════ */
.jf-hero { position: relative; overflow: hidden; padding: 9rem 0 7rem; }
.jf-hero::before {
  content: ""; position: absolute; inset: -30% -10% auto; height: 70rem;
  background:
    radial-gradient(60% 60% at 30% 20%, rgb(170 92 195 / .28), transparent 70%),
    radial-gradient(50% 50% at 75% 10%, rgb(0 164 220 / .22), transparent 70%);
  filter: blur(20px); pointer-events: none;
}
.jf-hero__in {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: 1.05fr .95fr; gap: 5.6rem; align-items: center;
}
.jf-hero__txt  { display: flex; flex-direction: column; gap: 0; }
.jf-hero__txt h1 { margin-top: 1.6rem; }
.jf-entrada { font-size: 2rem; color: var(--tinta-2); max-width: 52ch; margin-top: 2rem; }
.jf-cta { display: flex; flex-direction: row; gap: 1.4rem; flex-wrap: wrap; margin-top: 3.2rem; align-items: center; }

/* la prueba visual: una biblioteca con carátulas, hecha con CSS (nada de captura ajena) */
.jf-tele {
  background: var(--noche-2); border: 1px solid var(--noche-3);
  border-radius: var(--radio); padding: 1.8rem; box-shadow: var(--sombra);
}
.jf-tele__barra { display: flex; gap: .6rem; margin-bottom: 1.6rem; }
.jf-tele__barra i { width: 1rem; height: 1rem; border-radius: 50%; background: var(--noche-3); display: block; }
.jf-rejilla { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.2rem; }
.jf-car {
  aspect-ratio: 2/3; border-radius: 8px; position: relative; overflow: hidden;
  display: flex; align-items: flex-end; padding: 1rem;
  font-weight: 700; font-size: 1.3rem; text-shadow: 0 1px 6px rgb(0 0 0 / .8);
}
.jf-car::after { content: ""; position: absolute; inset: 0; background: linear-gradient(transparent 45%, rgb(0 0 0 / .75)); }
.jf-car span   { position: relative; z-index: 1; line-height: 1.2; }
.jf-c1 { background: linear-gradient(160deg, #4A2B6B, #8E4FB0); }
.jf-c2 { background: linear-gradient(160deg, #123A5C, #00A4DC); }
.jf-c3 { background: linear-gradient(160deg, #5C2438, #C3566B); }
.jf-c4 { background: linear-gradient(160deg, #1F4A3C, #3DDC97); }
.jf-c5 { background: linear-gradient(160deg, #3A2E6B, #6C63D6); }
.jf-c6 { background: linear-gradient(160deg, #5A3A16, #FFB454); }
.jf-c7 { background: linear-gradient(160deg, #2B2F4A, #57608E); }
.jf-c8 { background: linear-gradient(160deg, #4A1F52, #AA5CC3); }
.jf-tele__pie { display: flex; align-items: center; gap: 1rem; margin-top: 1.6rem; font-size: 1.4rem; color: var(--tinta-2); }
.jf-punto { width: .8rem; height: .8rem; border-radius: 50%; background: var(--lima); flex: none; box-shadow: 0 0 0 4px rgb(61 220 151 / .18); }

/* ══════════════════════════════════ el viaje en 4 pasos ══════════════════════════════════ */
.jf-pasos {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 2rem;
  margin-top: 4rem; counter-reset: p; width: 100%; align-items: stretch;
}
.jf-paso {
  background: var(--noche); border: 1px solid var(--noche-3); border-radius: var(--radio);
  padding: 2.6rem 2.2rem; position: relative; display: flex; flex-direction: column;
  height: 100%; transition: .18s;
}
.jf-paso:hover { border-color: var(--morado); transform: translateY(-3px); }
.jf-paso::before {
  counter-increment: p; content: counter(p);
  font: 800 1.4rem/1 ui-monospace, monospace; color: var(--noche);
  background: var(--marca); width: 3rem; height: 3rem; border-radius: 50%;
  display: grid; place-items: center; margin-bottom: 1.6rem; flex: none;
}
.jf-paso h3 { font-size: 1.9rem; margin-bottom: .8rem; color: var(--tinta); }
.jf-paso p  { font-size: 1.5rem; color: var(--tinta-2); margin: 0; }
/* La tarjeta entera es pinchable estirando ESTE enlace sobre ella: un solo `<a>` en el árbol de
   accesibilidad, con el texto que describe el destino. */
.jf-paso__t { display: block; margin-top: auto; padding-top: 1.6rem; font-size: 1.4rem; font-weight: 700; color: var(--cian); text-decoration: none; }
.jf-paso__t::after { content: ""; position: absolute; inset: 0; }

/* ══════════════════════════════════ comparativa ══════════════════════════════════ */
.jf-tabla {
  width: 100%; border-collapse: collapse; margin-top: 3.6rem; font-size: 1.6rem;
  background: var(--noche-2); border-radius: var(--radio); overflow: hidden;
}
.jf-tabla th, .jf-tabla td { padding: 1.8rem; text-align: left; border-bottom: 1px solid var(--noche-3); }
.jf-tabla thead th { background: var(--noche-3); font-size: 1.4rem; letter-spacing: .04em; text-transform: uppercase; }
.jf-tabla td:first-child { font-weight: 600; }
.jf-si  { color: var(--lima);   font-weight: 700; }
.jf-no  { color: var(--tinta-2); }
.jf-ojo { color: var(--ambar);  font-weight: 700; }

/* ══════════════════════════════════ descargas ══════════════════════════════════ */
.jf-dos { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2.4rem; margin-top: 1rem; width: 100%; align-items: stretch; }
.jf-tarjeta {
  background: var(--noche); border: 1px solid var(--noche-3);
  border-radius: var(--radio); padding: 3rem; height: 100%;
}
.jf-tarjeta--srv { border-color: rgb(170 92 195 / .5); }
.jf-tarjeta--cli { border-color: rgb(0 164 220 / .5); }
.jf-tarjeta h2, .jf-tarjeta h3 { display: flex; align-items: center; gap: 1.2rem; margin-bottom: 1rem; font-size: 2.4rem; flex-wrap: wrap; }
.jf-chip {
  font: 700 1.2rem/1 ui-monospace, monospace; letter-spacing: .1em; text-transform: uppercase;
  padding: .6rem 1rem; border-radius: 99px; border: 1px solid currentColor;
}
.jf-chip--srv { color: var(--morado); }
.jf-chip--cli { color: var(--cian); }
.jf-tarjeta p { color: var(--tinta-2); font-size: 1.6rem; }
.jf-so { display: flex; flex-direction: row; flex-wrap: wrap; gap: 1rem; margin-top: 2.4rem; }
.jf-so a {
  display: inline-flex; align-items: center; gap: .8rem; padding: 1.1rem 1.6rem;
  border-radius: 10px; background: var(--noche-2); border: 1px solid var(--noche-3);
  text-decoration: none; font-weight: 600; font-size: 1.5rem; color: var(--tinta);
}
.jf-so a:hover { border-color: var(--cian); color: var(--tinta); }

/* El aviso «son dos programas distintos» + su diagrama. Es la confusión número uno del nicho y
   hasta la 2ª pasada solo se explicaba con palabras. */
.jf-aviso {
  display: grid; grid-template-columns: minmax(0, 1fr) 30rem; gap: 2.4rem 3.6rem;
  align-items: center; margin-top: 3.2rem; padding: 2.4rem;
  border-radius: var(--radio); background: rgb(255 180 84 / .1);
  border: 1px solid rgb(255 180 84 / .3); font-size: 1.6rem;
}
.jf-aviso b { color: var(--ambar); }
.jf-aviso p { margin: 0; }
.jf-aviso__txt { display: flex; flex-direction: column; gap: 1rem; }
.jf-aviso__ojo {
  font: 700 1.2rem/1 ui-monospace, monospace; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ambar);
}
.jf-diag { display: flex; flex-direction: column; gap: 1.2rem; }
.jf-diag__svg { width: 100%; height: auto; display: block; }
.jf-diag__pie { font-size: 1.3rem; color: var(--tinta-2); line-height: 1.5; }
.jf-diag__pie b { color: var(--tinta); }

/* ── el aviso de versión (reescritura F6, 2026-08-12) ────────────────────────────────────────
   Reutiliza la caja del aviso porque es el mismo gesto —«párate y lee esto»— pero NO lleva
   diagrama al lado, y `.jf-aviso` reserva una segunda columna fija de 30 rem para él. Medido a
   1280 con un solo hijo: el texto se estrangulaba a 766 px y quedaban ~386 px de caja vacía a la
   derecha. Con `1fr` la caja es una y ocupa lo suyo.

   Y cambia el color por una razón de contenido, no de gusto: en esta página el ÁMBAR está
   reservado a la confusión número uno del nicho (servidor vs cliente), que va justo debajo. Dos
   avisos ámbar seguidos compiten entre sí y el segundo deja de leerse como aviso. Este es
   información que caduca, no un peligro: acento frío.

   Contrastes medidos sobre el fondo COMPUESTO (`--cian` al 8 % sobre `--noche` = #0A1C2F), que
   es el que ve el ojo, no el token:
     · texto `--tinta`   → 15,24:1
     · texto `--tinta-2` →  7,87:1
     · rótulo `--cian`   →  6,02:1  (1,2 rem en negrita: le basta con 4,5)
   El borde (cian al 30 % = #083C58) da 1,62:1 contra `--noche`: es un filete decorativo, la misma
   cifra y el mismo criterio que el borde ámbar del aviso de al lado. No transporta información.

   A 375 px no hace falta nada más: la media query de 900 ya pone `.jf-aviso` a `1fr` y este
   modificador se queda sin trabajo. Si no llegara a escribirse, esto se ve como el otro aviso
   (ámbar y con hueco a la derecha): degrada, no rompe. */
.jf-aviso--version {
  grid-template-columns: 1fr;
  background: rgb(0 164 220 / .08);
  border-color: rgb(0 164 220 / .3);
}
.jf-aviso--version .jf-aviso__ojo { color: var(--cian); }

/* ── los pasos por sistema operativo ─────────────────────────────────────────────────────────
   Antes: cinco cajas idénticas —mismo fondo, mismo ancho, sin un icono— apiladas en 2.056 px de
   muro gris. El fondo era además el MISMO `--noche-2` de la sección que las contiene, así que ni
   se recortaban contra ella.
   Ahora: retícula de dos columnas (la de macOS ocupa las dos porque es la única con servidor Y
   cliente), tarjeta sobre `--noche` para que despegue del fondo, y cada sistema con su acento y
   su icono. El acento se declara UNA vez por tarjeta en `--acc` y lo heredan el icono, el filete
   superior, los h3 y los enlaces: identidad sin repetir un hex en seis reglas. */
.jf-sistemas {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 2.4rem;
  margin-top: 4rem; width: 100%; align-items: start;
}
.jf-sistema {
  --acc: var(--cian); --acc-suave: rgb(0 164 220 / .14);
  background: var(--noche); border: 1px solid var(--noche-3); border-radius: var(--radio);
  padding: 2.8rem; width: 100%; position: relative; overflow: hidden; transition: .18s;
}
.jf-sistema::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--acc), transparent 85%);
}
.jf-sistema::after {   /* el resplandor del acento, esquina superior derecha */
  content: ""; position: absolute; top: -14rem; right: -10rem; width: 28rem; height: 28rem;
  border-radius: 50%; background: var(--acc-suave); filter: blur(30px); pointer-events: none;
}
.jf-sistema:hover { border-color: var(--acc); transform: translateY(-3px); }
.jf-sistema--ancho { grid-column: 1 / -1; }
/* 🔴 macOS NO puede llevar el plateado `--tinta-2` (#A6AFC9), que fue el primer intento: es
   EXACTAMENTE el color del texto secundario, así que los enlaces de dentro de la tarjeta
   quedaban indistinguibles de la prosa que los rodea. Un acento que no se distingue del texto
   no es un acento. Se usa un índigo derivado del que ya está en el mockup aprobado (la carátula
   `.jf-c5`, #6C63D6), aclarado a #9A93F0 para llegar al AA sobre `--noche` (6,6:1 frente a los
   3,8:1 del original). Es la 4ª desviación declarada respecto del mockup. */
.jf-sistema--macos     { --acc: #9A93F0; --acc-suave: rgb(154 147 240 / .13); }
.jf-sistema--windows   { --acc: #00A4DC; --acc-suave: rgb(0 164 220 / .14); }
.jf-sistema--android   { --acc: #3DDC97; --acc-suave: rgb(61 220 151 / .13); }
.jf-sistema--androidtv { --acc: #AA5CC3; --acc-suave: rgb(170 92 195 / .16); }
.jf-sistema--ios       { --acc: #FFB454; --acc-suave: rgb(255 180 84 / .12); }
/* ── los tres sistemas que estrena la reescritura F6 (2026-08-12) ────────────────────────────
   Mismo criterio que los cinco de arriba: UNA decisión por tarjeta (`--acc`) que tiñe el filete,
   el resplandor, los h3 y los enlaces. Sin esto heredan el `--acc` por defecto de `.jf-sistema`,
   que es `--cian`: correcto de contraste, pero deja Windows, Linux, Docker y Escritorio pintados
   del mismo color y la retícula vuelve a ser el muro gris que la 2ª pasada quitó.

   Sobre `--noche` (fondo de la tarjeta) y sobre `--noche-2` (el de `.jf-seccion--alt`, que es
   donde cae la retícula del servidor), medidos los dos:
     · #FFB454 linux      → 10,74:1 / 9,80:1
     · #3DDC97 docker     → 10,71:1 / 9,78:1
     · #9A93F0 escritorio →  7,04:1 / 6,43:1
   Los tres son de la paleta ya aprobada (`--ambar`, `--lima` y el índigo de la carátula `.jf-c5`
   aclarado, el mismo que macOS): se escriben en hex como los cinco de arriba porque `--acc` es
   un valor, no un token nuevo — no se inventa una segunda paleta.

   ⚠️ NINGÚN acento se repite dentro de su retícula, que es la comprobación que importa:
     servidor  → windows cian · macOS índigo · Linux ámbar · Docker lima
     cliente   → Android lima · Android TV morado · iOS ámbar · Escritorio índigo
   Ni en las dos columnas de escritorio (Linux y Docker son `--ancho` y van uno debajo de otro;
   Escritorio queda a la derecha de iOS y debajo de Android TV) ni al apilar a una columna por
   debajo de 900 px hay dos vecinos del mismo color. El índigo se repite entre macOS y Escritorio
   y el lima entre Docker y Android, pero en SECCIONES distintas y sin verse nunca juntos: con
   cinco colores para ocho tarjetas la repetición es inevitable, y el sitio donde no puede
   ocurrir es dentro de la misma retícula.

   ⚠️ Estas tres tarjetas no llevan icono (el mu-plugin no tiene `so-linux`, `so-docker` ni
   `so-escritorio`, y `[angi_svg]` con un id inexistente devuelve cadena vacía: la pastilla de
   5,2 rem saldría pintada y VACÍA). No hace falta ninguna regla para eso: sin `.brxe-shortcode`
   dentro de `.jf-sistema__cab` no hay pastilla que pintar. Cuando el mu-plugin los tenga, se
   dibujan solos y este bloque no cambia. */
.jf-sistema--linux      { --acc: #FFB454; --acc-suave: rgb(255 180 84 / .12); }
.jf-sistema--docker     { --acc: #3DDC97; --acc-suave: rgb(61 220 151 / .13); }
.jf-sistema--escritorio { --acc: #9A93F0; --acc-suave: rgb(154 147 240 / .13); }
/* Cabecera de la tarjeta: icono en su pastilla + título. El `.brxe-shortcode` es el envoltorio
   que Bricks pone alrededor del SVG; se le da forma a él para no meter un div de más. */
.jf-sistema__cab {
  display: flex; flex-direction: row; align-items: center; gap: 1.6rem;
  margin-bottom: 2rem; position: relative; z-index: 1;
}
.jf-sistema__cab > .brxe-shortcode {
  flex: none; width: 5.2rem; height: 5.2rem; border-radius: 13px;
  background: var(--acc-suave); border: 1px solid var(--acc-suave);
  color: var(--acc); display: grid; place-items: center;
}
.jf-sistema__cab svg { width: 2.8rem; height: 2.8rem; display: block; }
.jf-sistema__cab h2 { margin: 0; font-size: 2.2rem; }
.jf-sistema > *:not(.jf-sistema__cab) { position: relative; z-index: 1; }
.jf-sistema h3 { font-size: 1.7rem; margin: 2.4rem 0 1rem; color: var(--acc); }
.jf-sistema ol, .jf-sistema ul { margin: 0 0 0 2.2rem; color: var(--tinta-2); font-size: 1.6rem; }
.jf-sistema li { margin-bottom: .8rem; }
.jf-sistema a  { color: var(--acc); }

/* ══════════════════════════════════ tarjetas de guía ══════════════════════════════════ */
.jf-rej3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2.4rem; margin-top: 4rem; width: 100%; align-items: stretch; }
.jf-guia {
  position: relative; background: var(--noche-2); border: 1px solid var(--noche-3);
  border-radius: var(--radio); overflow: hidden; transition: .18s; height: 100%;
  display: flex; flex-direction: column;
}
.jf-guia:hover { transform: translateY(-3px); border-color: var(--cian); }
.jf-guia__c { padding: 2.2rem; display: flex; flex-direction: column; gap: .8rem; }
.jf-guia h2, .jf-guia h3 { font-size: 1.8rem; margin: 0; }
/* Tarjeta entera pinchable SIN envolverla en un <a>: el enlace del título se estira sobre la
   tarjeta. Así el lector de pantalla anuncia un solo enlace con el texto del título, en vez de
   un <a> gigante que contiene todo. */
.jf-guia a { color: var(--tinta); text-decoration: none; }
.jf-guia a::after { content: ""; position: absolute; inset: 0; }
.jf-guia p { font-size: 1.5rem; color: var(--tinta-2); margin: 0; }
.jf-guia__m { display: flex; flex-direction: row; gap: 1.2rem; margin-top: auto; padding-top: 1.2rem; font-size: 1.3rem; color: var(--tinta-2); align-items: center; flex-wrap: wrap; }
/* 🔴 Cada pieza del meta es un elemento de Bricks (un `shortcode`, un `text`) y el `text` trae
   su `<p>`, que es BLOQUE: sin esto la línea se parte en tres y el separador «·» aparecía solo,
   en su propia línea, con toda la pinta de ser un punto perdido. Medido en el staging. */
.jf-guia__m > *, .jf-art__meta > * { display: inline-flex; align-items: center; width: auto; }
.jf-guia__m p, .jf-art__meta p { margin: 0; display: inline; }
.jf-guia__m > * + *::before { content: "·"; margin-right: .8rem; }
/* La rejilla del archivo reutiliza la tarjeta, pero con el título de Bricks dentro del loop. */
.jf-guia .brxe-post-title a { color: var(--tinta); text-decoration: none; }
.jf-guia--simple .brxe-post-excerpt { font-size: 1.5rem; color: var(--tinta-2); }

/* ── portadas SVG de las entradas ([angi_portada]) ───────────────────────────────────────────
   Sustituyen a la franja de degradado que hacía de cabecera de tarjeta: cada entrada tiene ahora
   su portada, con el motivo de su tema y su tono. CLS 0 por construcción: el `viewBox` + el
   `height:auto` reservan la caja por PROPORCIÓN antes de que se pinte nada. */
.jf-portada { display: block; width: 100%; height: auto; }
.jf-portada__a { display: block; line-height: 0; }
.jf-guia__port { line-height: 0; overflow: hidden; }
.jf-guia__port .jf-portada { transition: transform .35s cubic-bezier(.2, .7, .3, 1); }
.jf-guia:hover .jf-portada { transform: scale(1.04); }
/* En la entrada, la portada abre la lectura y se acota al ancho del texto: una banda a todo el
   ancho de la columna dejaría el dibujo más ancho que el artículo que ilustra. */
.jf-art__portada {
  max-width: var(--lectura); margin: 0 0 3.2rem; border-radius: var(--radio); overflow: hidden;
  border: 1px solid var(--papel-borde); line-height: 0;
}

/* ══════════════════════════════════ artículo (zona clara) ══════════════════════════════════ */
.jf-art { background: var(--papel); color: var(--papel-tinta); padding: 6rem 0; }
.jf-art__in {
  display: grid; grid-template-columns: minmax(0, 1fr) 30rem; gap: 4.8rem; align-items: start;
}
.jf-art__main { min-width: 0; }
.jf-art h1 { font-size: 4rem; color: var(--noche); }
.jf-art h2, .jf-art h3, .jf-art h4 { color: var(--noche); }
.jf-art__meta {
  display: flex; flex-direction: row; gap: 1.6rem; font-size: 1.4rem;
  color: var(--papel-tinta-2); margin: 1.6rem 0 3.2rem; align-items: center; flex-wrap: wrap;
}
.jf-art__meta > * + *::before { content: "·"; color: var(--papel-tinta-2); margin-right: .8rem; }
/* 🔴 Un `position:sticky` dentro de una retícula NO se pega si su celda mide lo mismo que él:
   el sticky viaja dentro de su caja y ahí no hay recorrido. La celda se estira (`align-self`) y
   el rail se acota a la altura de pantalla, o un índice largo nunca se vería entero. */
.jf-art__lat {
  display: flex; flex-direction: column; gap: 2.4rem;
  position: sticky; top: 9rem; align-self: start;
  max-height: calc(100vh - 11rem); overflow-y: auto;
}
/* 🔴 EL LATERAL SE ADELANTA A LOS COMENTARIOS, Y ESO SE DECIDE AQUÍ, NO EN EL CONSTRUCTOR.
   En móvil la retícula colapsa a una columna y el orden pasa a ser el del DOCUMENTO. Con los
   comentarios dentro de `.jf-art__main` —donde estaban— el lateral caía DETRÁS de ellos, y con
   él **una de las 3 unidades de anuncio de la entrada**: medido a 375 px en
   /como-acceder…/, el primer comentario arrancaba en y=7.211 y el anuncio del lateral en
   **y=12.389**, o sea 5.178 px más abajo, al final de un documento de 13.707. Una unidad
   regalada justo en las páginas que traen el tráfico (~113 clics en 3 meses) y en el dispositivo
   donde está el grueso del nicho.

   La cura tiene DOS mitades y ninguna sirve sola:
     · los comentarios salen de `.jf-art__main` a su propio bloque `.jf-art__coment`, TERCER hijo
       de la retícula, para que el orden del DOM sea artículo → lateral → comentarios. El CSS
       `order` no habría valido: cambia lo que se pinta, no el orden del documento, y el orden del
       documento es el que manda para un lector de pantalla y para el recorrido con teclado;
     · y en escritorio se declara la colocación a mano, para que **nada cambie ahí**: el lateral
       sigue en la columna derecha y los comentarios debajo del artículo, en la izquierda. El
       lateral abarca las dos filas (`grid-row: 1 / span 2`) para conservar exactamente el
       recorrido que hoy tiene su `sticky` — si solo ocupara la fila del artículo, el rail dejaría
       de pegarse antes.

   La separación con el formulario de comentarios NO es cosmética: la política de AdSense pide que
   la publicidad no quede pegada a un elemento interactivo ni pueda confundirse con él. Entre el
   anuncio y los comentarios quedan la tarjeta «Más guías» y el filete de `.jf-art__coment`. */
.jf-art__main   { grid-column: 1; grid-row: 1; }
.jf-art__lat    { grid-column: 2; grid-row: 1 / span 2; }
.jf-art__coment { grid-column: 1; grid-row: 2; min-width: 0; }
/* El anuncio que `angi-navegacion.php` inserta DENTRO del cuerpo, tras la primera sección. */
.angi-ad-articulo { margin: 3.2rem 0; clear: both; }

/* migas (shortcode [angi_migas] de angi-navegacion.php) */
.angi-migas ol { list-style: none; display: flex; flex-wrap: wrap; gap: .6rem; margin: 0; padding: 0; font-size: 1.4rem; }
.angi-migas li + li::before { content: "›"; margin-right: .6rem; color: var(--papel-tinta-2); }
.jf-art .angi-migas, .jf-art .angi-migas a { color: var(--papel-tinta-2); }
.jf-banda .angi-migas, .jf-banda .angi-migas a { color: var(--tinta-2); }

/* índice lateral (shortcode [angi_toc]) */
.angi-toc {
  background: var(--papel-2); border: 1px solid var(--papel-borde);
  border-radius: var(--radio); padding: 2.2rem;
}
.angi-toc h4 {
  font-size: 1.3rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--papel-tinta-2); margin-bottom: 1.4rem; font-weight: 700;
}
.angi-toc ol { list-style: none; counter-reset: t; display: flex; flex-direction: column; gap: 1rem; margin: 0; padding: 0; }
.angi-toc li { counter-increment: t; font-size: 1.5rem; }
.angi-toc a  { text-decoration: none; color: var(--papel-tinta); display: flex; gap: .8rem; }
/* 🔴 `flex: none` NO es decorativo: el `::before` es un hijo flex y, como todo hijo flex, se
   encoge por defecto (`flex-shrink: 1`). Con el título del epígrafe al lado reclamando sitio, la
   caja del número se estrangulaba a **7,3 px de ancho y 74 px de alto**: el «01» se partía en un
   «0» encima de un «1», estirado a lo alto de todo el enlace. Medido en el staging el 11-08 —en
   las 10 entradas con índice—, y es el MISMO fallo de familia que el pie: un hijo de una fila
   flex al que nadie le dijo que no encogiera. */
.angi-toc a::before {
  content: counter(t, decimal-leading-zero); flex: none;
  font: 700 1.2rem/1.5 ui-monospace, monospace; color: var(--morado-tinta);
}
.angi-toc a:hover { color: var(--morado-tinta); }

.angi-lectura { display: inline-flex; align-items: center; gap: .5rem; }

/* «Más guías» del lateral: mismo lenguaje que el índice, sin numerar. */
.jf-art__mas { background: var(--papel-2); border: 1px solid var(--papel-borde); border-radius: var(--radio); padding: 2.2rem; }
.jf-art__mas h4 { font-size: 1.3rem; letter-spacing: .12em; text-transform: uppercase; color: var(--papel-tinta-2); margin-bottom: 1.4rem; font-weight: 700; }
.jf-art__mas .brxe-post-title { font-size: 1.5rem; margin-bottom: 1rem; line-height: 1.35; }
.jf-art__mas a { color: var(--papel-tinta); text-decoration: none; }
.jf-art__mas a:hover { color: var(--morado-tinta); }

/* ══════════════════════════════════ cuerpo del artículo ══════════════════════════════════ */
/* `.blog-content` es la clase que Divi colgaba del módulo post_content en el body layout 547.
   En Bricks la lleva el elemento `post-content`. Se conserva porque el HTML de las 10 entradas
   la referencia por dentro. */
.blog-content {
  max-width: var(--lectura);
  font-size: 1.75rem;
  line-height: 1.75;
  color: var(--papel-tinta);
}
.blog-content h2 { font-size: 2.6rem; margin: 4rem 0 1.6rem; color: var(--noche); }
.blog-content h3 { font-size: 2.1rem; margin: 3.2rem 0 1.2rem; color: var(--noche); }
.blog-content h4, .blog-content h5, .blog-content h6 { color: var(--noche); font-weight: 700; }
.blog-content p  { margin-bottom: 1.8rem; }
/* 🔴 El rojo puro que viene DENTRO del cuerpo de un artículo, en un `style=` de hace años.
   `#FF0000` da **3,70:1** sobre `--papel` (y 4,00:1 sobre blanco: tampoco pasaba antes del
   rediseño, así que no es una regresión — es una deuda que ahora se ve). Y lo que dice es un
   aviso de seguridad: «te rogamos que no cambies nada en tu router si no sabes muy bien lo que
   haces», en /como-acceder-a-mis-contenidos-de-jellyfin-desde-fuera-de-casa/. Un aviso que no
   se lee bien es el peor sitio donde dejar 3,70.
   Se remapea al rojo accesible (5,76:1) conservando la intención —sigue siendo rojo, sigue
   siendo énfasis— y SIN tocar el texto de Miguel. El `!important` es aquí legítimo y único: un
   `style=` en línea no lo desbanca ninguna otra cosa. Hoy es una aparición en un artículo de
   diez; la regla es por si vuelve. */
.blog-content [style*="#ff0000"],
.blog-content [style*="#FF0000"] { color: #C1121F !important; }
.blog-content ul, .blog-content ol { margin: 0 0 1.8rem 2.2rem; }
.blog-content li { margin-bottom: .8rem; }
.blog-content a  { color: var(--morado-tinta); text-decoration: underline; }
.blog-content a:hover { color: var(--noche); }
.blog-content img { max-width: 100%; height: auto; border-radius: 8px; }
.blog-content blockquote {
  position: relative; padding: 2rem 2.4rem; font-size: 1.8rem;
  background: var(--papel-2); border-left: 4px solid var(--morado);
  border-radius: 0 var(--radio) var(--radio) 0; margin: 2.4rem 0;
}
/* 🔴 El blockquote flotado a 400 px de Divi se queda FUERA: a 375 px desbordaba. El gesto
   (destacar la cita) se conserva con el borde de color, que no depende del ancho. */

.blog-content table { width: 100%; border-collapse: collapse; font-size: 1.5rem; }
.blog-content th, .blog-content td { padding: 1.2rem; border: 1px solid var(--papel-borde); text-align: left; }
.blog-content thead th { background: var(--papel); }

/* Tablas anchas: hacen scroll ELLAS, no la página (lo envuelve angi-navegacion.php). */
.angi-tabla-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }

/* Pies de foto */
.wp-caption { border: 0; text-align: center; background: transparent; margin-bottom: 1.5rem; max-width: 100%; padding: .8rem; }
.wp-caption img { max-width: 100%; height: auto; }
.wp-caption-text { font-size: 1.3rem; color: var(--papel-tinta-2); }

/* `.avance`: dos columnas de texto. La usan /descargar-jellyfin/, /ayuda/ y el FAQ. */
.avance { column-count: 2; column-gap: 3rem; }
@media only screen and (max-width: 767px) { .avance { column-count: 1; } }

/* Prosa sobre fondo oscuro (páginas /ayuda/ y FAQ, que no son lectura larga). */
.jf-prosa { color: var(--tinta-2); font-size: 1.7rem; max-width: 78ch; }
.jf-prosa h2 { color: var(--tinta); margin: 3.2rem 0 1.4rem; }
.jf-prosa h3 { color: var(--cian); margin: 2.4rem 0 1rem; }
.jf-prosa a  { color: var(--cian); }
.jf-prosa ul, .jf-prosa ol { margin: 0 0 1.8rem 2.2rem; }

/* ── `<code>` sobre fondo oscuro (reescritura F6, 2026-08-12) ────────────────────────────────
   El texto F6 de /descargar-jellyfin/ usa `<code>` **30 veces**: nombres de fichero
   (`jellyfin_10.11.11_windows-x64.exe`), órdenes de terminal (`sudo bash install-debuntu.sh`),
   direcciones (`http://localhost:8096`) e identificadores de app. Hoy salen en la monoespaciada
   por defecto del navegador y nada más: no desbordan —el `overflow-wrap: anywhere` de arriba
   está escrito justo por esto— pero no se distinguen de la prosa que los rodea, y en una página
   donde el dato ES el nombre exacto del fichero eso importa.
   Se estilan los tres contenedores oscuros donde el `<code>` aparece de verdad y NINGUNO más:
   `.blog-content` (la zona clara, `--papel`) tiene su propio sistema y esta pastilla oscura
   dentro de un artículo sería un cuerpo extraño.
     · `--tinta` sobre `--noche-3` → 13,40:1
     · el `--tinta-2` que hereda en `.jf-prosa`, si algún día se quita el color → 6,91:1

   🔴 Y EL `<code>` DENTRO DE UN ENLACE VUELVE A SU ENLACE, que es el fleco que este bloque
   estuvo a punto de crear. En las tarjetas de Windows y de Mac el nombre del fichero ES el
   enlace de descarga (`<a …><code>jellyfin_10.11.11-arm64.dmg</code></a>`, tres casos hoy).
   `.jf-sistema code` (0,1,1) y `.jf-sistema a` (0,1,1) empatan en especificidad, así que ganaba
   el que se escribiera después —este— y el enlace de descarga se habría quedado del color del
   texto normal: la pieza que más se pincha de la página, sin su señal de enlace.
   Se le devuelve el color con `a code`, que gana en (0,2,1), y además se le quita la pastilla:
   dentro de un enlace **no se introduce una superficie nueva**, porque el acento de la tarjeta
   está medido contra el fondo de la tarjeta y no contra `--noche-3`. La cuenta, que es el motivo
   real de la regla: cian 5,29:1 e índigo 5,62:1 sobre `--noche-3` aguantan, pero el morado de
   Android TV se cae a **3,62:1** — y ese es el acento de una tarjeta que hoy no tiene ningún
   `<code>` enlazado y mañana puede tenerlo. Sin superficie nueva no hay trampa que armar. */
.jf-prosa code, .jf-sistema code, .jf-aviso code {
  background: var(--noche-3); border-radius: 6px; padding: .2rem .6rem;
  font-size: .92em; color: var(--tinta);
}
.jf-prosa a code, .jf-sistema a code, .jf-aviso a code {
  background: none; padding: 0; color: inherit;
}

/* Etiquetas del artículo ([taxonomy_info]): el único camino hacia los archivos de /tag/. */
.jf-tags { margin-top: 3.2rem; padding-top: 2.4rem; border-top: 1px solid var(--papel-borde); font-size: 1.5rem; color: var(--papel-tinta-2); }
.jf-tags a {
  display: inline-block; margin: .4rem .6rem .4rem 0; padding: .6rem 1.2rem;
  border-radius: 99px; background: var(--papel-2); border: 1px solid var(--papel-borde);
  color: var(--morado-tinta); text-decoration: none; font-weight: 600; font-size: 1.4rem;
}
.jf-tags a:hover { border-color: var(--morado-tinta); }

/* ══════════════════════════════════ comentarios ══════════════════════════════════ */
/* El aire que separaba los comentarios del artículo lo pone ahora el `row-gap` de la retícula
   (4,8 rem), porque los comentarios ya no van dentro de la columna de lectura sino en su propia
   celda. Dejar además el margen sumaba los dos y abría un hueco de 9,6 rem. */
.jf-art .brxe-post-comments { margin-top: 0; }
.jf-art .comment-form input[type=text],
.jf-art .comment-form input[type=email],
.jf-art .comment-form input[type=url],
.jf-art .comment-form textarea {
  width: 100%; padding: 1.2rem; border: 1px solid var(--papel-borde);
  border-radius: 8px; background: var(--papel-2); font: inherit; font-size: 1.6rem;
}
.jf-art .comment-form .submit {
  background: var(--marca); color: #0B1020; border: 0; border-radius: 99px;
  padding: 1.2rem 2.4rem; font-weight: 700; font-size: 1.6rem; cursor: pointer;
}
.jf-art .comment-notes, .jf-art .logged-in-as { font-size: 1.4rem; color: var(--papel-tinta-2); }
/* 🔴 Lo que el rediseño NO re-tematizó y no se ve leyendo el CSS propio: el gris por defecto que
   Bricks le pone a los comentarios. Medido el 2026-08-11: `.comment-meta` (y por herencia la
   fecha, «Responder» y las etiquetas del formulario) se servía en **#9E9E9E sobre #F4F6FC =
   2,48:1**, cuando el AA pide 4,5. No lo escribía ninguna regla nuestra: se HEREDA de la hoja de
   Bricks, así que buscar «9e9e9e» en este fichero no lo encuentra —hay que preguntarle al color
   COMPUTADO de la página servida—. `--papel-tinta-2` da 5,1:1 sobre el mismo fondo.
   La lección de familia: al re-tematizar un elemento del constructor no basta con estilar lo que
   uno escribe; hay que barrer el contraste de lo que el elemento pinta por su cuenta.

   🔴 Y NO se escribe con el prefijo `.jf-art`, que fue mi primera versión y se quedó corta: los
   comentarios salen en DOS sitios con fondos opuestos —la entrada, sobre `--papel`, y la FAQ,
   con **44 comentarios sobre `--noche`**— y encima el «Responder» se pinta siempre sobre una
   pastilla CLARA propia, esté el hilo donde esté. Con la regla atada a la plantilla de entrada,
   la FAQ se quedaba con el 2,48 intacto. Así que: el defecto va para oscuro, `.jf-art` lo pisa
   para claro, y el «Responder» se fija aparte porque no depende del fondo de la sección sino
   del suyo. Tres reglas, ningún sitio olvidado. */
.comment-meta, .comment-meta a, .comment-form label { color: var(--tinta-2); }          /* → 8,7:1 sobre --noche */
.jf-art .comment-meta,
.jf-art .comment-meta a,
.jf-art .comment-form label { color: var(--papel-tinta-2); }                            /* → 5,1:1 sobre --papel */
/* 🔴 Y con su especificidad puesta a mano, que es la tercera vez que este fichero tropieza con lo
   mismo: el «Responder» vive DENTRO de `.comment-meta`, así que `.comment-meta a` —(0,1,1)— le
   gana a `.comment-reply-link` —(0,1,0)— por más abajo que se escriba. Medido tras desplegar:
   se quedó en `--tinta-2` a 2,02:1 sobre su pastilla clara. Con el ancestro delante empata en
   (0,2,0) y manda el orden. */
.comment-meta .comment-reply-link,
.comment-reply-link { color: var(--papel-tinta-2); }                                    /* pastilla clara propia */
.comment-meta .comment-reply-link:hover,
.comment-reply-link:hover, .comment-meta a:hover { color: var(--morado-tinta); }

/* ══════════════════════════════════ pie ══════════════════════════════════ */
.jf-pie {
  position: relative; background: var(--noche-2); border-top: 1px solid var(--noche-3);
  padding: 3.2rem 0 0; font-size: 1.5rem; color: var(--tinta-2);
}
/* Mismo filete de marca que separa las secciones: el pie es una frontera más. */
.jf-pie::before {
  content: ""; position: absolute; inset: -1px 0 auto 0; height: 1px; pointer-events: none;
  background: linear-gradient(90deg, transparent, rgb(170 92 195 / .6) 28%,
                              rgb(0 164 220 / .6) 72%, transparent);
}
/* 🔴 EL PIE DESCUADRADO, Y LA CAUSA EXACTA (medida el 2026-08-11, 2ª pasada).
   Era `flex-wrap: wrap` con tres hijos y NINGÚN `flex-basis`. Los `.brxe-block` de Bricks nacen
   con `width: 100%` dentro de `@layer bricks`, así que las dos columnas de navegación medían
   **1.152 px cada una** —el contenedor entero— y se apilaban una debajo de otra: el pie crecía a
   **727 px** de alto con la mitad derecha vacía.
   La cura es la misma doctrina que el resto del rediseño: **retícula declarada aquí, no anchos en
   el constructor**. Con `grid-template-columns` el ancho de cada columna es una propiedad de la
   RETÍCULA, no del hijo, así que ningún `width:100%` heredado puede estropearla — y apilar en
   móvil sigue siendo una sola media query. */
.jf-pie__in {
  display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 4rem; align-items: start;
}
.jf-pie a { color: var(--tinta-2); }
.jf-pie a:hover { color: var(--tinta); }
.jf-pie h3 { font-size: 1.5rem; color: var(--tinta); margin-bottom: 1.2rem; }
.jf-pie__marca { max-width: 46ch; }
.jf-pie__marca p { font-size: 1.4rem; margin-bottom: 1rem; }
.jf-pie__col { min-width: 0; }
/* La lista del menú traía la sangría por defecto de un `<ul>` y las cuatro entradas salían
   descolgadas de su propio título. */
.jf-pie .bricks-nav-menu {
  flex-direction: column; gap: .8rem; margin: 0; padding: 0; list-style: none;
}
/* Y la sangría no estaba en el `<ul>` sino en el `<li>`: los enlaces salían 30 px a la derecha
   de su propio encabezado, como una lista dentro de otra cosa. Medido, no supuesto. */
.jf-pie .bricks-nav-menu > li { padding: 0; margin: 0; }
.jf-pie .bricks-nav-menu > li > a { color: var(--tinta-2); text-decoration: none; font-size: 1.4rem; padding: 0; }
/* 🔴 Y EL TERCER ACTO DEL PIE, este solo visible a 375 (medido el 2026-08-11, cierre de la 2ª
   pasada). El elemento `nav-menu` de Bricks trae su propio punto de colapso (`mobile_landscape`):
   por debajo de él **esconde la lista y pinta una hamburguesa**. En la cabecera eso es lo
   correcto; en el pie, la columna «Secciones principales» se quedaba con un icono ☰ de 20×16 px
   y CERO enlaces —los cuatro enlaces del pie desaparecían en móvil, que es donde está la mitad
   del tráfico—, y encima duplicaba en el DOM un menú deslizante fuera de pantalla.
   Un pie no tiene navegación «móvil»: tiene una lista de enlaces, y una lista cabe siempre. La
   regla no necesita `!important` porque Bricks pone las suyas en `@layer bricks` y una regla sin
   capa gana a cualquier regla dentro de una capa (la misma doctrina que el resto del fichero). */
.jf-pie .bricks-nav-menu-wrapper { display: block; }
.jf-pie .bricks-mobile-menu-toggle,
.jf-pie .bricks-mobile-menu-wrapper,
.jf-pie .bricks-mobile-menu-overlay { display: none; }
.jf-pie__col .brxe-post-title { font-size: 1.4rem; line-height: 1.4; margin-bottom: .9rem; }
.jf-pie__legal {
  display: flex; flex-direction: row; flex-wrap: wrap; gap: 1.2rem;
  justify-content: space-between; align-items: center; width: 100%;
  margin-top: 2rem; padding: 1.6rem 0; border-top: 1px solid var(--noche-3); font-size: 1.3rem;
}
.jf-pie__legal p { margin: 0; }

/* ══════════════════════════════════ huecos de anuncio ══════════════════════════════════ */
/* El hueco nunca debe empujar el contenido al cargar (CLS). El aspecto del hueco en staging lo
   pinta `angi-staging-sin-anuncios.php`; en producción es el `<ins>` real. */
.brxe-shortcode ins.adsbygoogle { display: block; }
.jf-ad  { margin: 3.2rem 0; min-height: 10rem; width: 100%; }
.jf-ad--lat { margin: 0; }
/* El rótulo del hueco de staging es una cadena monoespaciada larga y en el rail de 300 px se
   salía de su caja. Solo se ve en el taller (en producción ahí va el `<ins>` real), pero un
   hueco que desborda hace dudar de la colocación, que es justo lo que se viene a auditar. */
.jf-ad > div, .jf-ad--lat > div { max-width: 100%; overflow-wrap: anywhere; }
.jf-ad--lat > div { font-size: 1.1rem; padding: 1rem; }

/* ══════════════════════════════════ animación ══════════════════════════════════
   «Algo discreto y de calidad» (encargo de la 2ª pasada). Tres reglas que no son negociables y
   que están escritas en la propia estructura de este bloque, no en la buena voluntad:

   1. 🔴 TODO va dentro de `@media (prefers-reduced-motion: no-preference)`. No es lo mismo que
      apagarlo con `reduce`: así, para quien pide menos movimiento, las reglas **no existen** —no
      hay que acordarse de anular cada una, ni puede colarse una animación nueva sin desactivar.
   2. 🔴 Solo se animan `transform` y `opacity`. Ninguna de las dos participa en el layout, así
      que **el CLS es cero por construcción**: la caja de cada elemento está donde estaría sin
      animación desde el primer fotograma. En una web que vive de AdSense, un salto de maqueta
      cuesta dinero.
   3. 🔴 El estado «oculto» de la entrada al hacer scroll depende de la clase `.jf-anim` que pone
      el JS en `<html>`. Sin JS —o con JS a medias— nunca hay nada oculto: se ve todo, quieto.
      Es la única forma de que un fallo del script no se lleve por delante el contenido. */

@media (prefers-reduced-motion: no-preference) {

  /* el resplandor del hero, que respira muy despacio */
  .jf-hero::before {
    animation: jfDeriva 28s ease-in-out infinite alternate;
    will-change: transform;
  }
  @keyframes jfDeriva {
    from { transform: translate3d(-2%, 0, 0) scale(1); }
    to   { transform: translate3d(4%, 2%, 0) scale(1.12); }
  }

  /* la biblioteca cobrando vida: las carátulas entran escalonadas al cargar */
  .jf-rejilla .jf-car { animation: jfSube .6s cubic-bezier(.2, .7, .3, 1) both; }
  .jf-rejilla .jf-car:nth-child(1) { animation-delay: .06s; }
  .jf-rejilla .jf-car:nth-child(2) { animation-delay: .12s; }
  .jf-rejilla .jf-car:nth-child(3) { animation-delay: .18s; }
  .jf-rejilla .jf-car:nth-child(4) { animation-delay: .24s; }
  .jf-rejilla .jf-car:nth-child(5) { animation-delay: .30s; }
  .jf-rejilla .jf-car:nth-child(6) { animation-delay: .36s; }
  .jf-rejilla .jf-car:nth-child(7) { animation-delay: .42s; }
  .jf-rejilla .jf-car:nth-child(8) { animation-delay: .48s; }
  @keyframes jfSube {
    from { opacity: 0; transform: translate3d(0, 14px, 0) scale(.97); }
    to   { opacity: 1; transform: none; }
  }

  /* el piloto de «tu servidor, en tu casa»: un anillo que se abre y se apaga */
  .jf-punto { position: relative; }
  .jf-punto::after {
    content: ""; position: absolute; inset: -.3rem; border-radius: 50%;
    border: 1px solid var(--lima); animation: jfAnillo 3s ease-out infinite;
  }
  @keyframes jfAnillo {
    from { transform: scale(.6); opacity: .8; }
    to   { transform: scale(2.1); opacity: 0; }
  }

  /* entrada escalonada de las tarjetas al entrar en pantalla (IntersectionObserver) */
  html.jf-anim .jf-paso,
  html.jf-anim .jf-guia,
  html.jf-anim .jf-tarjeta,
  html.jf-anim .jf-sistema {
    opacity: 0; transform: translate3d(0, 16px, 0);
  }
  html.jf-anim .jf-visto {
    opacity: 1; transform: none;
    transition: opacity .55s ease, transform .55s cubic-bezier(.2, .7, .3, 1);
    transition-delay: var(--jf-retardo, 0ms);
  }
  /* El `:hover` de la tarjeta también es transform: se le devuelve el suyo cuando ya ha entrado,
     o el primer hover la haría saltar desde el estado de entrada. */
  html.jf-anim .jf-visto.jf-paso:hover,
  html.jf-anim .jf-visto.jf-guia:hover { transform: translate3d(0, -3px, 0); }
  html.jf-anim .jf-visto.jf-sistema:hover { transform: translate3d(0, -3px, 0); }
}

/* El cerrojo explícito. El bloque de arriba ya deja fuera toda la animación de entrada por estar
   dentro de un `no-preference`, pero quedan las micro-transiciones de `:hover` (la tarjeta que
   sube 3 px, la portada que hace zoom), que viven fuera y SÍ son movimiento. Quien pide menos
   movimiento no lo pide solo al cargar. Además, así la comprobación es inequívoca y medible:
   con `reduce`, `animation-name: none` y `transition-duration: 0s` en TODO lo que se anima. */
@media (prefers-reduced-motion: reduce) {
  .jf-hero::before, .jf-rejilla .jf-car, .jf-punto::after { animation: none; }
  .jf-paso, .jf-guia, .jf-tarjeta, .jf-sistema, .jf-visto,
  .jf-guia__port .jf-portada, .jf-guia:hover .jf-portada {
    transition: none; transform: none; opacity: 1;
  }
  /* 🔴 …y los `:hover` con su PROPIA especificidad, o el cerrojo tiene un agujero. Medido el
     11-08 emulando `reduce` sobre el CSSOM del staging: `.jf-guia:hover` es (0,2,0) y la línea
     de arriba, `.jf-guia`, es (0,1,0) — o sea que el `transform: translateY(-3px)` del hover
     seguía ganando. Con `transition: none` el salto era instantáneo y no animado, así que no
     rompía la promesa por los pelos; pero un cerrojo que depende de que nadie mire la
     especificidad no es un cerrojo. Se anulan por nombre, que es lo único que no se puede
     perder de vista. */
  .jf-paso:hover, .jf-guia:hover, .jf-tarjeta:hover, .jf-sistema:hover,
  html.jf-anim .jf-visto.jf-paso:hover, html.jf-anim .jf-visto.jf-guia:hover,
  html.jf-anim .jf-visto.jf-sistema:hover { transform: none; }
}

/* ══════════════════════════════════ responsive ══════════════════════════════════ */
/* 🔴 UNA SOLA media query manda sobre TODAS las retículas. Es la cura estructural del fallo de
   los 5 contenedores en fila: no hay ninguna retícula que pueda olvidarse de apilar, porque
   ninguna declara su ancho en Bricks. */
@media (max-width: 900px) {
  .jf-hero__in,
  .jf-pasos,
  .jf-rej3,
  .jf-dos,
  .jf-sistemas,
  .jf-aviso,
  .jf-pie__in,
  .jf-art__in { grid-template-columns: minmax(0, 1fr); }

  /* 🔴 `minmax(0, 1fr)` Y NO `1fr`, y esto cortaba texto en las 14 entradas (2026-08-13).
     Una pista `1fr` tiene mínimo `auto`, o sea el ancho del hijo más ancho; y dentro del lateral
     va un `<iframe>` de AdSense que se autoimpone `width: 375px`. Resultado: la pista crecía a
     367 px empezando en x=18 → el texto llegaba a 385 px en un viewport de 375, y **`body
     { overflow-x: hidden }` lo recortaba EN SILENCIO**: no aparecía barra de scroll lateral, así
     que ninguna de las comprobaciones de «¿desborda?» lo veía. Medido a 375 px:
     `scrollWidth` 387 vs `clientWidth` 375 en **14 de 14 entradas y 0 de 5 páginas** — o sea que
     el sospechoso era justo lo que solo tienen las entradas: el rail con el anuncio.
     40 líneas cortadas por la derecha solo en `/jellyfin-sin-abrir-puertos/`.
     `minmax(0, …)` devuelve el mínimo a 0 y la pista vuelve a obedecer al contenedor.
     Lección: **`overflow-x: hidden` no arregla un desbordamiento, lo esconde** — y de paso
     esconde el instrumento con el que lo habrías encontrado. */
  .jf-art__main, .jf-art__lat, .jf-art__coment { min-width: 0; }
  .jf-pasos   { gap: 1.4rem; }
  .jf-pie__in { gap: 3.2rem; }
  .jf-aviso   { gap: 2rem; }
  .jf-diag    { max-width: 34rem; }
  .jf-hero    { padding: 5rem 0 4rem; }
  .jf-seccion { padding: 5.6rem 0; }
  .jf-rejilla  { grid-template-columns: repeat(3, 1fr); }
  .jf-art h1   { font-size: 3.2rem; }

  /* 🔴 CON UNA SOLA COLUMNA, LA COLOCACIÓN EXPLÍCITA DE ARRIBA HAY QUE DESHACERLA.
     `grid-column: 2` sobre una retícula de UNA columna no se ignora: el navegador CREA una
     segunda columna implícita y el lateral se va a ella. Es el modo de fallo clásico de mezclar
     colocación explícita con retículas responsive, y no lo delata ningún desbordamiento. Con
     `auto` mandan otra vez el orden del documento y la única columna que hay:
     artículo → lateral (con su anuncio) → comentarios. */
  .jf-art__main, .jf-art__lat, .jf-art__coment { grid-column: auto; grid-row: auto; }
  /* Y el rail deja de ser un rail: sin `sticky` no hay nada a lo que pegarse, y con la
     `max-height` de escritorio puesta el bloque se convertiría en una CAJA CON SCROLL PROPIO de
     ~700 px con un anuncio dentro. Hoy no se dispara por 68 px (medido: contenido 634, tope 702),
     pero es una trampa armada: basta un índice de un epígrafe más para que el anuncio quede
     dentro de un contenedor desplazable. */
  .jf-art__lat { position: static; max-height: none; overflow: visible; }
  /* El corte visible entre «lo que acompaña al artículo» y «la conversación». Cumple doble
     función: cierra la lectura y deja claro que el anuncio de arriba NO forma parte del
     formulario de comentarios (política de AdSense sobre publicidad junto a elementos
     interactivos). Un filete y un padding: ni una petición, ni un salto de maqueta. */
  .jf-art__coment { border-top: 1px solid var(--papel-borde); padding-top: 1.6rem; }
}

@media (max-width: 600px) {
  .jf-env { padding-inline: 1.8rem; }
  .jf-tarjeta, .jf-sistema { padding: 2rem; }
  .jf-tabla th, .jf-tabla td { padding: 1.2rem; font-size: 1.4rem; }
  /* 🔴 A 375 px la cabecera medía 434 y la página entera se desplazaba en horizontal: logo +
     CTA + hamburguesa no caben con los tamaños de escritorio, y el `nowrap` del botón (que
     arregla el escritorio) impide que ceda. MEDIDO en un iframe de 375 px, que es la única
     forma de que las media queries se evalúen de verdad a ese ancho.
     El CTA NO se esconde: es el camino al dinero. Se aprieta. */
  .jf-top__in { gap: 1.2rem; }
  .jf-logo { font-size: 1.7rem; }
  .jf-logo svg { width: 2.6rem; height: 2.6rem; }
  .jf-top .brxe-button { padding: 1rem 1.4rem; font-size: 1.3rem; }
  .jf-hero__in, .jf-art__in { gap: 3.2rem; }

  /* ── la tabla de TV, en fichas (reescritura F6, 2026-08-12) ─────────────────────────────────
     Esto NO es un rescate de desbordamiento: la tabla ya no desborda. Va envuelta en
     `.angi-tabla-scroll`, y medido a 375 px ocupa 339 px sin scroll propio y la página entera
     mide `scrollWidth == clientWidth == 375`. Lo que arregla es la LEGIBILIDAD: tres columnas de
     prosa en 339 px son **824 px de tabla** en la que «Desinstalar antes la de Homebrew o modo
     desarrollador» cae en una tira de ~100 px de ancho. Cada fila pasa a ser una ficha y cada
     dato lleva encima su etiqueta, que es la misma gramática que usa la tabla de respaldo de la
     herramienta `[jf_tv_compat]` que va justo encima: una sola forma de leer lo mismo.

     🔴 VA DENTRO DEL `@media (max-width: 600px)` QUE YA EXISTE, no en una media query nueva.
     Este fichero tiene dos puntos de corte y solo dos —900 para las retículas, 600 para los
     aprietes de móvil— y añadir un tercero para una sola tabla es empezar a tener un sistema
     que hay que recordar en vez de leer. Por eso también va al FINAL del bloque: `.jf-tabla th,
     .jf-tabla td` de más arriba empata en especificidad (0,1,1) con `.jf-tv-tabla td`, así que
     lo único que decide es el orden.

     ⚠️ Ni un `width` en estas reglas. Un `width: 100%` sobre celdas CON padding depende de que
     alguien haya puesto `box-sizing: border-box` (lo pone Bricks, pero es de otro): en cuanto no
     esté, la ficha mide 100 % + 3,2 rem y desborda justo lo que se venía a arreglar. Un bloque
     con `width: auto` ya ocupa su contenedor y el padding se le come por dentro, pase lo que pase
     con la caja. La regla dura de esta página —a 375 px no desborda nada— no puede depender de
     una propiedad que escribe otro fichero.

     ⚠️ Y la forma NO me la he inventado: la herramienta `[jf_tv_compat]` que va justo encima ya
     apila SU tabla de respaldo en fichas (`.jfh-tabla`, en `angi-herramientas-jellyfin.php`), y
     en esta página las dos tablas se ven una detrás de otra. Se copia su geometría —el relleno
     va en la FICHA y las celdas solo respiran en vertical, la cabecera se esconde con la técnica
     de recorte— traducida a los tokens de la casa. Dos tablas seguidas que se apilan de dos
     maneras distintas es exactamente el segundo sistema que este fichero evita.
     La ETIQUETA, en cambio, se escribe como `.jf-tabla thead th` y no como los micro-rótulos
     monoespaciados de la marca (`.jf-et`, `.jf-chip`): es literalmente la cabecera de columna
     mudada dentro de la celda, así que lleva sus versalitas y su `letter-spacing: .04em`.

     Contrastes de la ficha: texto `--tinta` sobre `--noche` 16,77:1, etiqueta `--tinta-2` sobre
     `--noche` 8,65:1, enlaces `--cian` sobre `--noche` 6,62:1 (los pinta la regla de familia de
     `.jf-bajada a, .jf-tabla a`). El borde `--noche-3` da 1,25:1: es el mismo filete decorativo
     que llevan todas las tarjetas de la web, no transporta información.
     Si este bloque no llegara a escribirse, la tabla sigue siendo una tabla dentro de su
     contenedor con scroll: degrada, no rompe. */
  .jf-tv-tabla, .jf-tv-tabla thead, .jf-tv-tabla tbody,
  .jf-tv-tabla tr, .jf-tv-tabla th, .jf-tv-tabla td { display: block; }
  /* Sin celdas, el fondo de la tabla solo pintaría los huecos entre fichas. Lo pone cada ficha. */
  .jf-tv-tabla { background: none; }
  /* La cabecera se va SOLO para el ojo: los rótulos siguen en el árbol de accesibilidad y además
     los repite el `data-col` de cada celda. Se esconde con la técnica de recorte y no con un
     `left: -9999px`, que es una caja de verdad a 9.999 px de su ancestro posicionado. El `th`
     entra en el `display: block` de arriba por lo mismo: si se queda de tabla, sus celdas se
     maquetan a su ancho natural (medido: 463 px) dentro de una caja de 1 px. No desborda la
     página —el recorte y el `overflow` las contienen, `scrollWidth` sigue siendo 375— pero deja
     un elemento con `right > clientWidth` que hace saltar la comprobación de desbordamiento de
     cualquiera que audite esta página después. */
  .jf-tv-tabla thead {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .jf-tv-tabla tbody tr {
    border: 1px solid var(--noche-3); border-radius: var(--radio);
    background: var(--noche); padding: 1.2rem 1.6rem; margin-bottom: 1.2rem;
  }
  .jf-tv-tabla tbody tr:last-child { margin-bottom: 0; }
  /* El relleno horizontal lo pone la ficha, no la celda: así ninguna caja de este bloque lleva
     `width` ni depende de que alguien haya declarado `box-sizing: border-box` (lo declara Bricks,
     que es otro fichero). Un bloque con `width: auto` ocupa su contenedor pase lo que pase. */
  .jf-tv-tabla tbody td { border: 0; padding: .6rem 0; font-size: 1.5rem; }
  /* 🔴 Y con `line-height` PROPIO. El del cuerpo es 1,65 —el de leer párrafos— y aplicado a un
     rótulo de una línea son 20 px de caja para 12 px de letra: medido, los tres rótulos de cada
     ficha sumaban 26 px de aire vertical por ficha y 180 px en la tabla, sin pintar nada. Un
     interlineado de lectura en una etiqueta de una línea es altura regalada, y aquí se paga en
     scroll de móvil. */
  .jf-tv-tabla tbody td::before {
    content: attr(data-col); display: block; margin-bottom: .2rem;
    font-size: 1.2rem; line-height: 1.3; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--tinta-2);
  }
  /* El nombre del aparato es el título de la ficha: es lo que se busca con el pulgar. */
  .jf-tv-tabla tbody td:first-child { font-size: 1.8rem; font-weight: 700; line-height: 1.3; }
}

/* ── ICONOS DE MARCA ──────────────────────────────────────────────────────────────────────
   Los logos de plataforma de la página de descargas (2026-08-13). Van con `currentColor`, así
   que cada tarjeta los pinta con SU acento sin duplicar un solo fichero: el color lo pone
   `.jf-sistema--<sistema> .jf-sistema__cab`, que ya existía. */
.jf-icono-marca {
  width: 2.8rem; height: 2.8rem;      /* la pastilla de la cabecera mide 5,2rem: deja aire */
  display: block; flex: none;
}
/* En la tabla de televisores el icono acompaña al nombre en la misma línea. `vertical-align`
   por el centro de la equis y no por la línea base: un logo cuadrado alineado a la base
   «flota» sobre el texto. */
.jf-icono-fila {
  width: 1.6rem; height: 1.6rem;
  display: inline-block; vertical-align: -.25em; margin-right: .5rem;
  opacity: .92;
}
/* Sin JavaScript ni imágenes externas: si el mu-plugin no encuentra la clave devuelve cadena
   vacía y aquí no queda ni un hueco reservado — la pastilla se encoge sola porque no hay caja
   con tamaño fijo esperando a nada. */

/* ══════ fila 380 · el índice, por delante del cuerpo en móvil (2026-08-13) ══════
   El problema medido a 375 px: el cuerpo acababa en y≈7.163 y «En esta página» arrancaba en
   7.195 — un índice de una página que ya has leído entera. Es tráfico interno, no estética: un
   índice bajo el H1 da la forma del artículo antes de leerlo y ofrece saltos.

   🔴 Por qué NO se puede resolver moviendo el índice a secas: `.angi-toc` no es hijo de la
   retícula, vive DENTRO de `.jf-art__lat` (rail = índice + anuncio + más guías), y el CSS no
   reparenta. `display: contents` disuelve la caja del rail y sus tres hijos pasan a ser celdas
   de `.jf-art__in`; entonces sí se puede adelantar solo el índice.

   ✅ EL INVARIANTE QUE COSTÓ LA FILA 372 SE CONSERVA POR CONSTRUCCIÓN: solo el índice lleva
   `order`. El anuncio y «Más guías» se quedan en orden de documento, o sea detrás del cuerpo y
   DELANTE de los comentarios. No se toca su posición.

   🔴 Y LA FILA 411 NO PUEDE VOLVER: el `min-width: 0` que protegía la pista lo llevaba
   `.jf-art__lat`, que con `display: contents` deja de ser celda. Se traslada a sus hijos — sin
   esto, el `<iframe>` de AdSense (que se autoimpone `width: 375px`) volvería a estirar la pista
   y a cortar texto por la derecha en silencio bajo `overflow-x: hidden`.

   ⚠️ Desviación declarada: `order` cambia lo que se PINTA, no el orden del documento. Para
   teclado y lector de pantalla el índice sigue estando después del cuerpo. Se acepta porque es
   un `<nav>` etiquetado (`aria-label="Contenido del artículo"`), sigue siendo alcanzable y sus
   enlaces funcionan; la alternativa —sacarlo del rail en la plantilla— cambiaría el escritorio
   aprobado y dejaría el rail con el anuncio casi solo, que es justo lo que el mockup evita. */
@media (max-width: 900px) {
  .jf-art__lat { display: contents; }
  .jf-art__lat > * { min-width: 0; }
  .jf-art__lat > .brxe-shortcode:first-child { order: -1; }
}

/* El ancla de la sección «¿de qué va esto?» de la portada. La cabecera es pegajosa a ≥900 px, así
   que sin esto un salto a `#de-que-va` deja el H2 debajo de la barra. Hoy no la enlaza nadie
   (la sección es lo siguiente que se ve al bajar del hero); esto es para cuando se enlace. */
#de-que-va { scroll-margin-top: 9rem; }

/* ══════ sección «¿de qué va esto?» de la portada — remaquetada 2026-08-14 (fila 442) ══════
   Miguel: «tiene saltos de línea y la maquetación no es buena». No había ni un `<br>`: el
   problema era que `.jf-env` reparte el MISMO aire entre todo, así que cada H3 flotaba igual de
   lejos de su propio párrafo que del bloque anterior y se leía como texto suelto. Medido a
   1280 px, la sección ocupaba 1.740 px cuando las otras cuatro rondan 750.
   El arreglo es de ritmo (un bloque = un grupo con su gap interno menor) y de forma (cuatro
   formas distintas, ninguna repetida del resto de la portada). Mockup aprobado en
   `entregables/mockups/jellyfinapp-de-que-va/`. Cero tokens nuevos. */

.jf-bloque{ display:flex; flex-direction:column; gap:1.6rem; margin-top:2.4rem; }
.jf-bloque__t{ display:flex; align-items:baseline; gap:1.2rem; }
.jf-bloque__n{
  font:700 1.2rem/1 ui-monospace, SFMono-Regular, Menlo, monospace; color:var(--tinta-2);
  border:1px solid var(--noche-3); border-radius:99px; padding:.5rem .9rem; flex:none;
}

/* 01 · concepto a dos columnas */
.jf-doble{ display:grid; grid-template-columns:minmax(0,1fr) 34rem; gap:3.2rem; align-items:center; }
.jf-figura{
  background:var(--noche-2); border:1px solid var(--noche-3);
  border-radius:var(--radio); padding:2.4rem; box-shadow:var(--sombra); min-width:0;
}

/* 02 · la balanza */
.jf-balanza{ display:grid; grid-template-columns:repeat(2,1fr); gap:2.4rem; }
.jf-col{
  background:var(--noche-2); border:1px solid var(--noche-3);
  border-radius:var(--radio); padding:2.4rem; min-width:0;
}
.jf-col--no{ --acc:var(--ambar); }
.jf-col--si{ --acc:var(--lima); }
.jf-col h4{
  font:700 1.3rem/1 ui-monospace, monospace; letter-spacing:.12em; text-transform:uppercase;
  color:var(--acc); margin-bottom:1.8rem;
}
.jf-lista{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:1.4rem; }
.jf-lista li{
  display:grid; grid-template-columns:2.2rem 1fr; gap:1.2rem; align-items:start;
  font-size:1.6rem; color:var(--tinta-2);
}
.jf-lista b{ color:var(--tinta); font-weight:700; display:block; }
/* `color-mix` NO: Safari lo soporta desde la 16.2 y esto es un fondo, no un adorno. Se declara
   el rgb con alfa a mano por cada variante, que funciona en todo lo que sirve la web. */
.jf-marca{
  width:2.2rem; height:2.2rem; border-radius:50%; display:grid; place-items:center;
  color:var(--acc); flex:none; margin-top:.2rem;
}
.jf-col--no .jf-marca{ background:rgb(255 180 84 / .16); }
.jf-col--si .jf-marca{ background:rgb(61 220 151 / .16); }
.jf-marca svg{
  width:1.2rem; height:1.2rem; stroke:currentColor; stroke-width:3;
  fill:none; stroke-linecap:round; stroke-linejoin:round;
}

/* 03 · requisitos. `--req` anula el `margin-top: 4rem` que `.jf-rej3` trae para las guías:
   aquí la rejilla va DENTRO de un bloque que ya pone su propio aire. */
.jf-rej3--req{ margin-top:0; }
.jf-req{
  background:var(--noche-2); border:1px solid var(--noche-3); border-radius:var(--radio);
  padding:2.4rem; display:flex; flex-direction:column; gap:1rem; min-width:0;
}
.jf-req__i{
  width:4.4rem; height:4.4rem; border-radius:12px; display:grid; place-items:center;
  background:rgb(0 164 220 / .14); color:var(--cian);
}
.jf-req__i svg{
  width:2.2rem; height:2.2rem; stroke:currentColor; stroke-width:1.8;
  fill:none; stroke-linecap:round; stroke-linejoin:round;
}
.jf-req h4{ font-size:1.8rem; }
.jf-req p{ margin:0; color:var(--tinta-2); font-size:1.55rem; }
.jf-nota{ color:var(--tinta-2); font-size:1.55rem; }

/* 04 · veredictos */
.jf-veredictos{ display:grid; grid-template-columns:repeat(2,1fr); gap:2.4rem; }
.jf-ver{
  border-radius:var(--radio); padding:2.4rem; border:1px solid var(--noche-3);
  background:var(--noche-2); border-top:3px solid var(--acc); min-width:0;
}
.jf-ver--si{ --acc:var(--lima); }
.jf-ver--no{ --acc:var(--ambar); }
.jf-ver h4{ color:var(--acc); font-size:1.8rem; margin-bottom:.8rem; }
.jf-ver p{ margin:0; color:var(--tinta-2); font-size:1.6rem; }
.jf-cierre{ display:flex; flex-wrap:wrap; gap:1.6rem 2.4rem; align-items:center; margin-top:.8rem; }
.jf-mini{ color:var(--tinta-2); font-size:1.5rem; }

@media (max-width: 900px) {
  .jf-doble, .jf-balanza, .jf-veredictos { grid-template-columns:minmax(0,1fr); }
  .jf-bloque__t{ align-items:flex-start; }
}
