/* Stella Nova — layout y componentes.
 *
 * "Página de papel": una hoja cálida (la-sn-paper) flota sobre el campo
 * gris ahuesado (el atelier). El contenido es el protagonista; el chrome
 * se retira a un filete. Mobile-first. Tokens en tokens.css; tipos en
 * fonts.css. No reestructura el DOM de extensiones (ver skinStyles).
 */

/* ───────────────────────── Base ───────────────────────── */
html { box-sizing: border-box; -webkit-text-size-adjust: 100%; }
*, *::before, *::after { box-sizing: inherit; }

body {
	margin: 0;
	font-family: var(--sn-font-text);
	font-size: var(--sn-fs-base);
	font-weight: 400;
	line-height: var(--sn-leading);
	color: var(--sn-ink);
	background-color: var(--sn-field);
	/* Grano de papel: ruido fractal SVG a muy baja opacidad, solo en el
	   campo. Da atmósfera sin coste de imagen ni daño al contraste. */
	background-image:
		radial-gradient(circle at 15% 0%, rgba(255,255,255,.5), transparent 55%),
		url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
	background-blend-mode: soft-light;
	background-attachment: fixed;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
	font-feature-settings: "kern", "liga", "calt";
}
body::before { /* fija la opacidad del grano vía token sin tocar el bg */
	content: ""; position: fixed; inset: 0; pointer-events: none; z-index: -1;
	background: var(--sn-field); opacity: calc(1 - var(--sn-field-grain) * 6);
	mix-blend-mode: normal;
}

a { color: var(--sn-link); text-underline-offset: .14em;
	text-decoration-thickness: .06em; }
a:hover { color: var(--sn-link-hover); }
a:visited { color: var(--sn-link-visited); }

/* ── Redlinks (a páginas inexistentes) y :visited ─────────────────
   Overrides de MediaWiki:Common.css (heredado de producción), que
   trae:
     a[href].new { color: rgba(0,0,0,.3) !important; }   ← negro en dark
     a:visited   { color: #681603; }                     ← rojo carbón
   En claro funcionan (faded grey y rojo oscuro sobre cream). En oscuro
   son inaccesibles (WCAG <1.5:1 sobre paper, falla tanto en .sn-body
   como en .catlinks). Por eso el override sólo aplica en oscuro: en
   claro respetamos el aire de producción. Especificidad bumpeada por
   `:root[data-…]` (0,1,1,1+) gana al `a:visited` de Common; el
   !important de redlinks empata al !important del site CSS, y la
   especificidad mayor desempata. */
a.new, a[href].new { color: var(--sn-link-new); }
/* El redlink NO se oscurece al visitarse: mantiene el rosado pálido por
   encima de la regla global `a:visited`. */
a.new:visited, a[href].new:visited { color: var(--sn-link-new); }
:root[data-sn-theme="dark"] a.new,
:root[data-sn-theme="dark"] a[href].new {
	color: var(--sn-link-new) !important;
}
:root[data-sn-theme="dark"] a:visited {
	color: var(--sn-link-visited);
}
@media (prefers-color-scheme: dark) {
	:root[data-sn-theme="auto"] a.new,
	:root[data-sn-theme="auto"] a[href].new {
		color: var(--sn-link-new) !important;
	}
	:root[data-sn-theme="auto"] a:visited {
		color: var(--sn-link-visited);
	}
}

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

/* Foco: el anillo es la nova. Nunca solo color. */
:where(a, button, input, select, textarea, summary, [tabindex], [contenteditable]):focus-visible {
	outline: 2px solid var(--sn-nova);
	outline-offset: 2px;
	border-radius: var(--sn-radius-s);
}
:focus:not(:focus-visible) { outline: none; }

