/* ==========================================================================
   politics-wiki — css/wiki.css
   Hoja de estilos ÚNICA del sitio. Sin framework, sin webfonts, sin @import,
   sin peticiones de red. Debe funcionar igual en file:// y en http://.

   --------------------------------------------------------------------------
   1. TOKENS DE COLOR (custom properties) — todos los colores del sitio
   --------------------------------------------------------------------------
   Superficies / fondos
     --pw-bg              Fondo general de la página. Blanco cálido.
     --pw-surface         Fondo de tarjetas, panel de TOC, infobox, resultados.
     --pw-surface-2       Fondo hundido: filas de tabla, chips, hover.
     --pw-wash            Lavado neutro muy tenue (hover, estado activo).
     --pw-sidebar-bg      Fondo de la barra lateral.
     --pw-header-bg       Fondo de la cabecera fija.
   Tinta / texto
     --pw-ink             Texto principal. Tinta oscura cálida.
     --pw-ink-soft        Texto secundario (subtítulos, definiciones).
     --pw-ink-muted       Texto terciario (metadatos, pies, notas al margen).
     --pw-ink-faint       Tinta al 18 % — barras de bloque, filetes gruesos.
     --pw-on-accent       Texto sobre el color de acento del infobox. Blanco.
   Enlaces
     --pw-link            Azul de enlace wiki.
     --pw-link-hover      Azul de enlace al pasar el cursor / enfocar.
     --pw-link-visited    Azul de enlace visitado.
     --pw-missing         Rojo apagado: wikilink a un slug inexistente.
   Bordes y líneas
     --pw-border          Gris claro, 1 px. El borde por defecto del sitio.
     --pw-border-soft     Gris aún más claro: separadores internos.
     --pw-border-strong   Gris medio: barra de cita, hover de tarjeta.
   Estados
     --pw-focus           Color del anillo de foco :focus-visible.
     --pw-focus-halo      Halo translúcido del anillo de foco.
     --pw-empty-fg        Texto de los estados vacíos / de error.

   --------------------------------------------------------------------------
   2. TOKENS NO-COLOR
   --------------------------------------------------------------------------
   --pw-font-serif   Títulos, marca, cifras de referencias. Serif del sistema.
   --pw-font-sans    Interfaz y cuerpo del texto. Sans del sistema.
   --pw-fs-*         Escala tipográfica (xs → 3xl).
   --pw-space-1..8   Escala de espaciado en base 4 px.
   --pw-radius-*     Radios de borde (2 / 4 / 6 px).
   --pw-shadow-*     Sombras mínimas: solo hover y panel infobox.
   --pw-lh-*         Interlineados (1.25 apretado / 1.70 cuerpo / 1.75 amplio).
   --pw-header-h     Alto de la cabecera fija.
   --pw-sidebar-w    Ancho de la columna lateral fija (≥1024px).
   --pw-aside-w      Ancho de la columna TOC + infobox (≥1200px).
   --pw-measure      Ancho máximo de la columna de texto (≈70-75 caracteres).

   --------------------------------------------------------------------------
   3. CLASES AÑADIDAS POR EL AUTOR DE CSS FUERA DE CONTRACT.md §6
      (documentadas aquí para el autor de JS; NO modificar el contrato)
   --------------------------------------------------------------------------
   · .pw-related-card    Tarjeta de "Artículos relacionados". Grid 3.1:
                         <a class="pw-related-card" href="article.html?title=slug">
                           <span class="pw-card-title">Título</span>
                           <span class="pw-card-sub">Subtítulo</span>
                         </a>
                         (declarada en § Artículos relacionados)
   · .pw-empty           Estado vacío / de error: "artículo no encontrado",
                         "sin resultados de búsqueda". (declarada en § Estados)
   · .pw-toc-link.is-active   Resaltado de scrollspy del índice. También se
                         acepta [aria-current="true"] sobre el enlace. (§ Índice)
   · .pw-note-label      Etiqueta "Nota" que el contrato §6.3 exige dentro de
                         .pw-note, pero no nombra. (declarada en § Notas)
   · .pw-figure-credit   Crédito de la ilustración dentro de .pw-figure.
                         (declarada en § Figuras)
   · .pw-loading         Estado "cargando" estático de article.html, que app.js
                         sustituye al renderizar. (declarada en § Estados)
   · .pw-sidebar-toggle  Etiqueta del conmutador plegable de la barra lateral
                         en <1024px; hace clic en el checkbox .pw-nav-toggle.
                         (declarada en § Barra lateral)
   · .pw-nav-toggle      Checkbox oculto que implementa el plegado sin JS.
   · .pw-browse          Contenedor de las categorías en la portada
                         (#categorias-browse). Requisito de la tarea.
   · .pw-search-page     Modificador de tamaño para el buscador del héroe.
   · .pw-article-mount   Contenedor #articulo de article.html.
   · .pw-section         Bloque <section> de la portada (§6.4).

   El color de acento de la cabecera del infobox lo inyecta JS como
   background inline (contrato §6.3) — única excepción documentada a la regla
   de no usar style="" en el HTML. Aquí solo se fija el color del texto.
   ========================================================================== */


/* ==========================================================================
   1 · TOKENS
   ========================================================================== */
:root {
  /* --- superficies --- */
  --pw-bg: #fbfaf7;
  --pw-surface: #ffffff;
  --pw-surface-2: #f4f2ec;
  --pw-wash: #f0ede5;
  --pw-sidebar-bg: #f6f4ef;
  --pw-header-bg: #fbfaf7;

  /* --- tinta --- */
  --pw-ink: #1c1b18;
  --pw-ink-soft: #3d3b35;
  --pw-ink-muted: #6c6860;
  --pw-ink-faint: #c9c4b8;
  --pw-on-accent: #ffffff;

  /* --- enlaces --- */
  --pw-link: #2a4b8d;
  --pw-link-hover: #1b3568;
  --pw-link-visited: #5a4b7d;
  --pw-missing: #a3271b;

  /* --- bordes --- */
  --pw-border: #ddd8cd;
  --pw-border-soft: #eae6dd;
  --pw-border-strong: #b6b0a3;

  /* --- estados --- */
  --pw-focus: #1a56c4;
  --pw-focus-halo: rgba(26, 86, 196, 0.22);
  --pw-empty-fg: var(--pw-ink-muted);

  /* --- tipografía --- */
  --pw-font-serif: "Iowan Old Style", "Palatino Linotype", Palatino,
    "Book Antiqua", Georgia, "Times New Roman", Times, serif;
  --pw-font-sans: system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;

  --pw-fs-xs: 0.75rem;
  --pw-fs-sm: 0.8125rem;
  --pw-fs-base: 1rem;
  --pw-fs-md: 1.0625rem;
  --pw-fs-lg: 1.125rem;
  --pw-fs-xl: 1.3125rem;
  --pw-fs-2xl: clamp(1.4rem, 1.18rem + 0.9vw, 1.75rem);
  --pw-fs-3xl: clamp(1.75rem, 1.35rem + 1.9vw, 2.5rem);

  /* --- espaciado (base 4 px) --- */
  --pw-space-1: 0.25rem;
  --pw-space-2: 0.5rem;
  --pw-space-3: 0.75rem;
  --pw-space-4: 1rem;
  --pw-space-5: 1.5rem;
  --pw-space-6: 2rem;
  --pw-space-7: 3rem;
  --pw-space-8: 4rem;

  /* --- bordes / sombras / ritmo --- */
  --pw-radius-sm: 2px;
  --pw-radius: 4px;
  --pw-radius-lg: 6px;
  --pw-shadow-sm: 0 1px 2px rgba(28, 27, 24, 0.06);
  --pw-shadow: 0 1px 3px rgba(28, 27, 24, 0.08),
    0 8px 20px -8px rgba(28, 27, 24, 0.12);
  --pw-shadow-pop: 0 8px 28px -8px rgba(28, 27, 24, 0.26);
  --pw-lh-tight: 1.25;
  --pw-lh-body: 1.7;
  --pw-lh-loose: 1.75;

  /* --- layout --- */
  --pw-header-h: 3.5rem;
  --pw-sidebar-w: 15rem;
  --pw-aside-w: 18rem;
  --pw-measure: 36rem;

  /* --- movimiento ---
     El único token de movimiento del fichero y el que más reglas consumen.
     No lleva duración ni curva propias: dice las de v6 · 1, que son las que
     documentan qué escalón es cada una. No es una dependencia hacia adelante
     —una variable se resuelve al leerla, no donde se escribe—, y la
     declaración gemela de v6 · 1, igual y más abajo, gana por cascada. */
  --pw-transition: var(--pw-dur-1) var(--pw-ease-out);
}

/* --- Tema oscuro: solo se redefinen los tokens; todas las clases los usan --- */
@media (prefers-color-scheme: dark) {
  :root {
    --pw-bg: #14150f;
    --pw-surface: #1c1d17;
    --pw-surface-2: #24251e;
    --pw-wash: #2b2c24;
    --pw-sidebar-bg: #191a14;
    --pw-header-bg: #14150f;

    --pw-ink: #ebe8e0;
    --pw-ink-soft: #cfccc3;
    --pw-ink-muted: #9c988d;
    --pw-ink-faint: #45443c;
    --pw-on-accent: #ffffff;

    --pw-link: #8fb0e6;
    --pw-link-hover: #b9cdf2;
    --pw-link-visited: #bda8e0;
    --pw-missing: #e08a7d;

    --pw-border: #34352d;
    --pw-border-soft: #292a23;
    --pw-border-strong: #54544a;

    --pw-focus: #7fa9f0;
    --pw-focus-halo: rgba(127, 169, 240, 0.28);

    --pw-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --pw-shadow: 0 1px 3px rgba(0, 0, 0, 0.45),
      0 8px 20px -8px rgba(0, 0, 0, 0.6);
    --pw-shadow-pop: 0 8px 28px -8px rgba(0, 0, 0, 0.7);
  }
}


/* ==========================================================================
   2 · BASE
   ========================================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--pw-header-h) + var(--pw-space-4));
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body.pw-body {
  margin: 0;
  background: var(--pw-bg);
  color: var(--pw-ink);
  font-family: var(--pw-font-sans);
  font-size: var(--pw-fs-md);
  line-height: var(--pw-lh-body);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection {
  background: var(--pw-wash);
  color: var(--pw-ink);
}

img,
svg {
  max-width: 100%;
  height: auto;
}

a {
  color: var(--pw-link);
  text-decoration: none;
}

a:hover {
  color: var(--pw-link-hover);
}

/* Foco visible y coherente en todo el sitio */
a:focus-visible,
button:focus-visible,
input:focus-visible,
label:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--pw-focus);
  outline-offset: 2px;
  border-radius: var(--pw-radius-sm);
}

/* Sólo lectura por teclado: sin anillo de foco en clic de ratón */
a:focus:not(:focus-visible),
button:focus:not(:focus-visible),
input:focus:not(:focus-visible) {
  outline: none;
}


/* ==========================================================================
   3 · CABECERA FIJA
   ========================================================================== */
.pw-header {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pw-space-3) var(--pw-space-5);
  min-height: var(--pw-header-h);
  padding: var(--pw-space-2) var(--pw-space-5);
  background: var(--pw-header-bg);
  border-bottom: 1px solid var(--pw-border);
}

.pw-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--pw-space-2);
  color: var(--pw-ink);
  flex: 0 0 auto;
}

.pw-brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--pw-radius-sm);
  background: var(--pw-ink);
  color: var(--pw-bg);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-sm);
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1;
}

.pw-brand-text {
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-lg);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: var(--pw-lh-tight);
  white-space: nowrap;
}

.pw-brand:hover .pw-brand-text {
  color: var(--pw-link);
}

.pw-header-links {
  display: flex;
  align-items: center;
  gap: var(--pw-space-4);
  margin-left: auto;
  flex: 0 0 auto;
}

.pw-header-link {
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-sm);
  font-weight: 600;
  white-space: nowrap;
}

.pw-header-link:hover {
  color: var(--pw-link);
  text-decoration: underline;
  text-underline-offset: 3px;
}


/* ==========================================================================
   4 · BUSCADOR
   ========================================================================== */
.pw-search {
  position: relative;
  flex: 1 1 16rem;
  max-width: 26rem;
  min-width: 0;
}

.pw-search-input {
  display: block;
  width: 100%;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  background: var(--pw-surface);
  color: var(--pw-ink);
  font-family: inherit;
  font-size: var(--pw-fs-sm);
  line-height: 1.5;
}

.pw-search-input::placeholder {
  color: var(--pw-ink-muted);
}

.pw-search-input:focus {
  border-color: var(--pw-focus);
  box-shadow: 0 0 0 3px var(--pw-focus-halo);
}

.pw-search-input:focus-visible {
  outline: 2px solid var(--pw-focus);
  outline-offset: 1px;
}

.pw-search-results {
  position: absolute;
  top: calc(100% + var(--pw-space-1));
  left: 0;
  right: 0;
  z-index: 40;
  max-height: 24rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  box-shadow: var(--pw-shadow-pop);
}

.pw-search-results[hidden] {
  display: none;
}

.pw-search-item {
  display: block;
  padding: var(--pw-space-2) var(--pw-space-3);
  color: var(--pw-ink);
  border-bottom: 1px solid var(--pw-border-soft);
}

.pw-search-item:last-child {
  border-bottom: 0;
}

.pw-search-item:hover,
.pw-search-item:focus-visible {
  background: var(--pw-wash);
  color: var(--pw-ink);
}

.pw-search-item-title {
  display: block;
  font-size: var(--pw-fs-base);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
}

.pw-search-item-sub {
  display: block;
  margin-top: 2px;
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  line-height: var(--pw-lh-tight);
}

/* Variante del héroe de la portada */
.pw-search-page {
  max-width: 34rem;
  flex: 1 1 auto;
  margin: var(--pw-space-5) auto 0;
}

.pw-search-page .pw-search-input {
  padding: 0.65rem 0.9rem;
  font-size: var(--pw-fs-md);
}


/* ==========================================================================
   5 · LAYOUT Y BARRA LATERAL
   ========================================================================== */
.pw-layout {
  min-height: 60vh;
}

.pw-main {
  padding: var(--pw-space-6) var(--pw-space-5) var(--pw-space-8);
}

.pw-sidebar {
  position: relative;
  padding: var(--pw-space-3) var(--pw-space-4) var(--pw-space-4);
  background: var(--pw-sidebar-bg);
  border-bottom: 1px solid var(--pw-border);
}

.pw-sidebar-title {
  margin: var(--pw-space-4) 0 var(--pw-space-2);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  line-height: var(--pw-lh-tight);
}

.pw-nav-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.pw-nav-item {
  margin: 0;
}

.pw-nav-link {
  display: block;
  padding: 0.2rem 0.55rem;
  border-left: 2px solid transparent;
  border-radius: var(--pw-radius-sm);
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-sm);
  line-height: 1.45;
  text-decoration: none;
}

.pw-nav-link:hover {
  background: var(--pw-wash);
  color: var(--pw-ink);
}

.pw-nav-link-active,
.pw-nav-link[aria-current="page"] {
  border-left-color: var(--pw-ink);
  background: var(--pw-wash);
  color: var(--pw-ink);
  font-weight: 600;
}

/* Conmutador plegable (solo <1024px) */
.pw-nav-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.pw-sidebar-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pw-space-2);
  width: 100%;
  padding: var(--pw-space-2) var(--pw-space-3);
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  color: var(--pw-ink-soft);
  font-family: inherit;
  font-size: var(--pw-fs-sm);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
  cursor: pointer;
  user-select: none;
}

.pw-sidebar-toggle::after {
  content: "";
  flex: 0 0 auto;
  width: 0.45rem;
  height: 0.45rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--pw-transition);
}

.pw-nav-toggle:checked ~ .pw-sidebar-toggle::after {
  transform: translateY(2px) rotate(225deg);
}

.pw-nav-toggle:focus-visible ~ .pw-sidebar-toggle {
  outline: 2px solid var(--pw-focus);
  outline-offset: 2px;
}

/* Plegado: contraído por defecto en móvil, desplegado en escritorio */
.pw-sidebar-title,
.pw-nav-list {
  display: none;
}

.pw-nav-toggle:checked ~ .pw-sidebar-title,
.pw-nav-toggle:checked ~ .pw-nav-list {
  display: block;
}

.pw-nav-toggle:checked ~ .pw-sidebar-title:first-of-type {
  margin-top: var(--pw-space-4);
}

@media (min-width: 1024px) {
  .pw-layout {
    padding-left: var(--pw-sidebar-w);
  }

  .pw-sidebar {
    position: fixed;
    top: var(--pw-header-h);
    left: 0;
    bottom: 0;
    z-index: 20;
    width: var(--pw-sidebar-w);
    padding: var(--pw-space-5) var(--pw-space-3) var(--pw-space-7);
    overflow-y: auto;
    overscroll-behavior: contain;
    border-right: 1px solid var(--pw-border);
    border-bottom: 0;
  }

  .pw-nav-toggle,
  .pw-sidebar-toggle {
    display: none;
  }

  .pw-sidebar-title,
  .pw-nav-list {
    display: block;
  }

  .pw-main {
    padding: var(--pw-space-7) var(--pw-space-6) var(--pw-space-8);
  }
}

@media (max-width: 1023px) {
  .pw-nav-list > .pw-nav-item + .pw-nav-item {
    margin-top: 2px;
  }
}


/* ==========================================================================
   6 · PIE
   ========================================================================== */
.pw-footer {
  padding: var(--pw-space-5) var(--pw-space-5);
  border-top: 1px solid var(--pw-border);
  background: var(--pw-surface-2);
}

.pw-footer-text {
  max-width: var(--pw-measure);
  margin: 0 auto;
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-sm);
  text-align: center;
  line-height: var(--pw-lh-body);
}

/* El pie pasó de una línea muda a cinco. Todas las líneas nuevas comparten caja
   entre sí —48rem en vez de `--pw-measure`, que es una medida de prosa y aquí
   hay líneas de navigation que se salen de ella— y `.pw-footer-text` conserva la
   suya y su `margin: 0`, que por eso sigue siendo la primera: al abrir en ella
   no queda un hueco de más arriba. El ritmo vertical lo da `--pw-space-3`.

   El correo es un `<a>` normal, así que toma `--pw-link` de los tres temas por su
   cuenta y hereda el anillo de `a:focus-visible` sin necesitar una regla más. Va
   escrito con entidades HTML por una razón concreta: un `mailto:` en claro en el
   HTML público lo cosecha cualquier bot en segundos, y eso no se puede
   deshacer después. Los navegadores lo decodifican antes de analizar el
   atributo, así que el enlace funciona igual de bien para una persona.

   Los separadores van en el marcado como `<span aria-hidden="true">` y no como
   `::before`: un punto medio generado por CSS lo vocaliza mal algunos lectores
   de pantalla, y aquí la línea ya es larga. */

.pw-footer-nav,
.pw-footer-keys,
.pw-footer-contact,
.pw-footer-colophon {
  max-width: 48rem;
  margin: var(--pw-space-3) auto 0;
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-sm);
  text-align: center;
  line-height: var(--pw-lh-body);
}

.pw-footer-nav span {
  margin: 0 var(--pw-space-2);
}

/* Las mismas piezas que `.pw-keys kbd` del modal, que están scopeadas a él y no
   alcanzan al pie. Copiadas, no compartidas, para no tocar la regla del modal. */
.pw-footer-keys kbd {
  display: inline-block;
  min-width: 1.4rem;
  padding: 0.05rem 0.3rem;
  border: 1px solid var(--pw-border-strong);
  border-bottom-width: 2px;
  border-radius: var(--pw-radius-sm);
  background: var(--pw-surface-2);
  color: var(--pw-ink);
  font-family: inherit;
  font-size: var(--pw-fs-sm);
  line-height: 1.5;
  text-align: center;
}


/* ==========================================================================
   7 · PORTADA
   ========================================================================== */
.pw-home {
  max-width: 72rem;
  margin: 0 auto;
}

.pw-hero {
  position: relative;
  padding: var(--pw-space-6) 0 var(--pw-space-6);
  margin-bottom: var(--pw-space-6);
  text-align: center;
  border-bottom: 1px solid var(--pw-border);
}

.pw-hero::before {
  content: "";
  display: block;
  width: 3rem;
  height: 2px;
  margin: 0 auto var(--pw-space-5);
  background: var(--pw-ink);
}

.pw-hero-title {
  max-width: 22ch;
  margin: 0 auto;
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-3xl);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.pw-hero-sub {
  max-width: 52ch;
  margin: var(--pw-space-4) auto 0;
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-lg);
  line-height: var(--pw-lh-loose);
  text-wrap: pretty;
}

.pw-section {
  margin-bottom: var(--pw-space-8);
  scroll-margin-top: calc(var(--pw-header-h) + var(--pw-space-5));
}

.pw-section > .pw-h2:first-child {
  margin-top: 0;
}

.pw-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16.5rem), 1fr));
  gap: var(--pw-space-4);
}

.pw-card {
  display: flex;
  flex-direction: column;
  gap: var(--pw-space-1);
  padding: var(--pw-space-4);
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  color: var(--pw-ink);
  line-height: var(--pw-lh-body);
  transition: border-color var(--pw-transition), box-shadow var(--pw-transition),
    transform var(--pw-transition);
}

.pw-card:hover,
.pw-card:focus-visible {
  border-color: var(--pw-border-strong);
  box-shadow: var(--pw-shadow);
  transform: translateY(-2px);
  color: var(--pw-ink);
}

.pw-card-title {
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-xl);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
  letter-spacing: -0.01em;
}

.pw-card-sub {
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
}

.pw-card-text {
  margin-top: var(--pw-space-2);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
  text-wrap: pretty;
}

.pw-card-meta {
  margin-top: auto;
  padding-top: var(--pw-space-3);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: var(--pw-lh-tight);
}

/* Categorías de la portada */
.pw-browse {
  display: grid;
  gap: var(--pw-space-6);
}

.pw-browse > .pw-h3 {
  margin: 0 0 var(--pw-space-2);
}

.pw-browse .pw-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pw-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.pw-browse .pw-list > li {
  margin: 0;
}

.pw-browse .pw-list a {
  display: inline-block;
  padding: 0.15rem 0.6rem;
  background: var(--pw-surface-2);
  border: 1px solid var(--pw-border);
  border-radius: 999px;
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-sm);
}

.pw-browse .pw-list a:hover {
  background: var(--pw-wash);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
}


/* ==========================================================================
   8 · ARTÍCULO: estructura
   ========================================================================== */
.pw-article-mount {
  display: flow-root;
}

.pw-article {
  max-width: 72rem;
  margin: 0 auto;
}

.pw-breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pw-space-2);
  margin-bottom: var(--pw-space-4);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-tight);
}

.pw-breadcrumb-link {
  color: var(--pw-ink-muted);
}

.pw-breadcrumb-link:hover {
  color: var(--pw-link);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.pw-breadcrumb-sep {
  color: var(--pw-ink-faint);
}

.pw-breadcrumb-current {
  color: var(--pw-ink-soft);
  font-weight: 600;
}

.pw-title {
  margin: 0;
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-3xl);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.022em;
  text-wrap: balance;
}

.pw-subtitle {
  max-width: 60ch;
  margin: var(--pw-space-3) 0 0;
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-lg);
  line-height: var(--pw-lh-loose);
  text-wrap: pretty;
}

.pw-article-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pw-space-2);
  margin: var(--pw-space-4) 0 0;
  padding-bottom: var(--pw-space-5);
  border-bottom: 1px solid var(--pw-border);
}

.pw-tag {
  display: inline-block;
  padding: 0.1rem 0.55rem;
  border: 1px solid var(--pw-border);
  border-radius: 999px;
  background: var(--pw-surface-2);
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.6;
}

/* La categoría es la etiqueta primaria: presence real, sin color de acento */
.pw-tag-category {
  background: var(--pw-surface);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
}

.pw-tag-updated {
  background: transparent;
  color: var(--pw-ink-muted);
  font-weight: 400;
  letter-spacing: 0;
}

.pw-content {
  display: grid;
  gap: var(--pw-space-6);
  margin-top: var(--pw-space-5);
}

.pw-text {
  max-width: var(--pw-measure);
  min-width: 0;
  font-size: var(--pw-fs-md);
}

.pw-aside {
  min-width: 0;
}

/* 1024-1199px: la columna lateral se convierte en una franja doble sobre el texto */
@media (min-width: 1024px) and (max-width: 1199px) {
  .pw-content {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
  }

  .pw-aside {
    order: -1;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--pw-space-4);
    align-items: start;
  }

  .pw-text {
    grid-column: 1 / -1;
  }
}

/* ≥1200px: columna de índice + infobox a la derecha */
@media (min-width: 1200px) {
  .pw-content {
    grid-template-columns: minmax(0, 1fr) var(--pw-aside-w);
    gap: var(--pw-space-7);
    align-items: start;
  }

  .pw-toc {
    position: sticky;
    top: calc(var(--pw-header-h) + var(--pw-space-4));
  }
}


/* ==========================================================================
   9 · BLOQUES DE CONTENIDO (CONTRATO §6.3)
   ========================================================================== */
.pw-p {
  margin: 0 0 var(--pw-space-4);
  text-indent: 1.25em;
  text-wrap: pretty;
}

.pw-p + .pw-p {
  margin-bottom: 0;
}

.pw-lead {
  margin-bottom: var(--pw-space-5);
  font-size: var(--pw-fs-lg);
  font-weight: 600;
  line-height: var(--pw-lh-loose);
  text-indent: 0;
  text-wrap: pretty;
}

.pw-lead + .pw-p {
  margin-top: 0;
}

.pw-h2 {
  margin: var(--pw-space-7) 0 var(--pw-space-4);
  padding-bottom: var(--pw-space-2);
  border-bottom: 1px solid var(--pw-border);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-2xl);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
  letter-spacing: -0.015em;
  scroll-margin-top: calc(var(--pw-header-h) + var(--pw-space-4));
}