.sn-vh {
	position: absolute !important; width: 1px; height: 1px;
	padding: 0; margin: -1px; overflow: hidden;
	clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Política global de íconos: nunca negro pleno ni carmín. Tinta al ~70%
   en reposo; tinta plena al hover/foco/activo del control que los
   contiene. Color propio (no heredan del texto: aunque el enlace se
   ponga en acento, el ícono no). */
.sn-i { display: inline-block; flex: none; vertical-align: middle;
	fill: none; stroke: currentColor; stroke-width: 1.75;
	stroke-linecap: round; stroke-linejoin: round;
	color: var(--sn-icon); transition: color var(--sn-dur-1); }
.sn-i use { fill: inherit; stroke: inherit; }
:is(a, button, summary, label, [role="button"]):hover > .sn-i,
:is(a, button, summary, label, [role="button"]):hover .sn-i,
:is(a, button, summary, label, [role="button"]):focus-visible .sn-i,
[data-sn-menu][aria-expanded="true"] .sn-i,
.sn-tabs li.selected a .sn-i, .sn-tabs li.active a .sn-i {
	color: var(--sn-icon-active);
}

/* ─────────────────────── Skip links ─────────────────────── */
.sn-skip {
	position: fixed; top: var(--sn-s-3); left: var(--sn-s-3);
	z-index: var(--sn-z-skip);
	transform: translateY(-180%);
	background: var(--sn-ink); color: var(--sn-paper);
	padding: var(--sn-s-2) var(--sn-s-4);
	border-radius: var(--sn-radius); font-weight: 500;
	text-decoration: none;
	transition: transform var(--sn-dur-2) var(--sn-ease);
}
.sn-skip:focus { transform: translateY(0); color: var(--sn-paper); }
.sn-skip + .sn-skip:focus { left: calc(var(--sn-s-3) + 11rem); }

/* ─────────────────────── App / shell ─────────────────────── */
.sn-app { display: flex; flex-direction: column; min-height: 100vh; }

.sn-header {
	position: sticky; top: 0; z-index: var(--sn-z-header);
	background: color-mix(in oklab, var(--sn-field) 60%, transparent);
	-webkit-backdrop-filter: saturate(1.5) blur(8px);
	backdrop-filter: saturate(1.5) blur(8px);
	/* Sin filete inferior: la cabecera se separa del cuerpo por el blur y el
	   fondo translúcido, no por una regla horizontal (doctrina "sin filetes"). */
}
/* CONTAINING-BLOCK TRAP: `backdrop-filter` hace que la cabecera sea el bloque
   contenedor de cualquier descendiente `position: fixed` —y todos los menús
   modales (.sn-menu-list) y el buscador modal (.sn-search) cuelgan de aquí—.
   En móvil eso ancla el modal a la posición de ORIGEN del header sticky (arriba
   del documento) en vez del viewport: estando con scroll abajo, el modal se
   abre fuera de pantalla mientras el scroll-lock congela la página. Mientras
   haya un modal abierto (el JS marca `data-sn-modal` en <html>), anulamos el
   filtro: el modal cubre todo con papel opaco, así que el blur no se ve, y su
   `fixed` vuelve a ser relativo al viewport → se abre donde estés. */
html[data-sn-modal] .sn-header {
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
}
/* Cabecera despejada: isotipo · pestañas de página · buscador · usuario.
   La navegación del sitio vive en el pie. */
.sn-header-bar {
	display: flex; align-items: center;
	justify-content: space-between;       /* repartidos de forma homogénea */
	gap: var(--sn-s-5);
	max-width: var(--sn-shell); margin-inline: auto;
	padding: var(--sn-s-3) var(--sn-s-4);
}

/* Marca + navegación juntas a la izquierda (iso pegado a la hamburguesa). */
.sn-brandnav {
	display: flex; align-items: center; gap: var(--sn-s-2); flex: none;
}

/* — Isotipo: logotipo "Casiopea" + constelación (SVG colorizable). El
   wordmark va EN el SVG; sin texto aparte. Monocromo currentColor →
   tematizable (tinta en claro/oscuro; acento sutil al hover). */
/* Mismo color que el resto de íconos/enlaces del toolbar (nunca rojo):
   semitransparente en reposo, opaco al hover. */
/* :visited/:link explícitos para ganarle a `a:visited` (que si no, tiñe
   el isotipo con el color de enlace visitado — el "rojo"). */
.sn-isotype, .sn-isotype:link, .sn-isotype:visited {
	display: inline-flex; align-items: center;
	text-decoration: none; color: var(--sn-icon);
	flex: none; padding: var(--sn-s-1);
	border-radius: var(--sn-radius);
	transition: color var(--sn-dur-1);
}
.sn-isotype:hover, .sn-isotype:focus-visible, .sn-isotype:active {
	color: var(--sn-icon-active);
}
/* El SVG colorea DIRECTAMENTE con el token de icono, NO `inherit` del <a>.
   Motivo: `--sn-icon` lleva alfa (color-mix … transparent), y por privacidad
   los navegadores IGNORAN colores con transparencia en `:visited` → el <a>
   visitado caía al morado de `--sn-link-visited` y el SVG (color:inherit) lo
   heredaba. Coloreando el <svg> (que no es enlace, no sufre la restricción)
   el logo es inmune al estado link/visited y NUNCA se ve morado. */
.sn-isotype svg {
	display: block; height: 2rem; width: auto; max-width: 100%;
	color: var(--sn-icon);
}
.sn-isotype:hover svg,
.sn-isotype:focus-visible svg,
.sn-isotype:active svg {
	color: var(--sn-icon-active);
}
.sn-isotype :is(img, .sn-isotype-img) { display: block; height: 2rem; width: auto; max-width: 12em; }
/* Doble variante inline: wordmark (desktop) + glifo cuadrado (mobile).
   El swap por viewport está al final del archivo, sección Responsive. */
.sn-isotype :is(.sn-isotype-full, .sn-isotype-compact) { display: contents; }
.sn-isotype .sn-isotype-compact { display: none; }
.sn-isotype .sn-isotype-compact svg { height: 2rem; width: 2rem; border-radius: var(--sn-radius); }

/* — Búsqueda — En reposo: fondo transparente, sin borde visible (borde
   transparente para no descuadrar al enfocar). Al enfocar: fondo papel
   + borde del filete normal (sin rojo, sin halo). En desktop el form
   se muestra inline; en compact viewport el form se colapsa y el
   trigger (.sn-search-trigger) lo abre como modal — ver media query
   al final del archivo, sección Responsive. */
.sn-searchbox {
	flex: 1 1 auto; max-width: none; min-width: 0;
	display: flex; align-items: center;
}
/* Trigger y close: especificidad bumpeada (.sn-searchbox …) para ganarle
   a .sn-iconbtn { display: inline-grid }, que aparece más abajo en la
   cascada y los re-mostraría en desktop si fueran solo .sn-search-trigger. */
.sn-searchbox .sn-search-trigger { display: none; }   /* sólo en compact */
.sn-searchbox .sn-search-close   { display: none; }   /* sólo en compact-modal */
.sn-search {
	flex: 1; min-width: 0;
	height: var(--sn-ctl); box-sizing: border-box;
	display: flex; align-items: center; gap: var(--sn-s-1);
	background: transparent;
	border: 1px solid var(--sn-hairline);
	border-radius: 999px;
	padding: 0 var(--sn-s-1) 0 var(--sn-s-4);
	transition: background var(--sn-dur-1), border-color var(--sn-dur-1);
}
.sn-search:focus-within {
	background: var(--sn-paper);
	border-color: var(--sn-hairline);
}
.sn-search :is(input[type="search"], #searchInput, .mw-searchInput) {
	flex: 1; min-width: 0; border: 0; background: transparent;
	font: inherit; color: var(--sn-ink); padding: 0; line-height: 1.2;
}
.sn-search input::placeholder { color: var(--sn-ink-faint); }
.sn-search :is(input):focus { outline: none; }
/* "Buscar" ahora es el ícono (texto → tooltip/aria-label). Botón-ícono
   discreto; el ícono hereda el color del texto, acento al activar. */
.sn-search-go {
	flex: none; cursor: pointer; display: grid; place-items: center;
	width: 2rem; height: 2rem; padding: 0; margin: 0;
	background: none; border: 0; border-radius: 999px;
	color: var(--sn-ink-soft);
	transition: background var(--sn-dur-1), color var(--sn-dur-1);
}
.sn-search-go:hover, .sn-search-go:focus-visible {
	background: var(--sn-sunk); color: var(--sn-icon-active);
}

/* — Zona 5 · Herramientas del usuario —
   Se pega al .sn-pagecluster (mismo cluster de íconos a la derecha): el
   gap del header-bar es --sn-s-5, y aquí lo compensamos con un margen
   negativo para dejar exactamente --sn-s-1 entre ambos. Search ya tiene
   `flex: 1 1 auto` y devora todo el espacio entre la marca y este cluster. */
.sn-usertools {
	display: flex; align-items: center; gap: var(--sn-s-1);
	flex: none; margin-left: calc(var(--sn-s-1) - var(--sn-s-5));
}
.sn-tempbadge {
	display: grid; place-items: center; width: 2rem; height: 2rem;
	color: var(--sn-warn); border-radius: 999px;
	background: color-mix(in oklab, var(--sn-warn) 14%, transparent);
}
.sn-notif :is(a) { color: var(--sn-ink-soft); }

.sn-usermenu-trigger {
	display: inline-grid; place-items: center;
	width: var(--sn-ctl); height: var(--sn-ctl); padding: 0;
	box-sizing: border-box;
	background: transparent; color: var(--sn-ink);
	border: 1px solid transparent; border-radius: var(--sn-radius);
	cursor: pointer; transition: background var(--sn-dur-1), border-color var(--sn-dur-1);
}
.sn-usermenu-trigger:hover,
.sn-usermenu-trigger[aria-expanded="true"] {
	background: var(--sn-paper); border-color: var(--sn-hairline);
}

/* — Menús colapsables del header: Navegación · Página · Usuario — */
.sn-md { position: relative; }
.sn-md-trigger {
	display: inline-flex; align-items: center; gap: var(--sn-s-2);
	height: var(--sn-ctl); box-sizing: border-box;
	background: none; border: 1px solid transparent;
	padding: 0 var(--sn-s-3);
	font-family: var(--sn-font-text); font-size: var(--sn-fs-sm);
	font-weight: 500; line-height: 1.2; color: var(--sn-ink);
	text-decoration: none; cursor: pointer; white-space: nowrap;
	border-radius: var(--sn-radius);
	transition: background var(--sn-dur-1), border-color var(--sn-dur-1);
}
.sn-md-trigger:hover,
.sn-md-trigger[aria-expanded="true"] {
	background: var(--sn-paper); border-color: var(--sn-hairline);
}
/* Disparador solo-ícono (Navegación / Página): el texto pasa a
   tooltip/aria-label; compacto, con cheurón pegado al ícono. */
.sn-md-icononly { gap: .15rem; padding: 0 var(--sn-s-2); }

/* Bloque "Página": acciones de edición (solo-ícono) + menú, agrupadas
   como una sola zona de la barra. */
.sn-pagecluster {
	display: flex; align-items: center; gap: var(--sn-s-1); flex: none;
}
.sn-iconbtn {
	display: inline-grid; place-items: center;
	width: var(--sn-ctl); height: var(--sn-ctl); flex: none;
	box-sizing: border-box;
	color: var(--sn-ink); text-decoration: none;
	background: transparent;
	border: 1px solid transparent; border-radius: var(--sn-radius);
	transition: background var(--sn-dur-1), border-color var(--sn-dur-1), color var(--sn-dur-1);
}
.sn-iconbtn:hover, .sn-iconbtn:focus-visible {
	background: var(--sn-paper); border-color: var(--sn-hairline);
	color: var(--sn-icon-active);
}
/* Íconos del MISMO color que el texto (heredan currentColor del botón);
   nunca acento. El cheurón solo rota. */
.sn-md-caret { transition: transform var(--sn-dur-1) var(--sn-ease); }
.sn-md-trigger[aria-expanded="true"] .sn-md-caret { transform: rotate(180deg); }
.sn-md .sn-menu-list { left: 0; right: auto; }
.sn-usermenu .sn-menu-list, .sn-pagemenu .sn-menu-list { right: 0; left: auto; }

/* Popover base */
.sn-menu-list {
	list-style: none; margin: 0; padding: var(--sn-s-2);
	position: absolute; right: 0; top: calc(100% + .4rem);
	min-width: 14rem; z-index: var(--sn-z-panel);
	background: var(--sn-paper); color: var(--sn-ink);
	border: 1px solid var(--sn-hairline);
	border-radius: var(--sn-radius-l);
	box-shadow: var(--sn-lift);
	opacity: 0; visibility: hidden; transform: translateY(-.4rem);
	transition: opacity var(--sn-dur-1), transform var(--sn-dur-1), visibility var(--sn-dur-1);
}
[data-sn-menu][aria-expanded="true"] + .sn-menu-list {
	opacity: 1; visibility: visible; transform: translateY(0);
}
.sn-menu-list li { margin: 0; list-style: none; }
.sn-menu-list ul { list-style: none; margin: 0; padding: 0; }
.sn-menu-list :is(a, .sn-menu-list-item) {
	display: flex; align-items: center; gap: var(--sn-s-2);
	padding: var(--sn-s-2) var(--sn-s-3);
	border-radius: var(--sn-radius); text-decoration: none;
	color: var(--sn-ink); font-family: var(--sn-font-text);
	font-size: var(--sn-fs-sm); font-weight: 400;
}
.sn-menu-list :is(a:hover, a:focus-visible) { background: var(--sn-sunk); color: var(--sn-nova); }
.sn-menu-list li.selected a, .sn-menu-list li.active a {
	color: var(--sn-ink); font-weight: 600;
}
.sn-menu-list li.new a { color: var(--sn-link); }

/* Página: grupos (espacios · vistas · acciones · variantes) con filete */
.sn-mm-grp { padding: var(--sn-s-1) 0; }
.sn-mm-grp + .sn-mm-grp {
	border-top: 1px solid var(--sn-hairline-soft); margin-top: var(--sn-s-1);
}

/* Navegación: megamenú en secciones, varias columnas si caben */
.sn-megamenu {
	min-width: min(34rem, calc(100vw - 2 * var(--sn-s-4)));
	max-height: min(72vh, 32rem); overflow-y: auto;
	columns: 2; column-gap: var(--sn-s-5); padding: var(--sn-s-4);
}
.sn-mm-sec { break-inside: avoid; margin-bottom: var(--sn-s-4); }
.sn-mm-sec:last-child { margin-bottom: 0; }
.sn-mm-h {
	margin: 0 0 var(--sn-s-1); font-family: var(--sn-font-text);
	font-size: var(--sn-fs-xs); font-weight: 600;
	text-transform: uppercase; letter-spacing: .08em;
	color: var(--sn-ink-faint);
}
@media (max-width: 34rem) { .sn-megamenu { columns: 1; } }

/* ───────────── Shell: una sola columna centrada ───────────── */
/* Sin riel: la hoja queda centrada y más ancha sobre el campo. */
.sn-shell {
	flex: 1;
	display: flex;
	justify-content: center;
	width: 100%;
	margin-inline: auto;
	/* Sin padding inferior: el campo ya se fusiona con el pie (mismo --sn-field),
	   así la hoja no deja un colchón muerto antes del pie. */
	padding: clamp(var(--sn-s-5), 5vw, var(--sn-s-7)) var(--sn-s-4) 0;
}
/* El ancho de la HOJA está fijado por --sn-measure (no es preferencia de
   usuario: la métrica de la hoja desparametriza el ajuste al baseline grid).
   El contenido va al 100% de la hoja. */
.sn-paper-wrap {
	width: 100%;
	max-width: var(--sn-measure);
	min-width: 0;
}

.sn-portlet-h {
	margin: 0 0 var(--sn-s-2);
	font-family: var(--sn-font-text);
	font-size: var(--sn-fs-xs); font-weight: 600;
	text-transform: uppercase; letter-spacing: .09em;
	color: var(--sn-ink-faint);
}
.sn-portlet-b ul, .sn-portlet ul, .sn-managed ul {
	list-style: none; margin: 0; padding: 0;
	display: flex; flex-direction: column; gap: .15rem;
}
.sn-portlet-b li a, .sn-portlet li a, .sn-managed a {
	display: block; padding: .3rem var(--sn-s-3) .3rem var(--sn-s-2);
	margin-left: calc(-1 * var(--sn-s-2));
	color: var(--sn-ink-soft); text-decoration: none;
	border-left: 2px solid transparent;
	border-radius: 0 var(--sn-radius-s) var(--sn-radius-s) 0;
	transition: color var(--sn-dur-1), border-color var(--sn-dur-1), background var(--sn-dur-1);
}
.sn-portlet-b li a:hover, .sn-portlet li a:hover, .sn-managed a:hover {
	color: var(--sn-ink); border-left-color: var(--sn-nova);
	background: var(--sn-nova-wash);
}
.sn-portlet-b li.selected a, .sn-portlet li.active a {
	color: var(--sn-ink); border-left-color: var(--sn-nova);
}

/* ─────────────────────── La hoja (paper) ─────────────────── */
.sn-paper-wrap { min-width: 0; }
.sn-paper {
	/* --sn-paper-px / --sn-paper-py exponen como contrato el padding del
	   paper. Cualquier hijo que quiera reventar (p.ej. .full-width) lo
	   niega con margin-inline / margin-block: calc(-1 * var(--…)). */
	--sn-paper-px: clamp(var(--sn-s-4), 4vw, var(--sn-s-7));
	--sn-paper-py: clamp(var(--sn-s-5), 4vw, var(--sn-s-7));
	background: var(--sn-paper);
	border: 1px solid var(--sn-paper-edge);
	border-radius: var(--sn-radius-paper);
	box-shadow: var(--sn-lift-paper);
	padding: var(--sn-paper-py) var(--sn-paper-px);
	min-width: 0;
}
@media (min-width: 90rem) {
	.sn-paper {
		--sn-paper-px: clamp(var(--sn-s-7), 6vw, var(--sn-s-8));
	}
}

/* .sn-indicators y #contentSub son metadata (badges/loaders por página y
   avisos contextuales como "Redirigido desde X") que MW inyecta dentro del
   <article>. Visualmente NO son contenido editorial — se tratan como
   marcadores adheridos al borde de la hoja: chips arriba a la derecha
   asomando del paper, y un kicker line proyectado por encima.

   El DOM no se mueve (mantenemos el contrato de fidelidad con MW: gadgets
   y scripts core esperan estos contenedores como anchors estables); el
   efecto es 100% CSS via position:absolute con el paper como coordinate
   system. overflow:visible (default del paper) deja que protruyan. */
.sn-paper { position: relative; }

/* Indicators: chip-stickers asomando del borde superior-derecho del paper.
   translateY(-50%) los pone mitad sobre la hoja, mitad afuera — el efecto
   "marcador adherido". El SMW entity-examiner u otros loaders que viven
   acá heredan el estilo de chip. */
.sn-indicators {
	position: absolute;
	inset-block-start: 0;
	inset-inline-end: clamp(var(--sn-s-4), 4vw, var(--sn-s-6));
	transform: translateY(-50%);
	display: flex; flex-wrap: nowrap; gap: var(--sn-s-2);
	margin: 0;
	z-index: 2;
	pointer-events: none;
}
.sn-indicators:empty { display: none; }
/* Ocultar indicadores "muertos" — los que vienen como wrapper con hijo
   vacío (caso del smw-entity-examiner loader que viene <div></div>
   adentro). Mismo patrón que ya aplica a #contentSub. */
.sn-indicators > .mw-indicator:empty,
.sn-indicators > .mw-indicator:has(> *:only-child:empty),
/* También ocultar el SMW entity examiner loader específicamente — es un
   indicador técnico de background que el lector no necesita ver. */
.sn-indicators > .mw-indicator:has(> .smw-entity-examiner) {
	display: none;
}
.sn-indicators > .mw-indicator {
	pointer-events: auto;
	background: var(--sn-paper);
	border: 1px solid var(--sn-paper-edge);
	/* Pill: mismas esquinas que el input del buscador (border-radius: 999px). */
	border-radius: 999px;
	padding: .25rem .7rem;
	font-size: var(--sn-fs-xs);
	line-height: 1.2;
	color: var(--sn-ink-soft);
	box-shadow: 0 2px 6px rgb(0 0 0 / .07), 0 1px 2px rgb(0 0 0 / .04);
}
/* Indicators con ícono (#mw-indicator-mw-helplink y variantes que core u
   otras extensiones puedan registrar siguiendo el patrón de un span con
   sufijo `-icon` a la izquierda del texto): le da aire al ícono para
   que no se pegue al texto, vía `margin-right`. Anulamos el
   `padding-right: 10px` que core fija en `.mw-helplink-icon` para que
   el espacio quede limpio (no se acumula con el margin). El selector
   replica el ID para empatar especificidad core (1,1,1) y ganar por
   orden de cascada (este archivo se carga después del módulo core). */
#mw-indicator-mw-helplink a .mw-helplink-icon,
.sn-indicators > [id^="mw-indicator-"] a [class$="-icon"] {
	padding-right: 0;
	margin-right: var(--sn-s-2);
}
@media (max-width: 36rem) {
	/* En móvil vuelve al flujo dentro del paper para no chocar con la chrome
	   ni quedar comido por el header. */
	.sn-indicators {
		position: static; transform: none;
		justify-content: flex-end;
		margin-block-end: var(--sn-s-3);
	}
	.sn-indicators > .mw-indicator {
		background: transparent; border: 0; box-shadow: none; padding: 0;
	}
}

/* Aviso gestionado (Stella-Nova:Aviso): lo PRIMERO dentro de la hoja,
   full-bleed a los bordes del papel — niega el padding (--sn-paper-py arriba,
   --sn-paper-px a los lados) con la misma convención que .full-width. Fondo
   --sn-warn con tinta que contrasta en ambos temas (--sn-warn-ink), esquinas
   superiores al radio de la hoja, y botón de cerrar. Se descarta por versión:
   el pre-pintado oculta sin parpadeo vía html[data-sn-notice-hide]; skin.js
   persiste el cierre en localStorage. */
.sn-notice {
	margin: calc(-1 * var(--sn-paper-py)) calc(-1 * var(--sn-paper-px)) var(--sn-s-5);
	border: 0;
	border-radius: var(--sn-radius-paper) var(--sn-radius-paper) 0 0;
	background: var(--sn-notice-bg); color: var(--sn-notice-ink);
	display: flex; align-items: flex-start; gap: var(--sn-s-3);
	padding: var(--sn-s-3) var(--sn-paper-px);
	font-size: var(--sn-fs-sm);
}
html[data-sn-notice-hide] .sn-notice { display: none; }
.sn-notice-body { flex: 1; min-width: 0; }
.sn-notice-body > :first-child { margin-top: 0; }
.sn-notice-body > :last-child { margin-bottom: 0; }
.sn-notice-body a { color: inherit; text-decoration: underline; }
.sn-notice-x {
	flex: none; cursor: pointer; color: inherit;
	display: grid; place-items: center;
	width: 1.9rem; height: 1.9rem; padding: 0;
	margin: -.1rem -.2rem 0 0;
	background: transparent; border: 0; border-radius: var(--sn-radius);
	transition: background var(--sn-dur-1);
}
.sn-notice-x:hover, .sn-notice-x:focus-visible {
	background: color-mix(in oklab, var(--sn-warn-ink) 16%, transparent);
}
.sn-notice-x .sn-i { color: inherit; }

/* — firstHeading: el título de la página, la voz editorial del documento.
   Mayor que cualquier h1 del cuerpo (--sn-fs-page-title). Ocupa
   line-height = 2 baselines + margin-bottom = 1 baseline (total 3
   baselines), todo múltiplo entero para que los párrafos que vienen
   después calcen exactamente en la retícula. (PRUEBA 2026-06-06: bajado de
   3 → 2 baselines; con 3 el interlineado se veía excesivo en títulos
   multilínea. 2 es el mínimo que respeta la regla "1 o 2 baselines" del
   grid —ver tokens.css.) El margin-top NEGATIVO de medio baseline recorta
   el aire superior (el leading simétrico de la primera línea + el inset del
   papel): como el título es el PRIMER hijo en flujo, un margen superior
   negativo sube TODA la columna por igual → se preserva el ritmo interno
   entre bloques, solo cambia dónde empieza el contenido respecto al padding
   del papel. Sigue siendo medio baseline (cantidad limpia y reversible).
   Sin offset cap-based: la simetría del integer-baseline conserva el
   colapso de márgenes y permite alineación robusta en multicolumna. — */
:is(.sn-paper, .sn-canvas) :is(h1#firstHeading, .firstHeading, .mw-first-heading) {
	font-family: var(--sn-font-text);
	font-weight: 300;
	font-size: var(--sn-fs-page-title);
	line-height: var(--sn-baseline-2);
	letter-spacing: -.018em;
	color: var(--sn-nova);
	margin: calc(var(--sn-baseline) * -0.5) 0 var(--sn-baseline);
	max-width: 34ch;
	text-wrap: balance;
}
/* Solo inyecciones core (subtítulo/redirección, restaurar). Sin caja ni
   filete: cuando hay contenido es una línea sobria; vacío no inserta nada. */
.sn-subtitle {
	color: var(--sn-ink-soft); font-size: var(--sn-fs-sm); margin: 0;
	text-transform: uppercase;
	font-stretch: condensed;
	letter-spacing: .25ex;
}
/* Vacío (caso normal, p. ej. portada: solo el #mw-content-subtitle hueco)
   → cero altura, ninguna inserción visible. Solo cuando trae algo real
   (aviso de redirección, enlace de restaurar) reserva un respiro.
   ":empty" estricto NO cubre el caso de #contentSub, que MW emite con un
   <div id="mw-content-subtitle"> dentro (hijo vacío). Se añade el caso
   ":has(> *:only-child:empty)" para esa estructura. */
.sn-subtitle:empty,
.sn-subtitle:has(> *:only-child:empty) { display: none; }

/* #contentSub (avisos de redirección / contexto) cuando aporta contenido:
   se proyecta arriba del paper como kicker line — sticker editorial, no
   compite con el cuerpo. inset-inline limitado al padding lateral del
   paper alinea el kicker con el ancho de la columna interna.
   #contentSub2 (undelete admin) queda con la regla del flujo normal. */
.sn-paper > #contentSub:has(a),
.sn-paper > #contentSub:has(*:not(:empty)) {
	position: absolute;
	inset-block-start: 0;
	inset-inline: var(--sn-paper-px);
	transform: translateY(-100%);
	padding-block-end: var(--sn-s-2);
	margin: 0;
	font-size: var(--sn-fs-xs);
	line-height: 1.4;
	color: var(--sn-ink-soft);
	z-index: 1;
}
.sn-paper > #contentSub2:has(a),
.sn-paper > #contentSub2:has(*:not(:empty)) {
	margin: 0 0 var(--sn-s-3);
}
@media (max-width: 36rem) {
	/* Móvil: kicker vuelve al flujo */
	.sn-paper > #contentSub:has(a),
	.sn-paper > #contentSub:has(*:not(:empty)) {
		position: static; transform: none;
		padding: 0; margin: 0 0 var(--sn-s-3);
		font-size: var(--sn-fs-sm);
	}
}
.sn-subtitle a { color: var(--sn-ink-soft); }

/* ─────────────── Cuerpo: ritmo editorial ─────────────── */
/* Ancho condensado del cuerpo vía el eje wdth (token --sn-text-width).
   font-stretch hereda, así que párrafos, listas, tablas y captions lo toman;
   las cabeceras lo resetean a 100% más abajo. Las DOS familias de texto —IBM
   Plex Sans y Roboto Serif— declaran el mismo rango wdth 75–100, así que el
   cuerpo condensa por igual aunque el lector alterne a serif. Mono no tiene
   eje wdth → lo ignora sin efecto. */
.sn-body { min-width: 0; font-stretch: var(--sn-text-width); }
/* El ancho de la HOJA lo fija --sn-measure (.sn-paper-wrap). Esa es la
   medida de lectura: TODO el contenido —párrafos, cabeceras, listas, tablas,
   fichas, figuras— vive dentro de ella al 100 %. (Las imágenes `.full-width`
   son la única excepción: sangran hasta --sn-shell con margen negativo.) */

/* Títulos en IBM Plex Sans (familia única para todas las cabeceras). h1–h2
   son la voz editorial sobria; h3 es la subsección en color nova; h4–h6
   son labels secundarios. line-heights y márgenes son múltiplos enteros
   de --sn-baseline: el ritmo vertical es continuo entre p, listas y
   cabeceras, y escala con la preferencia FontSize del usuario (la única
   tipográfica modulable; interlineado y ancho de hoja son fijos). Sin
   filetes ni reglas horizontales — la jerarquía la hacen tamaño, peso y
   color. */