.pw-h3 {
  margin: var(--pw-space-6) 0 var(--pw-space-3);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-xl);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
  letter-spacing: -0.01em;
  scroll-margin-top: calc(var(--pw-header-h) + var(--pw-space-4));
}

.pw-headline {
  font: inherit;
  color: inherit;
  scroll-margin-top: calc(var(--pw-header-h) + var(--pw-space-4));
}

.pw-headline a {
  color: inherit;
  text-decoration: none;
}

.pw-h2:target > .pw-headline,
.pw-h3:target > .pw-headline {
  background: var(--pw-wash);
  box-shadow: 0 0 0 3px var(--pw-wash);
  border-radius: var(--pw-radius-sm);
}

.pw-editlink {
  margin: calc(-1 * var(--pw-space-3)) 0 var(--pw-space-3);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-sm);
  text-align: right;
  line-height: var(--pw-lh-tight);
}

.pw-editlink a {
  color: var(--pw-link);
}

.pw-editlink a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* --- Listas --- */
.pw-list {
  margin: 0 0 var(--pw-space-4);
  padding-left: 1.4rem;
  line-height: var(--pw-lh-body);
}

.pw-list > li {
  margin-bottom: var(--pw-space-1);
  text-wrap: pretty;
}

.pw-list > li::marker {
  color: var(--pw-ink-muted);
}

.pw-list-num {
  list-style: decimal;
}

/* --- Definiciones --- */
.pw-dl {
  margin: 0 0 var(--pw-space-4);
  line-height: var(--pw-lh-body);
}

.pw-dl dt {
  margin-top: var(--pw-space-3);
  font-weight: 700;
  line-height: var(--pw-lh-body);
}

.pw-dl dt:first-child {
  margin-top: 0;
}

.pw-dl dd {
  margin: 0 0 0 var(--pw-space-4);
  color: var(--pw-ink-soft);
  text-wrap: pretty;
}

/* --- Cita --- */
.pw-quote {
  margin: var(--pw-space-5) 0;
  padding: var(--pw-space-1) 0 var(--pw-space-1) var(--pw-space-4);
  border-left: 3px solid var(--pw-border-strong);
  color: var(--pw-ink-soft);
  font-style: italic;
  line-height: var(--pw-lh-loose);
}

.pw-quote p {
  margin: 0 0 var(--pw-space-3);
  text-indent: 0;
}

.pw-quote p:last-of-type {
  margin-bottom: 0;
}

.pw-quote cite {
  display: block;
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-sm);
  font-style: normal;
  text-align: right;
  line-height: var(--pw-lh-body);
}

.pw-quote cite::before {
  content: "— ";
}

/* --- Notas --- */
.pw-note {
  margin: var(--pw-space-5) 0;
  padding: var(--pw-space-3) var(--pw-space-4);
  background: var(--pw-surface-2);
  border: 1px solid var(--pw-border-soft);
  border-left: 3px solid var(--pw-ink-faint);
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-loose);
  border-radius: 0 var(--pw-radius-sm) var(--pw-radius-sm) 0;
}

.pw-note p {
  margin: 0;
  text-indent: 0;
}

.pw-note p + p {
  margin-top: var(--pw-space-2);
}

.pw-note-label {
  display: block;
  margin-bottom: 2px;
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: var(--pw-lh-tight);
}

/* --- Figuras --- */
.pw-figure {
  margin: var(--pw-space-5) 0;
  padding: var(--pw-space-4);
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  text-align: center;
}

.pw-figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--pw-radius-sm);
}

.pw-figure figcaption {
  margin-top: var(--pw-space-3);
  padding-top: var(--pw-space-2);
  border-top: 1px solid var(--pw-border-soft);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-sm);
  font-style: italic;
  line-height: var(--pw-lh-body);
  text-indent: 0;
  text-wrap: pretty;
}

.pw-figure-credit {
  display: block;
  margin-top: var(--pw-space-1);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-style: normal;
}

/* --- Tablas --- */
.pw-wtable,
.pw-infobox {
  width: 100%;
  margin: var(--pw-space-5) 0;
  border-collapse: collapse;
  border: 1px solid var(--pw-border);
  background: var(--pw-surface);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
}

.pw-wtable th,
.pw-wtable td,
.pw-infobox th,
.pw-infobox td {
  padding: var(--pw-space-2) var(--pw-space-3);
  border: 1px solid var(--pw-border);
  text-align: left;
  vertical-align: top;
}

.pw-wtable th {
  background: var(--pw-surface-2);
  font-weight: 700;
  white-space: nowrap;
}

.pw-wtable tbody tr:nth-child(even) td,
.pw-wtable tbody tr:nth-child(even) th {
  background: var(--pw-surface-2);
}

.pw-wtable caption {
  padding-bottom: var(--pw-space-2);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  text-align: left;
}

/* --- Enlaces internos --- */
.pw-wikilink {
  color: var(--pw-link);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.pw-wikilink:hover {
  color: var(--pw-link-hover);
  text-decoration-thickness: 2px;
}

.pw-wikilink:visited {
  color: var(--pw-link-visited);
}

.pw-wikilink-missing,
.pw-wikilink-missing:visited {
  color: var(--pw-missing);
  text-decoration-style: dotted;
  text-decoration-thickness: 1px;
}


/* ==========================================================================
   10 · REFERENCIAS Y CATEGORÍAS
   ========================================================================== */
.pw-references-wrap {
  margin-top: var(--pw-space-7);
}

.pw-references {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: pw-ref;
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
}

.pw-references > li {
  position: relative;
  margin-bottom: var(--pw-space-2);
  padding-left: 2.1rem;
  color: var(--pw-ink-soft);
  text-indent: 0;
  text-wrap: pretty;
}

.pw-references > li::before {
  counter-increment: pw-ref;
  content: "[" counter(pw-ref) "]";
  position: absolute;
  top: 0;
  left: 0;
  color: var(--pw-ink-muted);
  font-variant-numeric: tabular-nums;
}

.pw-catlinks {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--pw-space-2);
  margin-top: var(--pw-space-6);
  padding-top: var(--pw-space-4);
  border-top: 1px solid var(--pw-border);
}

.pw-catlinks-label {
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  line-height: var(--pw-lh-tight);
}

.pw-catlinks-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pw-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.pw-catlinks-list > li {
  margin: 0;
}

.pw-catlinks-list a,
.pw-catlinks-list > li > span {
  display: inline-block;
  padding: 0.1rem 0.6rem;
  background: var(--pw-surface-2);
  border: 1px solid var(--pw-border);
  border-radius: 999px;
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-xs);
  line-height: 1.7;
}

.pw-catlinks-list a:hover {
  background: var(--pw-wash);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
}


/* ==========================================================================
   11 · ÍNDICE (TOC) E INFOBOX
   ========================================================================== */
.pw-toc {
  padding: var(--pw-space-3) var(--pw-space-4) var(--pw-space-4);
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
}

.pw-toc-title {
  padding-bottom: var(--pw-space-2);
  margin-bottom: var(--pw-space-2);
  border-bottom: 1px solid var(--pw-border);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  line-height: var(--pw-lh-tight);
}

.pw-toc-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.pw-toc-item {
  margin: 0;
}

.pw-toc-link {
  display: block;
  padding: 0.15rem 0.5rem;
  border-left: 2px solid transparent;
  border-radius: var(--pw-radius-sm);
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-sm);
  line-height: 1.5;
  text-decoration: none;
}

.pw-toc-link:hover {
  background: var(--pw-wash);
  color: var(--pw-ink);
}

/* Estado activo del scrollspy (añadido por CSS, §3) */
.pw-toc-link.is-active,
.pw-toc-link[aria-current="true"],
.pw-toc-item.is-active > .pw-toc-link {
  border-left-color: var(--pw-ink);
  background: var(--pw-wash);
  color: var(--pw-ink);
  font-weight: 600;
}

.pw-infobox {
  margin: 0;
  box-shadow: var(--pw-shadow-sm);
  font-size: var(--pw-fs-sm);
}

.pw-infobox caption,
.pw-infobox th[colspan],
.pw-infobox td[colspan] {
  padding: var(--pw-space-2) var(--pw-space-3);
  background: var(--pw-ink);
  color: var(--pw-on-accent);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-lg);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
  text-align: center;
  border: 0;
}

.pw-infobox th {
  width: 38%;
  background: var(--pw-surface-2);
  font-weight: 700;
}

.pw-infobox th[colspan],
.pw-infobox th:not([colspan]) {
  width: auto;
}

.pw-infobox tbody tr:nth-child(even) td {
  background: var(--pw-surface-2);
}

@media (min-width: 1024px) and (max-width: 1199px) {
  .pw-infobox {
    margin: 0;
  }
}


/* ==========================================================================
   12 · ARTÍCULOS RELACIONADOS
   ========================================================================== */
.pw-related {
  margin-top: var(--pw-space-8);
  padding-top: var(--pw-space-5);
  border-top: 1px solid var(--pw-border);
}

.pw-related > .pw-h2 {
  margin-top: 0;
}

.pw-related-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
  gap: var(--pw-space-3);
}

.pw-related-card {
  display: flex;
  flex-direction: column;
  gap: var(--pw-space-1);
  padding: var(--pw-space-3) var(--pw-space-4);
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  color: var(--pw-ink);
  transition: border-color var(--pw-transition), box-shadow var(--pw-transition),
    transform var(--pw-transition);
}

.pw-related-card:hover,
.pw-related-card:focus-visible {
  border-color: var(--pw-border-strong);
  box-shadow: var(--pw-shadow-sm);
  transform: translateY(-1px);
  color: var(--pw-ink);
}

.pw-related-card .pw-card-title {
  font-size: var(--pw-fs-lg);
}

.pw-related-card .pw-card-sub {
  font-size: var(--pw-fs-xs);
  color: var(--pw-ink-muted);
}


/* ==========================================================================
   13 · ESTADOS: carga, vacío y error
   ========================================================================== */
.pw-loading {
  margin: 0;
  padding: var(--pw-space-6) 0;
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-sm);
  letter-spacing: 0.04em;
}

.pw-loading::before {
  content: "";
  display: inline-block;
  width: 0.7em;
  height: 0.7em;
  margin-right: 0.6em;
  border: 2px solid var(--pw-border-strong);
  border-top-color: transparent;
  border-radius: 50%;
  vertical-align: -1px;
  animation: pw-spin 700ms linear infinite;
}

@keyframes pw-spin {
  to {
    transform: rotate(360deg);
  }
}

.pw-empty {
  margin: var(--pw-space-5) 0;
  padding: var(--pw-space-6) var(--pw-space-4);
  background: var(--pw-surface-2);
  border: 1px dashed var(--pw-border-strong);
  border-radius: var(--pw-radius);
  color: var(--pw-empty-fg);
  text-align: center;
  line-height: var(--pw-lh-loose);
}

.pw-empty p {
  margin: 0;
  text-indent: 0;
}

.pw-empty p + p {
  margin-top: var(--pw-space-2);
}

.pw-empty a {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.pw-search-results .pw-empty {
  margin: 0;
  padding: var(--pw-space-4);
  border: 0;
  border-radius: 0;
  background: transparent;
  font-size: var(--pw-fs-sm);
}


/* ==========================================================================
   14 · PANTALLAS PEQUEÑAS
   ========================================================================== */
@media (max-width: 767px) {
  .pw-header {
    gap: var(--pw-space-2) var(--pw-space-3);
    padding: var(--pw-space-2) var(--pw-space-4);
  }

  .pw-search {
    order: 3;
    flex: 1 1 100%;
    max-width: none;
    margin: var(--pw-space-1) 0 0;
  }

  .pw-main {
    padding: var(--pw-space-5) var(--pw-space-4) var(--pw-space-7);
  }

  .pw-hero {
    padding: var(--pw-space-5) 0;
  }

  .pw-card-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .pw-aside {
    display: grid;
    gap: var(--pw-space-4);
  }

  .pw-wtable {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
  }

  .pw-wtable th,
  .pw-wtable td {
    white-space: normal;
  }
}

@media (max-width: 480px) {
  .pw-brand-text {
    font-size: var(--pw-fs-base);
  }

  .pw-header-links {
    gap: var(--pw-space-3);
  }

  .pw-subtitle {
    font-size: var(--pw-fs-md);
  }

  .pw-lead {
    font-size: var(--pw-fs-md);
  }

  .pw-dl dd {
    margin-left: 0;
  }

  .pw-catlinks {
    flex-direction: column;
    gap: var(--pw-space-2);
  }
}


/* ==========================================================================
   15 · MOVIMIENTO REDUCIDO
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ==========================================================================
   16 · IMPRESIÓN
   ========================================================================== */
@media print {
  .pw-header,
  .pw-sidebar,
  .pw-footer,
  .pw-toc,
  .pw-article-actions,
  .pw-editlink,
  .pw-related {
    display: none;
  }

  .pw-layout {
    padding-left: 0;
  }

  .pw-main {
    padding: 0;
  }

  .pw-text {
    max-width: none;
  }
}


/* ===== v2 · interfaz y funciones ===== */
/* ==========================================================================
   ===== v2 · interfaz y funciones =====
   Capa nueva de UI + las tres preferencias de lectura de FEATURES.md §5.

   Reglas de esta ampliación:
   · Solo se AÑADE al final. Ninguna regla anterior se reescribe ni se borra.
   · No se redefine ninguna clase de CONTRACT.md §6. Donde una clase ya
     existente hay que ajustarla en un contexto concreto (el anillo de foco
     global, el estado vacío dentro de un popover) se hace con un selector
     nuevo de contexto, nunca pisando la clase base; cada caso se comenta.
   · Todo se apoya en los tokens de §1, así que los temas claro / oscuro /
     sepia los recolorean sin tocar una sola clase.
   · Bordes redondeados: --pw-radius (4px) o --pw-radius-sm (2px). Ningún
     color de acento sobre superficies redondeadas: los estados activos usan
     solo tinta (--pw-ink / --pw-ink-soft / --pw-surface / --pw-wash /
     --pw-border-strong).

   Índice
     1 · Temas     html[data-pw-theme="claro|oscuro|sepia"]   (auto = media query)
     2 · Tamaño   html[data-pw-size="sm|md|lg|xl"] -> --pw-text-scale
     3 · Ancho    html[data-pw-measure="estrecha|normal|amplia"]
     4 · [hidden] gana a todo
     5 · Cabecera: barra de herramientas
     6 · Popovers y preferencias de lectura
     7 · Progreso de lectura y volver arriba
     8 · Ficha del artículo y favoritos
     9 · Diálogo de atajos
     10 · Hovercards
     11 · Permalinks de sección
     12 · Índice alfabético
     13 · Impresión, pantallas pequeñas y movimiento reducido
   ========================================================================== */


/* ==========================================================================
   v2 · 1 · TEMAS  (FEATURES.md §5)
   --------------------------------------------------------------------------
   `auto` no se declara: es exactamente el comportamiento vigente, o sea la
   media query `prefers-color-scheme: dark` de §1. Los otros tres se declaran
   aquí, al final del fichero y con el selector `html[attr]` (0,1,1), que gana
   a `:root` (0,1,0) de la media query sea cual sea su orden. De ahí que
   `data-pw-theme="oscuro"` gane a `prefers-color-scheme: light` y `claro`
   gane a `prefers-color-scheme: dark`.
   Todos los bloques repiten la lista completa de tokens que §1 cambia por
   tema, incluido --pw-empty-fg: es un `var(--pw-ink-muted)` resuelto ya en
   :root, así que hay que volver a declararlo aquí para que apunte a la tinta
   del tema activo y no a la del tema del sistema.
   ========================================================================== */
html[data-pw-theme="claro"] {
  --pw-bg: #fbfaf7;
  --pw-surface: #ffffff;
  --pw-surface-2: #f4f2ec;
  --pw-wash: #f0ede5;
  --pw-sidebar-bg: #f6f4ef;
  --pw-header-bg: #fbfaf7;

  --pw-ink: #1c1b18;
  --pw-ink-soft: #3d3b35;
  --pw-ink-muted: #6c6860;
  --pw-ink-faint: #c9c4b8;
  --pw-on-accent: #ffffff;

  --pw-link: #2a4b8d;
  --pw-link-hover: #1b3568;
  --pw-link-visited: #5a4b7d;
  --pw-missing: #a3271b;

  --pw-border: #ddd8cd;
  --pw-border-soft: #eae6dd;
  --pw-border-strong: #b6b0a3;

  --pw-focus: #1a56c4;
  --pw-focus-halo: rgba(26, 86, 196, 0.22);
  --pw-empty-fg: var(--pw-ink-muted);

  --pw-shadow-sm: 0 1px 2px rgba(28, 27, 24, 0.06);
  --pw-shadow: 0 1px 3px rgba(28, 27, 24, 0.08),
    0 8px 20px -8px rgba(28, 27, 24, 0.12);
  --pw-shadow-pop: 0 8px 28px -8px rgba(28, 27, 24, 0.26);
}

html[data-pw-theme="oscuro"] {
  --pw-bg: #14150f;
  --pw-surface: #1c1d17;
  --pw-surface-2: #24251e;
  --pw-wash: #2b2c24;
  --pw-sidebar-bg: #191a14;
  --pw-header-bg: #14150f;

  --pw-ink: #ebe8e0;
  --pw-ink-soft: #cfccc3;
  --pw-ink-muted: #9c988d;
  --pw-ink-faint: #45443c;
  --pw-on-accent: #ffffff;

  --pw-link: #8fb0e6;
  --pw-link-hover: #b9cdf2;
  --pw-link-visited: #bda8e0;
  --pw-missing: #e08a7d;

  --pw-border: #34352d;
  --pw-border-soft: #292a23;
  --pw-border-strong: #54544a;

  --pw-focus: #7fa9f0;
  --pw-focus-halo: rgba(127, 169, 240, 0.28);
  --pw-empty-fg: var(--pw-ink-muted);

  --pw-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --pw-shadow: 0 1px 3px rgba(0, 0, 0, 0.45),
    0 8px 20px -8px rgba(0, 0, 0, 0.6);
  --pw-shadow-pop: 0 8px 28px -8px rgba(0, 0, 0, 0.7);
}

/* Sepia: fondo cálido #f4ecd8, tinta #3b2f1e, enlace #7a4a1e.
   Contraste sobre #f4ecd8: tinta 11,1:1 · tinta suave 7,2:1 · tinta apagada
   5,5:1 · enlace 6,3:1 · enlace visitado 7,5:1 · faltan 6,2:1 · anillo de
   foco 5,8:1. Todos ≥ AA (4,5:1 texto normal, 3:1 componentes de interfaz). */
html[data-pw-theme="sepia"] {
  --pw-bg: #f4ecd8;
  --pw-surface: #fbf6e9;
  --pw-surface-2: #ece0c4;
  --pw-wash: #e7dabb;
  --pw-sidebar-bg: #f0e6cd;
  --pw-header-bg: #f4ecd8;

  --pw-ink: #3b2f1e;
  --pw-ink-soft: #5b4a34;
  --pw-ink-muted: #6d5c42;
  --pw-ink-faint: #b3a17c;
  --pw-on-accent: #ffffff;

  --pw-link: #7a4a1e;
  --pw-link-hover: #5e3614;
  --pw-link-visited: #6d3a4f;
  --pw-missing: #9c2f1c;

  --pw-border: #ded0b0;
  --pw-border-soft: #e8ddc3;
  --pw-border-strong: #c3b189;

  --pw-focus: #8a4b12;
  --pw-focus-halo: rgba(138, 75, 18, 0.25);
  --pw-empty-fg: var(--pw-ink-muted);

  --pw-shadow-sm: 0 1px 2px rgba(59, 47, 30, 0.1);
  --pw-shadow: 0 1px 3px rgba(59, 47, 30, 0.1),
    0 8px 20px -8px rgba(59, 47, 30, 0.16);
  --pw-shadow-pop: 0 8px 28px -8px rgba(59, 47, 30, 0.3);
}


/* ==========================================================================
   v2 · 2 · TAMAÑO DEL TEXTO  (FEATURES.md §5)
   --------------------------------------------------------------------------
   La base de todo el texto del sitio son tokens rem (§1), así que el tamaño
   se escala multiplicando esos tokens, nunca escribiendo px: la fuente
   relativa es el `rem` de cada token de §1 y el multiplicador es
   --pw-text-scale. Sin atributo en <html> el valor es 1 y el sitio se ve
   exactamente igual que antes.
   ========================================================================== */
:root {
  --pw-text-scale: 1;

  --pw-fs-xs: calc(0.75rem * var(--pw-text-scale));
  --pw-fs-sm: calc(0.8125rem * var(--pw-text-scale));
  --pw-fs-base: calc(1rem * var(--pw-text-scale));
  --pw-fs-md: calc(1.0625rem * var(--pw-text-scale));
  --pw-fs-lg: calc(1.125rem * var(--pw-text-scale));
  --pw-fs-xl: calc(1.3125rem * var(--pw-text-scale));
  --pw-fs-2xl: calc(clamp(1.4rem, 1.18rem + 0.9vw, 1.75rem) * var(--pw-text-scale));
  --pw-fs-3xl: calc(clamp(1.75rem, 1.35rem + 1.9vw, 2.5rem) * var(--pw-text-scale));
}

html[data-pw-size="sm"] { --pw-text-scale: 0.94; }
html[data-pw-size="md"] { --pw-text-scale: 1; }
html[data-pw-size="lg"] { --pw-text-scale: 1.09; }
html[data-pw-size="xl"] { --pw-text-scale: 1.18; }


/* ==========================================================================
   v2 · 3 · ANCHO DE COLUMNA  (FEATURES.md §5)
   ========================================================================== */
html[data-pw-measure="estrecha"] { --pw-measure: 32rem; }
html[data-pw-measure="normal"] { --pw-measure: 36rem; }
html[data-pw-measure="amplia"] { --pw-measure: 44rem; }


/* ==========================================================================
   v2 · 4 · EL ATRIBUTO hidden MANDA
   .pw-popover, .pw-hovercard, .pw-totop y .pw-favs-* empiezan ocultos en el
   HTML; sin esto, cualquier `display` de sus reglas los sacaría a la luz.
   ========================================================================== */
[hidden] {
  display: none !important;
}


/* ==========================================================================
   v2 · 5 · CABECERA: BARRA DE HERRAMIENTAS
   --------------------------------------------------------------------------
   `.pw-tools` se queda en el flujo de la cabecera (flex-wrap de §3 la lleva
   a su propia línea por debajo de 768px) y es el contexto de posicionamiento
   de los dos popover, que cuelgan de él y se ancran por la derecha.
   Sin `margin-left: auto` a propósito: el hueco elástico lo absorbe
   `.pw-header-links` (§3) y así los enlaces siguen donde estaban; solo por
   debajo de 768px se alinean a la derecha las herramientas (v2 · 13).
   ========================================================================== */
.pw-tools {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--pw-space-1);
  flex: 0 0 auto;
  margin-left: 0;
}

.pw-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--pw-radius);
  background: transparent;
  color: var(--pw-ink-soft);
  font-family: inherit;
  font-size: var(--pw-fs-sm);
  line-height: 1;
  cursor: pointer;
  transition: background var(--pw-transition), color var(--pw-transition),
    border-color var(--pw-transition);
}

.pw-btn:hover {
  background: var(--pw-wash);
  color: var(--pw-ink);
}

/* Estado abierto: tinta, nunca acento */
.pw-btn[aria-expanded="true"] {
  background: var(--pw-surface-2);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
}

.pw-btn-glyph {
  display: block;
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-sm);
  font-weight: 600;
  line-height: 1;
  pointer-events: none;
}

.pw-btn-badge {
  position: absolute;
  top: -3px;
  right: -4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1rem;
  height: 1rem;
  padding: 0 0.22rem;
  border: 1px solid var(--pw-bg);
  border-radius: 999px;
  background: var(--pw-ink);
  color: var(--pw-bg);
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

/* El anillo de foco global (§2 BASE) fija border-radius: 2px a todo botón
   enfocado; los botones nuevos conservan su propio radio para que el foco
   no deforme la forma. */
.pw-btn:focus-visible { border-radius: var(--pw-radius); }
.pw-totop:focus-visible { border-radius: 50%; }
.pw-fav-remove:focus-visible,
.pw-permalink:focus-visible { border-radius: var(--pw-radius-sm); }
.pw-seg:focus-visible,
.pw-step:focus-visible,
.pw-btn-text:focus-visible { border-radius: var(--pw-radius); }


/* ==========================================================================
   v2 · 6 · POPOVERS Y PREFERENCIAS DE LECTURA
   ========================================================================== */
.pw-popover {
  position: absolute;
  top: calc(100% + var(--pw-space-2));
  right: 0;
  z-index: 45;
  width: min(20rem, calc(100vw - 2.5rem));
  padding: var(--pw-space-3);
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  box-shadow: var(--pw-shadow-pop);
  color: var(--pw-ink);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
}

.pw-popover-wide {
  width: min(25rem, calc(100vw - 2.5rem));
}

/* §4 no define qué elemento lleva .is-open: se interpreta como refuerzo del
   popover ya abierto, sin decidir su visibilidad (eso es de `hidden`, §3.1). */
.pw-popover.is-open {
  border-color: var(--pw-border-strong);
  box-shadow: var(--pw-shadow-pop);
}

.pw-popover-title {
  padding-bottom: var(--pw-space-2);
  margin-bottom: var(--pw-space-1);
  border-bottom: 1px solid var(--pw-border);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  line-height: var(--pw-lh-tight);
}

.pw-pref-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--pw-space-1) var(--pw-space-3);
  padding: var(--pw-space-2) 0;
}

.pw-pref-row + .pw-pref-row {
  border-top: 1px solid var(--pw-border-soft);
}

.pw-pref-row-end {
  justify-content: flex-end;
  padding-top: var(--pw-space-3);
}

.pw-pref-label {
  flex: 0 0 auto;
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: var(--pw-lh-tight);
}