/* Reset cascada de core: `mediawiki.skinning.interface` impone sobre h*
   y .mw-heading* lo siguiente, que rompe la doctrina del skin:
     · border-bottom: 1px solid #aaa  (filete bajo h1/h2 — NO va)
     · padding: 0.5em/0.17em           (offset que descuadra el ritmo)
     · display: flow-root              (innecesario, vuelve a block)
   Neutralizamos para AMBAS formas: <hN> directo (markup legacy y el
   propio firstHeading) y `.mw-headingN` que Parsoid (MW ≥ 1.39)
   envuelve alrededor del <hN>. */
.sn-body :is(h1, h2, h3, h4, h5, h6),
.sn-body :is(.mw-heading, .mw-heading1, .mw-heading2, .mw-heading3, .mw-heading4, .mw-heading5, .mw-heading6) {
	border: 0;
	padding: 0;
	display: block;
	font-stretch: 100%;   /* ancho normal: la condensada (--sn-text-width) es solo del cuerpo */
}
/* Las cabeceras envueltas por Parsoid: el <hN> interno también a 100% (hereda
   de .sn-body el font-stretch condensado si no se resetea aquí). */
.sn-body :is(.mw-heading1, .mw-heading2, .mw-heading3, .mw-heading4, .mw-heading5, .mw-heading6) > :is(h1, h2, h3, h4, h5, h6) {
	font-stretch: 100%;
}
/* Además, MW `elements` impone `h1..h6, .mw-heading { color:#000 }` con
   especificidad bare (0,0,1) → en modo oscuro deja headings negros sobre
   el papel oscuro allí donde el skin no fija color explícito (el drawer
   `.sn-prefs`, cabeceras de modales, contenido fuera de `.sn-body`).
   Reseteamos a `inherit` en toda la chrome del skin y en el drawer (que
   vive fuera de `.sn-app`): cada heading lee la tinta del contexto.
   Reglas específicas (`.sn-paper :is(h1#firstHeading,…) { color: nova }`
   en (1,1,1); `.sn-body :is(h3,…)` en (0,1,1) DELANTE en source-order)
   siguen ganando por especificidad/orden. */
.sn-app :is(h1, h2, h3, h4, h5, h6, .mw-heading),
.sn-canvas :is(h1, h2, h3, h4, h5, h6, .mw-heading) {
	color: inherit;
}
/* El filete #aaa de core (elements.less, `.mw-heading1,h1,.mw-heading2,h2`)
   NO va en NINGÚN contexto del skin. El reset de `.sn-body` de arriba ya lo
   mata dentro del artículo; esto lo extiende a la chrome fuera del cuerpo —
   pie editable (Stella-Nova:Pie, donde un `== Título ==` se vuelve h2),
   modales y drawer— para que la doctrina "sin filetes" sea total. */
.sn-app :is(h1, h2, .mw-heading1, .mw-heading2),
.sn-canvas :is(h1, h2, .mw-heading1, .mw-heading2) {
	border-bottom: 0;
}
/* Cuando Parsoid envuelve el heading: el wrapper carga la tipografía y
   el <hN> interno hereda. Sin esto, la margin del <hN> se suma a la del
   wrapper y aparecen dobles separaciones. */
.sn-body :is(.mw-heading1, .mw-heading2, .mw-heading3, .mw-heading4, .mw-heading5, .mw-heading6) > :is(h1, h2, h3, h4, h5, h6) {
	margin: 0;
	font: inherit;
	color: inherit;
	letter-spacing: inherit;
	text-transform: inherit;
}

/* Cabeceras del cuerpo (jerarquía bajo firstHeading). Familia única
   (IBM Plex Sans, sin caps). line-heights = 2 ó 1 baseline, márgenes
   en múltiplos enteros de baseline. La jerarquía la hacen tamaño,
   peso y color — no transformaciones tipográficas. */
.sn-body :is(h1, .mw-heading1) {
	font-family: var(--sn-font-text);
	font-weight: 400; line-height: var(--sn-baseline-2);
	letter-spacing: -.016em; color: var(--sn-ink);
	font-size: var(--sn-fs-display);
	margin: var(--sn-baseline-2) 0 var(--sn-baseline);
}
.sn-body :is(h2, .mw-heading2) {
	font-family: var(--sn-font-text);
	font-weight: 500; line-height: var(--sn-baseline-2);
	letter-spacing: -.012em; color: var(--sn-ink);
	font-size: var(--sn-fs-xl);
	margin: var(--sn-baseline-2) 0 var(--sn-baseline);
}
.sn-body :is(h3, .mw-heading3) {
	font-family: var(--sn-font-text);
	font-weight: 500; line-height: var(--sn-baseline);
	letter-spacing: 0; color: var(--sn-nova);
	font-size: var(--sn-fs-lg);
	margin: var(--sn-baseline-2) 0 var(--sn-baseline);
}
.sn-body :is(h4, h5, h6, .mw-heading4, .mw-heading5, .mw-heading6) {
	font-family: var(--sn-font-text); line-height: var(--sn-baseline);
	letter-spacing: 0; color: var(--sn-ink);
}
.sn-body :is(h4, .mw-heading4) {
	font-size: var(--sn-fs-md);
	margin: var(--sn-baseline) 0 0; font-weight: 700;
}
.sn-body :is(h5, .mw-heading5) {
	font-size: var(--sn-fs-base);
	margin: var(--sn-baseline) 0 0; font-weight: 500;
}
.sn-body :is(h6, .mw-heading6) {
	font-size: var(--sn-fs-base);          /* no menor que el cuerpo */
	margin: var(--sn-baseline) 0 0; font-weight: 400;   /* un punto más liviano */
	color: var(--sn-nova);                 /* rojo */
}
.sn-body :is(h1, h2, h3, h4, h5, h6, .mw-heading1, .mw-heading2, .mw-heading3, .mw-heading4, .mw-heading5, .mw-heading6):first-child {
	margin-top: 0;
}
/* Párrafos del cuerpo: justificados con hyphenation. La hyphenation depende
   de que el documento declare `lang` (MW pone `<html lang="es">`); para otros
   idiomas, el motor del navegador usa su diccionario. */
.sn-body p {
	margin: 0 0 var(--sn-baseline);
	text-align: justify;
	text-justify: inter-word;
	-webkit-hyphens: auto;
	hyphens: auto;
	hyphenate-limit-chars: 6 3 3;
}
/* La medida de lectura ahora la fija --sn-measure en la hoja (.sn-paper-wrap),
   una sola restricción de ancho. (Antes había aquí un `width:80%` sobre los <p>
   de nivel superior; se retiró porque max-width:--sn-measure ya hace el trabajo
   sin desalinear bloques ni competir con el ancho de la hoja.) */
.sn-body :is(ul, ol) { margin: 0 0 var(--sn-baseline); padding-left: var(--sn-s-5); }
.sn-body li { margin: 0; }
.sn-body li::marker { color: var(--sn-ink-faint); }
/* Enlaces del contenido: el token manda. Especificidad .sn-body a*
   (0,0,2,x) para vencer a MediaWiki:Common.css `a:visited{...}`, que
   carga después del skin (módulo `site`) con igual especificidad. */
.sn-body a { color: var(--sn-link); }
.sn-body a:hover { color: var(--sn-link-hover); }
.sn-body a:visited { color: var(--sn-link-visited); }
.sn-body a:visited:hover { color: var(--sn-link-hover); }

/* Enlaces externos: mismos tokens que los internos (carmín) — el ícono
   `external-link` basta como señal de "sale del sitio". Hay que sobrescribir
   el azul que impone la feature `content-links` de core
   (`.mw-parser-output a.external`, especificidad 0,0,2,1). El ÍCONO lo
   define el skin (Feather external-link) y se pinta como ::after con mask,
   así que hereda currentColor automáticamente. */
.sn-body a.external,
.sn-body .mw-parser-output a.external {
	color: var(--sn-link);
	background: none !important; padding-right: 0 !important;
}
.sn-body a.external:hover,
.sn-body .mw-parser-output a.external:hover { color: var(--sn-link-hover); }
.sn-body a.external:visited,
.sn-body .mw-parser-output a.external:visited { color: var(--sn-link-visited); }
.sn-body a.external:visited:hover,
.sn-body .mw-parser-output a.external:visited:hover { color: var(--sn-link-hover); }
.sn-body a.external::after {
	content: ""; display: inline-block;
	width: .68em; height: .68em; margin-left: .28em;
	vertical-align: -.02em;
	background-color: currentColor;
	-webkit-mask: var(--sn-ext-icon) center / contain no-repeat;
	mask: var(--sn-ext-icon) center / contain no-repeat;
	opacity: .75;
}
.sn-body a.external:hover::after { opacity: 1; }
.sn-body a.external:is(.image, .no-icon)::after,
.sn-body a.external:has(img)::after { content: none; }

/* Citas y poemas: familia "de contraste" frente al cuerpo. Consumen
   --sn-font-quote (default = serif Roboto Serif; sans Plex cuando el lector
   alterna familia desde el menú). El <poem> de la extensión Poem se
   renderiza como .poem dentro de mw-parser-output. Line-height y márgenes en
   baselines: el ritmo se mantiene aunque el cuerpo cambie.
   La CITA se lee a ancho NATURAL (font-stretch: 100%): tanto Roboto Serif
   como Plex Sans tienen eje wdth y heredarían la condensada del cuerpo si no
   se reseteara aquí; la cita no debe condensarse. El VERSO, en cambio, tiene
   su propio tratamiento (ver .poem más abajo). */
.sn-body blockquote {
	margin: var(--sn-baseline) 0;
	padding: 0 0 0 var(--sn-s-5);
	font-family: var(--sn-font-quote); font-optical-sizing: auto;
	font-stretch: 100%;
	font-size: var(--sn-fs-md);
	color: var(--sn-ink-soft); line-height: var(--sn-baseline);
}
.sn-body blockquote p:last-child { margin-bottom: 0; }
/* Verso: voz editorial propia (tokens --sn-poem-*): columna más estrecha que
   el cuerpo (font-stretch condensado), tamaño algo menor —% del cuerpo, así
   respeta la preferencia FontSize— y peso medio. line-height sigue en el
   baseline para no romper el ritmo pese al tamaño menor. */
.sn-body .poem {
	font-family: var(--sn-font-quote); font-optical-sizing: auto;
	font-stretch: var(--sn-poem-width);
	font-weight: var(--sn-poem-weight);
	font-size: var(--sn-poem-size); line-height: var(--sn-baseline);
	color: var(--sn-ink);
	white-space: pre-wrap;
}
.sn-body .poem p { margin: 0 0 var(--sn-baseline); }
/* La extensión Poem inserta <br/> tras cada línea (Poem.php). Con
   white-space: pre-wrap el \n del código ya da el salto, así que el
   <br/> sobra y produciría doble línea. Lo neutralizamos. */
.sn-body .poem br { display: none; }
/* MW feature `elements` impone `code, pre, .mw-code { color:#000;
   background:#f8f9fa; border: 1px solid @border-color-muted }` con
   especificidad (0,0,1). Las hex de Codex se compilan a literales (no
   pasan por var()), así que en modo oscuro: tinta negra sobre --sn-sunk
   (#181610) → ilegible, y filete claro #c8ccd1 → alto contraste sobre
   papel oscuro. Tinta y fondo ya los pisábamos; el filete claro
   sobrevivía en `code` inline. Lo normalizamos:
   · inline (code/kbd/samp) → sin borde y sin caja sólida: wash
     translúcido derivado de --sn-ink (~6%) que voltea solo claro/oscuro
     y se siente parte del cuerpo, no una "pastilla" importada.
   · bloque (pre/.mw-code)  → borde --sn-hairline (warm dark en oscuro). */
.sn-body :is(code, kbd, samp) {
	font-family: var(--sn-font-mono); font-size: .9em;
	color: var(--sn-ink);
	background: color-mix(in oklab, var(--sn-ink) 6%, transparent);
	padding: .08em .32em;
	border: 0;
	border-radius: var(--sn-radius-s);
}
.sn-body :is(pre, .mw-code) {
	background: var(--sn-sunk); color: var(--sn-ink);
	border: 1px solid var(--sn-hairline);
	border-radius: var(--sn-radius);
	padding: var(--sn-s-4); overflow-x: auto;
	font-family: var(--sn-font-mono); font-size: var(--sn-fs-sm); line-height: 1.5;
}
.sn-body pre code { background: none; padding: 0; }
.sn-body hr {
	border: 0; height: 1px; background: var(--sn-hairline);
	margin: var(--sn-s-7) 0;
}
.sn-body :is(img, video) { max-width: 100%; height: auto; }
.sn-body figure { margin: var(--sn-s-5) 0; }

/* .img-circle: override de MediaWiki:Common.css.
   En Common.css la regla aplica clip-path:circle() al <figure>, NO al <img>.
   Cuando el figure contiene <figcaption> el bounding box mide imagen+caption,
   el centro de circle() cae más abajo que el centro de la imagen, y el
   círculo recorta los px superiores de la imagen y devora la caption.

   Lo movemos al elemento real (la imagen): clip-path al <img> directamente
   sobre su propio cuadrado, sin contaminación del caption. La caption se
   oculta dentro de .img-circle porque en .vcard el nombre ya aparece
   abajo como .person-name (duplicación no aporta). */
.sn-body .img-circle { clip-path: none; }
.sn-body .img-circle :is(img, .mw-file-element) {
	clip-path: circle();
	display: block;
}
.sn-body .img-circle > figcaption { display: none; }

/* Full-bleed: imagen/figura/wrapper con class="full-width" se revienta del
   padding lateral del .sn-paper para ir borde-a-borde del <article>.
   Funciona para tres patrones de wikitexto:
     [[Archivo:foo.jpg|class=full-width]]                → clase en <img>
     [[Archivo:foo.jpg|frameless|class=full-width]]      → en <a class="image">
     <div class="full-width">[[Archivo:foo.jpg]]</div>   → wrapper manual
   El truco: width:auto + max-width:none + margin-inline negativo igual al
   padding lateral del paper (expuesto como --sn-paper-px). La imagen interna
   se estira al 100% del nuevo ancho. Si está dentro de un .thumb la caja
   thumb también se revienta — la leyenda queda alineada al nuevo ancho. */
/* Match defensivo: además de `.full-width`, aceptamos el token
   `'full-width'` (con apóstrofes) que MediaWiki preserva cuando el
   wikitexto se escribe como `[[Imagen:foo|class='full-width']]`. El
   selector de atributo `[class~="'full-width'"]` matchea ese token
   tal cual. El canónico sigue siendo `class=full-width` sin comillas. */
:is(.sn-paper, .sn-canvas) :is(
	.full-width, .thumb.full-width, figure.full-width,
	[class~="'full-width'"], .thumb[class~="'full-width'"], figure[class~="'full-width'"]
) {
	display: block;
	width: auto;
	max-width: none;
	margin-inline: calc(-1 * var(--sn-paper-px));
	margin-block: var(--sn-s-5);
	float: none;  /* anula el float que ponen thumb-left/right de core */
}
:is(.sn-paper, .sn-canvas) :is(
	.full-width, .thumb.full-width, figure.full-width,
	[class~="'full-width'"], .thumb[class~="'full-width'"], figure[class~="'full-width'"]
) :is(img, video),
:is(.sn-paper, .sn-canvas) :is(img, video):is(.full-width, [class~="'full-width'"]) {
	display: block;
	width: 100%;
	height: auto;
	max-width: 100%;
	margin-inline: 0;
}
/* Si el .full-width es un <img> o un <a class="image"> directo (no envuelto
   en .thumb), aplicar la misma regla — class va al elemento mismo. */
:is(.sn-paper, .sn-canvas) :is(
	a.image.full-width, img.full-width, video.full-width,
	a.image[class~="'full-width'"], img[class~="'full-width'"], video[class~="'full-width'"]
) {
	display: block;
	width: auto;
	max-width: none;
	margin-inline: calc(-1 * var(--sn-paper-px));
	margin-block: var(--sn-s-5);
}
:is(.sn-paper, .sn-canvas) :is(a.image.full-width, a.image[class~="'full-width'"]) img {
	width: 100%; height: auto; display: block;
}
/* La leyenda dentro de un .thumb full-width vuelve al ancho del cuerpo para
   no quedar pegada al borde del paper. */