.pw-segmented,
.pw-stepper {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  max-width: 100%;
  padding: 2px;
  background: var(--pw-surface-2);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
}

.pw-seg,
.pw-step {
  flex: 0 1 auto;
  min-width: 0;
  padding: 0.2rem 0.5rem;
  border: 1px solid transparent;
  border-radius: var(--pw-radius-sm);
  background: transparent;
  color: var(--pw-ink-soft);
  font-family: inherit;
  font-size: var(--pw-fs-xs);
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--pw-transition), color var(--pw-transition),
    border-color var(--pw-transition);
}

.pw-seg:hover,
.pw-step:hover {
  background: var(--pw-wash);
  color: var(--pw-ink);
}

/* Seleccionado: superficie + borde fuerte + tinta (nunca acento) */
.pw-seg[aria-checked="true"] {
  background: var(--pw-surface);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
  font-weight: 700;
}

.pw-step:active {
  background: var(--pw-surface);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
}

.pw-step-value {
  min-width: 4.25rem;
  padding: 0 0.3rem;
  color: var(--pw-ink);
  font-size: var(--pw-fs-xs);
  font-weight: 600;
  text-align: center;
  line-height: 1.5;
}

.pw-btn-text {
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  background: var(--pw-surface);
  color: var(--pw-ink-soft);
  font-family: inherit;
  font-size: var(--pw-fs-xs);
  font-weight: 600;
  line-height: 1.6;
  cursor: pointer;
  transition: background var(--pw-transition), color var(--pw-transition),
    border-color var(--pw-transition);
}

.pw-btn-text:hover {
  background: var(--pw-wash);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
}

/* --- Panel de favoritos (rellenado por features.js) --- */
.pw-favs {
  max-height: 17rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.pw-fav-item {
  display: flex;
  align-items: center;
  gap: var(--pw-space-2);
  padding: var(--pw-space-2) 0;
  border-bottom: 1px solid var(--pw-border-soft);
  color: var(--pw-ink);
  text-decoration: none;
}

.pw-fav-item:first-child {
  padding-top: var(--pw-space-1);
}

.pw-fav-item:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}

.pw-fav-item:hover {
  color: var(--pw-ink);
}

.pw-fav-item-title {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
  line-height: var(--pw-lh-tight);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.pw-fav-item-sub {
  flex: 0 1 auto;
  max-width: 45%;
  overflow: hidden;
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  white-space: nowrap;
  text-overflow: ellipsis;
  text-align: right;
  line-height: var(--pw-lh-tight);
}

/* La × se alinea a la derecha de la fila: es el último hijo flexible */
.pw-fav-remove {
  flex: 0 0 auto;
  width: 1.35rem;
  height: 1.35rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--pw-radius-sm);
  background: transparent;
  color: var(--pw-ink-muted);
  font-family: inherit;
  font-size: var(--pw-fs-sm);
  line-height: 1;
  cursor: pointer;
  transition: background var(--pw-transition), color var(--pw-transition),
    border-color var(--pw-transition);
}

.pw-fav-remove:hover {
  background: var(--pw-wash);
  border-color: var(--pw-border);
  color: var(--pw-ink);
}

/* `.pw-empty` ya existe (§13) con márgenes de bloque y borde discontinuo.
   Aquí solo se ajusta la INSTANCIA que vive dentro de un popover, igual que
   hace `.pw-search-results .pw-empty` en §13: la clase no se redefine. */
.pw-popover .pw-empty {
  margin: 0;
  padding: var(--pw-space-4);
  background: transparent;
  border: 0;
  font-size: var(--pw-fs-xs);
  line-height: var(--pw-lh-body);
}


/* ==========================================================================
   v2 · 7 · PROGRESO DE LECTURA Y VOLVER ARRIBA
   --------------------------------------------------------------------------
   La barra escala con transform: scaleX() sobre el ancho completo del
   viewport; el contenedor no pinta fondo y no intercepta el ratón, así que
   se puede hacer scroll y pulsar a través de ella.
   ========================================================================== */
.pw-progress {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  z-index: 60;
  height: 3px;
  background: transparent;
  pointer-events: none;
}

.pw-progress-bar {
  width: 100%;
  height: 100%;
  background: var(--pw-ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 120ms linear;
}

/* La barra que persigue el scroll no necesita JavaScript para hacerlo bien.
   `animation-timeline: scroll()` deja que el motor la mueva él, en el hilo de
   composición: no hay un `scroll` que despierte, ni un `style.transform` que
   escribir, ni un ciclo de render que la barra pueda retrasar. Es exactamente la
   recomendación de web.dev para las animaciones ligadas al scroll, y aquí el
   caso es literal, porque lo que hay hoy es un escuchador que escribe la
   propiedad en cada evento.

   Aquí SÍ se declara sin miedo a Firefox. No es presumir: esta es la técnica más
   compatible de todo el bloque, porque la que se usa por defecto —la regla de
   arriba, con JS— sigue intacta para los motores que no la tienen. Es una mejora
   encima de la vía que ya funcionaba, no un reemplazo. La regla base gana en los
   navegadores sin scroll timeline porque el `@supports` entero se descarta y no
   llega a existir.

   `scroll(root block)` y no `scroll()` a secas: `root` es el documento, que es lo
   que mide la barra, y `block` es el eje vertical, que es la dirección en la que
   el lector avanza. Sin los dos, la barra avanzaría también en horizontal y
   llegaría al final en cuanto el artículo fuese más ancho que la ventana.
   `linear` porque la barra tiene que ir pegada al ojo, como decía el comentario
   de § v6 · 1. */
@supports (animation-timeline: scroll()) {
  .pw-progress-bar {
    animation: pw-progress-grow linear both;
    animation-timeline: scroll(root block);
    transition: none;
  }

  @keyframes pw-progress-grow {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
  }
}

.pw-totop {
  position: fixed;
  right: var(--pw-space-4);
  bottom: var(--pw-space-4);
  z-index: 50;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid var(--pw-border-strong);
  /* §4 lo pide circular: 50 % es el radio del único control redondo del sitio. */
  border-radius: 50%;
  background: var(--pw-surface);
  box-shadow: var(--pw-shadow-sm);
  color: var(--pw-ink-soft);
  font-family: inherit;
  font-size: var(--pw-fs-sm);
  line-height: 1;
  cursor: pointer;
  transition: background var(--pw-transition), color var(--pw-transition),
    box-shadow var(--pw-transition);
}

.pw-totop:hover {
  background: var(--pw-wash);
  box-shadow: var(--pw-shadow);
  color: var(--pw-ink);
}


/* ==========================================================================
   v2 · 8 · FICHA DEL ARTÍCULO
   --------------------------------------------------------------------------
   Misma densidad que `table.pw-infobox` (§11): 1px, tipografía --pw-fs-sm,
   interlineado de cuerpo, filas con zebra y rótulo en negrita al 40 %.
   ========================================================================== */
.pw-articleinfo {
  margin: var(--pw-space-4) 0 0;
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  box-shadow: var(--pw-shadow-sm);
  color: var(--pw-ink);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
  overflow: hidden;
}

.pw-articleinfo-title {
  padding: var(--pw-space-2) var(--pw-space-3);
  background: var(--pw-surface-2);
  border-bottom: 1px solid var(--pw-border);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-lg);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
}

.pw-articleinfo-list {
  margin: 0;
  padding: 0;
}

.pw-articleinfo-row {
  display: flex;
  align-items: baseline;
  gap: var(--pw-space-2);
  padding: 0.3rem var(--pw-space-3);
  border-bottom: 1px solid var(--pw-border-soft);
}

.pw-articleinfo-row:nth-child(even) {
  background: var(--pw-surface-2);
}

.pw-articleinfo-row:last-child {
  border-bottom: 0;
}

.pw-articleinfo-row dt {
  flex: 0 0 40%;
  font-weight: 700;
  line-height: 1.5;
}

.pw-articleinfo-row dd {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  color: var(--pw-ink-soft);
  font-variant-numeric: tabular-nums;
  text-align: right;
  line-height: 1.5;
}

.pw-fav-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--pw-space-2);
  width: 100%;
  padding: var(--pw-space-2) var(--pw-space-3);
  border: 0;
  border-top: 1px solid var(--pw-border);
  border-radius: 0;
  background: var(--pw-surface);
  color: var(--pw-ink-soft);
  font-family: inherit;
  font-size: var(--pw-fs-sm);
  font-weight: 600;
  line-height: 1.5;
  cursor: pointer;
  transition: background var(--pw-transition), color var(--pw-transition);
}

.pw-fav-toggle:hover {
  background: var(--pw-wash);
  color: var(--pw-ink);
}

.pw-fav-toggle[aria-pressed="true"] {
  background: var(--pw-surface-2);
  color: var(--pw-ink);
  font-weight: 700;
}

.pw-fav-star {
  font-size: var(--pw-fs-base);
  line-height: 1;
  pointer-events: none;
}

.pw-fav-label {
  flex: 0 1 auto;
  min-width: 0;
  overflow-wrap: break-word;
  text-align: center;
  line-height: 1.5;
}


/* ==========================================================================
   v2 · 9 · DIÁLOGO DE ATAJOS
   --------------------------------------------------------------------------
   <dialog> nativo. No se declara `display` en el estado cerrado: el
   `dialog:not([open])` de la hoja del navegador hace ese trabajo y así
   `.showModal()` de features.js sigue funcionando. Solo se declara
   `display: block` cuando está abierto.
   ========================================================================== */
.pw-modal {
  position: fixed;
  inset: 0;
  z-index: 80;
  width: calc(100% - 2rem);
  max-width: 420px;
  max-height: calc(100% - 2rem);
  margin: auto;
  padding: var(--pw-space-4);
  overflow-y: auto;
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  box-shadow: var(--pw-shadow-pop);
  color: var(--pw-ink);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
}

.pw-modal:not([open]) {
  display: none;
}

.pw-modal[open] {
  display: block;
}

.pw-modal::backdrop {
  background: rgba(28, 27, 24, 0.4);
}

.pw-modal-title {
  padding-bottom: var(--pw-space-2);
  margin-bottom: var(--pw-space-3);
  border-bottom: 1px solid var(--pw-border);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-lg);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
}

.pw-keys {
  margin: 0;
}

.pw-key-row {
  display: flex;
  align-items: baseline;
  gap: var(--pw-space-3);
  padding: var(--pw-space-1) 0;
}

.pw-key-row + .pw-key-row {
  border-top: 1px solid var(--pw-border-soft);
}

.pw-key-row dt {
  flex: 0 0 3.5rem;
  margin: 0;
}

.pw-key-row dd {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  color: var(--pw-ink-soft);
  text-indent: 0;
}

.pw-keys kbd {
  display: inline-block;
  min-width: 1.5rem;
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--pw-border-strong);
  border-bottom-width: 2px;
  border-radius: var(--pw-radius-sm);
  background: var(--pw-surface-2);
  color: var(--pw-ink);
  font-family: var(--pw-font-sans);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  line-height: 1.5;
  text-align: center;
}

/* El <dialog> es un bloque: el "Cerrar" se va a la esquina inferior derecha */
.pw-modal > .pw-btn-text {
  display: block;
  margin: var(--pw-space-4) 0 0 auto;
}


/* ==========================================================================
   v2 · 10 · HOVERCARDS
   --------------------------------------------------------------------------
   Un solo elemento reutilizado que features.js añade a <body> y coloca con
   top/left en coordenadas de viewport (por eso `fixed`, y se oculta al
   hacer scroll). `pointer-events: none` para no robar el hover al enlace.
   ========================================================================== */
.pw-hovercard {
  position: fixed;
  z-index: 70;
  width: max-content;
  max-width: 320px; /* 20rem, §4 */
  padding: var(--pw-space-2) var(--pw-space-3);
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  box-shadow: var(--pw-shadow-pop);
  color: var(--pw-ink);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
  pointer-events: none;
}

.pw-hovercard-title {
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-base);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
}

.pw-hovercard-sub {
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  line-height: var(--pw-lh-tight);
}

.pw-hovercard-text {
  margin-top: var(--pw-space-1);
  padding-top: var(--pw-space-1);
  border-top: 1px solid var(--pw-border-soft);
  color: var(--pw-ink-soft);
  text-wrap: pretty;
}


/* ==========================================================================
   v2 · 11 · PERMALINKS DE SECCIÓN
   --------------------------------------------------------------------------
   features.js añade el botón como hermano de `.pw-headline` dentro de
   `.pw-h2`; los selectores cubren también el caso de que lo añada como
   hermano del propio h2. Invisible hasta que la sección se enfoca o se
   pasa por encima, y siempre visible donde no hay hover (§4).
   ========================================================================== */
.pw-permalink {
  display: inline-block;
  margin-left: var(--pw-space-2);
  padding: 0 0.3rem;
  border: 1px solid transparent;
  border-radius: var(--pw-radius-sm);
  background: transparent;
  color: var(--pw-ink-muted);
  font-family: inherit;
  font-size: var(--pw-fs-sm);
  font-weight: 600;
  line-height: 1;
  vertical-align: baseline;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--pw-transition), background var(--pw-transition),
    color var(--pw-transition), border-color var(--pw-transition);
}

.pw-h2:hover .pw-permalink,
.pw-h2:focus-within .pw-permalink,
.pw-h2:hover + .pw-permalink,
.pw-permalink:focus-visible {
  opacity: 1;
}

.pw-permalink:hover {
  background: var(--pw-wash);
  border-color: var(--pw-border);
  color: var(--pw-ink);
}

/* Confirmación efímera de §6.8: destello de tinta, nada de acento */
.pw-permalink.is-copied {
  background: var(--pw-ink);
  border-color: var(--pw-ink);
  color: var(--pw-bg);
  opacity: 1;
}

@media (hover: none) {
  .pw-permalink {
    opacity: 1;
  }
}


/* ==========================================================================
   v2 · 12 · ÍNDICE ALFABÉTICO
   --------------------------------------------------------------------------
   `min-width` en cada letra para que todas ocupen el mismo ancho y queden
   alineadas. El estado `aria-current` es un tono de tinta. Si features.js no
   encuentra iniciales no renderiza nada: `:empty` evita el hueco de su
   `margin-bottom`.
   ========================================================================== */
.pw-azindex {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pw-space-1);
  margin-bottom: var(--pw-space-4);
}

.pw-azindex:empty {
  display: none;
}

.pw-azindex-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.6rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius-sm);
  background: var(--pw-surface);
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-sm);
  font-weight: 600;
  line-height: 1.6;
  text-decoration: none;
  /* La letra cambiaba de tinta, de fondo y de filete DE GOLPE: al pasar el
     cursor por encima se leía como un fogonazo, no como una hilera de
     decisiones. Aquí van las tres, y sólo las tres, con el token de hover
     (`--pw-dur-1` con `--pw-ease-out`, § v6 · 1) para que duren lo que dura
     cualquier otro hover del sitio y cambien con él si algún día suben.

     Va en la BASE y no en el `:hover` a propósito: es lo que hace que la
     entrada y la salida las pinte el motor con la misma curva —escribirlo en
     `:hover` daría una ida con easing y una vuelta sin él— y de paso anima las
     dos marcas de estado que se ponen por atributo, `[aria-pressed]` (§ v3) y
     `[aria-current]` (§ v2 · 12), sin duplicar la lista.

     `text-decoration` queda FUERA a propósito: el subrayado de la letra
     pulsada tiene que aparecer de golpe. Subrayado a medio camino se lee como
     un subrayado mal puesto, no como un estado. §15 lo colapsa a 0,01ms con el
     movimiento reducido, así que aquí no hace falta ningún seguro más. */
  transition:
    background-color var(--pw-transition),
    border-color var(--pw-transition),
    color var(--pw-transition);
}

.pw-azindex-link:hover {
  background: var(--pw-wash);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
  text-decoration: none;
}

.pw-azindex-link[aria-current="true"] {
  background: var(--pw-ink);
  border-color: var(--pw-ink);
  color: var(--pw-bg);
  font-weight: 700;
}


/* ==========================================================================
   v2 · 13 · IMPRESIÓN, PANTALLAS PEQUEÑAS Y MOVIMIENTO REDUCIDO
   ========================================================================== */
@media print {
  .pw-progress,
  .pw-totop,
  .pw-tools,
  .pw-azindex,
  .pw-permalink,
  .pw-articleinfo {
    display: none;
  }
}

@media (max-width: 767px) {
  /* Por debajo de 768px la cabecera ya envuelve a varias líneas (§14): las
     herramientas se alinean a la derecha de su propia línea. Entre 768 y
     1023px no se toca nada, porque el buscador vuelve a estar en línea y el
     `margin-left: auto` de `.pw-header-links` debe seguir siendo el único
     hueco elástico. */
  .pw-tools {
    margin-left: auto;
  }
}

@media (max-width: 560px) {
  .pw-btn {
    width: 1.75rem;
    height: 1.75rem;
  }

  .pw-tools {
    gap: 2px;
  }
}

/* Un término largo en la infobox no debe abrir scroll horizontal a 375px
   cuando el texto está a 1,18×: se le deja partir antes de desbordar. */
.pw-infobox th,
.pw-infobox td {
  overflow-wrap: anywhere;
}

@media (prefers-reduced-motion: reduce) {
  /* §15 ya anula transiciones y animaciones; solo se quita el suavizado
     propio de la barra de progreso, que no depende de transition. */
  .pw-progress-bar {
    transition: none;
  }
}


/* ===== v3 · los cinco dominios ===== */
/* ==========================================================================
   ===== v3 · los cinco dominios =====
   Capa de CONTRACT-V3.md §8: conmutador de dominio, tarjeta con franja de
   color y resumen, y las cuatro piezas de las fichas nuevas.

   Reglas de esta ampliación:
   · Solo se AÑADE al final. Ninguna regla de §1, §7 ni del bloque v2 se
     reescribe ni se borra. Donde una clase que ya existía choca con el
     contexto nuevo, el ajuste va en el punto 9 de este bloque, con selector
     de CONTEXTO y su comentario: nunca se redefine la clase base.
   · Todo lo nuevo se apoya en los tokens de §1, así que los CUATRO temas
     (claro, oscuro, sepia y auto = prefers-color-scheme de §1) recolorean
     esta capa entera sin tocar una sola regla. Aquí no hay ni un literal de
     color: los únicos valores que no son tokens son --flag-a y --flag-b, que
     son DATO del contenido (§8.4), el color real del partido o de la
     organización, no color de marca.
   · Estados activos solo con TINTA (--pw-ink, --pw-ink-soft, --pw-surface,
     --pw-surface-2, --pw-wash, --pw-border-strong). Nunca un color de acento
     sobre superficie redondeada, igual que hace .pw-seg[aria-checked="true"]:
     el conmutador de dominio se lee como parte del mismo sistema.
   · Radios: --pw-radius (4px) o --pw-radius-sm (2px) (§8.4). El único
     control redondo del sitio sigue siendo .pw-totop, con 50 %, intacto.

   Índice
     1 · Conmutador de dominio    .pw-domains .pw-domain .pw-domain-count
     2 · Tarjeta: franja de color .pw-card-flag
     3 · Tarjeta: resumen         .pw-card-summary
     4 · Tarjeta: etiquetas       .pw-tag (en el contexto nuevo)
     5 · Ficha de partido         .pw-party-colors .pw-ideology-chips .pw-chip
     6 · Ficha: sección destacada .pw-section-featured
     7 · Ficha de geopolítica     .pw-actors .pw-actor
     8 · Ficha de organización    .pw-member-count
      9 · Ajustes de CONTEXTO sobre clases que ya existían
     10 · Encabezados de grupo     .pw-catgroup .pw-catgroup-title
     11 · Impresión
   ========================================================================== */


/* ==========================================================================
   v3 · 1 · CONMUTADOR DE DOMINIO
   --------------------------------------------------------------------------
   Va en index.html entre el héroe y el catálogo (§8.1). Se alinea a la
   izquierda, no centrado como el héroe, porque filtra la rejilla de abajo:
   queda en la misma línea base que los encabezados de grupo y que el índice
   alfabético, y se leen como un par de filtros. `.pw-azindex` y `.pw-domains`
   comparten densidad, radio y separador de 2px.
   ========================================================================== */
.pw-domains {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  max-width: 100%;
  margin-bottom: var(--pw-space-6);
  padding: 2px;
  background: var(--pw-surface-2);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
}

.pw-domain {
  /* `flex: 0 1 auto` + `min-width: 0` dejan que el botón encoja en vez de
     desbordar: con data-pw-size="xl" a 375px la fila se reparte en varias
     líneas y ningún botón empuja la anchura. `overflow-wrap: anywhere` es la
     red de seguridad para un rótulo más largo del previsto: min-content cae a
     1ch y el texto puede partirse en lugar de abrir scroll horizontal. */
  flex: 0 1 auto;
  min-width: 0;
  padding: 0.3rem 0.65rem;
  border: 1px solid transparent;
  border-radius: var(--pw-radius-sm);
  background: transparent;
  color: var(--pw-ink-soft);
  font-family: inherit;
  font-size: var(--pw-fs-sm);
  font-weight: 600;
  line-height: 1.5;
  text-align: center;
  overflow-wrap: anywhere;
  cursor: pointer;
  transition: background var(--pw-transition), color var(--pw-transition),
    border-color var(--pw-transition);
}

.pw-domain:hover {
  background: var(--pw-wash);
  color: var(--pw-ink);
}

/* Activo: exactamente el lenguaje visual de `.pw-seg[aria-checked="true"]`
   (superficie + borde fuerte + tinta). Es la misma receta, no una nueva. */
.pw-domain[aria-pressed="true"] {
  background: var(--pw-surface);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
  font-weight: 700;
}

.pw-domain[aria-pressed="true"] .pw-domain-count {
  color: var(--pw-ink-soft);
}

/* Foco de teclado: el anillo global de §2 (2px de --pw-focus) se mantiene y
   se le conserva su propio radio, como se hizo con .pw-seg en v2. El borde
   fuerte hace que el botón enfocado no se confunda con el dominio inactivo. */
.pw-domain:focus-visible {
  background: var(--pw-surface);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
  border-radius: var(--pw-radius-sm);
}

.pw-domain-count {
  display: inline-block;
  min-width: 2ch; /* §8.2 */
  margin-left: 0.35em;
  color: var(--pw-ink-muted);
  font-size: 0.85em;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  line-height: 1;
  text-align: right;
  vertical-align: baseline;
}


/* ==========================================================================
   v3 · 2 · FRANJA DE COLOR DE LA TARJETA
   --------------------------------------------------------------------------
   §8.2 y §8.4: 4px de alto, degradado entre --flag-a y --flag-b, radio solo
   arriba porque se apoya en el borde superior de la tarjeta. NO es color de
   marca: es el color real del partido, por eso el degradado se construye con
   los valores que inyecta el contenido y no con tokens del sitio.
   --flag-from / --flag-to son locales a la franja y resuelven el caso de un
   solo color (§8.4): degradado hacia su versión más clara, mezclando con
   --pw-on-accent, que es blanco en los tres temas. Si `color-mix` no existiera,
   la sustitución sería inválida, `background-image` caería a `none` y la franja
   seguiría mostrando el color plano de abajo: nunca desaparece.
   ========================================================================== */
.pw-card-flag {
  --flag-from: var(--flag-a, var(--pw-ink-muted));
  --flag-to: var(--flag-b, color-mix(in srgb, var(--flag-from) 45%, var(--pw-on-accent)));
  display: block;
  flex: 0 0 auto;
  height: 4px;
  /* Se sale del relleno de la tarjeta para apoyarse en su borde superior: por
     eso el radio va arriba y solo arriba. El `gap` de .pw-card (4px) se suma al
     margen de abajo, así que de la franja al título queda --pw-space-4, el
     mismo aire que el resto de la tarjeta. */
  margin: calc(-1 * var(--pw-space-4)) calc(-1 * var(--pw-space-4)) var(--pw-space-3);
  border-radius: var(--pw-radius-sm) var(--pw-radius-sm) 0 0;
  background-color: var(--flag-from);
  background-image: linear-gradient(90deg, var(--flag-from), var(--flag-to));
}


/* ==========================================================================
   v3 · 3 · RESUMEN DE LA TARJETA
   --------------------------------------------------------------------------
   §8.2 la pide en las cuatro tarjetas; en las de ideología no se usa, así que
   no afecta a ninguna tarjeta existente.

   El `line-clamp` a 3 líneas es lo que mantiene la rejilla compacta y
   homogénea, y por eso se declara con altura fija: sin el `min-height`, un
   resumen de dos líneas dejaría su tarjeta un renglón más corta que la de al
   lado y la fila se escalonaría. Con las tres líneas reservadas, todas las
   tarjetas de la fila miden igual y lo único que puede variar es el título.
   El alto se calcula con --pw-lh-body, no con un número: escala con
   data-pw-size igual que el texto.
   ========================================================================== */
.pw-card-summary {
  margin: var(--pw-space-2) 0 0;
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
  /* Un término largo no debe abrir scroll horizontal a 375px con el texto a
     1,18×: se le deja partir antes de desbordar (como .pw-infobox en v2). */
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  /* El equivalente estándar va DESPUÉS del prefijado, y ese orden es lo que
     importa: en los navegadores que ya no necesitan el prefijo manda esta, y
     en los que solo entienden la de arriba se sigue recortando con la de
     arriba. Puestas al revés, la declaración estándar se descartaría al no
     ser reconocida y la tarjeta quedaría sin recortar. */
  line-clamp: 3;
  min-height: calc(var(--pw-lh-body) * 3 * 1em);
}


/* ==========================================================================
   v3 · 4 · ETIQUETAS DENTRO DE .pw-card-meta
   --------------------------------------------------------------------------
   §8.2 las coloca en `.pw-card-meta` de las tarjetas nuevas. El ajuste va en
   el punto 9 de este bloque, junto a los demás ajustes de contexto: aquí solo
   se deja constancia de por qué.
   ========================================================================== */