:is(.sn-paper, .sn-canvas) :is(.thumb.full-width, .thumb[class~="'full-width'"]) .thumbcaption {
	padding-inline: var(--sn-paper-px);
	text-align: start;
}

/* Bleed vertical de borde: cuando .full-width es el PRIMER o ÚLTIMO elemento
   del cuerpo (.sn-body), también revienta el padding vertical del paper
   y redondea las esquinas para alinearse con el border-radius del <article>.

   La cadena de wrappers desde .sn-body hasta el <span class="full-width"> que
   emite MW es típicamente:
     .sn-body > #mw-content-text > .mw-parser-output > <p> > .full-width
   El bleed solo aplica si .full-width es :first-child (o :last-child) en
   cada nivel — así no se dispara para imágenes que están a media página
   dentro del primer <p> después de texto. Se enumeran los 4 niveles de
   anidamiento típicos; basta con que uno matchee.

   El colapso de margen propaga el margin-block negativo a través de los
   wrappers (ninguno tiene padding/border vertical) hasta detenerse en
   .sn-paper (que sí tiene padding-block). overflow:hidden clipea el <img>
   interno al radio del paper en las esquinas tocadas.

   Caveat: en páginas con <h1#firstHeading> visible, el bleed-top tira la
   imagen hacia arriba y la superpone al título. Usar __NOTITLE__ en páginas
   diseñadas con hero al tope, o no marcar la primera imagen como full-width.
   .sn-indicators está ahora position:absolute (ver más arriba) y no ocupa
   flujo, por lo que .sn-body es siempre el primer elemento de flujo dentro
   del paper en páginas con __NOTITLE__. */
.sn-body > :is(.full-width, [class~="'full-width'"]):first-child,
.sn-body > *:first-child > :is(.full-width, [class~="'full-width'"]):first-child,
.sn-body > *:first-child > *:first-child > :is(.full-width, [class~="'full-width'"]):first-child,
.sn-body > *:first-child > *:first-child > *:first-child > :is(.full-width, [class~="'full-width'"]):first-child {
	margin-block-start: calc(-1 * var(--sn-paper-py));
	border-start-start-radius: var(--sn-radius-paper);
	border-start-end-radius:   var(--sn-radius-paper);
	overflow: hidden;
}
.sn-body > :is(.full-width, [class~="'full-width'"]):last-child,
.sn-body > *:last-child > :is(.full-width, [class~="'full-width'"]):last-child,
.sn-body > *:last-child > *:last-child > :is(.full-width, [class~="'full-width'"]):last-child,
.sn-body > *:last-child > *:last-child > *:last-child > :is(.full-width, [class~="'full-width'"]):last-child {
	margin-block-end: calc(-1 * var(--sn-paper-py));
	border-end-start-radius: var(--sn-radius-paper);
	border-end-end-radius:   var(--sn-radius-paper);
	overflow: hidden;
}

/* Override fullscreen: como `.sn-canvas-body` está centrado con
   `max-width: --sn-shell`, las imágenes `.full-width` necesitan
   trascender el cuerpo hasta el borde REAL del viewport. La técnica
   `width: 100vw` + `margin-inline: calc((100% - 100vw) / 2)` posiciona
   el elemento extendiendo a ambos lados del cuerpo centrado, alcanzando
   los bordes del viewport sin importar lo angosto que sea el shell.
   Wins por orden de cascada (misma especificidad que la regla
   `:is(.sn-paper, .sn-canvas) :is(.full-width, …)` de arriba). */
.sn-canvas :is(
	.full-width, .thumb.full-width, figure.full-width,
	[class~="'full-width'"], .thumb[class~="'full-width'"], figure[class~="'full-width'"]
) {
	width: 100vw;
	max-width: 100vw;
	margin-inline: calc((100% - 100vw) / 2);
}

/* El skin DECRETA que <canvas> e <iframe> embebidos (p5.js, widgets como
   {{#widget:P5js}} que incrustan un sitio externo, lienzos experimentales)
   NO llevan borde, outline NI box-shadow — ni en foco. Se neutraliza
   también el box-shadow porque el del widget (inline, hex de 8 dígitos)
   Firefox lo renderiza como una raya negra gruesa (causa real del bug).
   El skin se hace dueño de esto pase lo que pase en el estilo inline del
   widget. !important por si Common.css (módulo `site`, carga tras el
   skin) o el UA intentan imponerlo. */
canvas, iframe,
.mw-body-content :is(canvas, iframe),
.p5m-sketch, .p5m-sketch canvas,
.sn-canvas canvas {
	border: 0 !important;
	outline: 0 !important;
	box-shadow: none !important;
}
:is(canvas, iframe):focus, :is(canvas, iframe):focus-visible,
.p5m-sketch:focus, .p5m-sketch:focus-visible { outline: 0 !important; }
.p5m-sketch { background: transparent; }
.sn-body figcaption, .sn-body .thumbcaption {
	font-size: var(--sn-fs-sm); color: var(--sn-ink-soft);
	font-style: italic; margin-top: var(--sn-s-2);
}

/* — Badge — chip de metadatos (estado, etiqueta, categoría). Estaba
   documentado en el especimen (specimen.css) pero NO se shippeaba en el
   skin: ahora es el componente REAL. Base neutra rellena + variantes de
   color por rol (wash + tinta del rol). El borde transparente reserva la
   caja para que las variantes "fantasma" (p. ej. categorías sin página)
   no cambien de tamaño al pintar un filete. Las categorías del pie
   (.catlinks li) COMPARTEN esta base: son badges también. */
.sn-badge, .catlinks li {
	display: inline-flex; align-items: center;
	padding: 2px var(--sn-s-2);
	font-size: var(--sn-fs-xs); font-weight: 500;
	background: var(--sn-sunk); color: var(--sn-ink-soft);
	border: 1px solid transparent; border-radius: var(--sn-radius);
}
.sn-badge-nova   { background: var(--sn-nova-wash);   color: var(--sn-nova); }
.sn-badge-ok     { background: var(--sn-ok-wash);     color: var(--sn-ok); }
.sn-badge-warn   { background: var(--sn-warn-wash);   color: var(--sn-warn); }
.sn-badge-danger { background: var(--sn-danger-wash); color: var(--sn-danger); }

/* — Categorías — el pie las re-viste como badges (comparten la base de
   arriba) y distingue por ESTADO de la página de categoría:
     · existe  → fondo igual al CAMPO fuera de la hoja (--sn-field) y texto
       en color de enlace (--sn-link): categoría real, navegable.
     · no existe (redlink a.new) → fondo muy pálido, casi blanco, y texto
       gris claro (--sn-ink-faint). De-énfasis: "esta categoría aún no tiene
       página". El borde transparente (de la base) iguala la altura. */
.catlinks, .mw-normal-catlinks {
	margin-top: var(--sn-s-7);
	padding-top: var(--sn-s-4);
	/* Sin filete superior: las categorías se separan del cuerpo por el aire
	   (margin/padding), no por una regla horizontal (doctrina "sin filetes"). */
	font-size: var(--sn-fs-sm); color: var(--sn-ink-soft);
}
.catlinks ul { display: inline-flex; flex-wrap: wrap; gap: var(--sn-s-2);
	list-style: none; margin: 0; padding: 0; }
.catlinks li a { color: inherit; text-decoration: none; }
/* Categoría EXISTENTE: fondo del campo (como fuera de la hoja), texto enlace. */
.catlinks li {
	background: var(--sn-field);
	color: var(--sn-link);
}
.catlinks li a:hover { color: var(--sn-nova); }
/* Categoría sin página (redlink a.new): fondo casi blanco, texto gris claro. */
.catlinks li:has(a.new) {
	background: color-mix(in oklab, var(--sn-ink) 5%, var(--sn-paper));
	border-color: transparent;
	color: var(--sn-ink-faint);
}
.catlinks li:has(a.new) a:hover { color: var(--sn-ink-soft); }
.catlinks.catlinks-allhidden { display: none; }

/* — Enlace de patrullaje nativo ("Marcar esta página como verificada") —
   El core lo renderiza como
     <div class="patrollink"><a><button class="cdx-button
     cdx-button--action-progressive">…</button></a></div>   (Article.php).
   El skin lo re-viste como nav-pill (mismo lenguaje que las categorías),
   neutralizando el botón Codex primario. Scoped a `.patrollink`: NO afecta
   a los demás cdx-button del wiki (VE, InputBox, Thanks, etc.). La mayor
   especificidad (`.patrollink button.cdx-button`) le gana al CSS de Codex
   sin necesidad de !important. */
.patrollink { margin-top: var(--sn-s-6); }
.patrollink a { text-decoration: none; }
.patrollink button.cdx-button {
	display: inline-flex; align-items: center; gap: var(--sn-s-1);
	min-height: 0;
	padding: .3rem .85rem;
	font: inherit; font-size: var(--sn-fs-sm); font-weight: 500;
	color: var(--sn-ink);
	background: var(--sn-sunk);
	border: 1px solid var(--sn-hairline);
	border-radius: 999px;
	box-shadow: none; cursor: pointer;
	transition: background var(--sn-dur-1), border-color var(--sn-dur-1), color var(--sn-dur-1);
}
.patrollink a:hover button.cdx-button,
.patrollink a:focus-visible button.cdx-button {
	background: var(--sn-paper);
	border-color: var(--sn-nova);
	color: var(--sn-nova);
}

/* ──────────────── Índice (ToC NATIVO, en su sitio) ────────────────
   No se relocaliza ni se duplica: MediaWiki lo inyecta donde marca
   __TOC__ (o en su posición automática) y el skin solo lo VISTE para
   que tenga el lenguaje de diseño del resto.

   EXCEPCIÓN AL BASELINE GRID (decisión deliberada): el índice es una
   TARJETA, no texto corrido. Sus márgenes/paddings usan la escala de
   espaciado (--sn-s-*), no --sn-baseline, y su alto total es variable
   (depende del nº de entradas), así que no calza la grilla de línea de
   base ni se pretende que lo haga. El texto que sigue conserva su propio
   ritmo interno; solo el origen global de la grilla se desplaza un poco
   tras la tarjeta. NO "arreglar" pasando los márgenes a baseline: el
   desfase por el alto interno variable seguiría existiendo. Por eso el
   h2 interior puede llevar line-height: 1 sin romper nada. */
.sn-body .toc, .mw-parser-output .toc {
	display: block;            /* full-width: ocupa el 100% de la hoja */
	width: 100%;
	margin: var(--sn-s-4) 0 var(--sn-s-6);
	border: 1px solid var(--sn-hairline-soft);
	border-radius: var(--sn-radius);
	background: color-mix(in oklab, var(--sn-sunk) 60%, transparent);
	padding: 0 0 var(--sn-s-3);
	font-size: var(--sn-fs-sm);
}
.sn-body .toc .toctitle {
	display: flex; align-items: center; justify-content: space-between;
	gap: var(--sn-s-3);
	padding: var(--sn-s-3) var(--sn-s-4) var(--sn-s-2);
	text-align: left;
}
.sn-body .toc .toctitle :is(h2, h3) {
	margin: 0; padding: 0; border: 0; font-family: var(--sn-font-text);
	font-size: var(--sn-fs-sm); font-weight: 600;
	line-height: 1;
	letter-spacing: 0;
	color: var(--sn-ink-soft);
}
/* Colapsable: el checkbox nativo se oculta visualmente pero queda
   enfocable por teclado; el label es un cheurón que rota y, al marcar,
   oculta la lista (mecanismo CSS puro de MediaWiki). */