/* ==========================================================================
   v3 · 5 · FICHA DE PARTIDO
   --------------------------------------------------------------------------
   `.pw-party-colors` es la franja de la cabecera: mismo mecanismo de degradado
   que la tarjeta y más alto (6px), porque aquí es el elemento de identidad de
   la ficha y no un adorno de 4px.
   ========================================================================== */
.pw-party-colors {
  --flag-from: var(--flag-a, var(--pw-ink-muted));
  --flag-to: var(--flag-b, color-mix(in srgb, var(--flag-from) 45%, var(--pw-on-accent)));
  display: block;
  /* Si el motor la deja en la fila de etiquetas de la cabecera, ocupa su propia
     línea; en cualquier otro contexto es un ancho completo. */
  flex: 1 1 100%;
  width: 100%;
  min-width: 0;
  height: 6px;
  margin: 0 0 var(--pw-space-3);
  border-radius: var(--pw-radius-sm);
  background-color: var(--flag-from);
  background-image: linear-gradient(90deg, var(--flag-from), var(--flag-to));
}

.pw-ideology-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pw-space-1);
  min-width: 0;
  margin: var(--pw-space-4) 0 0;
}

.pw-chip {
  display: inline-block;
  max-width: 100%;
  min-width: 0;
  padding: 0.1rem 0.55rem;
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius-sm);
  background: var(--pw-surface-2);
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-xs);
  font-weight: 600;
  line-height: 1.7;
  overflow-wrap: anywhere;
  text-decoration: none;
  transition: background var(--pw-transition), color var(--pw-transition),
    border-color var(--pw-transition);
}

.pw-chip:hover {
  background: var(--pw-wash);
  border-color: var(--pw-border-strong);
  color: var(--pw-ink);
}

/* Un chip que es enlace tiene que parecerse a un enlace al pulsarlo y al
   enfocarlo: subrayado, sin cambiar de color de marca. */
a.pw-chip:hover,
a.pw-chip:focus-visible {
  color: var(--pw-ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}


/* ==========================================================================
   v3 · 6 · SECCIÓN DESTACADA DE LA FICHA
   --------------------------------------------------------------------------
   §8.3: una sola por ficha, discreta, sin gritar. El tratamiento es de TINTA:
   un filete corto delante del titular —el mismo gesto que el filete de 2px del
   héroe en §7— y el filete de debajo pasa a borde fuerte. Ni un acento, ni un
   fondo de color, ni una etiqueta inventada.
   La marca va sobre el `h2.pw-h2` de la sección `practica` porque §6 no envuelve
   las secciones en ningún contenedor: el DOM del artículo es plano. Por eso el
   filete de debajo se limita al caso `h2`, y no se aplica si el motor eligiera
   un contenedor: solo perdería el refuerzo, no se rompería nada.
   ========================================================================== */
.pw-section-featured::before {
  content: "";
  display: inline-block;
  width: 1.25rem;
  height: 2px;
  margin-right: var(--pw-space-2);
  vertical-align: middle;
  background: var(--pw-ink);
}

h2.pw-section-featured {
  border-bottom-color: var(--pw-border-strong);
}


/* ==========================================================================
   v3 · 7 · FICHA DE GEOPOLÍTICA
   --------------------------------------------------------------------------
   Rejilla de actores tal cual la pide §8.4. `.pw-actor` lleva borde de 1px y
   superficie, y NUNCA sombra: ni en reposo ni al pasar por encima, para que la
   rejilla se lea como una lista de fichas y no como una hilera de ventanas.
   El indicador de `power` se pinta con tinta en tres pasos —lavado, tinta suave,
   tinta maciza— en vez de con rojo, verde o un color de acento.
   ========================================================================== */
.pw-actors {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: var(--pw-space-3);
  margin: var(--pw-space-4) 0;
}

.pw-actor {
  display: flex;
  flex-direction: column;
  gap: var(--pw-space-1);
  min-width: 0;
  padding: var(--pw-space-3);
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  box-shadow: none; /* §8.4 lo exige: sin sombra. */
  color: var(--pw-ink);
  line-height: var(--pw-lh-body);
}

.pw-actor > * {
  min-width: 0;
  overflow-wrap: anywhere;
}

.pw-actor > :first-child {
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-md);
  font-weight: 600;
  color: var(--pw-ink);
  line-height: var(--pw-lh-tight);
}

/* El `role` y cualquier línea intermedia: texto de apoyo. */
.pw-actor > *:not(:first-child) {
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-sm);
  line-height: 1.55;
}

/* El indicador de `power`: el último hijo, y solo si es el tercero o
   posterior. §5.3 da tres campos por actor (name, role, power), así que el
   último hijo es el indicador; el `:nth-child(n+3)` es la red de seguridad para
   que un actor con solo dos hijos no acabe con el `role` pintado de etiqueta. */
.pw-actor > :nth-child(n+3):last-child {
  align-self: flex-start;
  max-width: 100%;
  margin-top: var(--pw-space-1);
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius-sm);
  background: var(--pw-wash);
  color: var(--pw-ink);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.6;
}

/* Si el motor marca el nivel con el atributo `data-power` del §5.3, los tres
   escalones se reparten en un solo tono de tinta. Si no lo emite, el indicador
   se queda en el lavado neutro de arriba: mismo lenguaje, sin inventar nada. */
.pw-actor [data-power="alta"] {
  background: var(--pw-ink);
  border-color: var(--pw-ink);
  color: var(--pw-bg);
}

.pw-actor [data-power="baja"] {
  background: transparent;
  border-color: var(--pw-border);
  color: var(--pw-ink-muted);
}


/* ==========================================================================
   v3 · 8 · FICHA DE ORGANIZACIÓN
   --------------------------------------------------------------------------
   Cifra grande de miembros, con `memberSince` al lado en cuerpo pequeño: mismo
   carácter de rótulo que los datos de .pw-articleinfo, para que la cifra se
   lea como un dato de la ficha y no como un titular.
   ========================================================================== */
.pw-member-count {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--pw-space-2);
  max-width: 100%;
  color: var(--pw-ink);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-xl);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: var(--pw-lh-tight);
}

/* `memberSince` al lado, sea el elemento que sea, en el mismo tono de rótulo
   que usa .pw-articleinfo-row dt / .pw-catlinks-label. */
.pw-member-count > *:not(:first-child),
.pw-member-count small,
.pw-member-count sub,
.pw-member-count em {
  color: var(--pw-ink-muted);
  font-family: var(--pw-font-sans);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.6;
}


/* ==========================================================================
   v3 · 9 · AJUSTES DE CONTEXTO SOBRE CLASES QUE YA EXISTÍAN
   --------------------------------------------------------------------------
   Aquí es donde se resuelven los tres roces con reglas anteriores. Ninguna
   clase base se redefine: los tres ajustes son selectores de CONTEXTO, con la
   clase antigua al principio de la cadena, para que sus demás usos —la ficha
   de ideología, la portada v1— queden exactamente como estaban.
   ========================================================================== */

/* (a) `.pw-tag` hereda de `.pw-card-meta` su `text-transform: uppercase` y su
   `letter-spacing`, pensados para una línea de texto y no para una etiqueta.
   §8.2 pide que la etiqueta tenga el mismo lenguaje visual que
   `.pw-catlinks-list a`, que va en caja normal. Se anula la herencia; la
   definición de `.pw-tag` de §6 no se toca, así que las etiquetas de la ficha
   del artículo (`.pw-article-actions`) quedan como estaban.
   El margen sustituye a un `gap`: `.pw-card-meta` es un bloque y las
   etiquetas fluyen en línea, así que el aire entre ellas lo da el margen. */
.pw-card-meta .pw-tag {
  margin: 0 var(--pw-space-1) var(--pw-space-1) 0;
  text-transform: none;
  letter-spacing: 0;
}

/* (b) `.pw-card-meta` reserva `--pw-space-3` de aire superior para una línea
   de texto. Con etiquetas en línea, ese aire se sumaría a su propio margen y
   la separación sería de 16px en vez de 12px. `:has()` solo para esto; si no
   estuviera soportado, la fila de etiquetas simplemente respiraría un poco más. */
.pw-card-meta:has(> .pw-tag) {
  padding-top: var(--pw-space-2);
}

/* (c) La tarjeta con franja recorta sus propias esquinas, para que la franja no
   asome por el radio de 4px. No hay ningún elemento saliente dentro de
   `.pw-card`, así que el recorte no puede recortar nada: ni el anillo de foco
   —un `outline` nunca lo recorta su propio contenedor— ni la etiqueta. */
.pw-card:has(> .pw-card-flag) {
  overflow: hidden;
}


/* ==========================================================================
   v3 · 10 · ENCABEZADOS DE GRUPO DEL CATÁLOGO
   --------------------------------------------------------------------------
   §7 agrupa los dominios con `<section class="pw-catgroup">` +
   `<h3 class="pw-catgroup-title">`: el mismo patrón que la portada de las
   categorías, con las mismas clases y sin inventar ninguna.

   `grid-column: 1 / -1` no es decoración: el grupo es hijo de la REJILLA
   EXTERIOR (`#articulos-grid`) y lleva su propia `.pw-card-grid` dentro. Sin
   esto, el `repeat(auto-fill, minmax(min(100%, 16.5rem), 1fr))` de §7 los
   colocaría uno al lado del otro en una celda de 16,5rem, y los grupos saldrían
   en dos columnas en vez de apilados. El grupo tiene que ocupar la fila entera.

   El título va un escalón por debajo de `.pw-h2` y por encima de `.pw-h3`:
   es una fila de catálogo —país, región, tipo—, no una sección de artículo.
   Mismo serif del sistema y un filete `--pw-border-soft`, más discreto que el
   del `h2` de la sección, para que la jerarquía del catálogo se lea de un
   vistazo.
   ========================================================================== */
.pw-catgroup {
  grid-column: 1 / -1;
  min-width: 0;
}

.pw-catgroup + .pw-catgroup {
  /* El `gap` de la rejilla exterior ya separa los grupos; esto solo ensancha
     esa separación, sin tocar la de la primera fila, que la marca el `h2`. */
  margin-top: var(--pw-space-5);
}

.pw-catgroup-title {
  margin: 0 0 var(--pw-space-3);
  padding-bottom: var(--pw-space-1);
  border-bottom: 1px solid var(--pw-border-soft);
  color: var(--pw-ink);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-lg);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
}


/* ==========================================================================
   v3 · 11 · IMPRESIÓN
   --------------------------------------------------------------------------
   El conmutador es un control: en papel no hay nada que conmutar, igual que
   el índice alfabético y las herramientas de la cabecera, que ya se ocultan en
   v2. Las fichas, en cambio, sí se imprimen enteras.
   ========================================================================== */
@media print {
  .pw-domains {
    display: none;
  }
}


/* ===== v4 · espectro ideológico y grafo de conexiones =====
 *
 * Dos piezas de lectura, dos ficheros nuevos (js/spectrum.js y js/graph.js) y
 * ninguna dependencia. Aquí solo vive su epidermis:
 *
 * - `#espectro` (portada) y `.pw-contextmap` (panel lateral de la ficha) son
 *   paneles con el mismo gramaje que `.pw-articleinfo`: superficie, borde de
 *   1px, `--pw-radius`, sombra corta SOLO en reposo. El rótulo repite el patrón
 *   `.pw-articleinfo-title`: superficie-2, filete y serif del sistema.
 * - Los nodos son `<a>` de SVG, así que el color de la tinta lo pone CSS, no un
 *   atributo: en reposo son superficie con borde y tinta de texto; al pasar el
 *   ratón o al enfocarse pasan a `--pw-link` con el texto a `--pw-on-accent`,
 *   que es el estado de enlace que ya usan `.pw-card` y `.pw-chip`. Nunca un
 *   color de acento sobre superficie redondeada, y nunca una sombra flotante al
 *   pasar el ratón: solo un halo de foco, como el resto del sitio.
 * - El nodo central del grafo sí es tinta maciza: es el artículo que se está
 *   leyendo, el mismo "estado actual = tinta" de `.pw-azindex-link[aria-current]`.
 * - Todo el color sale de tokens `--pw-*`, incluidos los que se pintan dentro
 *   del SVG, así que los cuatro temas funcionan sin tocar una sola regla más.
 */

/* --- 1 · Los dos paneles --- */

#espectro,
.pw-contextmap {
  min-width: 0;
  padding: var(--pw-space-4);
  background: var(--pw-surface);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  box-shadow: var(--pw-shadow-sm);
  color: var(--pw-ink);
  overflow: hidden;
}

/* El rótulo se pega a los bordes del panel: es su cabecera, no un título suelto
   dentro de una caja. El margen negativo cancela el padding del panel. */
#espectro > .pw-h2 {
  margin: calc(var(--pw-space-4) * -1) calc(var(--pw-space-4) * -1) var(--pw-space-4);
  padding: var(--pw-space-2) var(--pw-space-4);
  background: var(--pw-surface-2);
  border-bottom: 1px solid var(--pw-border);
}

.pw-contextmap {
  margin: var(--pw-space-4) 0 0;
  font-size: var(--pw-fs-sm);
}

.pw-contextmap-title {
  margin: calc(var(--pw-space-4) * -1) calc(var(--pw-space-4) * -1) var(--pw-space-3);
  padding: var(--pw-space-2) var(--pw-space-3);
  background: var(--pw-surface-2);
  border-bottom: 1px solid var(--pw-border);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-lg);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
}

/* `hidden` gana siempre: un SVG con viewBox se dibuja aunque su contenedor
   esté oculto, y el script lo usa para retirar la sección cuando el mapa no
   tiene nada que enseñar. */
#espectro[hidden],
.pw-contextmap[hidden] {
  display: none !important;
}

/* El SVG manda en su tamaño (`viewBox` + `width:100%`): con `data-pw-size="xl"`
   a 375px la caja se estrecha, el mapa se encoge y nunca hay scroll horizontal. */
#espectro svg,
.pw-contextmap svg {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
}

/* --- 2 · Espectro: ejes, polos y nodos --- */

.pw-spectrum a,
.pw-graph a {
  cursor: pointer;
}

.pw-spectrum .pw-spectrum-axis {
  fill: none;
  stroke: var(--pw-border);
  stroke-width: 1.5;
}

.pw-spectrum .pw-spectrum-pole {
  fill: var(--pw-ink-muted);
  font-family: var(--pw-font-serif);
  font-size: 18px;
  font-weight: 600;
}

.pw-spectrum .pw-spectrum-label {
  fill: var(--pw-ink);
  font-family: var(--pw-font-sans);
  font-size: 18px;
  font-weight: 600;
}

/* Un mapa es un gráfico de dispersión y las etiquetas tienen que sobrevivir a lo
   que se cruza con ellas: los ejes y el círculo de un vecino en una nube
   cerrada. El contorno lleva el color de la superficie y se pinta DEBAJO del
   relleno (`paint-order`), así que las letras quedan recortadas sobre una
   línea de color de fondo en lugar de ser tachadas por ella. */
.pw-spectrum .pw-spectrum-pole,
.pw-spectrum .pw-spectrum-label {
  paint-order: stroke fill;
  stroke: var(--pw-surface);
  stroke-width: 4;
  stroke-linejoin: round;
}

.pw-spectrum a circle {
  fill: var(--pw-surface);
  stroke: var(--pw-border-strong);
  stroke-width: 1.5;
}

.pw-spectrum a circle,
.pw-spectrum a text {
  transition: fill var(--pw-transition), stroke var(--pw-transition);
}

.pw-spectrum a:hover circle,
.pw-spectrum a:focus-visible circle {
  fill: var(--pw-link);
  stroke: var(--pw-link-hover);
}

.pw-spectrum a:hover text,
.pw-spectrum a:focus-visible text {
  fill: var(--pw-on-accent);
  stroke: var(--pw-link);
}

.pw-spectrum a:focus-visible {
  outline: 2px solid var(--pw-focus);
  outline-offset: 2px;
}

.pw-spectrum a:focus-visible circle {
  filter: drop-shadow(0 0 4px var(--pw-focus-halo));
}

/* --- 3 · Grafo: aristas, centro y vecinos --- */

.pw-graph .pw-graph-edge {
  stroke: var(--pw-border);
  stroke-width: 1;
  stroke-linecap: round;
}

.pw-graph .pw-graph-label {
  fill: var(--pw-ink-soft);
  font-family: var(--pw-font-sans);
  font-size: 12px;
  font-weight: 600;
}

.pw-graph a circle {
  fill: var(--pw-surface);
  stroke: var(--pw-border-strong);
  stroke-width: 1.5;
}

.pw-graph a circle,
.pw-graph a text {
  transition: fill var(--pw-transition), stroke var(--pw-transition);
}

.pw-graph a:hover circle,
.pw-graph a:focus-visible circle {
  fill: var(--pw-link);
  stroke: var(--pw-link-hover);
}

.pw-graph a:hover text,
.pw-graph a:focus-visible text {
  fill: var(--pw-on-accent);
}

.pw-graph a:focus-visible {
  outline: 2px solid var(--pw-focus);
  outline-offset: 2px;
}

.pw-graph a:focus-visible circle {
  filter: drop-shadow(0 0 4px var(--pw-focus-halo));
}

/* El nodo central es "aquí estoy": tinta maciza en reposo, con el texto al
   color de fondo, igual que `.pw-azindex-link[aria-current="true"]`. */
.pw-graph .pw-graph-label-hub {
  fill: var(--pw-bg);
  font-size: 13px;
  font-weight: 700;
}

.pw-graph .pw-graph-hub circle {
  fill: var(--pw-ink);
  stroke: var(--pw-ink);
}

.pw-graph .pw-graph-hub:hover circle,
.pw-graph .pw-graph-hub:focus-visible circle {
  fill: var(--pw-link);
  stroke: var(--pw-link-hover);
}

/* --- 4 · Rejilla del panel lateral entre 1024 y 1199px --- */

@media (min-width: 1024px) and (max-width: 1199px) {
  /* Ahí `.pw-aside` es una rejilla de dos columnas y el grafo es más ancho que
     cualquiera de las dos: ocupa la fila entera, como `.pw-text`. */
  .pw-contextmap {
    grid-column: 1 / -1;
  }
}

/* --- 5 · Impresión --- */

@media print {
  /* Dos mapas de lectura, no contenido: en papel no aportan y sí rompen la
     caja de 18rem. La ficha y su índice sí se imprimen enteros. */
  #espectro,
  .pw-contextmap {
    display: none;
  }
}

/* ==========================================================================
 * v5 · Página del espectro (espectro.html) y su selector de ideologías
 * ========================================================================== */

/* --- 1 · Contenedor de la página dedicada --- */

.pw-espectro-page {
  max-width: 72rem;
  margin: 0 auto;
}

.pw-espectro-page .pw-section > .pw-h2:first-child {
  margin-top: 0;
}

.pw-espectro-intro {
  max-width: 52ch;
  margin: 0 0 var(--pw-space-6);
  font-size: 1.0625rem;
  line-height: var(--pw-lh-body);
  color: var(--pw-ink-muted);
}

/* --- 2 · Selector de ideologías (chips) --- */

.pw-spectrum-pick {
  margin-bottom: var(--pw-space-5);
}

.pw-spectrum-pick-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--pw-space-3);
  margin-bottom: var(--pw-space-3);
}

.pw-spectrum-pick-label {
  font-family: var(--pw-font-serif);
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--pw-ink);
}

.pw-spectrum-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pw-space-2);
}

.pw-spectrum-chip {
  border: 1px solid var(--pw-border-strong);
  border-radius: 999px;
  background: transparent;
  color: var(--pw-ink);
  font: inherit;
  font-size: 0.9375rem;
  line-height: 1.2;
  padding: var(--pw-space-2) var(--pw-space-3);
  cursor: pointer;
  transition: color var(--pw-transition), background-color var(--pw-transition),
    border-color var(--pw-transition);
}

.pw-spectrum-chip:hover,
.pw-spectrum-chip:focus-visible {
  border-color: var(--pw-link);
  color: var(--pw-link);
}

.pw-spectrum-chip[aria-checked="true"] {
  background: var(--pw-link);
  border-color: var(--pw-link);
  color: var(--pw-on-accent);
}

.pw-spectrum-chip[aria-checked="true"]:hover,
.pw-spectrum-chip[aria-checked="true"]:focus-visible {
  background: var(--pw-link-hover);
  border-color: var(--pw-link-hover);
  color: var(--pw-on-accent);
}

.pw-spectrum-chip:focus-visible {
  outline: 2px solid var(--pw-link);
  outline-offset: 2px;
}

/* --- 3 · Estado vacío (menos de dos ideologías marcadas) --- */

.pw-spectrum-empty {
  max-width: 52ch;
  margin: 0 0 var(--pw-space-6);
  padding: var(--pw-space-4) var(--pw-space-5);
  border: 1px dashed var(--pw-border-strong);
  border-radius: var(--pw-radius);
  background: var(--pw-wash);
  color: var(--pw-ink);
}

/* --- 4 · Nota al pie --- */

.pw-espectro-note {
  margin-top: var(--pw-space-6);
  color: var(--pw-ink-muted);
  font-size: 0.9375rem;
}

/* --- 5 · Impresión --- */

@media print {
  /* En su propia página el mapa SÍ es contenido y se imprime: se anula con más
     especificidad la regla v4 que lo ocultaba (pensada para la portada). El
     selector y los avisos son controles y no pintan. */
  body.pw-body-espectro #espectro {
    display: block;
  }

  .pw-spectrum-pick,
  .pw-spectrum-empty {
    display: none;
  }
}


/* ==========================================================================
   v3 · FILTRO ALFABÉTICO
   --------------------------------------------------------------------------
   Las letras de `#pw-azindex` filtran la rejilla: `applyAzFilter()` en
   js/features.js marca `aria-pressed="true"` en la letra activa y pone
   `hidden` en el resto de tarjetas. Aquí solo hay que hacer VISIBLE la letra
   activa; las tarjetas ocultas no necesitan nada porque la regla global
   `[hidden] { display: none !important; }` (v2 · 4) las saca del flujo de la
   rejilla, y como `.pw-card-grid` usa `auto-fill` el hueco lo rellena la
   tarjeta siguiente: no quedan huecos.
   ========================================================================== */
.pw-azindex-link[aria-pressed="true"] {
  background: var(--pw-ink);
  border-color: var(--pw-ink);
  color: var(--pw-bg);
  font-weight: 700;
}

/* Distinto del `aria-current` del scroll-spy (v2 · 12) y nunca activo a la
   vez: mientras hay filtro, `markAzIndex()` retira el `aria-current`. El
   underline lo hace distinguible de un vistazo sin recurrír a color de
   acento, y sobrevive a los tres temas. */
.pw-azindex-link[aria-pressed="true"] {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

/* Una letra sin tarjetas visibles (no debería ocurrir: el índice se construye
   con las mismas tarjetas) no se queda pulsada. Con la `transition` de la base
   (§ v2 · 13) esto no la destapa: al pasar el cursor por encima los tres valores
   siguen siendo los mismos, así que no hay nada que interpolar y la letra
   pulsada se queda con su aspecto mientras dura el hover. Si algún día esta
   regla desapareciera, el hover la aclararía al instante —y para entonces ya
   se vería despacio, que es peor. */
.pw-azindex-link[aria-pressed="true"]:hover {
  background: var(--pw-ink);
  border-color: var(--pw-ink);
  color: var(--pw-bg);
}


/* ==========================================================================
   v3 · BUSCADOR DE CABECERA CON LUPA
   --------------------------------------------------------------------------
   `form.pw-search-cabecera` arranca con `hidden` en el HTML y lo despliega la
   lupa (`#pw-btn-search`). Se posiciona como panel ANCLADO a `.pw-header` —que
   es `position: sticky` (§3), así que establece bloque contenedor para sus
   descendientes absolutos—, con la misma mecánica y el mismo z-index de 45+ que
   los popover: cuelga por debajo de la barra sin crecerla ni desplazar la
   rejilla al abrirlo, y con `right: 0` queda pegado a su borde derecho.
   ========================================================================== */
.pw-search-cabecera {
  position: absolute;
  top: calc(100% + var(--pw-space-2));
  right: 0;
  z-index: 46;
  width: min(22rem, calc(100vw - 2 * var(--pw-space-4)));
  flex: none;          /* anula el `flex: 1 1 16rem` de `.pw-search` */
  max-width: none;
  margin: 0;
}

/* El panel de resultados cuelga del propio form (`.pw-search-results` es
   `position: absolute` desde §4) y su caja se recalcula sola con este ancho. */
.pw-search-cabecera .pw-search-results {
  width: 100%;
  max-height: min(20rem, 60vh);
}

/* La lupa es un SVG inline en vez de un glifo: `⌕` (U+2315) no está en las
   fuentes de sistema de Windows ni de Linux y cae en una caja vacía, mientras
   que un trazo de 1,6 px sobre `currentColor` acompaña al peso y al color del
   resto de `.pw-btn` en los tres temas. */
.pw-btn-icon {
  display: block;
  width: 1rem;
  height: 1rem;
  overflow: visible;
}

@media (max-width: 560px) {
  /* Por debajo de 560px los botones se encogen a 1,75rem (v2 · 13). El panel
     se sigue anclando a la cabecera (`position: absolute`), así que nunca
     abre scroll horizontal: el `min()` de arriba ya lo limita al ancho útil
     entre los dos `padding` de la cabecera, y aquí solo se le deja el ancho
     entero de la pantalla para que el campo no quede estrecho a 320px. */
  .pw-search-cabecera {
    width: calc(100vw - 2 * var(--pw-space-4));
  }
}


/* ==========================================================================
   v3.3 · MEJORAS DE NAVEGACIÓN Y LECTURA  (js/features.js · sección 14)
   --------------------------------------------------------------------------
   Ocho features que `js/features.js` monta SOBRE el DOM ya pintado, porque los
   botones de citar, compartir e imprimir se cuelgan de `.pw-article-actions`,
   que `render.js` crea en runtime y rehace en cada render. Aquí solo hay estilo:
   todo el DOM, los observadores y la lógica viven en el JS.

   Reglas que ha seguido este bloque:
     · Solo variables `--pw-*` (y el `#2a4b8d` de `--pw-link`), así que los tres
       temas —claro, oscuro y sepia— cambian solos sin repetir una regla.
     · Nada de `!important` salvo donde hay que ganar a `[hidden]` o al
       `display:flex` de `.pw-article-actions` (§4), y nada de estilo en línea:
       la CSP no admite 'unsafe-inline'.
     · Los estados (`:hover`, `[aria-current]`, `.is-copied`) se marcan con
       tinta, fondo y filete del propio tema; el único borde de color es el anillo
       de `:focus-visible` global. No hay filete de acento en ningún estado.
   ========================================================================== */


/* --------------------------------------------------------------------------
   v3.3 · 1 · BOTONES DE ACCIÓN: citar · compartir · imprimir (§17, §19)
   --------------------------------------------------------------------------
   Píldora fantasma con el mismo gramaje que `.pw-tag`, de la que solo se
   distingue por ser interactiva: la categoría y la fecha son datos, esto es un
   control. El envoltorio `.pw-action-btns` los separa de las etiquetas con un
   `margin-left: auto` —los empuja al extremo derecho de la fila de acciones— en
   vez de con un filete, que se rompería en cuanto las etiquetas bajan de línea.
   Al imprimirse no hay que ocultarlos a mano: `.pw-article-actions` ya es
   `display: none` en el bloque `@media print` de §4. */
.pw-action-btns {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--pw-space-2);
  margin-left: auto;
}

.pw-cite-btn,
.pw-share-btn,
.pw-print-btn {
  display: inline-block;
  padding: 0.1rem 0.6rem;
  border: 1px solid var(--pw-border);
  border-radius: 999px;
  background: transparent;
  color: var(--pw-ink-muted);
  font-family: inherit;
  font-size: var(--pw-fs-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.6;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--pw-transition), background var(--pw-transition),
    border-color var(--pw-transition);
}

.pw-cite-btn:hover,
.pw-share-btn:hover,
.pw-print-btn:hover {
  border-color: var(--pw-border-strong);
  background: var(--pw-wash);
  color: var(--pw-ink);
}

/* El rótulo vive en su propio `span` para que `flashLabel` lo cambie sin tocar
   el botón —es el mismo truco que `.pw-fav-label`—. Se declara para que herede
   el cuerpo del botón de forma explícita y no dependa de la cascada del agente
   de usuario. */
.pw-action-label {
  font: inherit;
  line-height: inherit;
}

/* Confirmación efímera: el mismo destello de tinta de `.pw-permalink.is-copied`
   (§6.8), para que «copiado» se lea igual en los dos sitios. */
.pw-cite-btn.is-copied,
.pw-share-btn.is-copied,
.pw-print-btn.is-copied {
  border-color: var(--pw-ink);
  background: var(--pw-ink);
  color: var(--pw-bg);
}

@media (max-width: 560px) {
  /* Por debajo de 560px los botones se encogen a 1,75rem (v2 · 13) y las tres
     píldoras caben con las dos etiquetas. */
  .pw-action-btns {
    margin-left: 0;
  }
}


/* --------------------------------------------------------------------------
   v3.3 · 2 · MIGA DE PAN CON DOMINIO Y CATEGORÍA (§15)
   --------------------------------------------------------------------------
   El dominio es un enlace al catálogo de §7, así que hereda el tratamiento de
   `.pw-breadcrumb-link`; la categoría no tiene ruta y va como texto, con la
   misma tinta que `.pw-breadcrumb-current` pero sin el peso de titular, para que
   la jerarquía del pan siga siendo Portada › Dominio › Categoría › Título. */
.pw-breadcrumb-domain {
  color: var(--pw-ink-muted);
}

a.pw-breadcrumb-domain {
  color: var(--pw-ink-muted);
  text-decoration: none;
  transition: color var(--pw-transition);
}

a.pw-breadcrumb-domain:hover {
  color: var(--pw-link);
  text-decoration: underline;
  text-underline-offset: 3px;
}


/* --------------------------------------------------------------------------
   v3.3 · 3 · ANTERIOR / SIGUIENTE (§16)
   --------------------------------------------------------------------------
   Bloque ancho al final de `.pw-article`, no dentro de la ficha: la ficha mide
   `--pw-aside-w` (18rem) y un título de partido se partiría en tres líneas.
   Dos columnas iguales con un filete vertical en medio, y la dirección marcada
   con el rótulo («Anterior» / «Siguiente») más que con una flecha: el texto se
   lee de un vistazo y con un lector de pantalla no depende de un glifo. */
.pw-prevnext {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--pw-space-4);
  margin-top: var(--pw-space-6);
  padding-top: var(--pw-space-5);
  border-top: 1px solid var(--pw-border);
}

.pw-prevnext-link {
  display: flex;
  flex-direction: column;
  gap: var(--pw-space-1);
  padding: var(--pw-space-3);
  border: 1px solid var(--pw-border);
  border-radius: var(--pw-radius);
  background: var(--pw-surface);
  color: var(--pw-ink);
  text-decoration: none;
  transition: border-color var(--pw-transition), background var(--pw-transition);
}

.pw-prevnext-link:hover {
  border-color: var(--pw-border-strong);
  background: var(--pw-wash);
}

/* La segunda columna se alinea a la derecha: «siguiente» se lee como
   continuación del artículo, no como otro artículo cualquiera. */
.pw-prevnext-prev {
  text-align: left;
}

.pw-prevnext-next {
  text-align: right;
}

.pw-prevnext-label {
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  line-height: var(--pw-lh-tight);
  text-transform: uppercase;
}

.pw-prevnext-title {
  color: var(--pw-link);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-md);
  font-weight: 600;
  line-height: var(--pw-lh-tight);
  text-wrap: pretty;
}

.pw-prevnext-link:hover .pw-prevnext-title {
  color: var(--pw-link-hover);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Un solo enlace (el primero o el último de su dominio): la columna vacía no
   deja hueco, y el enlace se queda con la mitad del ancho. */
.pw-prevnext-link:only-child {
  grid-column: 1 / -1;
  max-width: calc(50% - var(--pw-space-2));
}

.pw-prevnext-link:only-child.pw-prevnext-next {
  grid-column: 2;
  margin-left: auto;
}

@media (max-width: 560px) {
  .pw-prevnext {
    grid-template-columns: minmax(0, 1fr);
  }

  .pw-prevnext-next {
    text-align: left;
  }

  .pw-prevnext-link:only-child,
  .pw-prevnext-link:only-child.pw-prevnext-next {
    grid-column: 1;
    max-width: none;
    margin-left: 0;
  }
}


/* --------------------------------------------------------------------------
   v3.3 · 4 · ÍNDICE FLOTANTE DE SECCIONES (§14)
   --------------------------------------------------------------------------
   `js/features.js` construye el nodo siempre que el artículo tiene tres o más
   secciones con `id`, y lo cuelga de `.pw-content` —la rejilla de §8—, con su
   IntersectionObserver y su `aria-current` propios (mismo contrato que el de
   `render.js` §6). Aquí se decide CUÁNDO se ve, y la decisión sale de cómo está
   repartida la maqueta, no de un número redondo:

     · ≥1200px  NO se ve. `render.js` ya pone un `.pw-toc` pegado al texto en la
       columna `--pw-aside-w` de la derecha, con su propio resaltado y su propio
       scrollspy. Un segundo índice con el mismo contenido a 300px del primero es
       ruido, y además sus dos  los dos `aria-current`.
     · 1024-1199px  SÍ se ve. En esta franja §8 pone el aside —con su índice y su
       infobox— en la fila 1, y como no lleva `grid-column` se queda solo en la
       COLUMNA IZQUERDA: la mitad derecha de esa fila está vacía. El texto, con
       `grid-column: 1 / -1`, baja a la fila 2 ocupando las dos columnas, así que
       el texto se queda con 576px(--pw-measure) y deja ~400px muertos a su
       derecha. El índice entra ahí como elemento de la rejilla —no
       superpuesto— y por eso ni una tabla ancha puede colarse debajo; con
       `align-self: start` + `sticky` acompaña la lectura sin salirse de su
       columna. El índice del aside queda arriba a la izquierda, para elegir
       dónde saltar; este acompaña al que ya está leyendo.
     · <1024px  NO se ve. Debajo no hay gutter lateral que respetar: el texto
       ocupa `--pw-measure` sobre una pantalla casi entera, y con el aside en
       columna única debajo el índice ya se lee sin más.
   -------------------------------------------------------------------------- */
.pw-toc-float {
  display: none;
}

@media (min-width: 1024px) and (max-width: 1199px) {
  .pw-toc-float {
    display: block;
    grid-column: 2;
    grid-row: 2;
    align-self: start;
    position: sticky;
    top: calc(var(--pw-header-h) + var(--pw-space-4));
    justify-self: start;
    width: 100%;
    max-width: var(--pw-aside-w);
    padding: var(--pw-space-3) var(--pw-space-4) var(--pw-space-4);
    background: var(--pw-surface);
    border: 1px solid var(--pw-border);
    border-radius: var(--pw-radius);
    box-shadow: var(--pw-shadow-sm);
    font-size: var(--pw-fs-sm);
  }

  /* El texto cede la segunda columna de la fila 2 al índice. Antes se desplegaba
     por las dos y sobraban ~400px vacíos a su derecha: no se pierde medida de
     lectura, que ya venía capada por `--pw-measure`, sino aire muerto. */
  .pw-content > .pw-text {
    grid-column: 1;
  }
}

.pw-toc-float-title {
  padding-bottom: var(--pw-space-2);
  margin-bottom: var(--pw-space-2);
  border-bottom: 1px solid var(--pw-border);
  color: var(--pw-ink-muted);
  font-size: var(--pw-fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  line-height: var(--pw-lh-tight);
  text-transform: uppercase;
}

.pw-toc-float-list {
  max-height: 60vh;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
  scrollbar-width: thin;
}

.pw-toc-float-item {
  margin: 0;
}

.pw-toc-float-link {
  display: block;
  padding: 0.15rem 0.5rem;
  border-left: 2px solid transparent;
  border-radius: var(--pw-radius-sm);
  color: var(--pw-ink-soft);
  font-size: var(--pw-fs-sm);
  line-height: 1.5;
  text-decoration: none;
  transition: color var(--pw-transition), background var(--pw-transition),
    border-color var(--pw-transition);
}

.pw-toc-float-link:hover {
  background: var(--pw-wash);
  color: var(--pw-ink);
}

/* Sección que se está leyendo. Mismo indicador que el índice de `render.js`
   (filete + lavado), para que los dos no se lean como dos cosas distintas. */
.pw-toc-float-link[aria-current="true"] {
  border-left-color: var(--pw-ink);
  background: var(--pw-wash);
  color: var(--pw-ink);
  font-weight: 600;
}


/* --------------------------------------------------------------------------
   v3.3 · 5 · LÍNEAS DE TIEMPO (§18)
   --------------------------------------------------------------------------
   `.pw-timeline` se pinta DESPUÉS de su tabla, no en lugar de ella, y lleva el
   mismo cronograma que los catálogos de §7: un punto por fila sobre un filete
   vertical. El año va en su propia línea con cifras tabulares —`--pw-measure`
   no es el sitio de un eje temporal— y el texto debajo. En una sola columna
   (móvil) el filete y los puntos giran a horizontales: la misma información
   girada, no una capa de estilos nueva. */
.pw-timeline {
  margin: var(--pw-space-3) 0 var(--pw-space-5);
  padding: 0 0 0 var(--pw-space-4);
  border-left: 2px solid var(--pw-border);
  list-style: none;
}

.pw-timeline-item {
  position: relative;
  margin: 0;
  padding: 0 0 var(--pw-space-3) 0;
  font-size: var(--pw-fs-sm);
  line-height: var(--pw-lh-body);
}

.pw-timeline-item:last-child {
  padding-bottom: 0;
}

/* El punto va sobre el filete, no debajo: es el mismo eje el que comparten todas
   las filas, y así se ve de un vistazo que la lista es una serie y no un
   conjunto de viñetas sueltas. */
.pw-timeline-item::before {
  position: absolute;
  top: 0.45em;
  left: calc(-1 * var(--pw-space-4) - 2px + 0.25rem);
  width: 0.5rem;
  height: 0.5rem;
  border: 2px solid var(--pw-border);
  border-radius: 50%;
  background: var(--pw-bg);
  content: '';
  transform: translateX(-50%);
}

.pw-timeline-year {
  display: block;
  color: var(--pw-ink);
  font-family: var(--pw-font-serif);
  font-size: var(--pw-fs-md);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: var(--pw-lh-tight);
}

.pw-timeline-text {
  display: block;
  color: var(--pw-ink-soft);
}

.pw-timeline-link {
  color: var(--pw-link);
  text-decoration: none;
  transition: color var(--pw-transition);
}

.pw-timeline-link:hover {
  color: var(--pw-link-hover);
  text-decoration: underline;
  text-underline-offset: 3px;
}

@media (max-width: 560px) {
  .pw-timeline {
    padding: 0;
    border-left: 0;
    border-top: 2px solid var(--pw-border);
  }

  .pw-timeline-item {
    padding: 0 0 0 var(--pw-space-4);
  }

  .pw-timeline-item:last-child {
    padding-bottom: var(--pw-space-3);
  }

  .pw-timeline-item::before {
    top: 0.4em;
    left: -0.5rem;
  }
}


/* --------------------------------------------------------------------------
   v3.3 · 6 · RESALTADO DE `?q=` (§13)
   --------------------------------------------------------------------------
   `<mark class="pw-qmark">` lo pone `js/features.js` recorriendo text nodes con
   `splitText`, nunca `innerHTML`. Aquí solo se le quita el amarillo del agente de
   usuario —que no se lee ni en sepia ni en oscuro— y se sustituye por un lavado de
   la propia tinta, que sí acompaña a los tres temas. Filete en vez de bloque de
   color porque el subrayado aguanta el impreso en gris: en papel la coincidencia
   sigue localizándose sin depender de un color. */
.pw-qmark {
  padding: 0 0.1em;
  border-bottom: 2px solid var(--pw-border-strong);
  background: var(--pw-wash);
  color: inherit;
  font: inherit;
}


/* --------------------------------------------------------------------------
   v3.3 · 7 · IMPRESIÓN
   --------------------------------------------------------------------------
   Los cinco bloques `@media print` de §4 ya ocultan `.pw-tools`,
   `.pw-article-actions`, `.pw-articleinfo` y `.pw-toc` —y con ellos los tres
   botones de acción y la ficha—, así que aquí solo quedan las tres piezas que
   son de esta sección y no de las anteriores:

     · `.pw-toc-float` es navegación: sin destino al que saltar en papel.
     · `.pw-timeline` duplica la tabla que tiene justo encima y partiría la
       tabla entre dos páginas; la tabla ya está en el impreso.
     · `.pw-prevnext` son dos enlaces: en papel no llevan a ninguna parte.

   `.pw-qmark` NO se oculta a propósito: es la respuesta visible a «¿qué estaba
   buscando?», y en gris sigue diciendo dónde. Ni una regla de las de arriba
   queda tocada. */
@media print {
  .pw-toc-float,
  .pw-timeline,
  .pw-prevnext {
    display: none !important;
  }
}


/* ==========================================================================
   ===== v6 · movimiento y microinteracción =====
   --------------------------------------------------------------------------
   El sitio estaba compuesto como un libro bien iluminado y se leía como una
   maqueta quieta: de los veintidós componentes interactivos de una página,
   sólo siete contestaban al puntero. Los otros quince —la barra lateral
   entera, el mapa del espectro, el diálogo de atajos, la vista previa, la
   marca de la cabecera, el buscador, el índice flotante— eran pintados y nada
   más. En un texto que se lee despacio, un componente que no se mueve al
   tocarlo no parece un componente: parece un párrafo.

   Aquí se les da movimiento, con la misma disciplina de las secciones
   anteriores —se AÑADE al final, no se reescribe ni una regla previa, los
   estados se marcan con tinta y nunca con un color de acento, y todo se apoya
   en tokens para que los tres temas se recoloreen solos— y con tres reglas
   que este bloque respeta y conviene dejar escritas:

   · El movimiento que ESCONDE algo va siempre detrás de `html.pw-motion`, la
     clase que `js/motion.js` pone en `<html>` cuando el movimiento está
     permitido. Sin JS, o con el movimiento reducido en el sistema, ninguna de
     esas reglas existe: el artículo se ve entero, el diálogo se abre y el
     índice se despliega. Un sitio que depende de su CSS para SER legible es un
     sitio roto en cuanto falla un script.

   · En el camino del scroll sólo se mueven `opacity` y `transform`. Son las
     dos propiedades que el compositor resuelve sin recalcular la maquetación,
     así que un bloque que entra en pantalla no dispara un reflow del texto que
     tiene debajo. `width`, `height`, `top`, `left`, `margin`, `padding` y
     `box-shadow` quedan fuera de la ruta del scroll a propósito: son de
     pintado y de layout, y una wiki se lee en artículos largos.

   · Las entradas (popover, diálogo, hovercard, estados vacíos, mapa) usan
     `transform` + `opacity` en `@keyframes` en lugar de una transición,
     porque un elemento que estaba en `display: none` no tiene estado anterior
     del que arrancar una transición: al mostrarlo hay que reproducirla. Todas
     viven dentro de `@media (prefers-reduced-motion: no-preference)`, el
     mismo sitio donde §14 usa `scroll-behavior`, así que el bloque de
     §15 no necesita una sola línea más.

   Y una decisión de tipografía que se respeta entera: aquí no se toca ni un
   `font-size`, ni un interlineado, ni una medida, ni un punto de corte. El
   movimiento cambia CUÁNDO se ve una cosa, nunca CÓMO se ve.

   Índice
     1 · Tokens de movimiento y escalera de capas
     2 · El revelado que pide js/motion.js
     3 · Microinteracción: filete lateral, marca, buscador, lecturas
     4 · Entradas: estados vacíos, índice flotante, títulos apuntados
     5 · Paneles: popover, diálogo de atajos y hovercard
      6 · El mapa del espectro y sus piezas
      7 · Ajustes de premio, recalibrados sobre medida
   ========================================================================== */


/* --------------------------------------------------------------------------
   v6 · 1 · TOKENS DE MOVIMIENTO
   --------------------------------------------------------------------------
   No son colores, así que —a diferencia de §1 y de los cuatro bloques de tema
   de v2 · 1— no se repiten por tema: el claro, el oscuro y el sepia se mueven
   igual, que es lo correcto. Tres duraciones y un cuarto escalón:

     --pw-dur-1  180ms  lo que dura un hover: filete, tinta, botón
     --pw-dur-2  320ms  lo que dura una entrada de panel
     --pw-dur-3  300ms  lo que dura un bloque al entrar en pantalla
     --pw-dur-4  400ms  lo que dura una pieza grande y solitaria (el mapa)

   Las dos últimas bajaron de 520 y 820 ms. La razón está medida, no tasteada:
   NN/g (`nngroup.com/articles/scroll-animations`) fija 100-500 ms como banda de
   casi todo lo que se anima en una interfaz y avisa de que "at 500ms,
   animations start to feel like a real drag"; David Bushell ("Death to Scroll
   Fade!", enero 2026) ataca exactamente estos dos números en el efecto que
   más se ha reproducido en el mundo. 300 ms es el techo que la fuente da para
   contenido y 400 el margen que queda para la pieza grande. Bajarlo también
   acorta el fundido de salida que provoca `data-pw-reveal` al pintar, que
   a 520 ms se leía como un artículo que se apaga y luego se enciende.

   Tres curvas y ninguna más. `--pw-ease-out` es la del sitio: frena, o sea
   que el objeto llega y SE PARA, en vez de empezar y acabar al mismo ritmo,
   que es lo que hace que una transición se lea como un rebote barato.
   `--pw-ease-inout` se reserva para trayectos de ida y vuelta. Y
   `--pw-ease-spring` —la única con sobreimpulso— aparece UNA vez, en el
   cuadrado de la marca de la cabecera: 28px es el único elemento del sitio
     donde un rebote de un píxel se lee como precisión y no como juguete.

   `--pw-reveal-shift` es cuánto baja un bloque antes de subir a su sitio. 14px
   es poco para notarlo como movimiento y mucho para notarlo como salto.

   Y la escalera de capas, que el fichero llevaba escribiendo a mano (20, 30,
   40, 45, 46, 50, 60, 70, 80) sin nombre. Aquí se declara; las reglas
   anteriores conservan su número literal porque la ley de este fichero es no
   reescribir nada, pero a partir de ahora una capa nueva se nombra.
   -------------------------------------------------------------------------- */
:root {
  /* --- duraciones --- */
  --pw-dur-1: 180ms;
  --pw-dur-2: 320ms;
  --pw-dur-3: 300ms;
  --pw-dur-4: 400ms;

  /* --- curvas --- */
  --pw-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --pw-ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  --pw-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* --- recorrido del revelado --- */
  --pw-reveal-shift: 14px;

  /* --- escalera de capas (nombres de las que ya existen) --- */
  --pw-z-rail: 20;      /* .pw-sidebar */
  --pw-z-header: 30;    /* .pw-header */
  --pw-z-results: 40;   /* .pw-search-results */
  --pw-z-popover: 45;   /* .pw-popover */
  --pw-z-search: 46;    /* .pw-search-cabecera */
  --pw-z-fab: 50;       /* .pw-totop */
  --pw-z-progress: 60;  /* .pw-progress */
  --pw-z-preview: 70;   /* .pw-hovercard */
  --pw-z-dialog: 80;    /* .pw-modal */

  /*      El ÚNICO token de movimiento que tenía el fichero lo escribía §1
     como un literal, `150ms ease`; ahora §1 y aquí dicen exactamente lo mismo
     y manda esta segunda: `--pw-dur-1` con `--pw-ease-out`, o sea el escalón
     corto con la curva del sitio. Treinta y cuatro reglas lo consumen y
     ninguna se toca: con un solo token, las transiciones que ya existían
     dejan de ser lineales sin que su lista de propiedades cambie ni una coma,
     que es exactamente lo que se quería. La barra de
     progreso (§ v2 · 7) NO usa este token: su regla de base escribe su propio
     `120ms linear` y, donde el motor mueve la barra con `animation-timeline`,
     lo sustituye por una animación sin transición. Una barra que persigue la
     posición del scroll tiene que ir en línea con ella o se despegaría del
     ojo, y una transición de 180 ms con curva de frenado no llega a eso. */
  --pw-transition: var(--pw-dur-1) var(--pw-ease-out);
}

/* `--pw-ease-spring` es un rebote, y un rebote de verdad no es una curva que
   "pasa" el punto de destino: es un muelle con velocidad, masa y rozamiento. El
   `cubic-bezier` de arriba —0.34, 1.56, 0.64, 1— es la aproximación de siempre:
   barata y con un sobreimpulso que llega tarde y se amortigua de golpe, así que
   un píxel de más se queda un momento y luego vuelve de golpe.

   `linear()` deja escribir esa física como una lista de paradas de tiempo, y es
   Baseline desde diciembre de 2023 (Chrome 113, Firefox 112, Safari 17.2). Aun
   así la declaración va dentro de `@supports` a propósito: una declaración
   inválida dentro de un token no se descarta —rompe el token entero, no sólo la
   curva—, y el sitio sigue prometiendo abrir con `file://` en cualquier
   navegador. El `cubic-bezier` de arriba se queda como valor declarado y la
   versión física lo sobrescribe después.

   Los números son un muelle con poco rozamiento: sube rápido, pasa del 100%,
   vuelve al 96% y se asienta. Con 1px de recorrido en el cuadrado de la marca,
   este es el único sitio del sitio donde el rebote se lee como precisión. */
@supports (transition-timing-function: linear(0, 1)) {
  :root {
    --pw-ease-spring: linear(
      0, 0.006, 0.025 2.8%, 0.101 6.1%, 0.539 18.9%, 0.721 25.3%, 0.849 31.5%,
      0.937 38.1%, 0.968 41.8%, 0.991 45.7%, 1.006 50.1%, 1.015 55%, 1.017 63.9%,
      1.001 80.9%, 1
    );
  }
}


/* --------------------------------------------------------------------------
   v6 · 2 · EL REVELADO QUE PIDE js/motion.js
   --------------------------------------------------------------------------
   Contrato, tal cual: el JS pone `pw-motion` en `<html>` cuando el movimiento
   está permitido, marca cada bloque con `data-pw-reveal`, le pone
   `.is-revealed` al entrar en pantalla, y puede escribir `--pw-reveal-delay`
   en el elemento para escalonar un grupo. Aquí sólo se describen los dos
   estados.

   El estado inicial —invisible y 14px más abajo— va DENTRO de `html.pw-motion`.
   Es la decisión más importante de este bloque: si el script no se carga, si
   el navegador no soporta `IntersectionObserver`, o si el visitante tiene el
   movimiento reducido en el sistema, estas reglas no existen y el artículo se
   ve entero desde el primer momento. Un `opacity: 0` sin condiciones es un
   artículo que se pierde si algo falla; con condiciones, es sólo una
   animación.

   El desplazamiento NO va en `transform` sino en la propiedad `translate`, que
   es independiente y se compone con ella: una tarjeta que al mismo tiempo se
   revela y se levanta al pasar el cursor suma los dos desplazamientos en vez
   de pisarse. Con `transform` en los dos sitios, el `translateY(-2px)` de
   `.pw-card:hover` (§7) perdería contra la regla de revelado —que es más
   específica— y las tarjetas dejarían de levantarse en todas las páginas con
   `pw-motion`. Con `translate` no hay conflicto: cada propiedad lleva su
   transición. Además `translate: none` en el estado final deja de componer
   capa en el compositor, y un artículo largo con cuarenta bloques revelados no
   debe dejar cuarenta capas promover en memoria para siempre.

   Y aquí está el único sitio del bloque donde hay que devolverle a un
   componente la lista de `transition` que ya tenía: una transición es una
   lista, no un añadido, y la de arriba la sustituye entera. Cuatro de los
   objetivos que `js/motion.js` marca (`TARGETS`) trae ya su propia
   transición —`.pw-card`, `.pw-related-card`, `.pw-prevnext-link` y
   `.pw-spectrum-chip`—, así que se les devuelve con las suyas más las dos del
   revelado. Sin estas cuatro reglas, marcar una tarjeta para revelarla
   apagaría en ella el hover de borde y sombra que ya tenía.

   `[hidden]` (v2 · 4) manda con su `!important` y aquí no se le hace sombra:
   ninguna regla de este bloque escribe `display`, así que un bloque revelado
   que luego se oculta —una tarjeta filtrada por el índice alfabético, un
   panel que se cierra— sigue saliendo del flujo por la regla global y no por
   la suya.
   -------------------------------------------------------------------------- */
html.pw-motion [data-pw-reveal] {
  opacity: 0;
  translate: 0 var(--pw-reveal-shift);
  transition:
    opacity var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms),
    translate var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms);
}