.sn-body .toc .toctogglecheckbox {
	position: absolute; width: 1px; height: 1px;
	opacity: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.sn-body .toc .toctogglespan { display: flex; flex: none; }
/* Minimal: SOLO el cheurón que gira. Se suprime cualquier texto y los
   corchetes "[ocultar]/[mostrar]" que MediaWiki pinta vía CSS (en el
   label o en el span); font-size:0 mata texto y el cheurón se mide en
   rem (independiente del tamaño de fuente). */
.sn-body .toc .toctogglelabel {
	display: inline-flex; align-items: center; justify-content: center;
	width: 1.7rem; height: 1.7rem; margin: -.3rem -.3rem -.3rem 0;
	font-size: 0; line-height: 0;
	cursor: pointer; color: var(--sn-ink-faint);
	border-radius: var(--sn-radius-s);
	transition: color var(--sn-dur-1);
}
.sn-body .toc .toctogglelabel:hover { color: var(--sn-ink); }
.sn-body .toc .toctogglelabel::before,
.sn-body .toc .toctogglespan::before,
.sn-body .toc .toctogglespan::after { content: none !important; }
.sn-body .toc .toctogglelabel::after {
	content: "" !important; display: block;
	width: .42rem; height: .42rem; margin-top: -.16rem;
	border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
	transform: rotate(45deg);
	transition: transform var(--sn-dur-1) var(--sn-ease);
}
.sn-body .toc .toctogglecheckbox:checked ~ .toctitle .toctogglelabel::after {
	transform: rotate(-45deg); margin-top: .1rem;
}
.sn-body .toc .toctogglecheckbox:checked ~ ul { display: none; }
/* Colapsado: el contenedor `.toc` deja de aportar su `padding-bottom`
   (no hay lista que separar) y el `.toctitle` iguala su padding inferior
   al superior para que la caja quede simétrica arriba/abajo. */
.sn-body .toc:has(.toctogglecheckbox:checked) { padding-bottom: 0; }
.sn-body .toc:has(.toctogglecheckbox:checked) .toctitle {
	padding-bottom: var(--sn-s-3);
}
.sn-body .toc .toctogglecheckbox:focus-visible ~ .toctitle .toctogglelabel {
	outline: 2px solid var(--sn-nova); outline-offset: 2px;
}
.sn-body .toc ul {
	list-style: none; margin: 0; padding: 0 var(--sn-s-4);
}
.sn-body .toc ul ul {
	margin-left: var(--sn-s-3); padding: 0 0 0 var(--sn-s-3);
	border-left: 1px solid var(--sn-hairline-soft);
}
.sn-body .toc li { margin: 0; }
.sn-body .toc li a {
	display: flex; gap: var(--sn-s-2); padding: .24rem 0;
	color: var(--sn-ink-soft); text-decoration: none;
}
.sn-body .toc li a:hover { color: var(--sn-ink); }
.sn-body .toc .tocnumber {
	color: var(--sn-ink-faint); font-variant-numeric: tabular-nums; flex: none;
}
.sn-body .toc .toctext { min-width: 0; }

/* "Más" (acciones) y Variantes: bloque compacto al final de las pestañas. */
/* ───── Pie a 3 columnas: Stella-Nova:Pie · legal · herramientas ───── */
.sn-footer {
	/* El pie toma el tono de la HOJA (--sn-paper), no el del campo (decisión
	   2026-06-10: «página y pie del mismo color», más notorio en oscuro). El
	   pie se separa de la hoja por el aire (margin-top), no por filete ni por
	   un tono propio (doctrina "sin filetes"). */
	background: var(--sn-paper);
	font-size: var(--sn-fs-sm); color: var(--sn-ink-soft);
	margin-top: var(--sn-s-7);
}
/* El ancho del pie se alinea con la HOJA (--sn-measure), no con la chrome
   (--sn-shell), para que las columnas queden bajo la columna de lectura y
   no exhiban espacio muerto a los lados. Dos columnas en proporción 2fr 1fr:
   ANCHA (izq) = última edición + herramientas inline; ANGOSTA (der) =
   enlaces de sitio + bloque editable (Stella-Nova:Pie). */
.sn-footer-inner {
	max-width: var(--sn-measure); margin-inline: auto;
	padding: var(--sn-s-6) var(--sn-s-4);
	display: grid; gap: var(--sn-s-6) var(--sn-s-7);
	grid-template-columns: 2fr 1fr;
	align-items: start;
}
.sn-footer-col { min-width: 0; }

/* — Columna ancha — última edición + herramientas + enlaces de sitio —
   Todo este lado va un punto más pequeño que el resto del pie (--sn-fs-xs). */
.sn-footer-main {
	display: flex; flex-direction: column; gap: var(--sn-s-4);
	font-size: var(--sn-fs-xs);
}
.sn-foot-lastedit { margin: 0; color: var(--sn-ink-soft); }
.sn-foot-lastedit .sn-foot-user { color: var(--sn-link); text-decoration: none; }
.sn-foot-lastedit .sn-foot-user:hover { color: var(--sn-link-hover); text-decoration: underline; }

/* Uniformidad tipográfica de la columna ancha: TODOS los enlaces (herramientas,
   enlaces de sitio, licencia y el summary de "Herramientas") leen igual que
   .sn-foot-lastedit —misma familia, tamaño (--sn-fs-xs), peso y ancho—; sólo el
   color queda semántico (link vs. texto). Anula los font-size calc(fs-sm*.8)
   propios de cada lista. Scoped a .sn-footer-main: el pie de pantalla completa
   conserva su propia escala. */
.sn-footer-main :is(.sn-foot-toollist, .sn-foot-places, .sn-foot-license, .sn-foot-tools-summary) {
	font-family: var(--sn-font-text);
	font-size: var(--sn-fs-xs);
	font-weight: 400;
	font-stretch: normal;
}

/* Herramientas y más: SIN título, enlaces comunes en línea (color de enlace,
   subrayado sólo al hover/foco), cada uno con su ícono Feather delante
   (inyectado en PHP por id de item → ver SkinStellaNova::iconizeToollist). */
.sn-foot-toollist {
	list-style: none; margin: 0; padding: 0;
	display: flex; flex-wrap: wrap; gap: var(--sn-s-1) var(--sn-s-4);
	/* Mismo tamaño que los enlaces de sitio (.sn-foot-places), no el del
	   cuerpo del pie: los dos grupos de enlaces leen igual. */
	font-size: calc(var(--sn-fs-sm) * .8);
}
.sn-foot-toollist li { margin: 0; list-style: none; }
.sn-foot-toollist li a {
	display: inline-flex; align-items: center; gap: var(--sn-s-1);
	color: var(--sn-link); text-decoration: none;
	transition: color var(--sn-dur-1);
}
.sn-foot-toollist li a:hover, .sn-foot-toollist li a:focus-visible {
	color: var(--sn-link-hover); text-decoration: underline;
}
/* El ícono escala con el texto del item (queda pequeño en --sn-fs-xs). */
.sn-foot-ico { width: 1em; height: 1em; }

/* Enlaces de sitio (privacidad/acerca/descargos): a 80% del cuerpo de pie. */
.sn-foot-places {
	list-style: none; margin: 0; padding: 0;
	display: flex; flex-wrap: wrap; gap: var(--sn-s-1) var(--sn-s-3);
	font-size: calc(var(--sn-fs-sm) * .8);
}
.sn-foot-places li { margin: 0; }
.sn-foot-places a { color: var(--sn-ink-soft); text-decoration: none; }
.sn-foot-places a:hover { color: var(--sn-nova); }

/* "Herramientas" en el pie normal — disclosure NATIVO (<details>, sin JS),
   al comienzo de la línea de enlaces de sitio y con el mismo aspecto que
   esos enlaces (lo da el genérico .sn-foot-tools-summary, más abajo). A
   diferencia del pie de pantalla completa (que despliega hacia ABAJO), aquí
   la lista crece hacia ARRIBA, a su sitio de siempre sobre la línea: la
   columna del <details> es column-reverse → el summary queda abajo (en la
   línea) y la lista se apila encima. `align-items: flex-end` mantiene el
   summary alineado con los enlaces de sitio; al abrir, [open] ocupa la fila
   completa para que la lista no ensanche la línea. */
.sn-footer-main .sn-foot-line { align-items: flex-end; }
.sn-footer-main .sn-foot-tools-dd { display: flex; flex-direction: column-reverse; }
.sn-footer-main .sn-foot-tools-dd[open] { flex-basis: 100%; }

/* Licencia de la wiki (copyright de MediaWiki, $wgRights*): tras los enlaces
   de sitio, en el mismo tono pequeño y discreto; el enlace en color de enlace. */
.sn-foot-license {
	margin: 0;
	font-size: calc(var(--sn-fs-sm) * .8);
	color: var(--sn-ink-faint);
}
.sn-foot-license a { color: var(--sn-link); text-decoration: none; }
.sn-foot-license a:hover { color: var(--sn-link-hover); text-decoration: underline; }

/* — Columna angosta — bloque editable (Stella-Nova:Pie) — */
.sn-footer-side { display: flex; flex-direction: column; gap: var(--sn-s-5); }

/* Bloque institucional editable (Stella-Nova:Pie), al pie de la columna. */
.sn-footer-managed { color: var(--sn-ink); }
.sn-footer-managed :is(h1,h2,h3,h4) {
	font-family: var(--sn-font-text); color: var(--sn-ink);
	font-weight: 680; letter-spacing: -.012em;
	font-size: var(--sn-fs-md); margin: 0 0 var(--sn-s-2);
}
.sn-footer-managed a { color: var(--sn-link); }
.sn-footer-managed :is(p, ul, ol) { margin: 0 0 var(--sn-s-3); }
.sn-footer-managed > :last-child { margin-bottom: 0; }

/* ── Preferencias de lectura: apéndice del menú de usuario ──
   Ya no hay panel/diálogo aparte. Tras el separador (<hr>) del menú van
   los dos segmentos (Tema · Tamaño de letra). */
.sn-menu-sep {
	/* `height: 0; background: none` neutraliza el <hr> del core (elements.less:
	   `height: 1px; background-color: #aaa`), que si no se filtra como una caja
	   gris bajo el filete. La línea la da SOLO el border-top. */
	border: 0; border-top: 1px solid var(--sn-hairline-soft);
	height: 0; background: none;
	margin: var(--sn-s-2) 0;
}
.sn-menu-prefs {
	display: flex; flex-direction: column; gap: var(--sn-s-3);
	padding: var(--sn-s-1) var(--sn-s-3) var(--sn-s-2);
}
.sn-pref { border: 0; margin: 0; padding: 0; min-width: 0; }
.sn-pref legend {
	padding: 0 0 var(--sn-s-2); font-size: calc(var(--sn-fs-xs) * .88);
	font-weight: 600; color: var(--sn-ink-faint);
	text-transform: uppercase; letter-spacing: .06em;
}

/* Control segmentado */
.sn-seg {
	display: flex; gap: 2px; padding: 3px;
	background: var(--sn-sunk);
	border-radius: var(--sn-radius);
}
.sn-seg button {
	flex: 1; min-width: 0; cursor: pointer;
	background: transparent; border: 0; color: var(--sn-ink-soft);
	font: inherit; font-size: var(--sn-fs-sm);
	padding: .4rem .5rem; border-radius: calc(var(--sn-radius) - 2px);
	transition: background var(--sn-dur-1), color var(--sn-dur-1);
}
.sn-seg button:hover { color: var(--sn-ink); }
.sn-seg button[aria-pressed="true"], .sn-seg button.is-on {
	background: var(--sn-paper); color: var(--sn-ink); font-weight: 600;
	box-shadow: var(--sn-lift-soft);
}

/* Segmentos con contenido no-textual (íconos / letras de talla): el botón
   centra su contenido y, en el caso del ícono, éste sigue el color del
   botón (ink-soft en reposo, ink al activo/hover) en vez de la política
   global de íconos — aquí el ícono ES el control. */
.sn-seg-icon button, .sn-seg-sz button {
	display: inline-flex; align-items: center; justify-content: center;
	min-height: 1.6rem;
}
.sn-seg-icon .sn-i { color: inherit; }
/* Tallas S·M·L: letra que crece con la talla (guiño dimensional). */
.sn-seg-sz button { font-weight: 600; letter-spacing: .02em; line-height: 1; }
.sn-seg-sz button[data-v="small"]  { font-size: calc(var(--sn-fs-sm) * .82); }
.sn-seg-sz button[data-v="medium"] { font-size: var(--sn-fs-sm); }
.sn-seg-sz button[data-v="large"]  { font-size: calc(var(--sn-fs-sm) * 1.22); }

/* Familia Aa/Aa: cada botón muestra UNA 'a' en su propia familia primitiva
   (sans / serif) para que la elección sea visual, no textual. Forzamos las
   primitivas --sn-font-sans/--sn-font-serif, NO los alias --sn-font-text/
   --sn-font-quote — esos cambian con la preferencia y los specimens
   quedarían iguales al alternar (el bug a evitar). El glifo va a mayor
   tamaño que las S·M·L para que el contraste de familia se lea bien. */
.sn-seg-fam button { line-height: 1; padding: 0; }
.sn-fam-spec {
	font-size: calc(var(--sn-fs-sm) * 1.3);
	font-weight: 500; line-height: 1;
	display: inline-block;
}
.sn-fam-sans  { font-family: var(--sn-font-sans);  font-feature-settings: "ss01"; }
.sn-fam-serif { font-family: var(--sn-font-serif); font-optical-sizing: auto; }

/* ─────────── ExperimentalCanvas + UnifiedMenuAffordance ───────────
   Pantalla completa (__PANTALLACOMPLETA__): la página POSEE el viewport.
   El esqueleto se reduce a:
     · una barra mínima (.sn-fs-bar) con el isotipo cuadrado de Casiopea
       como único disparador → abre un modal único segmentado.
     · el contenido del wiki (.sn-canvas-body) con padding equivalente al
       de .sn-paper para que tipografía y márgenes vivan como en cualquier
       otra página.
   No hay botón "salir de pantalla completa": la salida vive en el modal
   (Portada / Navegación). */
.sn-canvas {
	/* Pantalla completa: el papel cubre el viewport entero —de lado a
	   lado y de arriba a abajo. El `.sn-canvas` NO se centra ni limita;
	   son `.sn-canvas-body` y `.sn-fs-bar` quienes cargan la `max-width`
	   del shell y `margin-inline: auto` para mantener el contenido en
	   una columna legible. Así el campo (con grano) NO se asoma por los
	   lados y la atmósfera es la de una hoja única.

	   Contrato de tokens: aliasamos `--sn-paper-px/py` al `--sn-canvas-px/py`
	   para que reglas legacy de `.sn-paper :is(.full-width, …)` operen
	   aquí sin duplicarse. */
	--sn-canvas-px: clamp(var(--sn-s-4), 4vw, var(--sn-s-7));
	--sn-canvas-py: clamp(var(--sn-s-5), 4vw, var(--sn-s-7));
	--sn-paper-px: var(--sn-canvas-px);
	--sn-paper-py: var(--sn-canvas-py);
	display: block;
	min-height: 100vh;
	padding: var(--sn-canvas-py) var(--sn-canvas-px);
	background: var(--sn-paper);
	color: var(--sn-ink);
	/* `.full-width` usa `width: 100vw` con margenes negativos calculados
	   en `vw`. Sin `clip`, micro-desfases sub-pixel pueden disparar scroll
	   horizontal en algún viewport. `clip` no crea contexto propio (a
	   diferencia de `hidden`, que rompería sticky/fixed). */
	overflow-x: clip;
}
/* Pantalla completa = contenido ABSOLUTAMENTE LIBRE: el cuerpo NO se centra
   ni se limita al ancho de lectura; ocupa todo el canvas (el único margen es
   el padding básico de `.sn-canvas`, --sn-canvas-px). La prosa ya no se acota
   a --sn-measure: el autor estructura con el framework de grilla (.grid). */
.sn-canvas-body {
	max-width: none;
	margin-inline: 0;
}
/* Afordancia única de pantalla completa: el isotipo SOBREPUESTO, fijo y EN
   FRENTE del contenido, alineado a la columna de lectura. Sin "barra": este
   contenedor full-width es solo el RIEL de posición; `pointer-events: none`
   deja pasar los clics al contenido salvo en el glifo (y el menú). El
   `padding-top` lo baja un poco respecto al borde del viewport. */
.sn-fs-md {
	position: fixed; inset: 0 0 auto 0; z-index: var(--sn-z-panel);
	display: flex; align-items: flex-start; justify-content: flex-end;
	max-width: calc(var(--sn-shell) + 2 * var(--sn-canvas-px));
	margin-inline: auto;
	padding: var(--sn-s-6) var(--sn-canvas-px) 0;
	pointer-events: none;
}
.sn-fs-md > * { pointer-events: auto; }
.sn-fs-trigger {
	display: inline-flex; align-items: center; justify-content: center;
	width: 2.5rem; height: 2.5rem; padding: 0;
	background: transparent; border: 0; cursor: pointer;
	color: var(--sn-ink);                /* el glifo SVG usa currentColor */
	border-radius: var(--sn-radius);
	transition: color var(--sn-dur-1), transform var(--sn-dur-1);
}
.sn-fs-trigger:hover, .sn-fs-trigger:focus-visible {
	color: var(--sn-nova);
}
.sn-fs-trigger:focus-visible {
	outline: 2px solid var(--sn-nova); outline-offset: 2px;
}
.sn-fs-glyph, .sn-fs-glyph svg {
	display: block; width: 2.5rem; height: 2.5rem;
}
.sn-fs-glyph svg {
	border-radius: var(--sn-radius);
	/* Cuño seco: brillo blanco duro y muy cercano bajo la constelación, como
	   el relieve del papel embossed (el isotipo como sello en seco). */
	filter: drop-shadow(1px 1px 0 rgba(255, 255, 255, .8));
}

/* — Modal único de pantalla completa — replica el patrón modal unificado
   (mismo look que la búsqueda/nav/prefs en compact), pero forzado a
   presentarse fullscreen INCLUSO en desktop, porque vivimos dentro de
   __PANTALLACOMPLETA__ y no hay cabecera donde anclar un popover. */
.sn-md.sn-md-fs[data-sn-open] .sn-menu-list {
	position: fixed; inset: 0;
	width: 100%; height: 100dvh;
	max-width: none; min-width: 0;
	display: flex; flex-direction: column;
	/* Traslúcido: desenfoca y satura el fondo, MISMO lenguaje que la barra
	   superior (algo más de blur y opacidad por ser superficie a pantalla
	   completa, para que el menú siga siendo legible sobre el contenido). */
	background: color-mix(in oklab, var(--sn-field) 72%, transparent);
	-webkit-backdrop-filter: saturate(1.5) blur(16px);
	backdrop-filter: saturate(1.5) blur(16px);
	color: var(--sn-ink);
	border: 0; border-radius: 0; box-shadow: none;
	margin: 0; padding: 0;
	z-index: 1000;
	overflow: hidden;
	opacity: 1; visibility: visible; transform: none;
	top: 0; right: auto; bottom: auto; left: 0;
	animation: sn-modal-in var(--sn-dur-2) var(--sn-ease) both;
}
/* Cabecera del modal fullscreen — mínima: SOLO el botón X flotando
   arriba a la derecha. Sin barra, sin filete, sin título visible (el
   contexto del modal es obvio: las propias secciones lo nombran). */
.sn-md.sn-md-fs .sn-modal-head {
	display: contents;
}
.sn-md.sn-md-fs .sn-modal-title {
	position: absolute; width: 1px; height: 1px;
	margin: -1px; padding: 0; overflow: hidden;
	clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.sn-md.sn-md-fs .sn-modal-x {
	/* Alineado al borde derecho de la columna de lectura — EXACTAMENTE donde
	   está el isotipo (mismo `top` y mismo borde), no pegado al viewport: al
	   abrir, la X cae sobre el lugar del iso. `(100% - shell)/2` es el margen
	   centrado del ancho de página; en viewports angostos cae a `canvas-px`. */
	position: absolute;
	top: var(--sn-s-6);
	right: max(var(--sn-canvas-px), calc((100% - var(--sn-shell)) / 2));
	z-index: 2;
	display: grid; place-items: center;
	background: transparent; border: 0; padding: 0;
	width: 2.5rem; height: 2.5rem;
	color: var(--sn-ink-soft); cursor: pointer;
	border-radius: var(--sn-radius);
}
.sn-md.sn-md-fs .sn-modal-x:hover,
.sn-md.sn-md-fs .sn-modal-x:focus-visible {
	background: var(--sn-sunk); color: var(--sn-ink);
}
/* Cuerpo del modal — CSS Grid con breakpoints explícitos: 1 columna por
   defecto, 2 a ≥ 48rem, 3 a ≥ 64rem (pantallas grandes). El scroll
   vertical funciona porque con grid (a diferencia de multicol) los
   items se apilan en filas nuevas en lugar de desbordar lateralmente.
   `align-content: start` evita que los items se estiren verticalmente.
   El padding superior reserva sitio bajo la X flotante. */
.sn-md.sn-md-fs .sn-modal-body {
	display: grid;
	grid-template-columns: 1fr;
	align-content: start;
	gap: var(--sn-s-6) var(--sn-s-8);   /* row-gap · column-gap (gutter ancho) */
	flex: 1; min-height: 0;
	overflow-y: auto; overscroll-behavior: contain;
	padding: calc(var(--sn-s-7) + var(--sn-s-2)) var(--sn-s-5) calc(var(--sn-s-6) + env(safe-area-inset-bottom));
	max-width: var(--sn-shell);
	margin-inline: auto;
	width: 100%;
}
@media (min-width: 48rem) {
	.sn-md.sn-md-fs .sn-modal-body { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 64rem) {
	.sn-md.sn-md-fs .sn-modal-body { grid-template-columns: repeat(3, 1fr); }
}
/* La sección del buscador atraviesa todas las columnas y centra el form
   con `max-width: 30em`: lectura cómoda sin estirarse de lado a lado
   en grids amplios. */
.sn-md.sn-md-fs .sn-fs-sec--search {
	grid-column: 1 / -1;
}
.sn-md.sn-md-fs .sn-fs-sec--search .sn-fs-search {
	max-width: 30em;
}
/* Secciones — bloques discretos. Sin filete horizontal: la separación
   la dan el grid-gap y los encabezados. */
.sn-fs-sec {
	display: flex; flex-direction: column;
	gap: var(--sn-s-3);
	margin: 0;
	min-width: 0;        /* protege el grid de 3: un enlace largo desborda
	                        dentro de su columna en vez de ensancharla */
}
.sn-fs-h {
	margin: 0;
	font-family: var(--sn-font-text);
	font-size: var(--sn-fs-xs); font-weight: 600;
	text-transform: uppercase; letter-spacing: .08em;
	color: var(--sn-ink-faint);
}
/* Subgrupos dentro de Navegación: cada portlet trae su label como h4. */
.sn-fs-group {
	display: flex; flex-direction: column; gap: var(--sn-s-2);
}
.sn-fs-group + .sn-fs-group { margin-top: var(--sn-s-3); }
.sn-fs-subh {
	margin: 0;
	font-family: var(--sn-font-text);
	font-size: var(--sn-fs-xs); font-weight: 600;
	color: var(--sn-ink-soft);
	letter-spacing: 0;
}

/* — Reset cascada: la regla genérica `.sn-menu-list :is(a, …) {
   display: flex; padding: …; … }` (los enlaces del menú-popover del
   chrome normal) tiene especificidad (0,2,0) y sobreescribe a nuestras
   reglas de pills/links. Anidamos bajo `.sn-fs-menu` para subir a
   (0,2,1)+ y devolver el control. Aplica a links comunes y a botones. */

/* Lista inline de enlaces separados por coma — usada en la sección
   Navegación: lectura corrida, sin afordancia de "botón". Cada <li> es
   inline; `::before` inserta la coma entre items (no antes del primero
   ni después del último). El link se ve como texto del cuerpo en
   `--sn-link` (carmín). */
.sn-fs-menu .sn-fs-links {
	list-style: none; margin: 0; padding: 0;
	display: block;
	line-height: var(--sn-leading);
}
.sn-fs-menu .sn-fs-links li {
	display: inline; margin: 0; padding: 0;
}
.sn-fs-menu .sn-fs-links li:not(:last-child)::after {
	/* Coma PEGADA al enlace que la precede (en su ::after, no en el ::before del
	   siguiente): así enlace+coma nunca se separan, y el ÚNICO punto de salto es
	   el espacio que sigue a la coma → ninguna fila empieza con ",". OJO: el
	   espacio va ESCAPADO (`\0020`), porque el minificador de ResourceLoader
	   borra el espacio literal de `content: ", "` y dejaría la lista sin punto
	   de quiebre. El navegador lee `\0020` como espacio normal (quebrable). */
	content: ",\0020";
	color: var(--sn-ink-faint);
}
.sn-fs-menu .sn-fs-links a {
	display: inline;                  /* anula display:flex del .sn-menu-list a */
	padding: 0;                       /* anula padding del .sn-menu-list a */
	background: transparent;
	color: var(--sn-link);            /* carmín del skin */
	text-decoration: none;
	border: 0;
	border-radius: 0;
	font: inherit;
	/* Mismo tamaño y ancho que los nav-pills (antes heredaba ~--sn-fs-base del
	   modal, mucho más grande, y por eso desbordaba). Al achicar a --sn-fs-xs
	   los enlaces caben en su columna en una sola línea. */
	font-size: var(--sn-fs-xs);
	font-stretch: normal;
	/* Enlace ATÓMICO: nunca se parte en dos filas. El salto ocurre ENTRE
	   enlaces, en el espacio que sigue a la coma (ver el ::after de arriba), y
	   la coma viaja pegada al enlace → ninguna fila empieza con ",". */
	white-space: nowrap;
	transition: color var(--sn-dur-1);
}
.sn-fs-menu .sn-fs-links a:hover,
.sn-fs-menu .sn-fs-links a:focus-visible {
	color: var(--sn-link-hover);
	background: transparent;
	text-decoration: underline;
	text-underline-offset: 2px;
}
.sn-fs-menu .sn-fs-links li.new a { color: var(--sn-link); opacity: .7; }

/* Nav-pills — el lenguaje secundario del modal (acciones). Cada ítem
   es una cápsula compacta; los grupos se acomodan con flex-wrap y gap. */
.sn-fs-menu .sn-fs-pills {
	list-style: none; margin: 0; padding: 0;
	display: flex; flex-wrap: wrap; gap: var(--sn-s-2);
}
.sn-fs-menu .sn-fs-pills li { margin: 0; padding: 0; }
.sn-fs-menu .sn-fs-pills :is(a, button) {
	display: inline-flex; align-items: center; gap: var(--sn-s-1);
	padding: calc(var(--sn-s-1) - 1px) var(--sn-s-3);
	background: var(--sn-sunk);
	color: var(--sn-ink);
	border: 1px solid var(--sn-hairline);
	border-radius: 999px;
	/* Mismo tamaño y font-stretch que los nav-links (igualados). */
	font: inherit; font-size: var(--sn-fs-xs); font-stretch: normal;
	text-decoration: none; cursor: pointer;
	transition: background var(--sn-dur-1), color var(--sn-dur-1),
		border-color var(--sn-dur-1);
}
.sn-fs-menu .sn-fs-pills :is(a, button):hover,
.sn-fs-menu .sn-fs-pills :is(a, button):focus-visible {
	background: var(--sn-paper);
	border-color: var(--sn-ink-faint);
	color: var(--sn-nova);
}
.sn-fs-menu .sn-fs-pills li.selected a,
.sn-fs-menu .sn-fs-pills li.active a {
	background: var(--sn-paper);
	border-color: var(--sn-ink-faint);
	color: var(--sn-nova);
}
/* MediaWiki marca enlaces "rojos" (a páginas inexistentes) con .new. */
.sn-fs-menu .sn-fs-pills li.new a { color: var(--sn-link); }

/* — Form de búsqueda dentro del modal — MISMO input que la barra superior
   (pill: transparente en reposo → papel al foco, lupa a la derecha). */
.sn-fs-search {
	display: flex; align-items: center; gap: var(--sn-s-1);
	height: var(--sn-ctl); box-sizing: border-box;
	padding: 0 var(--sn-s-1) 0 var(--sn-s-4);
	background: transparent;
	border: 1px solid var(--sn-hairline);
	border-radius: 999px;
	transition: background var(--sn-dur-1), border-color var(--sn-dur-1);
}
.sn-fs-search:focus-within { background: var(--sn-paper); border-color: var(--sn-hairline); }
.sn-fs-search :is(input[type="search"], #searchInput, .mw-searchInput) {
	flex: 1; min-width: 0;
	background: transparent; border: 0; outline: none;
	color: var(--sn-ink); font: inherit;
}

/* — Pie de pantalla completa — sobre el MISMO papel del canvas. Acompaña al
   contenido libre: NO se centra ni se acota a --sn-measure; queda alineado a
   la IZQUIERDA con el margen básico del canvas (--sn-canvas-px). UNA sola
   columna. Sin "última edición" ni columna derecha (Stella-Nova:Pie).
   Reutiliza .sn-foot-toollist / .sn-foot-places / .sn-foot-license; las
   herramientas van en un <details> discreto rotulado "Herramientas". */
.sn-fs-footer { margin-top: var(--sn-s-7); }
.sn-fs-footer-inner {
	max-width: none; margin-inline: 0;
	display: flex; flex-direction: column; gap: var(--sn-s-4);
	font-size: var(--sn-fs-xs); color: var(--sn-ink-soft);
}
/* "Herramientas" + enlaces de sitio en UNA línea, con el mismo ritmo de
   separación que entre los enlaces de sitio. */
.sn-foot-line {
	display: flex; flex-wrap: wrap; align-items: baseline;
	gap: var(--sn-s-1) var(--sn-s-3);
}
.sn-foot-tools-dd { margin: 0; }
/* El summary se ve EXACTAMENTE como un enlace de .sn-foot-places: mismo
   color, tamaño y peso normal (sin negrita), sin marcador del UA. Al abrir,
   la lista de herramientas cae debajo. */
.sn-foot-tools-summary {
	cursor: pointer; list-style: none;
	color: var(--sn-ink-soft);
	font-size: calc(var(--sn-fs-sm) * .8);
	font-weight: 400;
	transition: color var(--sn-dur-1);
}
.sn-foot-tools-summary::-webkit-details-marker { display: none; }
.sn-foot-tools-summary::marker { content: ""; }
.sn-foot-tools-summary:hover { color: var(--sn-nova); }

/* ── Disclosure animado (GENÉRICO, para todo <details> del skin) ──
   Anima apertura/cierre con la API moderna: ::details-content envuelve el
   contenido y `interpolate-size: allow-keywords` permite transicionar el
   block-size desde/hacia `auto`. Cubre el pie "Herramientas" (normal y
   pantalla completa) y cualquier <details> futuro (p. ej. secciones
   colapsables del skin). Donde no haya soporte (Firefox/Safari hoy) degrada
   a toggle instantáneo, sin daño. La duración usa --sn-dur-2, que ya se
   apaga con prefers-reduced-motion vía --sn-motion. Los colapsables de la
   wiki (mw-collapsible) ya animan con su propio slide del core JS, así que
   el comportamiento queda parejo en todo el skin. */
:root { interpolate-size: allow-keywords; }
details::details-content {
	block-size: 0; overflow: hidden; opacity: 0;
	transition: block-size var(--sn-dur-2) var(--sn-ease),
	            opacity var(--sn-dur-2) var(--sn-ease),
	            content-visibility var(--sn-dur-2) var(--sn-ease);
	transition-behavior: allow-discrete;
}
details[open]::details-content { block-size: auto; opacity: 1; }

/* ─────────────────────── Responsive ─────────────────────── */
/* Cabecera: por arriba de 48rem (tablet vertical) la barra es una sola
   línea con el form de búsqueda inline. Por debajo entra el modo COMPACT
   completo (ver bloque @48rem más abajo): el form se colapsa, aparece el
   trigger-lupa y todos los paneles se presentan como modal a pantalla
   completa. No hay estado intermedio de cabecera envuelta a 2 líneas. */
@media (max-width: 48rem) {
	.sn-footer-inner { grid-template-columns: 1fr; gap: var(--sn-s-5); }
}

/* ─── COMPACT (tablet vertical y menos): una línea de íconos · modales
   fullscreen ─── Punto de quiebre 48rem (≈ 768 px, iPad portrait) elegido
   para que el form de búsqueda nunca llegue a envolverse en 2 líneas: por
   encima cabe inline; por debajo se colapsa y aparece la lupa. La barra
   superior queda como una sola línea de íconos accionables, espaciados
   uniformemente. CADA panel emergente (Navegación · Página · Usuario ·
   Búsqueda · Preferencias) se presenta con EL MISMO patrón: modal a
   pantalla completa, fondo papel, cabecera sticky con título + botón
   cerrar (X), cuerpo con scroll propio, una sola columna. No hay scrim —
   el modal cubre todo. Cumple TransientPanel (DismissableByEscape,
   FocusContained, SelfContainedScroll y descarte por X). El JS sincroniza
   el mismo umbral en COMPACT_MQ. */
@media (max-width: 48rem) {
	/* — La barra: una línea, reparto homogéneo, sin envolver — */
	.sn-header-bar {
		flex-wrap: nowrap;
		justify-content: space-between;
		gap: var(--sn-s-2);
		padding-inline: var(--sn-s-3);
	}
	/* Íconos equidistantes: aplanamos los grupos con `display: contents` para
	   que cada ícono (isotipo · nav · búsqueda · lápiz · página · usuario) sea
	   ítem DIRECTO de la barra. Así el `space-between` del `.sn-header-bar`
	   reparte todos los gaps por igual, sin los espacios diferenciados que
	   metían los grupos (isotipo+nav juntos, lápiz+menú juntos). Sólo en móvil;
	   en desktop los grupos siguen intactos para anclar sus popovers. */
	.sn-brandnav, .sn-searchbox, .sn-pagecluster, .sn-usertools {
		display: contents;
	}

	/* Layout sin márgenes: la hoja va borde-a-borde y se pega contra la
	   barra superior arriba y contra el pie abajo — sin padding ambient en
	   el shell, sin radius, sin bordes laterales, sin sombra (la hoja ES la
	   pantalla). Se mantiene sólo la línea inferior, que coincide con el
	   border-top del pie — al colapsarse con margin-top:0 forman un único
	   filete entre hoja y pie, no doble. */
	.sn-shell { padding: 0; }
	.sn-paper {
		border-radius: 0;
		border-left: 0; border-right: 0; border-top: 0;
		box-shadow: none;
		margin-inline: 0;
	}
	.sn-footer { margin-top: 0; }

	/* Isotipo: glifo cuadrado en lugar del wordmark. */
	.sn-isotype .sn-isotype-full    { display: none; }
	.sn-isotype .sn-isotype-compact { display: contents; }

	/* Búsqueda: form colapsado, trigger (lupa) visible — sin fondo
	   propio (igual al resto de íconos de la barra). Especificidad
	   .sn-searchbox … para alinear con la regla base de más arriba y
	   no quedar pisada por .sn-iconbtn. */
	.sn-searchbox                       { flex: 0 0 auto; }
	.sn-searchbox .sn-search            { display: none; }
	.sn-searchbox .sn-search-trigger    { display: inline-grid; background: transparent; }

	/* Triggers de menú: ícono-cuadrado del mismo tamaño que el resto, sin
	   chevron (la abierta = modal). */
	.sn-md-icononly { padding: 0; width: var(--sn-ctl); justify-content: center; }
	.sn-md-icononly .sn-md-caret { display: none; }

	.sn-notif { display: none; }   /* notif vive dentro del menú-usuario */

	/* ───────────── PATRÓN MODAL UNIFICADO ─────────────
	   Aplica a:
	     · .sn-md[data-sn-open] .sn-menu-list — Navegación / Página / Usuario
	       (el menú de usuario incluye ahora las preferencias de lectura)
	     · html[data-sn-search-open] .sn-search — Búsqueda
	   Mismo tamaño, mismo fondo, misma posición, misma transición. */
	.sn-md[data-sn-open] .sn-menu-list,
	html[data-sn-search-open] .sn-search {
		position: fixed;
		inset: 0;
		width: 100%; height: 100dvh;
		max-width: none; min-width: 0;
		display: flex; flex-direction: column;
		background: var(--sn-paper); color: var(--sn-ink);
		border: 0; border-radius: 0;
		box-shadow: none;
		margin: 0; padding: 0;
		z-index: 1000;
		overflow: hidden;       /* el scroll vive en .sn-modal-body */
		animation: sn-modal-in var(--sn-dur-2) var(--sn-ease) both;
	}

	/* Anula los estilos de popover desktop dentro del modal. */
	.sn-md[data-sn-open] .sn-menu-list {
		opacity: 1; visibility: visible; transform: none;
		top: 0; right: auto; bottom: auto; left: 0;
	}

	/* — Cabecera modal: sticky arriba, título + X. — */
	.sn-modal-head {
		flex: none;
		display: flex; align-items: center; justify-content: space-between;
		gap: var(--sn-s-3);
		padding: var(--sn-s-3) var(--sn-s-4);
		border-bottom: 1px solid var(--sn-hairline-soft);
		background: var(--sn-paper);
		min-height: var(--sn-ctl);
	}
	.sn-modal-title {
		margin: 0; font-family: var(--sn-font-text);
		font-size: var(--sn-fs-md); font-weight: 600;
		color: var(--sn-ink); letter-spacing: -.008em;
	}
	.sn-modal-x {
		background: transparent; border: 0; padding: 0;
		width: 2.25rem; height: 2.25rem; flex: none;
		display: grid; place-items: center;
		color: var(--sn-ink-soft); cursor: pointer;
		border-radius: var(--sn-radius);
	}
	.sn-modal-x:hover, .sn-modal-x:focus-visible {
		background: var(--sn-sunk); color: var(--sn-ink);
	}

	/* — Cuerpo del modal: scrollable, una sola columna. — */
	.sn-modal-body {
		flex: 1; min-height: 0;
		overflow-y: auto; overscroll-behavior: contain;
		padding: var(--sn-s-4) var(--sn-s-4) calc(var(--sn-s-5) + env(safe-area-inset-bottom));
		display: flex; flex-direction: column; gap: var(--sn-s-4);
	}
	/* Megamenu: anula columns:2 del desktop dentro del modal. */
	.sn-megamenu { columns: auto; column-gap: 0; max-height: none; min-width: 0; }
	.sn-megamenu .sn-modal-body { columns: auto; }
	.sn-mm-sec { margin: 0; }

	/* — Búsqueda modal — el form ocupa toda la pantalla; el input es
	   visible sobre papel (no transparente sobre fondo oscuro). — */
	html[data-sn-search-open] .sn-searchbox .sn-search-close { display: inline-grid; }
	.sn-search-close {
		background: transparent; border: 0; padding: 0;
		width: 2.25rem; height: 2.25rem; flex: none;
		display: grid; place-items: center;
		color: var(--sn-ink-soft); cursor: pointer;
		border-radius: var(--sn-radius);
	}
	.sn-search-close:hover, .sn-search-close:focus-visible {
		background: var(--sn-sunk); color: var(--sn-ink);
	}
	html[data-sn-search-open] .sn-search {
		flex-direction: row; align-items: center;
		gap: var(--sn-s-3);
		padding: var(--sn-s-3) var(--sn-s-4);
		border-bottom: 1px solid var(--sn-hairline-soft);
		height: auto; min-height: var(--sn-ctl);
	}
	html[data-sn-search-open] .sn-search :is(input[type="search"], #searchInput, .mw-searchInput) {
		flex: 1; min-width: 0;
		background: var(--sn-sunk); color: var(--sn-ink);
		border: 1px solid var(--sn-hairline);
		border-radius: var(--sn-radius);
		padding: var(--sn-s-3) var(--sn-s-4);
		font-size: var(--sn-fs-md);
		height: auto; line-height: 1.4;
	}
	html[data-sn-search-open] .sn-search :is(input):focus {
		background: var(--sn-paper);
		border-color: var(--sn-ink-faint);
		outline: none;
	}
	html[data-sn-search-open] .sn-search-go {
		color: var(--sn-ink-soft); background: transparent;
	}

	/* Bloqueo de scroll del body cuando hay cualquier modal abierto. */
	html[data-sn-modal] { overflow: hidden; }
	html[data-sn-modal] body { overflow: hidden; }
}

/* En DESKTOP la cabecera modal no debe verse (el dropdown popover se
   posiciona junto al trigger; los hijos directos viven en su contenedor
   sin envoltura). */
@media (min-width: 36.01rem) {
	.sn-modal-head { display: none; }
	.sn-modal-body { display: contents; }
	.sn-modal-x    { display: none; }
}

@keyframes sn-modal-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

/* ─────────────── Entrada orquestada (una sola) ───────────────
   El movimiento ya no es preferencia del usuario; se respeta sólo la
   señal del SO (prefers-reduced-motion). */
@media (prefers-reduced-motion: no-preference) {
	.sn-paper {
		animation: sn-rise calc(640ms * var(--sn-motion)) var(--sn-ease) both;
	}
	.sn-header {
		animation: sn-fade calc(520ms * var(--sn-motion)) var(--sn-ease) both;
	}
}
@keyframes sn-rise {
	from { opacity: 0; transform: translateY(14px); }
	to   { opacity: 1; transform: translateY(0); }
}
@keyframes sn-fade { from { opacity: 0; } to { opacity: 1; } }

/* Pequeñas verdades de MediaWiki que conviene respetar */
/* Editar (junto a títulos): nav-pill sin corchetes, distanciado del
   título. Invisible por defecto; aparece al pasar el cursor sobre el
   heading o al recibir foco por teclado dentro de éste. Mantiene su
   espacio reservado (`opacity`, no `display`/`visibility`) para que el
   heading no salte al revelarse, y para no sacar el link del tab order
   (accesibilidad). Cubre AMBAS formas: <hN> directo (markup legacy, el
   .mw-editsection vive como hijo del hN) y .mw-headingN (Parsoid, el
   .mw-editsection es hermano del hN dentro del wrapper). */
.mw-editsection {
	display: inline-flex; align-items: center; gap: var(--sn-s-2);
	margin-inline-start: var(--sn-s-4);
	font-family: var(--sn-font-text); font-size: var(--sn-fs-xs);
	font-weight: 400; vertical-align: middle;
	/* Look IDÉNTICO en cualquier sección, sin importar el heading que lo
	   contiene. El botón hereda del hN todas sus métricas de texto; las
	   neutralizamos para que la píldora no cambie de tamaño entre niveles:
	     · `line-height: 1` — clave: sin esto el enlace hereda el
	       line-height del heading (h1/h2 = 2 baselines, h3–h6 = 1), lo que
	       estiraba la altura de la píldora según el título. Fijo en 1, la
	       altura = font-size + padding propios, constante.
	     · `letter-spacing: normal` — anula el tracking negativo de los hN.
	     · `font-weight`/`font-size`/`font-family` — propios, no del heading.
	   (text-transform ya no hace falta — los headings del cuerpo no usan
	   uppercase desde el refactor del baseline grid). */
	line-height: 1;
	letter-spacing: normal;
	opacity: 0;
	transition: opacity var(--sn-dur-1);
}
:is(h1, h2, h3, h4, h5, h6, .mw-heading, .mw-heading1, .mw-heading2, .mw-heading3, .mw-heading4, .mw-heading5, .mw-heading6):hover .mw-editsection,
:is(h1, h2, h3, h4, h5, h6, .mw-heading, .mw-heading1, .mw-heading2, .mw-heading3, .mw-heading4, .mw-heading5, .mw-heading6):focus-within .mw-editsection {
	opacity: 1;
}
/* Los corchetes `[ ]` y el divider `|` ya NO se emiten: SkinStellaNova
   sobreescribe doEditSectionLinksHTML() y no genera esos spans (antes se
   ocultaban con display:none, puro ruido en el DOM). Si por lo que fuera
   reaparecieran, este es el sitio donde volverlos a ocultar. */
.mw-editsection a {
	display: inline-flex; align-items: center;
	padding: .15rem var(--sn-s-2);
	border: 1px solid transparent; border-radius: 999px;
	color: var(--sn-ink-faint); text-decoration: none;
	transition: background var(--sn-dur-1), border-color var(--sn-dur-1), color var(--sn-dur-1);
}
.mw-editsection a:hover, .mw-editsection a:focus-visible {
	background: var(--sn-paper); border-color: var(--sn-hairline);
	color: var(--sn-nova);
}
.mw-redirectedfrom, .mw-revision { color: var(--sn-ink-soft);
	font-size: var(--sn-fs-sm); }
.error, .mw-message-box-error { color: var(--sn-danger); }
.mw-message-box, .cdx-message {
	border-left: 3px solid var(--sn-ink-faint);
	background: var(--sn-sunk); padding: var(--sn-s-3) var(--sn-s-4);
	border-radius: 0 var(--sn-radius) var(--sn-radius) 0; margin: var(--sn-s-4) 0;
}

/* ─── Tablas de contenido: la skin es dueña del estilo de TODAS las
   tablas — regulares (`wikitable`) y resultados de consulta semántica
   (`smwtable`/`broadtable`). Diseño común: SIN contorno exterior y solo
   filetes HORIZONTALES — un filete normal (`--sn-hairline`) bajo las
   cabeceras y filetes muy finos (`--sn-hairline-soft`) entre filas; nunca
   filetes verticales. Texto al 80% del cuerpo y todo alineado a la
   izquierda, celdas y cabeceras por igual. Enrutado por tokens para ser
   coherente en claro y oscuro. ─── */
.sn-body :is(table.wikitable, table.smwtable, table.broadtable) {
	border-collapse: collapse;
	border: 0;                       /* sin contorno exterior */
	background: var(--sn-paper);
	color: var(--sn-ink);
	font-size: var(--sn-fs-table);   /* achicado unificado (≈80% de base, absoluto) */
	/* Ritmo vertical: cada línea de celda ocupa EXACTAMENTE un baseline.
	   No se usa el 1.65 unitless del cuerpo porque, con el font al 80%,
	   daría 0.8 × baseline por línea → fuera de la retícula. Con el
	   line-height absoluto = `--sn-baseline`, toda fila (de una o de
	   muchas líneas, p. ej. listas con <br>) cae en la grilla, y su
	   medio-interlineado reparte el aire arriba/abajo del texto. */
	line-height: var(--sn-baseline);
	margin: var(--sn-baseline-2) 0;  /* margen de bloque = múltiplo entero de baseline */
	max-width: 100%;
}
.sn-body :is(table.wikitable, table.smwtable, table.broadtable) :is(th, td) {
	/* Padding SOLO horizontal: el espaciado vertical lo da el
	   medio-interlineado del line-height = baseline. Un padding vertical
	   tendría que ser un baseline ENTERO para no descuadrar la fila, lo
	   que duplicaría el alto de celda; aquí el aire ya está en la línea. */
	padding: 0 var(--sn-s-3);
	border: 0;
	/* Filete como box-shadow: NO ocupa layout, así el 1px no se acumula
	   fila a fila ni desplaza la retícula (un border real sí lo haría). */
	box-shadow: inset 0 -1px 0 var(--sn-hairline-soft);  /* filete fino entre filas */
	text-align: left;                /* celdas y cabeceras a la izquierda */
	vertical-align: top;
	color: var(--sn-ink);
}
/* Fila(s) de cabecera: sin relleno de fondo, filete NORMAL (más marcado)
   separando cabeceras del cuerpo. Cubre <thead> y la 1ª fila de <th>
   cuando no hay <thead> — el caso típico de los resultados #ask de SMW. */
.sn-body :is(table.wikitable, table.smwtable, table.broadtable) > thead > tr > th,
.sn-body :is(table.wikitable, table.smwtable, table.broadtable) > tbody > tr:first-child > th,
.sn-body :is(table.wikitable, table.smwtable, table.broadtable) > tr:first-child > th {
	background: transparent;
	font-weight: 600;
	box-shadow: inset 0 -1px 0 var(--sn-hairline);     /* filete normal cabecera→cuerpo */
}
/* Sin borde exterior: la última fila no lleva filete inferior (se leería
   como contorno de cierre). */
.sn-body :is(table.wikitable, table.smwtable, table.broadtable) > :is(tbody, tfoot) > tr:last-child > :is(th, td),
.sn-body :is(table.wikitable, table.smwtable, table.broadtable) > tr:last-child > :is(th, td) {
	box-shadow: none;
}
.sn-body :is(table.wikitable, table.smwtable, table.broadtable) > caption {
	caption-side: top; text-align: left;
	line-height: var(--sn-baseline);   /* caption = 1 baseline, mantiene la grilla */
	color: var(--sn-ink-soft);
}
/* Realce de fila al pasar el cursor: útil para seguir filas anchas en las
   tablas de resultados. */
.sn-body :is(table.smwtable, table.broadtable) > tbody > tr:hover > td {
	background: color-mix(in oklab, var(--sn-nova-wash) 45%, transparent);
}
/* ─── Cabeceras ordenables (jquery.tablesorter, clase `.sortable`) ───
   Se descarta el cromo de flechas del core (sus SVG `sort_*.svg` y sus
   variantes invertidas para modo oscuro). En su lugar, íconos Feather
   (chevron) pintados como `mask` + `background-color` tokenizado: el color
   sigue a `--sn-ink*` y se adapta solo a claro/oscuro, sin SVG aparte.
   Estado neutro = doble chevron tenue (afford. de "ordenable"); activo =
   chevron up (ascendente) / down (descendente) a tinta plena. */
.sn-body table.sortable {
	/* chevron-up + chevron-down (neutro) */
	--sn-sort-both: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='8 9 12 5 16 9'/%3E%3Cpolyline points='8 15 12 19 16 15'/%3E%3C/svg%3E");
	/* chevron-up (ascendente) */
	--sn-sort-up: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='18 15 12 9 6 15'/%3E%3C/svg%3E");
	/* chevron-down (descendente) */
	--sn-sort-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
/* Anula el background-image de flecha del core (antes y después del JS). */
.sn-body table.sortable th.headerSort,
.sn-body .sortable:not(.jquery-tablesorter) > * > tr:first-child > th {
	background-image: none !important;
}
.sn-body table.sortable th.headerSort {
	cursor: pointer;
	position: relative;
	padding-right: 1.7em;
	transition: background-color var(--sn-dur-1) var(--sn-ease);
}
.sn-body table.sortable th.headerSort::after {
	content: "";
	position: absolute;
	right: .45em; top: 50%;
	width: 1em; height: 1em;
	transform: translateY(-50%);
	background-color: var(--sn-ink-faint);
	-webkit-mask: var(--sn-sort-both) no-repeat center / contain;
	mask: var(--sn-sort-both) no-repeat center / contain;
	transition: background-color var(--sn-dur-1) var(--sn-ease);
}
.sn-body table.sortable th.headerSortUp::after {
	-webkit-mask-image: var(--sn-sort-up); mask-image: var(--sn-sort-up);
	background-color: var(--sn-ink);
}
.sn-body table.sortable th.headerSortDown::after {
	-webkit-mask-image: var(--sn-sort-down); mask-image: var(--sn-sort-down);
	background-color: var(--sn-ink);
}
/* Hover: realce del fondo de la cabecera y del ícono. */
.sn-body table.sortable th.headerSort:hover {
	background-color: color-mix(in oklab, var(--sn-nova-wash) 50%, transparent);
}
.sn-body table.sortable th.headerSort:hover::after {
	background-color: var(--sn-ink);
}

/* `.toccolours`: caja auxiliar (no es tabla de datos) — solo enrutado de
   color/borde por tokens, sin el diseño de filetes de arriba. */
.sn-body .toccolours {
	background: var(--sn-sunk); color: var(--sn-ink);
	border: 1px solid var(--sn-hairline);
}

/* `table.template` / `table.plantilla` (alias bilingüe): ficha vertical
   clave→valor (NO es tabla de datos; migrada desde MediaWiki:Common.css).
   Cabecera = etiqueta a la derecha en versalita tenue; sin filetes. */
.sn-body table:is(.template, .plantilla) {
	width: 100%; margin: var(--sn-s-6) auto;
	border: 0;
	/* Mismo achicado que las tablas generales: el token único --sn-fs-table
	   (≈80% de base, absoluto). Una sola fuente para ambas. */
	font-size: var(--sn-fs-table);
	/* Interlínea cómoda atada a la fuente (unitless): entre medio baseline
	   (apretado) y baseline completo (aireado) no hay valor que tile la
	   grilla, así que la ficha va fuera de grilla a 1.4 × fs-table. */
	line-height: 1.4;
}
.sn-body table:is(.template, .plantilla) > tbody > tr { vertical-align: top; text-transform: uppercase; font-size: 89%; letter-spacing: 0.045ex;}
.sn-body table:is(.template, .plantilla) > tbody > tr > :is(th, td) { border: 0; }
/* `:first-child` para empatar la especificidad (0,3,4) de la regla de
   "fila de cabecera" de wikitable, que si no marca esta primera th a 600.
   En una ficha la primera fila es un par clave→valor, no una cabecera. */
.sn-body table:is(.template, .plantilla) > tbody > tr > th,
.sn-body table:is(.template, .plantilla) > tbody > tr:first-child > th {
	width: 20%; min-width: 110px;
	padding-right: 1.5ex;
	text-align: right;
	font-weight: 355;
	letter-spacing: .03ex;
	/* gris neutro (sin la calidez café de --sn-ink-faint), misma luminosidad */
	color: light-dark(#8c8c8c, #6f6f6f); background: transparent;
}
/* SMW pinta ciertos valores (p. ej. el campo Código) como `<div class="smwpre">`
   con caja de <pre>: padding 1em, borde azul punteado, fondo gris y color negro
   fijo. Dentro de la ficha eso rompe la fila. Lo despojamos a texto plano: sin
   caja, monospaced del skin, un punto más pesado, y color/interlínea heredados
   del cuerpo (así sigue al tema claro/oscuro). */
.sn-body table:is(.template, .plantilla) .smwpre {
	margin: 0; padding: 0;
	border: 0; background: none;
	font-family: var(--sn-font-mono);
	font-weight: 600;
	color: inherit;
	line-height: inherit;
}
.sn-body :is(.thumbinner, .mw-default-size, figure.mw-default-size) {
	background: color-mix(in oklab, var(--sn-sunk) 60%, transparent);
	border-color: var(--sn-hairline);
}
.sn-body :is(.thumbimage, figure img, .mw-image-border img) {
	border-color: var(--sn-hairline);
}
/* Bordes/fondos genéricos de wikitexto de uso común. (El <hr> ya se define
   arriba, en `.sn-body hr`; aquí no se repite.) */
.sn-body :is(.mw-references-wrap, .reflist, .references) {
	color: var(--sn-ink-soft);
}

/* Cajas auxiliares de páginas especiales (leyenda de "Cambios recientes",
   notas de "Páginas especiales", etc.) — `.mw-specialpages-notes` viene
   pegada a `.mw-changeslist-legend` en el mismo elemento, así que un solo
   selector las cubre. Se homologan al lenguaje visual de la TOC:
   fondo papel, filete `--sn-hairline-soft` casi imperceptible, esquinas
   `--sn-radius` y mismo ritmo de padding/margen. El `float: right` que
   trae core en `.mw-changeslist-legend` se respeta (es el patrón
   esperado en Cambios Recientes); aquí solo cambia su vestimenta. */
.sn-body :is(.mw-changeslist-legend, .mw-specialpages-notes) {
	margin: var(--sn-s-4) 0 var(--sn-s-6);
	padding: var(--sn-s-3) var(--sn-s-4);
	border: 1px solid var(--sn-hairline-soft);
	border-radius: var(--sn-radius);
	background: var(--sn-paper);
	color: var(--sn-ink);
	font-size: var(--sn-fs-sm);
	line-height: var(--sn-leading);
}
/* Cuando va flotada (cambios recientes), respeta la separación lateral
   del bloque editorial sin pegarse al texto. */
.sn-body .mw-changeslist-legend {
	margin-left: var(--sn-s-4);
	max-width: 26rem;
}
.sn-body :is(.mw-changeslist-legend, .mw-specialpages-notes) summary {
	cursor: pointer;
	font-weight: 600;
	color: var(--sn-ink);
}
.sn-body :is(.mw-changeslist-legend, .mw-specialpages-notes) dl {
	margin: var(--sn-s-2) 0 0;
}

/* ─── Botones de wikitexto (.wiki-btn) — portados de MediaWiki:Common.css ───
   Patrón de la portada y guías: un <span class="wiki-btn [red|green]"> que
   envuelve un enlace wiki. Vive aquí (no en Common.css) para que herede los
   tokens del skin y voltee claro/oscuro solo. Cambios respecto al original
   de producción: borde 1px (era 2px), forma de píldora (border-radius 2ex),
   y TODO color tokenizado. La variante `.blue` (#059BE6) se ELIMINÓ — no
   tenía token equivalente y no la usa ninguna página.

   Texto sobre relleno = `--sn-nova-ink`: voltea solo (blanco en claro,
   casi-negro en oscuro) y sirve a las tres variantes porque los tres
   rellenos (ink-soft, nova, ok) son oscuros en claro y claros en oscuro. */
.sn-body .wiki-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 1ex 1.5ex;
	border-radius: 2ex;
	border: 1px solid var(--sn-ink-soft);
	background-color: transparent;
	color: var(--sn-ink-soft);
	white-space: nowrap;
	text-decoration: none;
	cursor: pointer;
	transition: background-color .2s ease-in-out, border-color .2s ease-in-out, color .2s ease-in-out;
}
.sn-body .wiki-btn:hover,
.sn-body .wiki-btn:focus-within {
	background-color: var(--sn-ink-soft);
	border-color: var(--sn-ink-soft);
}
.sn-body .wiki-btn a {
	font-weight: 700;
	line-height: 90%;
	color: inherit !important;
	text-decoration: none !important;
	white-space: nowrap;
	background-color: transparent;
	border: none;
	padding: 0;
	margin: 0;
	display: inline-block;
	transition: text-shadow .2s ease-in-out;
}
.sn-body .wiki-btn a:hover,
.sn-body .wiki-btn a:focus {
	text-shadow: 1px 1px 1px rgba(0, 0, 0, .2);
}
.sn-body .wiki-btn:hover a,
.sn-body .wiki-btn:focus-within a {
	color: var(--sn-nova-ink) !important;
}
/* Variante roja (la nova del skin). */
.sn-body .wiki-btn.red {
	border-color: var(--sn-nova);
	color: var(--sn-nova);
}
.sn-body .wiki-btn.red:hover,
.sn-body .wiki-btn.red:focus-within {
	background-color: var(--sn-nova);
	border-color: var(--sn-nova);
}
/* Variante verde (token de éxito). */
.sn-body .wiki-btn.green {
	border-color: var(--sn-ok);
	color: var(--sn-ok);
}
.sn-body .wiki-btn.green:hover,
.sn-body .wiki-btn.green:focus-within {
	background-color: var(--sn-ok);
	border-color: var(--sn-ok);
}

/* ─── Grilla / grid: framework de layout semántico para wikitexto.
   Reemplaza el patrón `.row > .col-md-*` del skin Bootstrap anterior.
   Nombre bilingüe: `.grid` (canónico) y `.grilla` (alias, retrocompat).
   Uso esperado:

       <div class="grid cols-2"> … </div>
       <div class="grid cols-3 gap-l"> … </div>
       <div class="grid cols-auto"> … </div>      ← tarjetas auto-fit
       <div class="grid cols1-2"> … </div>        ← 1/3 + 2/3
       <div class="grid stack gap-s"> … </div>    ← pila vertical
       <div class="grid cols-3 full"> … </div>    ← a todo el campo
       <div class="grilla cols-3"> … </div>       ← alias español (igual)

   Modificadores combinables:
     · Columnas .......... cols-1…6 · cols-auto · cols1-2 · cols2-1
     · Espaciado ......... gap-0 · gap-s · gap-m (=def) · gap-l
     · Flujo ............. (horizontal por defecto) · flujo-v / stack
     · Alineación celda .. align-top/center/bottom/baseline (+ arriba/centro/abajo)
     · Margen contenedor . sin-margen / flush (anula el margen vertical)
     · Ancho ............. full / completa (rompe --sn-measure a todo el campo)

   Funciona tanto en chrome normal como en __PANTALLACOMPLETA__: ambos
   wrappers de contenido llevan `.sn-body`. `minmax(0, 1fr)` evita
   desbordes cuando una celda lleva contenido sin wrap (URLs, código).
   Degradación: en pantallas medias (≤ 64rem) las grillas más densas
   (cols-4/5/6) reducen el número de columnas; por debajo de 48rem
   (tablet vertical) todas caen a una sola columna — las asimétricas
   cols1-2 / cols2-1 también colapsan a una columna ahí. */
.sn-body :is(.grid, .grilla) {
	--sn-grid-gap: var(--sn-s-4);
	display: grid;
	gap: var(--sn-grid-gap);
	margin: var(--sn-s-4) 0;
}
.sn-body :is(.grid, .grilla).cols-1 { grid-template-columns: minmax(0, 1fr); }
.sn-body :is(.grid, .grilla).cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.sn-body :is(.grid, .grilla).cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sn-body :is(.grid, .grilla).cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.sn-body :is(.grid, .grilla).cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.sn-body :is(.grid, .grilla).cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
/* Auto-fit: tantas columnas (≥ 16rem) como quepan; ideal para tarjetas. */
.sn-body :is(.grid, .grilla).cols-auto {
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}
/* Asimétricas: pareja de columnas en proporción 1:2 / 2:1 (tercios). */
.sn-body :is(.grid, .grilla).cols1-2 { grid-template-columns: 1fr 2fr; }
.sn-body :is(.grid, .grilla).cols2-1 { grid-template-columns: 2fr 1fr; }

/* Espaciado entre celdas (override del default --sn-s-4). */
.sn-body :is(.grid, .grilla).gap-0 { --sn-grid-gap: 0; }
.sn-body :is(.grid, .grilla).gap-s { --sn-grid-gap: var(--sn-s-2); }
.sn-body :is(.grid, .grilla).gap-m { --sn-grid-gap: var(--sn-s-4); }
.sn-body :is(.grid, .grilla).gap-l { --sn-grid-gap: var(--sn-s-6); }

/* Flujo vertical: pila las celdas en una sola columna con el mismo gap.
   Gana sobre cols-* (más específico por la 2ª clase) si se combinan por
   error; pensado para usarse solo, sin cols. */
.sn-body :is(.grid, .grilla):is(.flujo-v, .stack) {
	grid-template-columns: minmax(0, 1fr);
	grid-auto-flow: row;
}

/* Alineación vertical del contenido de cada celda (default: stretch). */
.sn-body :is(.grid, .grilla):is(.align-top, .arriba)        { align-items: start; }
.sn-body :is(.grid, .grilla):is(.align-center, .centro)     { align-items: center; }
.sn-body :is(.grid, .grilla):is(.align-bottom, .abajo)      { align-items: end; }
.sn-body :is(.grid, .grilla).align-baseline                 { align-items: baseline; }

/* Margen contenedor: anular el margen vertical por defecto. */
.sn-body :is(.grid, .grilla):is(.sin-margen, .flush) { margin: 0; }

/* Full-bleed: la grilla trasciende --sn-measure y ocupa todo el campo,
   con la misma técnica que `.full-width` (width 100vw + margen inline
   negativo calculado relativo al 100% del contenedor). Sirve igual en la
   hoja normal y en el canvas de __PANTALLACOMPLETA__. */
.sn-body :is(.grid, .grilla):is(.full, .completa) {
	width: 100vw;
	max-width: 100vw;
	margin-inline: calc((100% - 100vw) / 2);
}

@media (max-width: 64rem) {
	.sn-body :is(.grid, .grilla).cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
	.sn-body :is(.grid, .grilla).cols-5,
	.sn-body :is(.grid, .grilla).cols-6 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 48rem) {
	.sn-body :is(.grid, .grilla).cols-2,
	.sn-body :is(.grid, .grilla).cols-3,
	.sn-body :is(.grid, .grilla).cols-4,
	.sn-body :is(.grid, .grilla).cols-5,
	.sn-body :is(.grid, .grilla).cols-6,
	.sn-body :is(.grid, .grilla).cols1-2,
	.sn-body :is(.grid, .grilla).cols2-1 { grid-template-columns: minmax(0, 1fr); }
}

/* ─── Editor: tipografía monoespaciada recién instalada (IBM Plex Mono),
   un pelo más grande, padding 1ex; legible también en modo oscuro. ─── */
/* Marco tokenizado POR DEFECTO. Cuando se edita una página que NO es
   wikitexto —CSS/JS/JSON, p. ej. `Plantilla:…/style.css`— WikiEditor no
   realza el área: el textarea queda desnudo (`#wpTextbox1.mw-editfont-
   monospace`), sin el chrome `.wikiEditor-ui` que aportaba el borde. Antes
   este bloque forzaba `border:0` asumiendo ese chrome, así que el editor de
   esas páginas se veía sin marco (no "respetaba" el estilo tokenizado). Ahora
   el textarea trae SU PROPIO marco (borde + radio + papel); cuando WikiEditor
   sí lo envuelve, la regla de más abajo se lo quita para no duplicarlo. */
#wpTextbox1,
.mw-editfont-monospace,
.wikiEditor-ui-text textarea,
textarea.mw-editfont-monospace {
	font-family: var(--sn-font-mono) !important;
	font-size: var(--sn-fs-base);
	line-height: 1.65;
	padding: 1ex !important;
	color: var(--sn-ink);
	background: var(--sn-paper);
	border: 1px solid var(--sn-hairline);
	border-radius: var(--sn-radius);
	box-shadow: none !important;
}
/* WikiEditor SÍ envuelve el textarea (edición de wikitexto): el frame del
   chrome ya da borde y redondez → el textarea interior va sin marco para no
   duplicarlos. Especificidad mayor + después en orden → gana sobre la base. */
.wikiEditor-ui .wikiEditor-ui-text #wpTextbox1,
.wikiEditor-ui .wikiEditor-ui-text textarea {
	border: 0;
	border-radius: 0;
}
/* El anillo de foco lo da el chrome de WikiEditor cuando envuelve; en el
   textarea desnudo (CSS/JS/JSON) el foco lo marca su propio borde en nova
   (border:0 en contexto WikiEditor → ahí no se ve, no estorba). */
#wpTextbox1:focus, .wikiEditor-ui-text textarea:focus { outline: none; }
#wpTextbox1:focus { border-color: var(--sn-nova); }
/* Los componentes OOUI (botones, campos, casillas, diálogos) del editor
   y de todo el wiki se reskinea de forma centralizada y token-driven en
   resources/skinStyles/oojs-ui.css (registrado contra los módulos
   oojs-ui-* en skin.json). Aquí no se parchea OOUI. */

/* ajustes específicos para Casiopea */

/* Desplegables */
.mw-customtoggle{
	color: var(--sn-nova);
}

/* ─── Cabeceras de sección colapsables (mw-customtoggle + .title-toggle) ───
   La portada las arma con el patrón nativo de MediaWiki: el título lleva
   `.mw-customtoggle-ID .title-toggle` y el cuerpo es un `.mw-collapsible`
   hermano del heading. Le damos affordance EXPLÍCITA: feather `plus` cuando
   está colapsada, `minus` cuando está expandida, ambas con :hover.

   Estado fiable = `.mw-collapsed` sobre el `.mw-collapsible` hermano. Ojo:
   makeCollapsible NO marca el custom toggler con `-collapsed/-expanded`
   (usa defaultOpts {} para togglers a medida), así que el estado se lee del
   collapsible y se keyea con `:has(+ …)` desde el heading hacia su hermano.
   Icono = feather pintado como `mask` + `currentColor`, como los chevrons de
   `table.sortable`: sigue al color del texto (nova → nova-hover) y al tema. */
.sn-body :is(h1, h2, h3, h4, h5, h6) .title-toggle {
	--sn-plus:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='12' y1='5' x2='12' y2='19'/%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
	--sn-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
	cursor: pointer;
	color: var(--sn-nova);
	transition: color var(--sn-dur-1) var(--sn-ease);
}
.sn-body :is(h1, h2, h3, h4, h5, h6) .title-toggle::after {
	content: "";
	display: inline-block;
	width: .72em; height: .72em;
	margin-left: .5ex;
	vertical-align: -.06em;
	background-color: currentColor;            /* sigue al color del título */
	-webkit-mask: var(--sn-plus) no-repeat center / contain;
	mask: var(--sn-plus) no-repeat center / contain;
	transition: transform var(--sn-dur-1) var(--sn-ease);
}
/* Expandida: el collapsible hermano ya no tiene `.mw-collapsed` → minus. */
.sn-body :is(h1, h2, h3, h4, h5, h6):has(+ .mw-collapsible:not(.mw-collapsed)) .title-toggle::after {
	-webkit-mask-image: var(--sn-minus); mask-image: var(--sn-minus);
}
/* Hover (en ambos estados): el título profundiza a nova-hover —el icono lo
   hereda por currentColor— y el signo crece un punto para acusar el recibo. */
.sn-body :is(h1, h2, h3, h4, h5, h6) .title-toggle:hover {
	color: var(--sn-link-hover);
}
.sn-body :is(h1, h2, h3, h4, h5, h6) .title-toggle:hover::after {
	transform: scale(1.18);
}