html.pw-motion [data-pw-reveal].is-revealed {
  opacity: 1;
  translate: none;
}

/* Las transiciones propias de cuatro objetivos marcados, devueltas con las
   suyas delante. `background` y no `background-color` en la de anterior /
   siguiente porque así lo escribió su regla de § v3.3 · 3, y una lista de
   transición no se comporta igual si se le cambian las propiedades. */
html.pw-motion .pw-card[data-pw-reveal],
html.pw-motion .pw-related-card[data-pw-reveal] {
  transition:
    border-color var(--pw-transition),
    box-shadow var(--pw-transition),
    transform var(--pw-transition),
    opacity var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms),
    translate var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms);
}

html.pw-motion .pw-prevnext-link[data-pw-reveal] {
  transition:
    border-color var(--pw-transition),
    background var(--pw-transition),
    opacity var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms),
    translate var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms);
}

html.pw-motion .pw-prevnext-link[data-pw-reveal] {
  transition:
    border-color var(--pw-transition),
    background var(--pw-transition),
    opacity var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms),
    translate var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms);
}

/* El marcado no se puede ver.    `collect()` (§3 de `js/motion.js`) pone
   `data-pw-reveal` sobre bloques que `render.js` ya ha pintado visibles, así que
   la regla de arriba les cambia la opacidad de 1 a 0 CON transición: lo que se
   ve es un fundido de salida, y la entrada de verdad no llega hasta que
   `.is-revealed` entra al hacer scroll. Con 150 ms era un parpadeo que nadie
   notaba; a 300 ms es un fundido de salida corto y aceptable, y a los 520 ms
   que duraba este token antes de la recalibración era un artículo entero que se
   apagaba y luego se encendía.

   Mientras un bloque no ha entrado no hay nada que transicionar: su 0 se aplica
   de golpe. En cuanto llega `.is-revealed` esta regla deja de aplicar —`:not()`
   ya no casa— y la entrada sí se anima, con su retardo y su `--pw-dur-3`. El
   `!important` es para ganarle a las listas de `transition` de los objetivos
   concretos de arriba, que son más específicas; los seguros de §15 y de
   impresión no se ven afectados porque aquí no se toca `opacity`. */
html.pw-motion [data-pw-reveal]:not(.is-revealed) {
  transition: none !important;
}

/* El chip del selector sí se declara aquí y no en v6 · 6, donde están el resto
   de sus reglas: un chip marcado es un chip, y su lista de `transition` tiene
   que ser una sola. Las dos propiedades del revelado van en la misma lista
   aunque el chip no llegue a estar marcado nunca —una lista de transición
   puede nombrar una propiedad que no cambia nunca y no pasa nada—, y así este
   bloque no depende de que `js/motion.js` sepa o no qué clase marca. */

/* El revelado es el único estado de este bloque que puede dejar algo
   invisible, así que es el único que necesita su propio seguro. §15 ya
   colapsa cualquier duración a 0,01ms; esto además fija el estado final, para
   que un `pw-motion` mal puesto no pueda dejar un párrafo en blanco. */
@media (prefers-reduced-motion: reduce) {
  html.pw-motion [data-pw-reveal] {
    opacity: 1;
    translate: none;
    transition: none;
  }
}

/* Y el tercer seguro, el que no se suele recordar: en papel no hay scroll, y
   un bloque que el lector no llegó a desplazar saldría en blanco. Se imprime
   siempre en su estado final. */
@media print {
  html.pw-motion [data-pw-reveal] {
    opacity: 1;
    translate: none;
  }
}


/* --------------------------------------------------------------------------
   v6 · 3 · MICROINTERACCIÓN: FILETE LATERAL, MARCA, BUSCADOR Y LECTURA
   --------------------------------------------------------------------------
   Lo que se toca al pasar por encima y al enfocarse. Nada de esto esconde
   nada, así que no necesita `html.pw-motion`: con el movimiento reducido en el
   sistema, §15 las colapsa a 0,01ms y el hover sigue estando ahí, sólo que
   sin viaje. Es la diferencia entre quitar el efecto y quitar el estado.

   · El filete de la barra lateral. §5 ya dibuja el de la sección actual con
     `border-left: 2px`, siempre a la misma altura que la caja del enlace. El
     hover crece el mismo filete desde el centro de la fila en lugar de
     encender un segundo dibujo, así que activo y hover hablan el mismo
     idioma: 2px de tinta a la izquierda. El pseudo se sitúa en `left: -2px`
     para caer exactamente sobre la pista del `border-left` transparente, no
     un píxel más adentro. `--pw-dur-2` y no `--pw-dur-1`: once filas seguidas
     con un filete de 180ms se leen como un barrido, no como once decisiones.

   · La marca de la cabecera. El texto ya cambiaba de tinta al pasar por
     encima (§3) pero de golpe; el cuadrado no respondía. Ahora la tinta
     viaja con `--pw-ease-out` y el cuadrado se levanta 1px con la única
     curva con sobreimpulso del fichero. Un rebote de un píxel en un objeto de
     28px es un gesto; el mismo rebote en una tarjeta de 16rem es ruido.

   · El buscador. El anillo de foco ya existe (§4, `box-shadow` de 3px y
     `outline` de 2px) y no se toca ni un valor: sólo se le da una transición,
     para que el anillo se encienda en lugar de aparecer. Aquí sí se anima
     `box-shadow`, y es deliberado —no está en el camino del scroll— porque
     un anillo que aterriza de golpe se lee como un error de foco.

   · Los conmutadores de lectura. El grupo (`.pw-segmented` y `.pw-stepper`
     comparten caja y gramática en v2 · 6) no decía nada cuando el foco entraba
     en él, así que un usuario de teclado no sabía en qué grupo estaba: sólo
     se veía el anillo del botón suelto. `:focus-within` le da al grupo su
     propio filete de tinta —`--pw-border-strong`, nunca un acento— y los
     botones se hunden un 4% al pulsarse, que es el gesto que falta para que
     un control se sienta pulsado y no sólo marcado. El filete del grupo usa
     `--pw-ease-inout` y no `--pw-ease-out` porque se enciende y se apaga con
     el mismo peso —el foco entra y sale—, y una curva simétrica trata igual
     los dos sentidos; un `--pw-ease-out` en la salida haría que el grupo
     «cayera» al perder el foco.
   -------------------------------------------------------------------------- */
html.pw-motion .pw-nav-link {
  position: relative;
  transition: background var(--pw-transition), color var(--pw-transition);
}

html.pw-motion .pw-nav-link::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -2px;
  width: 2px;
  background: var(--pw-ink);
  transform: scaleY(0);
  transform-origin: 50% 50%;
  transition: transform var(--pw-dur-2) var(--pw-ease-out);
}

html.pw-motion .pw-nav-link:hover::before,
html.pw-motion .pw-nav-link:focus-visible::before {
  transform: scaleY(1);
}

html.pw-motion .pw-brand-text {
  transition: color var(--pw-transition);
}

html.pw-motion .pw-brand-mark {
  transition: transform var(--pw-dur-1) var(--pw-ease-spring);
}

html.pw-motion .pw-brand:hover .pw-brand-mark,
html.pw-motion .pw-brand:focus-visible .pw-brand-mark {
  transform: translateY(-1px);
}

html.pw-motion .pw-search-input {
  transition: border-color var(--pw-transition),
    box-shadow var(--pw-dur-2) var(--pw-ease-out);
}

html.pw-motion .pw-segmented,
html.pw-motion .pw-stepper {
  transition: border-color var(--pw-dur-2) var(--pw-ease-inout);
}

html.pw-motion .pw-segmented:focus-within,
html.pw-motion .pw-stepper:focus-within {
  border-color: var(--pw-border-strong);
}

html.pw-motion .pw-seg,
html.pw-motion .pw-step {
  transition: background var(--pw-transition), color var(--pw-transition),
    border-color var(--pw-transition),
    transform var(--pw-dur-1) var(--pw-ease-out);
}

html.pw-motion .pw-seg:active,
html.pw-motion .pw-step:active {
  transform: scale(0.96);
}


/* --------------------------------------------------------------------------
   v6 · 4 · ENTRADAS: ESTADOS VACÍOS, ÍNDICE FLOTANTE Y TÍTULOS APUNTADOS
   --------------------------------------------------------------------------
   Tres entradas de bajo volumen, todas con `transform` + `opacity` y todas
   dentro de `no-preference`.

   · El estado vacío (§13) no se dibujaba: aparecía entero, como un recorte
     con tijera. Sube 14px y se desvanece con `--pw-dur-2`, que es lo
     que tarda un recuadro en instalarse en la página. Como `js/app.js` lo
     reescribe en cada búsqueda sin resultados, la entrada se repite sola
     cuando toca, que es justo lo que se quiere: cada respuesta vacía nueva se
     lee como una respuesta nueva.

   · El índice flotante (v3.3 · 4) es el único panel que acompaña la lectura
     mientras el texto se mueve, y no se enteraba de nada. No se le da una
     entrada —sería una animación más cada vez que se cruza el breakpoint— sino
     una respuesta al puntero: al pasar por encima o al receber el foco, la
     sombra pasa de `--pw-shadow-sm` a `--pw-shadow`. Es profundidad, no
     movimiento, y por eso está en el hover y no en un `@keyframes`.

   · El título de una sección apuntada por un ancla (`§9`, `.pw-h2:target >
     .pw-headline`) se encendía de golpe con su lavado de 3px. Ahora el lavado
     entra: `background-color` y el `box-shadow` del anillo viajan con
     `--pw-dur-2`, que es lo que tarda un dedo en recorrer un subrayado. Es la
     única pista que recibe el lector de que ha llegado, y por eso vale la
     pena que no aparezca como un fogonazo.
   -------------------------------------------------------------------------- */
@keyframes pw-enter-block {
  from {
    opacity: 0;
    transform: translate3d(0, var(--pw-reveal-shift), 0);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: no-preference) {
  html.pw-motion .pw-empty {
    animation: pw-enter-block var(--pw-dur-2) var(--pw-ease-out) both;
  }
}

html.pw-motion .pw-toc-float {
  transition: box-shadow var(--pw-dur-2) var(--pw-ease-out);
}

html.pw-motion .pw-toc-float:hover,
html.pw-motion .pw-toc-float:focus-within {
  box-shadow: var(--pw-shadow);
}

html.pw-motion .pw-headline {
  transition: background-color var(--pw-dur-2) var(--pw-ease-out),
    box-shadow var(--pw-dur-2) var(--pw-ease-out);
}


/* --------------------------------------------------------------------------
   v6 · 5 · PANELES: POPOVER, DIÁLOGO DE ATAJOS Y HOVERCARD
   --------------------------------------------------------------------------
   Los tres se muestran retirando `hidden` (o `open`, en el `<dialog>`), así
   que no hay estado anterior del que arrancar una transición: entran con
   `@keyframes`, sobre `transform` + `opacity` y nada más.

   · `.pw-popover` cuelga de la barra de herramientas por su borde derecho, así
     que escala desde ARRIBA A LA DERECHA —`transform-origin: 100% 0`—: se abre
     como se abre una bandeja, desde el punto donde el dedo lo ha pedido. En
     `.pw-modal` el origen es el centro, porque un diálogo se coloca en el
     centro de la mesa. `.pw-hovercard` escala desde ARRIBA A LA IZQUIERDA,
     que es la esquina del texto que la ha invocado.

   · Las tres bajan de escala (0.98 → 1) en lugar de subirla: un panel que
     crece del 98% se está instalando; uno que llega al 100% desde más
     grande se está recogiendo. Con `--pw-dur-2` y `--pw-ease-out` la entrada
     dura lo que tarda una cubierta en dejarse caer.

   · El velo del `<dialog>` también se desvanece. El `background` del
     `::backdrop` es un literal de § v2 · 9 y no se toca; lo único que se anima
     es su opacidad, con la misma duración que el panel, para que no se vea la
     ventana aparecer sobre un fondo todavía opaco.
   -------------------------------------------------------------------------- */
@keyframes pw-enter-panel {
  from {
    opacity: 0;
    transform: scale(0.98);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: no-preference) {
  html.pw-motion .pw-popover.is-open {
    transform-origin: 100% 0;
    animation: pw-enter-panel var(--pw-dur-2) var(--pw-ease-out) both;
  }

  html.pw-motion .pw-modal[open] {
    transform-origin: 50% 50%;
    animation: pw-enter-panel var(--pw-dur-2) var(--pw-ease-out) both;
  }

  /* El velo sólo desvanece: `transform` en un `::backdrop` no está soportado
     por igual en todos los navegadores y un velo escalado dejaría un borde
     sin cubrir alrededor de la ventana. Opacidad y nada más. */
  html.pw-motion .pw-modal[open]::backdrop {
    animation: pw-fade-in var(--pw-dur-2) var(--pw-ease-out) both;
  }

  /* `:not([hidden])` en vez de una clase: el hovercard no tiene estado abierto
     —v2 · 10 lo deja clarísimo—, `features.js` sólo le quita `hidden`. La
     regla nunca pelea con ese `display: none !important` porque no escribe
     `display`. */
  html.pw-motion .pw-hovercard:not([hidden]) {
    transform-origin: 0 0;
    animation: pw-enter-panel var(--pw-dur-1) var(--pw-ease-out) both;
  }
}


/* --------------------------------------------------------------------------
   v6 · 6 · EL MAPA DEL ESPECTRO Y SUS PIEZAS
   --------------------------------------------------------------------------
   El mapa es un gráfico de dispersión dibujado en SVG, y en SVG el
   `transform` se aplica en el sistema de coordenadas del `viewBox`, no en
   píxeles: sin `transform-box: fill-box` un `scale(1.1)` se ancla en el origen
   del `viewBox` y el nodo sale disparado hacia la esquina. Por eso la caja
   de referencia se declara en el propio nodo y el origen es su centro
   geométrico, no el de la página.

   · Nodos. `.pw-spectrum-node` y `.pw-graph-node` (y `.pw-graph-hub`, que es
     un nodo más con el círculo más grande) ya cambiaban de tinta al pasar por
     encima, pero no se movían. Ahora el círculo crece un 12% sobre su propia
     caja y la etiqueta se queda quieta: agrandar el texto sí movería la
     tipografía que el resto de este bloque no toca, y una etiqueta que baila
     bajo el cursor es ruido. El filtro del halo de foco —que § v4 ya pinta
     con `--pw-focus-halo`— entra en la lista de `transition` con lo demás, de
     modo que el nodo se hincha y se ilumina en un solo movimiento.

   · Ejes. Las dos `<line>` del eje se dibujan desde el cruce central, que es
     justo el `transform-origin: 50% 50%` de la caja del `viewBox`. Es el
     gesto más único de la sección: el mapa se cuadricula solo, como se
     cuadricula un pliego antes de escribirlo. `--pw-dur-4`, el cuarto
     escalón, porque es la pieza más grande y más solitaria del sitio. Bajó de
     820ms a 400ms con el resto de la escala, y no lo necesita: 400ms ya dan
     para leer el dibujado sin que parezca una entrada Yakartá.

   · Polos. Las cuatro etiquetas de los extremos entran después del eje
     (medio `--pw-dur-4` de retardo) y sólo se desvanecen: son texto de
     18px, y moverlo 14px lo arrancaría del sitio al que pertenece. Los polos
     aparecen cuando la cuadrícula ya está puesta, que es el orden en que se
     lee un eje.

   · El selector de ideologías (`.pw-spectrum-pick`) es una caja neutra que no
     se puede mover sin arrastrar el mapa que tiene debajo, así que no recibe
     una entrada: recibe una respuesta. Al pasar por encima, la fila se
     levanta 1px —con `@media (hover: hover)` para que en un dedo no se quede
     pegada— y cada chip se hunde un 4% al pulsarse. Es la misma receta del
     punto 3 aplicada a la única superficie de la página que aún no decía
     nada al tocarla.
   -------------------------------------------------------------------------- */
@keyframes pw-fade-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes pw-axis-draw {
  from {
    transform: scale(0);
  }

  to {
    transform: scale(1);
  }
}

@media (prefers-reduced-motion: no-preference) {
  html.pw-motion .pw-spectrum .pw-spectrum-axis {
    transform-box: view-box;
    transform-origin: 50% 50%;
    animation: pw-axis-draw var(--pw-dur-4) var(--pw-ease-out) both;
  }

  html.pw-motion .pw-spectrum .pw-spectrum-pole {
    animation: pw-fade-in var(--pw-dur-2) var(--pw-ease-out) both;
    animation-delay: calc(var(--pw-dur-4) / 2);
  }
}

html.pw-motion .pw-spectrum a circle,
html.pw-motion .pw-graph a circle,
html.pw-motion .pw-spectrum a text,
html.pw-motion .pw-graph a text {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  transition: fill var(--pw-transition), stroke var(--pw-transition),
    transform var(--pw-dur-2) var(--pw-ease-out),
    filter var(--pw-dur-2) var(--pw-ease-out);
}

html.pw-motion .pw-spectrum a:hover circle,
html.pw-motion .pw-spectrum a:focus-visible circle,
html.pw-motion .pw-graph a:hover circle,
html.pw-motion .pw-graph a:focus-visible circle {
  transform: scale(1.12);
}

html.pw-motion .pw-spectrum-pick {
  transition: transform var(--pw-dur-2) var(--pw-ease-out);
}

@media (hover: hover) {
  html.pw-motion .pw-spectrum-pick:hover {
    transform: translateY(-1px);
  }
}

html.pw-motion .pw-spectrum-pick .pw-spectrum-chip {
  transition:
    color var(--pw-transition),
    background-color var(--pw-transition),
    border-color var(--pw-transition),
    transform var(--pw-dur-1) var(--pw-ease-out),
    opacity var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms),
    translate var(--pw-dur-3) var(--pw-ease-out) var(--pw-reveal-delay, 0ms);
}

html.pw-motion .pw-spectrum-pick .pw-spectrum-chip:active {
  transform: scale(0.96);
}


/* ==========================================================================
   ===== v7 · la capa viva del editorial =====
   --------------------------------------------------------------------------
   Con v6 los componentes aprendieron a contestar al puntero. Lo que seguía
   mudo era la hoja: las superficies no tienen luz propia —una tarjeta es un
   rectángulo de papel del mismo tono que el fondo, se lea como se lea— y las
   cifras, que son el alma de una wiki, ya estaban todas en la página antes de
   que nadie las mirara. Cuarenta y siete artículos y una columna de números
   que aparecen como si llevaran años puestos.

   Este bloque pone las dos cosas en marcha, y lo hace con el mismo contrato
   que los seis anteriores: se AÑADE al final, no se reescribe ni se borra
   ninguna regla previa; los estados se marcan con tinta y nunca con un color
   de acento; y todo se apoya en tokens, para que los cuatro temas se recoloreen
   solos sin que aquí haya un solo hexadecimal. Ocho piezas, en el orden en que
   el lector las encuentra: el título, la superficie, el botón, la cifra, la
   cabecera, el índice, y por último el conmutador de tema.

   Las reglas del bloque, para que se puedan leer de un tirón:

   · Todo lo que ESCONDE va detrás de `html.pw-fx`, la clase que
     `js/interactions.js` pone en `<html>` cuando puede mover cosas, Y detrás
     además de `@media (prefers-reduced-motion: no-preference)`. Son dos
     puertas encajadas, y la segunda es más estrecha que la primera: si el
     sistema pide quietud, la puerta del estado invisible sencillamente no
     existe. Sin JS, o con el movimiento reducido, el artículo se ve entero, la
     cifra se ve entera y el índice se ve entero. Un sitio que depende de su
     CSS para SER legible es un sitio roto en cuanto falla un script, y ya se
     Pagó esa deuda en v6 · 2; no se vuelve a pedir.

   · En el camino del scroll sólo se mueven `opacity`, `transform` y `translate`.
     Son las propiedades que el compositor resuelve sin recalcular la
     maquetación, así que una línea que sube no dispara un reflow de los
     cuarenta párrafos que tiene debajo. `width`, `height`, `top`, `left`,
     `margin` y `padding` quedan fuera de la ruta del scroll a propósito, y
     `box-shadow` también: hay exactamente una excepción en todo el bloque —
     la cabecera de §6— y está justificada allí, en su propio comentario, con
     su propio coste.

   · El desplazamiento de los revelados va en la propiedad `translate`, no en
     `transform`, por el mismo motivo que en v6 · 2 y con la misma razón: los
     hover de §7 y de v6 · 3 ya son dueños de `transform`, y con `transform`
     en los dos sitios el más específico gana y la tarjeta deja de levantarse.
     `translate` es independiente, se compone con `transform` y con `scale`, y
     cada propiedad lleva su transición.

   · Y la decisión de tipografía se respeta entera: aquí no se toca ni un
     `font-size`, ni un interlineado, ni una medida, ni un punto de corte. Lo
     único que se escribe de las cajas es `height` en dos sitios justificados
     y `padding` en ninguno. El movimiento cambia CUÁNDO se ve una cosa, nunca
     CÓMO se ve.

   Índice
     1 · Tokens de la v7
     2 · Split-lines del título
     3 · Spotlight
     4 · Botones magnéticos
     5 · Contadores numéricos
     6 · Cabecera compacta
     7 · Indicador deslizante del índice
     8 · View Transitions
   ========================================================================== */


/* --------------------------------------------------------------------------
   v7 · 1 · TOKENS DE LA v7
   --------------------------------------------------------------------------
   Siete custom properties, y todas las que escribe `js/interactions.js`:

     --pw-fx-x   --pw-fx-y        posición del puntero DENTRO del elemento
                                  que lo sigue, en px. En el elemento, no en
                                  `<html>`: si vivieran en la raíz, las veinte
                                  tarjetas de la portada se iluminarian con
                                  el mismo centro y se vería una sola mancha
                                  en lugar de veinte.
     --pw-fx-strength            intensidad maestra, de 0 a 1. Es lo que el
                                  JS sube al entrar el puntero en una
                                  superficie y baja al salir, y es lo que
                                  hace que §3 se degrade a nada sin borrar
                                  una sola regla.
     --pw-magnet-x --pw-magnet-y  desvío del botón, en px, ya recortado por el
                                  JS a `--pw-magnet-limit`.
     --pw-count                  avance del contador, de 0 a 1.
     --pw-toc-y                  desplazamiento del indicador del índice, en px,
                                  medido desde el borde superior de la lista.

   Los valores de arranque son los del reposo —cero en todas las partes
   móviles— y por eso el bloque entero es inocuo en el primer fotograma,
   antes de que el script escriba nada. Un token que arrancase desplazado
   daría un salto en la primera pasada del puntero.

   Y los cinco tokens de geometría que este fichero usa para no dejar números
   sueltos en las reglas. Ninguno lleva color: `--pw-spot-alpha` es una
   CANTIDAD de tinta, y el color siempre lo pone `--pw-ink`, de modo que los
   cuatro temas continuos se recolorean sin tocar una línea de §3.

     --pw-line-step      70ms  de retardo entre líneas del título
     --pw-line-shift   0,55em  cuánto sube una línea antes de llegar
     --pw-spot-alpha       9%  tinta en el centro del resplandor
     --pw-spot-falloff   55%   dónde se apagó del todo
     --pw-header-h-cond 2,75rem suelo de la cabecera condensada

   `--pw-line-shift` va en `em` y no en `px` a propósito: `--pw-fs-3xl` es un
   `clamp()` y a 375px mide 1,75rem mientras que a 1600px mide casi 2,5rem. Un
   desplazamiento en píxeles se quedaría corto en el móvil y se alargaría
   demasiado en la pantalla grande, y corregirlo significaría tocar el tamaño
   del título. En `em` acompaña al tamaño real de cada caso sin escribir un
   `font-size` en ninguna parte.

   Sobre las duraciones: este bloque NO declara ni una nueva. Las cuatro de v6
   · 1 dan para todo, y cada una donde toca:

     §2 líneas del título        --pw-dur-3   300ms
     §3 resplandor               sin transición: lo rampa el JS (§3)
     §4 imán, hacia el puntero   --pw-dur-1   180ms
     §4 imán, de vuelta          --pw-dur-2   320ms
     §5 contadores               --pw-dur-4   400ms
     §5 insignia del botón       --pw-dur-3   300ms
     §6 cabecera                 --pw-dur-2   320ms (sólo pintado)
     §7 indicador del índice     --pw-dur-2   320ms
     §8 conmutador de tema       --pw-dur-2   320ms

   La asimetría del imán —180ms tirando, 320ms soltanto— es la prueba de que
   la escala sirve: no hace falta un quinto escalón, hace falta usar dos. Y como
   no hay curva nueva, `--pw-ease-out` sigue siendo la del sitio y
   `--pw-ease-spring` sólo se abre un puesto más, en la insignia del contador:
   los dos objetos del fichero donde un rebote de un píxel se lee como
   confirmación y en ninguno más.
   -------------------------------------------------------------------------- */
:root {
  /* --- puntero (los escribe el JS, en el elemento que lo sigue) --- */
  --pw-fx-x: 50%;
  --pw-fx-y: 50%;
  --pw-fx-strength: 0;

  /* --- imán de los botones --- */
  --pw-magnet-x: 0px;
  --pw-magnet-y: 0px;
  --pw-magnet-limit: 6px;

  /* --- contadores --- */
  --pw-count: 0;

  /* --- indicador del índice --- */
  --pw-toc-y: 0px;

  /* --- geometría del bloque (ningún color) --- */
  --pw-line-step: 70ms;
  --pw-line-shift: 0.55em;
  --pw-spot-alpha: 9%;
  --pw-spot-falloff: 55%;
  --pw-header-h-cond: 2.75rem;
}

/* El `@property` del contador, y el único sitio donde aparece un valor
   que no es un color del §1. Se registra `--pw-count` como `<number>` para que
   el motor la sepa interpolar: así el CSS puede animar el avance de un
   contador con una sola transición y el JS no tiene que escribir un valor por
   fotograma. `inherits: true` NO es un descuido: el filete de §5 se dibuja en
   un `::after`, y una propiedad registrada sin herencia se reinicia en el
   pseudo-elemento con su valor inicial, con lo cual la barra no se movería
   nunca. Es el error clásico de las propiedades registradas, y por eso está
   escrito aquí y no en el código que las consume.

   Hay un segundo `@property` en el fichero, `--pw-sheen` en § v6 · 7. Los dos
   se descartan de la misma manera si el motor no los soporta.

   Si un motor no soporta `@property`, el bloque entero se descarta —es una
   regla atómica que no se parsea a medias—, `--pw-count` sigue siendo un token
   corriente, el JS puede seguir escribiéndolo fotograma a fotograma y
   `scaleX(var(--pw-count))` sigue funcionando: lo que se registra no es
   necesario para el resultado, sólo lo hace interpolable. */
@property --pw-count {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}


/* --------------------------------------------------------------------------
   v7 · 2 · SPLIT-LINES DEL TÍTULO
   --------------------------------------------------------------------------
   `.pw-hero-title` es un bloque de texto con `max-width: 22ch` y
   `text-wrap: balance`. Partirlo en líneas para animarlas exige meter un
   envoltorio alrededor de cada línea VISUAL, y esa es la parte delicada: un
   `<span>` con `display: block` parte el párrafo en bloques, y a partir de ahí
   el reparto de líneas lo manda el script y no el navegador. Se acepta,
   porque la alternativa —animar el título entero— no escalona nada, y porque
   el contenido en línea no cambia: los mismos glifos, los mismos espacios, el
   mismo `line-height` heredado. Lo único que cambia es quién decide dónde
   corta la línea, y de eso se encarga una medición hecha con la fuente ya
   cargada.

   · `display: block` y nada más en el estado permanente. Un envoltorio en
     línea no tiene caja estable, y sin caja no hay `overflow` que recorte ni
     `translate` que se componga.

   · El recorte. Una línea que sube 0,55em se sale por arriba de su propia caja
     y se-metería encima de la línea de arriba, así que necesita `overflow:
     hidden`. Pero la caja de una línea de texto es la LINE-BOX, que con
     `--pw-lh-tight` no siempre contiene la tinta: en una serif con ascent +
     descent de 1,15em dentro de un interlineado de 1,12 lasmayúsculas y las
     colas se salen. Por eso el recorte se abre 0,08em por arriba y por abajo
     y se devuelve con el mismo margen negativo: la caja crece 0,16em y el
     margen encoge 0,16em, así que la aportación al flujo no cambia ni un
     píxel y ningún trazo queda cortado. Cuesta dos declaraciones y evita
     un título con las cejas raspadas.

   · El coste de `overflow: hidden`: cada línea recortada es una capa de
     recorte y, durante su animación, una capa promovida. Con cinco o seis
     líneas de título son cinco o seis capas de 40px de alto, que se
     desmontan solas al terminar la animación porque el `fill-mode` es
     `backwards` y no `both`: al acabar, el elemento vuelve a su estilo propio
     y deja de componer. Con `both` el valor final se quedaría puesto para
     siempre, que es justo lo que v6 · 2 advertía.

   · La entrada es una ANIMACIÓN y no una transición, y no es un descuido de
     redacción: un envoltorio recién creado no tiene estado anterior del que
     arrancar una transición. La animación empieza sola en cuanto existe el
     nodo, y `backwards` cubre el periodo de retardo —que es justo donde la
     línea tiene que estar invisible— y suelta el elemento al terminar.

   · `--pw-line-i` lo escribe el JS en cada línea y el retardo lo calcula
     aquí: `calc(var(--pw-line-i) * 70ms)`. El script no sabe nada de
     duraciones, y cambiar el paso del escalonado más adelante es cambiar una
     línea.

   · Y el caso que hay que cubrir siempre: un título que el script no ha
     dividido. NINGUNA regla de este apartado toca `.pw-hero-title`; todas
     van a `.pw-line`. Sin hijos `.pw-line` el título es exactamente el bloque
     de §7, con su `balance` y sus 22ch, y no se nota que hubiera un plan. Por
     el mismo motivo `text-wrap: balance` se queda como está y no se anula: se
     hereda a los envoltorios, y en una línea ya medida no hace nada, pero si
     el script midió mal y una línea sale larga, es el envoltorio el que la
     vuelve a repartir en lugar de dejarla desbordar.
   -------------------------------------------------------------------------- */
@keyframes pw-line-in {
  from {
    opacity: 0;
    translate: 0 var(--pw-line-shift);
  }

  to {
    opacity: 1;
    translate: 0 0;
  }
}

html.pw-fx .pw-hero-title .pw-line {
  display: block;
  /* El aire del recorte se devuelve abajo: ver el comentario. */
  padding-block: 0.08em;
  margin-block: -0.08em;
  overflow: hidden;
}

@media (prefers-reduced-motion: no-preference) {
  html.pw-fx .pw-hero-title .pw-line {
    animation: pw-line-in var(--pw-dur-3) var(--pw-ease-out) backwards;
    animation-delay: calc(var(--pw-line-i, 0) * var(--pw-line-step));
  }
}

/* El primer seguro. Por construcción es redundante —el estado invisible de
   §2 vive dentro de un `animation` que sólo existe en `no-preference`, así que
   con el movimiento reducido no hay nada que arreglar—, pero se escribe igual
   que en v6 · 2: si algún día alguien mueve ese `animation` fuera del media
   query, esta red impide que se quede un título en blanco. */
@media (prefers-reduced-motion: reduce) {
  html.pw-fx .pw-hero-title .pw-line {
    opacity: 1;
    translate: none;
    animation: none;
  }
}

/* Y el seguro de papel: en un artículo impreso no hay nada que dispare una
   animación de entrada, porque la impresión no ejecuta scripts. */
@media print {
  html.pw-fx .pw-hero-title .pw-line {
    opacity: 1;
    translate: none;
  }
}


/* --------------------------------------------------------------------------
   v7 · 3 · SPOTLIGHT
   --------------------------------------------------------------------------
   Una capa de luz tibia que sigue al puntero sobre las superficies grandes. Es
   la pieza más discutible del bloque, así que van las tres decisiones, incluida
   la que se descartó:

   · POR QUÉ UN DEGRADADO EN `background-image` Y NO UNA CAPA PSEUDO-ELEMENTAL.
     La receta de manual —un `::after` con `position: absolute`, `inset: 0`,
     `overflow: hidden` y un `radial-gradient` encima— se descarta aquí por dos
     razones concretas. La primera es el coste: un pseudo-elemento con
     `border-radius: inherit` es una caja nueva que el motor tiene que
     rasterizar, componer y volver a componer en cada movimiento del puntero,
     por encima de una caja que ya existe. La segunda, más importante, es que
     un `::after` no se puede recortar con el radio de la tarjeta si la tarjeta
     lo tiene: hace falta un segundo contenedor o un `clip-path`, y los dos
     son una fuente nueva de artefactsos en las esquinas. El degradado va
     DENTRO del fondo del elemento, así que lo recorta el propio `border-radius`
     que ya tenía, hereda el `overflow: hidden` que ya tenía la tarjeta con
     franja, y no añade ni una caja.

   · POR QUÉ `at` Y NO `background-position`. La forma habitual de escribir un
     gradiente que sigue al puntero es la pareja `background-image` +
     `background-position`. Con dos valores, el degradado es un dibujo de tamaño
     fijo que hay que colocar, y al colocarlo aparecen tres problemas: hay que
     declarar `background-repeat: no-repeat` o el dibujo se repite, hay que
     declarar `background-size` o no llena la caja, y si el puntero se acerca a
     un borde, el disco se recorta contra el rectángulo y aparece un canto
     recto. La forma larga del degradado —`ellipse farthest-corner at x y`— hace
     las tres cosas de golpe: el 100 % del degradado cae en la esquina más
     lejana de la caja, así que SIEMPRE se apaga antes o justo en el borde y no
     hay canto recto que recortar; y como el tamaño es relativo a la caja, el
     resplandor se adapta a lo que hay: uno grande en una sección, uno pequeño
     en una tarjeta de 16,5rem. La posición la escribe el JS en dos números.

   · POR QUÉ `color-mix` PARA LA TINTA. Los cuatro temas de §1 y v2 · 1
     declaran la tinta en hexadecimal, no en canales: no hay ningún
     `--pw-ink-rgb` en este fichero al que pedirle una opacidad con la barra
     invertida, y escribir el canal a mano sería inventar un token por tema y
     meter un color literal en un bloque que no los tiene. `color-mix` es la
     forma de bajar el alfa de un color que ya existe sin tocarlo, y por eso
     el pico del resplandor es `color-mix(in srgb, --pw-ink 9%, transparent)`.
     Nueve puntos de tinta es lo que se ve sin que el texto tenga que competir
     con el resplandor; más que eso y la superficie deja de leerse como papel.

   · LA DEGRADACIÓN. Si un motor no soporta `color-mix`, la declaración entera
     es inválida y se descarta: no hay resplandor, y la tarjeta es la tarjeta.
     Si el JS no escribe `--pw-fx-strength`, el `var(..., 0)` del degradado da
     0 % de tinta y el color mezclado es completamente transparente. Si el JS
     escribe una basura en vez de un número, el `calc()` es inválido y la
     declaración se cae. Los tres caminos de fallo llevan al mismo sitio: no
     hay resplandor. No existe ningún caso en que una superficie se quede a
     medio pintar, que es el único fallo que de verdad importaría.

   · Y no hay `transition`, y ésa es la decisión. Una transición de
     `background-image` interpolaría el degradado entero en cada fotograma, que
     es lo caro; y la alternativa transitiva —`background-position`— es la que
     acaba de quedar descartada por lo del canto recto. La entrada y la salida
     las hace el JS subiendo y bajando `--pw-fx-strength` en escalones de un
     10 % por fotograma, que para el ojo es un fundido y para el motor es un
     repintado de un degradado acotado por la caja. El puntero dispara muchos
     más eventos que fotogramas, así que el JS escribe en `requestAnimationFrame`
     y este fichero sólo pide que la propiedad se LEA, nunca que se anime.

   · `@media (hover: hover)`: en un dedo no hay puntero que seguir, y un
     resplandor que se queda posado en el último toque es una mancha muerta en
     la pantalla.
   -------------------------------------------------------------------------- */
@media (hover: hover) {
  html.pw-fx .pw-section,
  html.pw-fx .pw-card,
  html.pw-fx .pw-related-card {
    background-image: radial-gradient(
      ellipse farthest-corner at var(--pw-fx-x, 50%) var(--pw-fx-y, 50%),
      color-mix(
        in srgb,
        var(--pw-ink) calc(var(--pw-fx-strength, 0) * var(--pw-spot-alpha)),
        transparent
      ) 0%,
      transparent var(--pw-spot-falloff)
    );
  }
}


/* --------------------------------------------------------------------------
   v7 · 4 · BOTONES MAGNÉTICOS
   --------------------------------------------------------------------------
   El botón se desplaza un poco hacia el puntero mientras está encima. En una
   wiki esto se hace con una contención de 6px, que es `--pw-magnet-limit`:
   más que eso, un control de 1,9rem deja de leerse como un botón y empieza a
   leerse como un juguete, y lo que se busca es que el dedo y el cursor se
  Quaisen de sitio, no que el botón se escape. El recorte lo hace el JS; aquí
   sólo se declara el contrato.

   · `translate`, no `transform`, por el motivo de siempre: los `:active` de
     v6 · 3 y v6 · 6 —`scale(0.96)` en `.pw-seg`, `.pw-step` y
     `.pw-spectrum-chip`— y los hover de §7 son dueños de `transform`, y con
     `transform` también aquí el más específico se llevaría el resto del
     movimiento. Con `translate` el botón se desplaza Y se hunde a la vez, cada
     propiedad con su transición, sin que ninguna se pise.

   · Cada botón recibe su PROPIA lista de `transition` de vuelta, con las suyas
     delante, exactamente como en v6 · 2: una lista de transición es una lista y
     no un añadido, así que la de § v2 · 6 (`.pw-btn`), la de `.pw-btn-text` y
     la de `.pw-totop` —que lleva `box-shadow` en vez de `border-color`— se
     devuelven tal cual, con `translate` al final. No se unifican en un solo
     selector aunque los tres se muevan igual: unificarlas haría que `.pw-btn`
     ganase una transición de `box-shadow` que nunca tuvo y `.pw-totop` una de
     `border-color` que nunca tuvo, y las listas de este fichero no se ensanchan
     por sorpresa.

   · LA ASIMETRIA, que es lo bonito de esta pieza. El estado de reposo —que
     también es el estado de SOLTAR— lleva `--pw-dur-2` (320ms) y el de
     `--hover` lleva `--pw-dur-1` (180ms). Atraer es instantáneo, soltar es
     elástico: es el gesto de una goma, y sale de dos escalones que ya
     existían. Para cambiar la duración sólo del `translate` sin tocar la de las
     demás propiedades se declara `transition-duration` en el hover, que es
     180ms: exactamente lo que ya valían `background`, `color`,
     `border-color` y `box-shadow`, así que no les cambia nada. El coste de ser
     un atajo es que si algún día se añade a esa lista una propiedad de 820ms,
     esta regla también la bajaría a 180ms; es un trueque que se acepta
     consciente y por eso está escrito.

   · SOLTAR NO DEPENDE DEL SCRIPT. El estado de reposo es `translate: 0 0` en
     CSS, no `var(--pw-magnet-x) var(--pw-magnet-y)`. Si el puntero se va, el
     selector `:hover` deja de coincidir y el botón vuelve al centro por sí
     solo, aunque el JS no haya limpiado nada. Un `pointercancel`, una
     ventana que pierde el foco, un dedo que se lleva el ratón: en los tres
     casos el botón se recentra. Si el estado de reposo leyera los tokens, el
     botón se quedaría desplazado para siempre en cuanto un solo evento se
     perdiera, y eso no lo arregla ninguna transición.

   · `.pw-seg` y `.pw-step` quedan fuera a propósito, y no por el `transform`:
     porque son un grupo. Dos controles contiguos atrayéndose el uno al
     puntero se empujan y el grupo deja de leerse como un solo control.

   · Sin `will-change`. Promover un botón de 1,9rem de forma permanente para
     ganar dos píxeles de viaje es memoria de compositor comprada para nada,
     y v6 · 2 ya firmó la paz con las capas que no se desmontan solas.

   · Con el movimiento reducido no hace falta nada: §15 colapsa las duraciones
     a 0,01ms, así que el botón sigue desplazándose —el ESTADO sigue ahí, que
     es lo que el puntero ha pedido— y sólo no hay viaje. Es la misma
     distinción que v6 · 3 hacía entre quitar el efecto y quitar el estado.
   -------------------------------------------------------------------------- */
@media (hover: hover) {
  html.pw-fx .pw-btn {
    translate: 0 0;
    transition: background var(--pw-transition), color var(--pw-transition),
      border-color var(--pw-transition),
      translate var(--pw-dur-2) var(--pw-ease-out);
  }

  html.pw-fx .pw-btn-text {
    translate: 0 0;
    transition: background var(--pw-transition), color var(--pw-transition),
      border-color var(--pw-transition),
      translate var(--pw-dur-2) var(--pw-ease-out);
  }

  html.pw-fx .pw-totop {
    translate: 0 0;
    transition: background var(--pw-transition), color var(--pw-transition),
      box-shadow var(--pw-transition),
      translate var(--pw-dur-2) var(--pw-ease-out);
  }

  html.pw-fx .pw-btn:hover,
  html.pw-fx .pw-btn-text:hover,
  html.pw-fx .pw-totop:hover {
    translate: var(--pw-magnet-x, 0px) var(--pw-magnet-y, 0px);
    transition-duration: var(--pw-dur-1);
  }
}


/* --------------------------------------------------------------------------
   v7 · 5 · CONTADORES NUMÉRICOS
   --------------------------------------------------------------------------
   Aquí la cifra es el dato, y el dato aparecía de golpe. Tres trabajos, y cada
   uno en su sitio:

   1 · Decir que la cifra se está calculando. Una entrada con `opacity` y
     `translate` de 0,35em, y `--pw-dur-4` —400ms— porque es la pieza más
     solitaria del sitio y es el único bloque que puede permitirse el escalón
     largo sin que se note como un retraso. Antes eran 820ms, y a esa duración
     el ojo ya había leído el número final antes de que terminara de formarse;
     el tope de 500ms que dan las guías de movimiento del sitio lo corrige solo.
     El desplazamiento va en `translate` y no en `transform`
     por el mismo motivo que en §2, y en un motor que no conhezca la propiedad
     `translate` la declaración se descarta dentro del `@keyframes`: la cifra
     aparece sin subir y no se rompe nada.

   2 · Que el texto no se mueva mientras los dígitos cambian. Aquí está la
     trampa de verdad de un contador, y no es el número: es la caja. Una
     columna de cifras de ancho proporcional hace que la línea que tiene
     debajo dé un tirón en cada dígito que cambia, y en una rejilla de
     `auto-fill` eso se propaga a las tarjetas vecinas. `tabular-nums` —que
     `--pw-domain-count` y `--pw-btn-badge` ya llevan— es lo que lo evita, y se
     declara aquí para las cifras que aún no lo tenían. No es un cambio
     tipográfico: no altera `font-size`, ni el interlineado, ni la medida, ni el
     grosor de la letra; sólo dice que los dígitos ocupen la misma caja todos.

   3 ·Dibujar el avance. Y aquí está la restricción que pedía comentario:
     una barra de progreso NO se anima con `width`. Un `width` en un
     `::after` dentro de un flujo de texto obliga a recalcular la maquetación
     del párrafo en cada fotograma, y en una portada con veinte tarjetas eso es
     veinte reflows por fotograma mientras cuentan. La misma barra con
     `transform: scaleX()` la resuelve el compositor sin tocar la caja de
     nadie, y el resultado en pantalla es idéntico. Se dibuja sólo en
     `.pw-domain-count`, que es el único contador del sitio con una caja
     verificada y propia —`display: inline-block` y `min-width: 2ch`— donde un
     filete debajo cae bien; en las cifras de la ficha de artículo, que viven
     en celdas de tabla, poner un `::after` exigiría forzar `inline-block` y
     abrir la caja de la celda, y una celda que se ensancha es un artículo que
     baila. Las demás cifras reciben la entrada y las cifras tabulares, que es
     todo lo que se les puede dar sin riesgo.

   · `--pw-count` es el avance y va del 0 al 1. El filete usa
     `transform: scaleX(var(--pw-count))` con origen a la izquierda, con lo
     cual el `transform` de la barra es suyo y nadie más lo disputa. La
     propiedad está registrada en §1 con `@property`, de modo que el CSS
     también podría interpolarla él solo; aquí la lleva el JS, que es quien
     sabe cuándo empieza y cuándo termina de contar.

   · `.pw-btn-badge` tiene su propia entrada, con `--pw-ease-spring` y escala
     de 0,5. Es la única cifra del sitio que no es texto en una línea sino una
     píldora en una esquina, y una píldora que aparece de 1 a 1 se lee como una
     etiqueta estática mientras sube con rebote se lee como una respuesta a lo
     que acabas de hacer. La curva con sobreimpulso, que en v6 · 1 estaba
     reservada a la marca de la cabecera, se abre un sitio más: los dos objetos
     del sitio donde un rebote de un píxel se lee como confirmación. Follows
     el mismo `backwards` que §2, para no dejar capa puesta.

   · El filete sólo existe con `is-counted`, no siempre: antes de que la cifra
     se calcule no hay un `::after` en el DOM, y un filete de 0 px en cada
     contador de cada tarjeta es trabajo gratis para el motor.
   -------------------------------------------------------------------------- */
@keyframes pw-count-in {
  from {
    opacity: 0;
    translate: 0 0.35em;
    scale: 0.94;
  }

  to {
    opacity: 1;
    translate: 0 0;
    scale: 1;
  }
}

@keyframes pw-count-pop {
  from {
    opacity: 0;
    scale: 0.5;
  }

  to {
    opacity: 1;
    scale: 1;
  }
}

html.pw-fx [data-pw-count] {
  font-variant-numeric: tabular-nums;
}

/* Sólo la caja que se ha verificado. Ver el tercer punto del comentario. */
html.pw-fx .pw-domain-count.is-counted {
  position: relative;
}

html.pw-fx .pw-domain-count.is-counted::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: -0.2em;
  left: 0;
  height: 1px;
  background: var(--pw-border-strong);
  transform: scaleX(var(--pw-count, 0));
  transform-origin: 0 50%;
}

@media (prefers-reduced-motion: no-preference) {
  html.pw-fx [data-pw-count].is-counted {
    animation: pw-count-in var(--pw-dur-4) var(--pw-ease-out) backwards;
  }

  html.pw-fx .pw-btn-badge[data-pw-count].is-counted {
    animation: pw-count-pop var(--pw-dur-3) var(--pw-ease-spring) backwards;
  }
}

@media (prefers-reduced-motion: reduce) {
  html.pw-fx [data-pw-count].is-counted {
    opacity: 1;
    translate: none;
    scale: none;
    animation: none;
  }

  html.pw-fx .pw-domain-count.is-counted::after {
    transform: scaleX(1);
  }
}

@media print {
  html.pw-fx [data-pw-count].is-counted {
    opacity: 1;
    translate: none;
    scale: none;
  }

  /* En papel la cuenta no la ejecuta nadie, así que el filete se imprime
     entero: una cifra con un subrayado a medio hacer parece un error de
     maquetación, no un avance. */
  html.pw-fx .pw-domain-count.is-counted::after {
    transform: scaleX(1);
  }
}


/* --------------------------------------------------------------------------
   v7 · 6 · CABECERA COMPACTA
   --------------------------------------------------------------------------
   La cabecera se encoge al bajar por el artículo y se abre al subir. Es la
   única pieza del bloque que toca una propiedad de maquetación, y por eso lleva
   el comentario más largo de los ocho.

   · QUÉ SE ENCOGE. Sólo la `min-height`, de 3,5 a 2,75 rem. Y el resultado es
     más pequeño de lo que parece porque los 3,5 rem de §3 no eran la altura
     real: la fila exige unos 3,05 rem —el buscador con su `padding` y sus
     bordes es lo más alto, y los botones de 1,9 rem van justo detrás— así que
     en la práctica la cabecera bajaba de 3,5 a unos 3,05. La v7 se queda con
     esa holgura y con nada más. Pedir más obligaría a recortar el `padding`, y
     el `padding` no se toca: §14 lo fija a `var(--pw-space-2)` en móvil a
     propósito, y pisarlo sería reescribir una regla anterior para ganar ocho
     píxeles.

   · POR QUÉ NO SE TRANSICIONA LA ALTURA, Y QUÉ PASA EN SU LUGAR. Una
     transición de `min-height` o de `padding` son 22 reflows del documento en
     320ms: la cabecera es el primer bloque en flujo, así que cada fotograma
     recalcula la altura de todo lo que hay debajo, y en un artículo largo eso
     es el artículo entero. Además el motor tiene un anclaje de scroll que
     intentaría compensar el salto y pelearía con el lector, con el resultado
     conocido de un texto que se mueve solo. Aquí la altura cambia de golpe,
     en un fotograma, y lo único que se anima es lo de pintado:

         border-color   --pw-border  →  --pw-border-strong
         box-shadow     ninguno     →  --pw-shadow-sm

     Son las dos señales de que la cabecera se ha convertido en una barra que
     flota sobre el texto, y las dos las resuelve el pintor sin tocar la
     maquetación. El visitante ve encogerse la cabecera Y la ve crawler a una
     barra; el precio es un reflow único, en el instante en que se cruza el
     umbral, y un reflow único no se nota. La holgura que se recorta es
     justamente la que existe para eso.

   · EL SALTO DE LOS ANCLAJES, QUE SE ACEPTA. `.pw-section` tiene
     `scroll-margin-top: calc(var(--pw-header-h) + var(--pw-space-5))` y
     `.pw-toc-float` se ancla a `calc(var(--pw-header-h) + var(--pw-space-4))`,
     y los dos leen el token de §1, no el de esta v7. Con la cabecera
     condensada, esos dos valores sobran ya 0,75rem: un anclaje baja un poco
     más abajo de lo necesario. Es la dirección SEGURA del error —nunca se
     tapa un título bajo la barra— y no se puede arreglar sin mentira: una
     regla que reduzca el `scroll-margin-top` mientras la cabecera esté
     condensada pondría un anclaje debajo de la cabecera en cuanto ésta se
     abriera otra vez, que es el fallo grave. Se deja el margen generoso y se
     dice aquí.

   · POR QUÉ NO HAY `prefers-reduced-motion` EN ESTA PIEZA. Porque el estado
     compacto no esconde nada: es un cambio de medida, y con el movimiento
     reducido §15 lo deja en un fotograma, que es exactamente lo que se quiere
     en una cabecera. No hay ningún estado invisible que asegurar aquí, y por
     eso esta pieza no trae los dos seguros que sí traen §2, §5 y §7.

   · Y `z-index: var(--pw-z-header)` reescribe el 30 de §3 sin cambiarlo. No
     hace falta para nada: lo pone para que el número suelto no vuelva a
     aparecer en un grep. La cabecera condensada es, de las tres piezas
     superpuestas del sitio, la única cuya caja cambia de tamaño, y si algún día
     alguna otra se le monta encima, al menos una de las dos tendrá su capa
     dicha con el nombre de la escalera.
   -------------------------------------------------------------------------- */
html.pw-fx .pw-header {
  z-index: var(--pw-z-header);
  transition: border-color var(--pw-transition),
    box-shadow var(--pw-dur-2) var(--pw-ease-out);
}

html.pw-fx .pw-header.is-condensed {
  min-height: var(--pw-header-h-cond);
  border-bottom-color: var(--pw-border-strong);
  box-shadow: var(--pw-shadow-sm);
}


/* --------------------------------------------------------------------------
   v7 · 7 · INDICADOR DESLIZANTE DEL ÍNDICE
   --------------------------------------------------------------------------
   El scrollspy ya encendía el filete del enlace activo de golpe, con
   `border-left-color` y un lavado (§ v2 · 3 y v3.3 · 5). Lo que falta es el
   trayecto: cuando el lector pasa del apartado 3 al 6, el filete debería
   bajar, no teleportarse. Este es el único indicador del sitio, así que la
   implementación tiene que ser barata de verdad.

   · UNA BARRA DE 2px POR 1,5em, Y POR QUÉ 1,5em. Los enlaces del índice llevan
     `font-size: var(--pw-fs-sm)` y `line-height: 1.5`, así que la caja de su
     línea mide exactamente 1,5em de alto. Escribir `1.5em` en lugar de un
     píxel medido a ojo no es una chapuza: hace que el indicador se ajuste solo
     si algún día cambia el `line-height` del enlace, y hace imposible que la
     barra y la fila midan cosas distintas. Mismo criterio que el de §1: la
     caja se deriva del token, no se vuelve a escribir.

   · VA ENCIMA DEL FILETE QUE YA EXISTE, A PROPÓSITO. El enlace activo ya
     pinta un `border-left` de 2px de tinta en su propio borde izquierdo, que
     cae en el mismo sitio que `left: 0` de la lista. Los dos se superponen
     exactamente, así que en reposo no se ve un filete doble sino UNO, y lo
     único nuevo es que se le ve llegar. No se quita el filete viejo —quedarse
     con él es lo que hace que este indicador no dependa de que el scrollspy
     funcione: si el JS no mueve nada, el enlace activo sigue marcado
     exactamente como antes.

   · `--pw-toc-y` lo mide el JS como el `offsetTop` del enlace activo menos el
     de la lista, y lo escribe en la lista o en el propio indicador. Se mueve
     con `translate` y con `--pw-dur-2`, que es lo que tarda un dedo en recorrer
     una fila de índice; y como es sólo `translate`, el hilo de scroll puede
     escribirlo en cada `requestAnimationFrame` sin que el motor recalcule
     nada. Cuesta un `position: relative` en las dos listas, y no hay más.

   · Y la entrada se hace con el `pw-fade-in` de v6 · 6, que ya existe y hace
     exactamente esto: declarar otro igual sería tener dos animaciones de un
     fotograma en el fichero. Con `backwards` como `fill-mode`, el indicador
     se apaga solo al terminar y no se queda promovido para siempre.

   · EL CONTRATO CON EL SCRIPT, QUE HAY QUE DEJAR ESCRITO: el indicador se
     inserta YA MEDIDO. Si se insertara antes de conocer la posición, se vería
     un latigazo de 320ms en el primer enlace del índice. El estado invisible
     no es un `opacity: 0` en la base —eso lo dejaría invisible para siempre
     si la animación no llegara a correr—, sino el `from` de la animación, que
     sólo existe dentro de `no-preference`.

   · El indicador solo se ve ENTRE 1024px y 1199px, que es exactamente donde
     `.pw-toc-float` pasa a `display: block` (§ v3.3 · 4). Fuera de esa franja
     el flotante está en `display: none`: por debajo porque no hay gutter que
     respetar y por encima porque ya hay sitio para el índice de la barra
     lateral. No se añade ningún punto de corte nuevo: los que hay bastan.

   · OJO al depurar esto: que `--pw-toc-y` se quede en `0px` fuera de la franja
     NO es un fallo. El script lo mide y lo publica solo cuando el flotante
     tiene caja, y por eso `updateIndicator()` sale antes de escribir nada si
     `getClientRects()` está vacío: un `0px` sobre un elemento que no se
     renderiza no dice nada y solo invita a perseguir un bug inexistente.
     Para verlo, abre la ventana entre 1024 y 1199: ahí el indicador mide
     2×1.5em y acompaña al enlace activo.
   -------------------------------------------------------------------------- */
html.pw-fx .pw-toc-list,
html.pw-fx .pw-toc-float-list {
  position: relative;
}

html.pw-fx .pw-toc-indicator {
  position: absolute;
  top: 0;
  left: 0;
  /* Inside del componente, no entre los de la escalera: el índice ya vive en
     la capa 20 y un `z-index: 1` local basta para pasar por delante de sus
     propios enlaces. */
  z-index: 1;
  width: 2px;
  height: 1.5em;
  border-radius: var(--pw-radius-sm);
  background: var(--pw-ink);
  translate: 0 var(--pw-toc-y, 0px);
  pointer-events: none;
  transition: translate var(--pw-dur-2) var(--pw-ease-out);
}

@media (prefers-reduced-motion: no-preference) {
  html.pw-fx .pw-toc-indicator {
    animation: pw-fade-in var(--pw-dur-2) var(--pw-ease-out) backwards;
  }
}

@media (prefers-reduced-motion: reduce) {
  html.pw-fx .pw-toc-indicator {
    opacity: 1;
    translate: 0 var(--pw-toc-y, 0px);
    animation: none;
  }
}

@media print {
  html.pw-fx .pw-toc-indicator {
    opacity: 1;
    translate: 0 var(--pw-toc-y, 0px);
  }
}


/* --------------------------------------------------------------------------
   v7 · 8 · VIEW TRANSITIONS
   --------------------------------------------------------------------------
   La única pieza del bloque que NO va detrás de `html.pw-fx`, y hay que decir
   por qué. Las otras siete son un lujo: si el script no las monta, no pasa
   nada. El conmutador de tema, en cambio, es un control del artículo, y tiene
   que funcionar con `interactions.js` cargado, sin cargar, o con el movimiento
   reducido. Su puerta no es una clase sino dos condiciones POSITIVAS —que el
   motor sepa de view transitions y que el sistema permita el movimiento— y las
   dos se pueden comprobar antes de escribir una regla.

   · Lo que se declara aquí es sólo la CAPA, no el disparo. Quien lo dispara es
     `document.startViewTransition()` en el JS. Estas reglas no coinciden con
     nada mientras nadie llame a eso, y no coinciden con nada en un motor que no
     tenga la función: un pseudo-elemento desconocido se descarta en el
     análisis, sin error y sin coste. Es la misma situación que una clase que
     no aparece nunca, y por eso aquí no hace falta ni un `@supports` de
     relleno: el `@supports` que sí hay, más abajo, es el que impide escribir
     duración de animación donde el motor no la entiende.

   · Y un detalle de `file://` que conviene tener escrito, porque este sitio se
     abre desde el disco. La forma de navegación —`@view-transition {
     navigation: auto }`— NO sirve aquí: hace falta comparar el origen y el
     destino como same-origin, y dos `file://` no lo son para esa comparación
     en la mayoría de los motores. La forma de documento único sí funciona
     desde `file://`, así que el disparo tiene que ser `startViewTransition()`
     y nada más. Por eso aquí no hay ninguna regla de navegación: sería código
     que no puede llegar a ejecutarse.

   · POR QUÉ UN FUNDIDO Y NO UN BARRIDO. Porque el conmutador de tema no mueve
     nada: cambia el TONO de la misma página, en el mismo sitio, con el mismo
     texto en el mismo sitio. Un barrido direccional afirma otra cosa —que el
     contenido nuevo viene de la derecha, que hay un antes y un después
     situados en un orden— y aquí no la hay. Y el coste de mentir no es
     abstracto: la instantánea de `root` es la ventana entera, así que un
     barrido arrastraría por la pantalla el texto que el lector tiene delante
     de los ojos, en la dirección que elija la implementación. Un fundido
     dice lo único que de verdad ha pasado.

   · `--pw-dur-2` y no `--pw-dur-1`: a 180ms un fundido del viewport entero se
     lee como un parpadeo, que es justo el defecto que se quiere evitar. 320ms
     es lo que tarda una página de papel en cambiar de luz.

   · `::view-transition-group(root)` es el pseudo que se ocupa de interpolar
     tamaño y posición de la instantánea. Sólo se le toca la duración; su
     curva, su origen y su nombre los deja el motor, que es donde están bien
     puestos. Y no se declara ningún `view-transition-name`: la raíz ya se
     llama `root` sola.

   · ¿Y los dos seguros de las otras siete piezas? No hacen falta aquí y por
     eso no se escriben, en vez de escribir dos bloques vacíos: el estado
     invisible de un view transition no lo tiene ninguna regla de este
     fichero, lo tiene el motor, y el motor lo suelta al acabar la transición
     pase lo que pase. Con el movimiento reducido no hay transición —el
     `@media` de arriba no existe— y en papel no hay conmutador que pulsar.
   -------------------------------------------------------------------------- */
@keyframes pw-vt-out {
  to {
    opacity: 0;
  }
}

@keyframes pw-vt-in {
  from {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: no-preference) {
  @supports (view-transition-name: root) {
    ::view-transition-old(root) {
      animation: pw-vt-out var(--pw-dur-2) var(--pw-ease-out) both;
    }

    ::view-transition-new(root) {
      animation: pw-vt-in var(--pw-dur-2) var(--pw-ease-out) both;
    }

    ::view-transition-group(root) {
      animation-duration: var(--pw-dur-2);
    }
  }
}


/* --------------------------------------------------------------------------
   v6 · 7 · AJUSTES DE PREMIO, RECALIBRADOS SOBRE MEDIDA
   --------------------------------------------------------------------------
   Este bloque no trae efectos nuevos: los cuatro ajustes que sí los traen están
   repartidos por arriba, en el sitio donde toca. Aquí van las tres cosas que
   no tenían un sitio natural y que se agrupan porque tienen una sola razón de
   ser —el mismo criterio— y porque escribirlas juntas deja escrito por qué no
   se hizo lo que se podría haber hecho.

   1 · `text-wrap` en los títulos que faltaban.
   2 · Un desplazamiento de dos píxeles en los enlaces del índice.
   3 · Continuidad de la cabecera entre páginas, con transiciones de vista.
   4 · Un `@property` que se usa en un solo sitio y sólo una vez.

   --------------------------------------------------------------------------
   1 · `text-wrap` — LA RELACIÓN COSTE/ BENEFICIO MÁS ALTA DEL FICHERO
   --------------------------------------------------------------------------
   De todo lo que se ha añadido al sitio, nada se percibe tanto como esto: no
   hace falta ninguna biblioteca, ni una línea de JavaScript, ni una decisión
   sobre el navegador. La investigación de comunidad que motive estos ajustes
   fue bastante tajante en lo contrario de lo que se suele decir —la señal de
   calidad percibida es la tipografía, el espacio en blanco y los recursos, no
   el movimiento—, y esto es la versión más barata de aplicar esa conclusión.

   `balance` reparte las líneas para que ninguna quede más corta que las demás
   cuando el título cabe en un bloque corto, que es el caso de los `h2` de
   sección. `pretty` es lo que ya se usa para los párrafos en quince sitios
   desde §1. Aquí sólo se completa lo que faltaba: `.pw-h2`, `.pw-h3` y
   `.pw-card-title`. Se añaden al final y con el selector de clase tal cual, así
   que mandan sobre las reglas de §1 por orden de cascada y no las reescriben.

   No se toca ningún otro título: los títulos en mayúsculas del índice flotante
   son de cuatro palabras y forzarles el balance es tan invisible como inútil.
   -------------------------------------------------------------------------- */
.pw-h2,
.pw-h3,
.pw-card-title {
  text-wrap: balance;
}


/* --------------------------------------------------------------------------
   2 · DOS PÍXELES EN EL ÍNDICE
   --------------------------------------------------------------------------
   El hover de `.pw-toc-float-link` ya encendía la tinta y el filete de § v3 · 3,
   pero el enlace no se movía: la señal era de color y la de posición, que es la
   que más rápido se lee, no estaba. Dos píxeles de sangría es todo lo que hace
   falta. Con once filas seguidas, más que eso se lee como una lista que se
   desliza, y el índice es un instrumento de navegación, no un carrusel.

   `--pw-dur-1` y no `--pw-dur-2`: éste es el mismo gesto que el que ya hace la
   fila activa de la barra lateral, y si la fila activa tarda 320 ms en moverse,
   el cursor la tiene que alcanzar.

   El desplazamiento va en `translate` y no en `padding-left` por el mismo
   motivo que el revelado (§ v6 · 2): `padding` cambia el flujo y con ella la
   posición de todo lo que viene detrás, en un elemento que además es `display:
   block` dentro de una lista. `translate` no toca el flujo, así que el
   desplazamiento no empuja nada y no puede provocar reflow al pasar el cursor. */
.pw-toc-float-link {
  transition: color var(--pw-transition), background var(--pw-transition),
    border-color var(--pw-transition), translate var(--pw-transition);
}

.pw-toc-float-link:hover,
.pw-toc-float-link:focus-visible {
  translate: 2px 0;
}


/* --------------------------------------------------------------------------
   3 · LA CABECERA NO CAMBIA DE SITIO ENTRE PÁGINAS
   --------------------------------------------------------------------------
   La cabecera es `position: sticky` y es el mismo elemento en las cinco
   páginas: la marca arriba a la izquierda, el buscador y los conmutadores a la
   derecha. Al navegar, esa barra se desvanecía y volvía a aparecer con el
   resto. Con una transición de vista entre documentos, la cabecera se queda
   donde está y sólo se funde el contenido: el lector atraviesa el sitio sin
   que el marco desaparezca.

   `navigation: auto` es la regla que enciende la transición para las navegaciones
   del propio documento —las anclas de la wiki, que no tienen ruta—. Es
   literalmente inerte donde no se soporta: no hay nada que detectar y no hay
   error posible, que es la razón por la que aquí no hace falta `@supports` al
   contrario que en el scroll-driven de § v2 · 7.

   El conmutador va dentro de `prefers-reduced-motion: no-preference` por lo
   mismo que la transición de `root` de arriba: con el movimiento reducido en el
   sistema, la navegación tiene que ser inmediata. Y en papel no hay navegación
   que fundir, así que no hace falta un seguro de impresión —no hay nada que
   pueda quedar a medias.

   `view-transition-name` va en la cabecera, no en el contenido. El contenido no
   se nombra porque `root` ya se llama `root` sola y nombrarlo duplicaría la
   referencia, que es el error que hace que el motor descarte la transición
   entera. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }

  .pw-header {
    view-transition-name: pw-header;
  }
}


/* --------------------------------------------------------------------------
   4 · `@property`: UN USARIO, UN SITIO, UNA VEZ
   --------------------------------------------------------------------------
   `@property` es Baseline desde julio de 2024 (Chrome 85, Firefox 128, Safari
   16.4), así que registrar una propiedad no necesita `@supports`: donde no se
   soporta, la declaración simplemente no existe y la variable sigue siendo un
   texto corriente. Se declara aquí sin condición por eso.

   Lo que sigue es un barrido de luz sobre el cuadrado de la marca, UNA vez al
   cargar, sin bucle. Es el único sitio del fichero donde se usa esta
   herramienta, y que sea el único es deliberado: la propuesta
   original era poner un borde luminoso en las tarjetas destacadas y un anillo
   cónico en el espectro, y no se ha hecho a propósito. En la misma
   investigación que motivó este bloque, el resplandor por todas partes aparece
   entre las señales que las comunidades de diseño señalan como marca de
   generación automática; ponerlo en veintiuna tarjetas sería exactamente el
   problema que se quería evitar. Un cuadrado de 28 píxeles, al cargar y sin
   volver, no se lee como automático: se lee como un detalle.

   El coste hay que decirlo: animar el ángulo de un `conic-gradient` no se
   compite en el compositor, porque un degradado no es una propiedad
   transformable —MDN avisa de esto mismo para las propiedades personalizadas
   registradas—, así que la comprobación de estilo corre en el hilo principal
   durante el segundo y medio que dura el barrido. En un elemento de 28 píxeles y
   una sola vez, es indetectable; en una lista de veinte elementos con esto
   repetido en cada hover, no lo sería. Por eso no se generaliza. */
@property --pw-sheen {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

/* El valor sin registrar, que es lo que leen los navegadores que no conocen
   `@property`: 0deg, o sea el conic-gradient se ve como un color plano. La
   declaración de arriba sólo añade la interpolación. */
.pw-brand-mark {
  --pw-sheen: 0deg;
  position: relative;
  isolation: isolate;
}

.pw-brand-mark::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: conic-gradient(
    from var(--pw-sheen) at 50% 50%,
    transparent 0deg,
    rgb(255 255 255 / 0.34) 40deg,
    transparent 90deg,
    transparent 360deg
  );
  opacity: 0;
  pointer-events: none;
  mix-blend-mode: soft-light;
}

@media (prefers-reduced-motion: no-preference) {
  .pw-brand-mark::after {
    animation: pw-sheen-sweep 900ms var(--pw-ease-out) both;
  }
}

@keyframes pw-sheen-sweep {
  0% {
    --pw-sheen: -90deg;
    opacity: 0;
  }
  35% {
    opacity: 1;
  }
  100% {
    --pw-sheen: 270deg;
    opacity: 0;
  }
}


/* --------------------------------------------------------------------------
   v8 · MOVIMIENTO QUE REACCIONA A LA INTENCIÓN
   --------------------------------------------------------------------------
   v6 y v7 responden a DÓNDE está el lector y a si un bloque ha entrado en
   pantalla. Eso es estado, y el estado se resuelve con una transición. Lo que
   sigue responde a cuatro cosas que aquí no había:

   1. La VELOCIDAD. La barra de progreso sabía en qué punto del documento
      estabas; no distinguía leer despacio de bajar a toda leche.
   2. La INTENCIÓN sobre la rejilla. Al filtrar por letra, las tarjetas que
      sobrevivían no se desplazaban: se les borraba el hueco de al lado y
      aparecían ya en su sitio. Un salto, no un movimiento.
   3. EL VIAJE de la tarjeta a la ficha. Al abrir una ficha, la tarjeta que se
      pulsó no se convertía en el título: lo sustituía.
   4. LA LUZ. Cambiar de tema era un repintado instantáneo de toda la página.

   Los cuatro son estructurales —no LUCEN un estado que ya existía, lo producen— y
   los cuatro respetan las dos redes que el sitio ya tenía:
   `prefers-reduced-motion` para lo que no depende de JS, y tokens `--pw-*` para
   las medidas.

   `@property` se usa aquí, igual que en el barrido de la marca y por el mismo
   motivo: `--pw-sheen` existe para que el ángulo del `conic-gradient` se pueda
   interpolar. La regla de casa sigue en pie —registrar una propiedad cuesta
   comprobación de estilo en el hilo principal—, pero allí animaba un cuadrado de
   28 px una sola vez. Esto interpola un número en la barra de progreso, que se
   escribe en un solo elemento y no provoca layout, así que el coste es distinto
   del que el bloque anterior documenta. Sigue sin generalizarse.
   -------------------------------------------------------------------------- */


/* -------------------------------------------------------------------------- */
/* v8 · 1 · LA BARRA RESPONDE A LA VELOCIDAD                                    */
/* -------------------------------------------------------------------------- */

/* `--pw-progress-v` vale de 0 a 1: 0 es quieto, 1 es bajar a toda leche.
   `inherits: false` a propósito —lo escribe un elemento y lo lee ese mismo— y
   `initial-value: 0` para que la barra tenga su grosor normal aunque el JS no
   llegue a escribir nunca (reduced-motion, motor sin WAAPI, `file://`). */
@property --pw-progress-v {
  syntax: '<number>';
  inherits: false;
  initial-value: 0;
}

/* El escalado va en `.pw-progress`, el CONTENEDOR, y no en `.pw-progress-bar`.
   Esa separación es la decisión que permite esta feature: la barra ya tiene su
   `transform` en manos de la `animation-timeline` del scroll en los motores que
   la tienen, y en manos de `style.transform` en `paintScroll` en los que no.
   Ninguna de las dos rutas deja hueco, y meter aquí el escalado habría hecho
   que esta regla compitiese por la misma propiedad —y perdiera, o ganara y
   dejara la barra quieta—. En el padre no hay competencia: `transform` del padre
   y del hijo se componen.

   `transform-origin: 50% 0` porque la barra está en `top: 0`: si creciera desde
   el centro, la mitad del grosor quedaría por encima de la ventana, invisible, y
   el efecto sería más pequeño del que parece en el CSS. Desde arriba crece hacia
   abajo, que es donde está el texto.

   Sin `will-change`: es una barra de 3 px que solo se mueve durante el scroll, y
   el bloque de la marca ya explica que las capas promovidas para siempre son un
   coste, no una optimización. */
.pw-progress {
  --pw-progress-v: 0;
  transform-origin: 50% 0;
  transform: scaleY(calc(1 + var(--pw-progress-v) * 1.2));
}

/* La red de seguridad. El JS ya respeta el movimiento reducido —no escribe la
   variable—, pero una regla que depende de una custom property y hace una
   transición visual tiene que seguir siendo correcta cuando esa variable no
   llega: con `prefers-reduced-motion` la barra se queda en su grosor normal,
   leyéndola o bajando a dos manos. */
@media (prefers-reduced-motion: reduce) {
  .pw-progress {
    transform: none;
  }
}


/* -------------------------------------------------------------------------- */
/* v8 · 2 · EL TEMA CAMBIA DE LUZ, NO DE PANTALLAZO                              */
/* -------------------------------------------------------------------------- */

/* `PW.interactions.withViewTransition` (§8 de `js/interactions.js`) ya existía
   publicado y sin adoptionar. Aquí lo adopta el cambio de tema, que es lo que su
   comentario llevaba tiempo pidiendo.

   Lo que hay que entender es que los pseudo-elementos de una transición de
   vista se seleccionan desde `root`, pero NO se pueden condicionar a nada del
   documento: `::view-transition-old(root)` no sabe por qué se está tomando la
   instantánea. Sin una condición se aplicaría también a la navegación entre
   páginas, donde ya hay un fundido propio, y este bloque lo pisaría.

   De ahí la clase. `:root.pw-vt-theme::view-transition-*` es MÁS específica que
   la regla suelta de v6 —que se sigue aplicando en la navegación— y solo existe
   mientras el cambio de tema está en curso. `js/features.js` §3 pone la clase
   antes de llamar al gancho y la quita cuando la transición acaba; si no hay
   transición —reduced-motion, motor sin soporte, pestaña oculta— la quita
   enseguida y no queda nada colgando.

   `pw-vt-theme-in` es un cross-fade con un 1.5% de escala. El cross-fade solo ya
   está, y con colores que cambian de golpe se lee como un fogonazo; el épsilon de
   escala da al ojo algo a lo que agarrarse mientras los tonos se cruzan. Va en
   `::view-transition-new`, o sea sobre la instantánea nueva, y con
   `transform-origin` al centro, que es como el UA centra las instantáneas de
   `root`. */
:root.pw-vt-theme::view-transition-old(root) {
  animation: pw-vt-theme-out var(--pw-dur-2) var(--pw-ease-inout) both;
}

:root.pw-vt-theme::view-transition-new(root) {
  animation: pw-vt-theme-in var(--pw-dur-2) var(--pw-ease-out) both;
}

@keyframes pw-vt-theme-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

@keyframes pw-vt-theme-in {
  from {
    opacity: 0;
    transform: scale(1.015);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}


/* -------------------------------------------------------------------------- */
/* v8 · 3 · LA TARJETA SE CONVIERTE EN LA FICHA                                */
/* -------------------------------------------------------------------------- */

/* La navegación entre páginas ya tenía su transición de v6 —la cabecera se
   queda quieta y solo se funde el contenido—, pero el contenido no se nombraba,
   y el comentario que hay en v6 explica muy bien por qué: `root` ya se llama
   `root` sola y nombrarlo otra vez duplica la referencia, que es el error que
   hace que el motor deseche la transición entera.

   Lo que se nombra aquí es OTRO elemento, con OTRO nombre, y sólo cuando hace
   falta: la tarjeta que el lector ha pulsado. Al abrir una ficha, ese `a.pw-card`
   y el `h1.pw-title` de destino comparten el nombre `pw-vt-open`, y el motor
   entiende que no son dos cosas que desaparecen sino una que cambia de sitio y
   de tamaño. Es el movimiento más explicativo de los tres porque el lector no
   tiene que interpretar nada: acaba de pulsar una tarjeta y la tarjeta está
   ahora en el título.

   La clase la pone `js/features.js` §7 en el origen, en el `click`, y §7 también
   la pone en el `h1` de la ficha al cargar. Va dentro de `no-preference` porque
   `@view-transition` —la regla que enciende esto para la navegación— ya lo
   está, y donde esa regla no existe el nombre no haría nada: sería un
   `view-transition-name` inerte, no un fallo, pero tampoco un motivo para
   gastarlo.

   En Firefox esto no ocurre, y no es una degradación que arreglar: la
   transición entre documentos no está ahí. El resultado es la navegación normal,
   con el fundido de siempre donde exista. El nombre se limpia en `pageshow` para
   que volver atrás con la caché no deje la tarjeta con el nombre puesto. */
@media (prefers-reduced-motion: no-preference) {
  .pw-vt-open {
    view-transition-name: pw-vt-open;
  }
}

