/* ============================================================
   CALDIR — main stylesheet
   Ported from the design system (tokens.css) plus a classic-theme
   reset, accessibility primitives and the WordPress-native
   article / archive / 404 / pagination / widget / footer layers.
   Front-page sections mirror the design composition: 1060 px
   editorial width, 32 px gap-row rhythm, 4-track hero, row-3 /
   row-rev / row-6 patterns.
   ============================================================ */


/* ------------------------------------------------------------
   1. LOCAL TOKENS (composition width, rhythm, and padding).

   1.10.64 — `--w` moved OUT of this block: ahora es un valor
   configurable desde el Customizer (Identidad visual → Ancho de
   página) y se inyecta vía caldir_logo_sizes_inline_css() en
   functions.php, adjunto al mismo <style> que --wordmark-size /
   --tagline-size. Declararlo aquí también lo habría dejado sin
   efecto: main.css carga DESPUÉS de ese inline, así que un `:root`
   duplicado aquí siempre habría ganado el cascade pasara lo que
   pasara en el Customizer. Fallback de por si acaso ese inline no
   llegara a imprimirse (tema mal cacheado a medias): var(--w, 1060px)
   en cada selector, no un segundo `:root`.
   ------------------------------------------------------------ */
:root {
	--pad:        20px;
	--gap-row:    48px;
}


/* ------------------------------------------------------------
   2. RESET
   ------------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

body {
	margin: 0;
	background: var(--color-bg);
	color: var(--color-ink);
	font-family: var(--font-sans);
	font-size: 15px;
	line-height: 1.5;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
	/* 2.7.8 — outer flex column so the topbar can live as a direct
	   body child (edge-to-edge), and `#page.site` still absorbs the
	   remaining height via `flex: 1 0 auto`. The sticky-footer
	   pattern that `.site-main { flex: 1 0 auto }` relies on is
	   unchanged — it still works inside `.site`. */
	display: flex;
	flex-direction: column;
	min-height: 100vh;
	/* 1.10.73 puso aquí `overflow-x: hidden` como red de seguridad
	   contra el desbordamiento horizontal en móvil — 1.10.75 la
	   retira: bloqueaba POR COMPLETO el scroll de toda la página
	   (rueda del ratón y dedo), probablemente por cómo el motor
	   resuelve overflow-x/-y mixto propagado desde `body` a un
	   `display:flex` con `min-height:100vh`. Ya no hace falta de
	   todos modos: la causa real del desbordamiento era `.site` sin
	   `width: 100%` (ver más abajo, arreglado en 1.10.74), no la
	   falta de un backstop aquí. */
}

img, picture, svg, video { display: block; max-width: 100%; height: auto; }

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }

a { color: inherit; text-decoration: none; }

hr { border: 0; border-top: var(--rule-hair); margin: var(--space-6) 0; }


/* ------------------------------------------------------------
   3. ACCESSIBILITY
   ------------------------------------------------------------ */
.screen-reader-text {
	border: 0;
	clip: rect(1px, 1px, 1px, 1px);
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute !important;
	width: 1px;
	word-wrap: normal !important;
}

.skip-link {
	position: absolute;
	top: -64px;
	left: 8px;
	background: var(--color-ink);
	color: var(--color-ink-inverse);
	padding: var(--space-2) var(--space-4);
	text-decoration: none;
	z-index: 1000;
	transition: top 0.12s ease;
}

.skip-link:focus { top: 8px; }

:focus-visible {
	outline: 2px solid var(--color-accent);
	outline-offset: 2px;
}

/* 2.7.6 + 2.8.2 — Suppress focus ring on the masthead wordmark.
   The wordmark anchor is a decorative entry point to the home
   page; the site title is redundant with the "Portada" link in
   the breadcrumb trail and the primary nav. Chrome preserves
   `:focus-visible` state across same-origin navigations, which
   used to paint a ring around the logo on arrival; reset it
   locally so the global focus ring keeps working everywhere
   else. (2.8.2: `.logo-mark` selectors removed — the element
   they targeted was retired in the same sub-phase when the
   floating "P" over the wordmark was cleaned up post-revert.) */
.wordmark a:focus,
.wordmark a:focus-visible {
	outline: none;
}


/* ------------------------------------------------------------
   4. LAYOUT SHELL
   ------------------------------------------------------------ */
/* `.site` no longer holds `min-height: 100vh` since 2.7.8 — that
   role moved up to `<body>` so the topbar can sit outside this
   wrapper. `.site` now absorbs the remaining viewport height via
   `flex: 1 0 auto` while keeping its inner flex column so
   `.site-main` can still grow to push the footer down. */
/* 1.10.72 — Marco sutil de todo el periódico (cabecera → footer),
   al ancho establecido (`--w`, ajustable desde el Customizer). Antes
   `.header`, `.nav`, `.ft__inner` y cada wrapper de contenido
   recalculaban su propio `max-width: var(--w) + margin: auto` por
   separado — cinco cálculos redundantes del mismo número que podían
   divergir entre sí (el filete del menú se quedaba corto respecto a
   la cabecera). Ahora `.site` es la única fuente de verdad: fija el
   ancho una sola vez y todo lo de dentro hereda ese mismo box. El
   topbar, fuera de `.site`, se queda edge-to-edge como hasta ahora. */
.site {
	display: flex;
	flex-direction: column;
	flex: 1 0 auto;
	/* 1.10.74 — `width: 100%` explícito. Sin esto, `.site` (un
	   elemento flex hijo de `body`) tenía `margin: 0 auto` con
	   `width` implícito (`auto`) — y un margen automático en el eje
	   cruzado de un flex item DESACTIVA el estirado por defecto
	   (`align-items: stretch`): el motor lo dimensiona por su
	   CONTENIDO en vez de por el espacio disponible, y solo reparte
	   el margen sobrante si el contenido es más estrecho que el
	   hueco. Con contenido más ancho que la pantalla (el riel del
	   menú sin salto de línea) el resultado era `.site` calculado a
	   ~820px en un móvil de 390px — el bug real detrás de "parece
	   escritorio". `width: 100%` hace el ancho de `.site` explícito,
	   por lo que ya no cuenta como "auto" y el estirado/max-width
	   normal vuelve a aplicar (confirmado con Chrome headless antes
	   de subir este cambio). */
	width: 100%;
	max-width: var(--w, 1060px);
	min-width: 0;
	margin: 0 auto;
	border: 1px solid var(--color-rule-faint);
}
/* 1.10.74 — `min-width: 0` por la misma razón que `.header`/`.nav`:
   `.site-main` es flex item de `.site` y por defecto no se encoge
   por debajo del contenido interno más ancho (grids, imágenes). */
.site-main { flex: 1 0 auto; min-width: 0; }

.wrap {
	max-width: var(--w, 1060px);
	margin-inline: auto;
	padding-inline: var(--pad);
	padding-block: var(--space-8);
}

.wrap--reading {
	max-width: 72ch;
	padding-block: var(--space-12);
}

/* Generic placeholder stripes (design system). */
.ph {
	background:
		repeating-linear-gradient(135deg, #ECEAE1 0, #ECEAE1 10px, #E2DFD5 10px, #E2DFD5 20px);
	border: 1px solid var(--color-rule-faint);
	position: relative;
}

.ph-dark {
	background:
		linear-gradient(135deg, #2A2825 0%, #3A3732 50%, #26231F 100%);
	position: relative;
}


/* ------------------------------------------------------------
   5. TYPOGRAPHY (body / headings / reading surface)
   ------------------------------------------------------------ */
h1, h2, h3, h4, h5, h6 {
	font-family: var(--font-display);
	line-height: var(--lh-tight);
	letter-spacing: var(--tracking-tight);
	margin: 0 0 var(--space-4);
	color: var(--color-ink);
	font-variation-settings: "opsz" 144;
}

.entry-content, .article-body {
	font-family: var(--font-serif-reading);
	font-size: var(--fs-lead);
	line-height: var(--lh-reading);
	color: var(--color-ink);
	user-select: text;
}

.entry-content p, .article-body p { margin: 0 0 var(--space-6); }

.entry-content a, .article-body a {
	color: var(--color-accent);
	text-decoration: underline;
	text-decoration-color: var(--color-rule-faint);
}

.entry-content a:hover, .article-body a:hover { text-decoration-color: var(--color-accent); }

.entry-content blockquote, .article-body blockquote {
	margin: var(--space-8) 0;
	padding-inline-start: var(--space-6);
	border-inline-start: 3px solid var(--color-accent);
	font-style: italic;
	color: var(--color-ink-muted);
}

.entry-content figure, .article-body figure { margin: var(--space-8) 0; }

.entry-content figcaption, .article-body figcaption {
	font-family: var(--font-sans);
	font-size: var(--fs-meta);
	color: var(--color-ink-soft);
	margin-top: var(--space-2);
}

.alignwide { max-width: min(var(--w, 1060px), calc(100vw - (var(--pad) * 2))); margin-inline: auto; }
.alignfull { max-width: none; margin-inline: calc(50% - 50vw); }


/* ------------------------------------------------------------
   6. KICKER / META / TIME (design-system primitives)
   ------------------------------------------------------------ */
/* Inline-flex so the ::before swatch + text keep a guaranteed gap
   regardless of parent element (anchor, span, list item). */
/* Kicker — one treatment for the entire theme: square dot + tracked
   caps to the right. `inline-block` (not inline-flex) so a flex-column
   parent can't blockify it and stretch the ::before into a
   full-width line. `align-self: start` keeps it left-aligned in any
   flex/grid parent regardless of stretch defaults. */
.kicker {
	display: inline-block;
	align-self: flex-start;
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-accent);
	text-decoration: none;
	line-height: 1.1;
	white-space: nowrap;
	border: 0;
	background: transparent;
	width: auto;
}

.kicker::before {
	content: "";
	display: inline-block;
	vertical-align: middle;
	width: 8px;
	height: 8px;
	margin-right: 10px;
	margin-top: -2px;
	background: currentColor;
}

.kicker::after { content: none; }

.kicker--light { color: var(--color-bg); }
.kicker--ink   { color: var(--color-ink); }

.meta {
	font-family: var(--font-sans);
	font-size: 12px;
	color: var(--color-ink-soft);
	letter-spacing: 0.04em;
}

.time {
	font-family: var(--font-mono);
	font-size: 11px;
	color: var(--color-ink-soft);
	letter-spacing: 0.04em;
}


/* ------------------------------------------------------------
   7. TOP BAR
   ------------------------------------------------------------ */
.topbar {
	background: var(--color-bg-subtle);
	border-bottom: var(--rule-faint);
	color: var(--color-ink-muted);
	font-size: 12px;
}

.topbar-in {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 8px var(--pad);
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	column-gap: 16px;
	align-items: center;
}

.topbar-l {
	display: flex;
	align-items: center;
	gap: 12px;
	min-width: 0;
	white-space: nowrap;
	overflow: hidden;
}

.topbar-r {
	display: flex;
	align-items: center;
	gap: 22px;
	flex-shrink: 0;
	font-family: var(--font-sans);
	font-size: 13px;
}

.topbar-r .topbar-social { font-size: 15px; line-height: 1; display: inline-flex; align-items: center; gap: 10px; }
.topbar-r .topbar-social a { color: var(--color-ink); display: inline-flex; }
.topbar-r .topbar-social a:hover { color: var(--color-accent); }
/* 1.10.64 — Facebook/Telegram/TikTok/Bluesky/Mastodon render as inline
   SVG (currentColor, no equivalent Unicode glyph like X's 𝕏); sized to
   match the 15px line the X glyph already sets. */
.topbar-r .topbar-social svg { width: 15px; height: 15px; }

.topbar .uh {
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-accent);
}

.topbar .uh::before {
	content: "";
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: currentColor;
	animation: caldir-uhpulse 1.4s infinite;
}

@keyframes caldir-uhpulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }

/* ---- Focus-bar (1.6.0, marquee in 1.6.4) — trending-tags widget --

   Replaces the 1.0–1.5.x `.uh-ticker` rotator. Same `.topbar` slot,
   same alignment, same `.uh` label (its styling above is reused for
   the "EN EL FOCO" tag).

   1.6.4 layout: the `.focus-bar-tags` wrapper is an overflow:hidden
   viewport with an edge-fade mask. The `<ul>` inside (the
   `.focus-bar-tags-track`) holds the tag list twice in a row and
   animates from `translateX(0)` to `translateX(-50%)`: when the first
   copy scrolls out the second is already in place, so the loop seams
   invisibly with a plain CSS keyframe — no JS, no jump. The label
   stays anchored at the left of the topbar, only the track animates.
   `:hover` pauses for reading and clicking; `prefers-reduced-motion`
   disables the animation entirely.

   `.focus-tags` keeps its base typography (font, size) for screens
   that disable the marquee fallback. */
.focus-tags {
	list-style: none;
	margin: 0;
	padding: 0;
	font-family: var(--font-sans);
	font-size: 12px;
	line-height: 1.5em;
}

.focus-bar-tags {
	display: flex;
	overflow: hidden;
	white-space: nowrap;
	flex: 1 1 auto;
	min-width: 0;
	position: relative;
	/* Edge fade so tags don't "pop" at the viewport boundary. */
	-webkit-mask-image: linear-gradient(to right, transparent 0, black 30px, black calc(100% - 30px), transparent 100%);
	mask-image: linear-gradient(to right, transparent 0, black 30px, black calc(100% - 30px), transparent 100%);
}

.focus-bar-tags-track {
	display: inline-flex;
	align-items: center;
	gap: 18px;
	padding: 0 18px 0 0;
	margin: 0;
	flex-wrap: nowrap;
	white-space: nowrap;
	animation: focus-marquee 30s linear infinite;
	will-change: transform;
}

.focus-bar-tags:hover .focus-bar-tags-track,
.focus-bar-tags:focus-within .focus-bar-tags-track {
	animation-play-state: paused;
}

@keyframes focus-marquee {
	0%   { transform: translateX(0); }
	100% { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
	.focus-bar-tags-track {
		animation: none;
	}
}

.focus-tags__item {
	display: inline-flex;
	align-items: center;
	margin: 0;
	flex-shrink: 0;
}

/* No bullet separator inside the marquee — the `gap: 18px` on the
   track supplies the spacing, and a separator between the last item
   of the first copy and the first item of the duplicate copy would
   draw attention to the loop seam. */
.focus-bar-tags-track .focus-tags__item + .focus-tags__item::before {
	content: none;
}

.focus-tags__link {
	color: var(--color-ink);
	text-decoration: none;
	font-weight: 500;
	/* 1.6.4 — tags always uppercase regardless of how they're stored
	   in the DB, so "irán", "Irán" and "IRÁN" all render as "IRÁN" in
	   the focus bar. Letter-spacing tightens the all-caps look. */
	text-transform: uppercase;
	letter-spacing: 0.04em;
	transition: color 0.15s ease;
}

.focus-tags__link:hover,
.focus-tags__link:focus-visible {
	color: var(--color-accent);
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* 1.6.4 — Tag-archive page H1 (`/tag/<slug>/`) in uppercase. WP adds
   `body.tag` on every tag-archive view, so the rule is scoped tightly
   and doesn't leak to category / author / date archives or to single
   posts. */
body.tag .section-title {
	text-transform: uppercase;
}

.btn-nl {
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--color-ink-muted);
}

.btn-nl:hover { color: var(--color-accent); }

.btn-sub {
	font-size: 13px;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--color-accent);
}

.btn-sub:hover { color: var(--color-accent-ink); }

@media (max-width: 768px) {
	.topbar-r .btn-nl,
	.topbar-r .btn-sub { display: none; }
}

/* 2026-08-05 — llamada a hacerse corresponsal, en la barra superior de
   las fichas de municipio (junto al logo/ticker y "Suscríbete"). Mismo
   tratamiento que `.btn-sub` (negrita, color de marca, sin caja — la
   barra es demasiado fina para una caja con relleno propio), con un
   icono de pluma a juego con los de `.topbar-social` de al lado. Vive
   fuera de `.topbar-r`'s siblings a propósito: no hereda `gap` de nadie
   más, se separa con su propio margen para no depender del orden en
   que el resto de items (opcionales) estén presentes o no. */
.topbar-corresponsal {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-family: var(--font-sans);
	font-size: 13px;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--color-accent);
	white-space: nowrap;
}
.topbar-corresponsal svg { width: 14px; height: 14px; flex: none; }
.topbar-corresponsal:hover { color: var(--color-accent-ink); }


/* ------------------------------------------------------------
   8. MASTHEAD
   See Section 99 at the end of this file — all masthead rules
   (`.header`, `.logo-*`, `.wordmark`, `.logo-tag`, `.hdr-ad`,
   `.hdr-slot`, `.hdr-r`, `.hdr-dt`, `.hdr-date`, `.hdr-clock`)
   are copied verbatim from `design/Prensa Prototipo/Portada-v2.html`
   in sub-phase 2.6.10. Kept at the end of the file so no earlier
   rule accidentally re-opens the divergence loop from the nine
   previous sub-phases.
   ------------------------------------------------------------ */


/* ------------------------------------------------------------
   9. PRIMARY NAV
   Horizontal scroll with edge-fade shadows when the menu overflows
   the 1060 px editorial width. JS adds `.has-scroll-left` /
   `.has-scroll-right` to the outer .nav when scrollable.
   ------------------------------------------------------------ */
.nav {
	/* 1.10.72 — max-width/centrado propios retirados (igual que en
	   `.header`, ver "1. LOCAL TOKENS" / `.site` más arriba): el
	   1.10.71 puso el filete aquí asumiendo que `.nav` con su propio
	   `max-width: var(--w)` coincidiría con `.header`, y en la
	   práctica seguía quedándose corto. Ahora `.nav` es simplemente
	   el 100% de `.site`, que fija el ancho una única vez — ya no hay
	   dos cálculos independientes que puedan divergir.
	   1.10.74 — `min-width: 0`: `.nav` es un elemento flex (hijo de
	   `.site`) y por defecto tiene `min-width: auto` — su contenido
	   (el riel de categorías, textos largos sin salto de línea) podía
	   forzarlo a no encogerse por debajo de ~790px, arrastrando a toda
	   la página con él en móvil, aunque `.nav-in` ya tuviera su propio
	   `overflow-x: auto` para el scroll interno. */
	width: 100%;
	min-width: 0;
	padding: 0 var(--pad);
	position: relative;
	border-top: 2px solid var(--color-ink);
	border-bottom: var(--rule-hair);
	/* 1.10.72 — aire uniforme hacia el contenido siguiente en TODAS
	   las plantillas (portada, artículo individual, archivos…): antes
	   cada plantilla tenía que aportar su propio margen (o no lo
	   aportaba, de ahí el "pegado" tanto en portada como en artículo). */
	margin-bottom: 28px;
}

/* Edge-fade shadow indicators. Painted on the outer .nav so they sit
   above the scrolling .nav-in rail. Shadows start opaque at the
   inner edge so the gradient is visible against the cream page bg. */
.nav::before,
.nav::after {
	content: "";
	position: absolute;
	top: 1px;
	bottom: 1px;
	width: 64px;
	pointer-events: none;
	z-index: 3;
	opacity: 0;
	transition: opacity 180ms ease;
}

.nav::before {
	left: 0;
	background: linear-gradient(to right, var(--color-bg) 10%, transparent);
}

.nav::after {
	right: 0;
	background: linear-gradient(to left, var(--color-bg) 10%, transparent);
}

.nav.has-scroll-left::before  { opacity: 1; }
.nav.has-scroll-right::after  { opacity: 1; }

.nav-in {
	display: flex;
	width: 100%;
	justify-content: space-between;
	align-items: stretch;
	flex-wrap: nowrap;
	gap: 20px;
	overflow-x: auto;
	overflow-y: hidden;
	scroll-snap-type: x proximity;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	list-style: none;
	margin: 0;
	padding: 0;
}

.nav-in::-webkit-scrollbar { display: none; }

/* wp_nav_menu outputs <ul><li><a>; collapse the ul/li to transparent
   boxes so the layout matches the flat design. `display: contents` is
   supported in every evergreen browser. */
.nav-in ul, .nav-in li { display: contents; }

.nav-in a {
	/* 1.10.72 — franja más estrecha y texto más pequeño (antes 16px/
	   13px): la barra de menú pesaba demasiado respecto al resto de
	   la cabecera. */
	padding: 11px 10px;
	font-family: var(--font-sans);
	font-size: 12px;
	font-weight: 500;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	color: var(--color-ink);
	white-space: nowrap;
	position: relative;
	margin-bottom: -1px;
	flex-shrink: 0;
	text-decoration: none;
	scroll-snap-align: start;
	transition: color 140ms ease;
}

.nav-in a:first-child { padding-left: 0; }
.nav-in a:last-child  { padding-right: 0; }

/* Hover / focus repaint. Selector is spelled out twice so the rule
   beats the WP-generated `.nav-menu li a { color: ... }` that ships
   with wp_nav_menu fallbacks. */
.nav-in a:hover,
.nav-in a:focus,
.nav-in a:focus-visible,
.nav-in ul li a:hover,
.nav-in ul li a:focus,
.nav-in ul li a:focus-visible,
#primary-menu a:hover,
#primary-menu a:focus-visible {
	color: var(--color-accent);
	outline: 0;
}

.nav-in .current-menu-item > a,
.nav-in .current_page_item > a,
.nav-in .current-menu-parent > a,
.nav-in .current-menu-ancestor > a,
.nav-in .current-cat > a,
.nav-in li.current-menu-item a,
.nav-in li.current_page_item a,
.nav-in li.current-cat a {
	color: var(--color-accent);
	font-weight: 600;
}

.nav-in .current-menu-item > a::after,
.nav-in .current_page_item > a::after,
.nav-in .current-menu-parent > a::after,
.nav-in .current-menu-ancestor > a::after,
.nav-in .current-cat > a::after,
.nav-in li.current-menu-item a::after,
.nav-in li.current_page_item a::after,
.nav-in li.current-cat a::after {
	content: "";
	position: absolute;
	left: 10px;
	right: 10px;
	bottom: -1px;
	height: 3px;
	background: var(--color-accent);
}

.nav-in a:first-child::after  { left: 0 !important; }
.nav-in a:last-child::after   { right: 0 !important; }

@media (max-width: 768px) {
	.nav-in { scroll-snap-type: x mandatory; }
	.nav-in a { padding: 12px; letter-spacing: 0.08em; }
}


/* ------------------------------------------------------------
   10. HERO
   ------------------------------------------------------------ */
.hero {
	/* 1.1.3 — `max-width`, outer `margin` and lateral `padding` removed.
	   Hero is now always rendered inside a `.sect` wrapper (see
	   inc/blocks/hero.php) which provides the wrap; double-applying it
	   here would offset the section header from the cards. */
	display: grid;
	grid-template-columns: 1.5fr 1fr 1fr 1fr;
	gap: 24px;
	align-items: stretch;
}

/* Fallback when the hero category has fewer than four posts. */
.hero[data-count="1"] { grid-template-columns: 1fr; }
.hero[data-count="2"] { grid-template-columns: 1.5fr 1fr; }
.hero[data-count="3"] { grid-template-columns: 1.5fr 1fr 1fr; }

/* Sibling grid that picks up posts beyond the hero's 4-slot anchor
   (1 big + 3 sm). Auto-fit so any number of additional cards reflows
   naturally without leaving empty tracks. Lives inside `.sect` (see
   inc/blocks/hero.php) so it inherits the horizontal wrap; only the
   `margin-top` separates it from the `.hero` above. */
.hero-extra {
	margin-top: 24px;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 24px;
	align-items: stretch;
}

.hero-big {
	position: relative;
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.hero-big .img {
	width: 100%;
	flex: 1;
	min-height: 380px;
	position: relative;
	display: flex;
	align-items: flex-end;
	overflow: hidden;
}

.hero-big .img img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	position: absolute;
	inset: 0;
}

/* The overlay for hero-big now lives in the unified image-with-title
   block at Section 50. Local rule removed. */

.hero-big .img-content {
	position: relative;
	z-index: 2;
	padding: 24px;
	color: var(--color-bg);
	width: 100%;
}

.hero-big h2 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 30px;
	line-height: 1.08;
	letter-spacing: -0.022em;
	margin: 12px 0 10px;
	/* 2.7.14 — `max-width: 22ch` removed. The hero-big sits in a
	   grid cell whose width is already controlled by the parent
	   `.hero` grid; capping the h2 at 22ch shortened long headlines
	   below the image width. `text-wrap: balance` keeps break points
	   readable without an arbitrary character cap. */
	color: var(--color-bg);
	font-variation-settings: "opsz" 144;
	text-wrap: balance;
	hyphens: none;
}

.hero-big h2 a { color: inherit; }
.hero-big h2 a:hover { opacity: 0.88; }

/* "Hero word" treatment (Principle 11): one italic 400 word inside a
   heavy display title to create typographic undulation. Only applied
   to titles at ≥ 26 px; the editor opts in by wrapping a word in <em>. */
.hero-big h2 em,
.u-card-big h4 em,
.tech-top-card h4 em {
	font-style: italic;
	font-weight: 400;
	font-variation-settings: "opsz" 144, "SOFT" 100;
	letter-spacing: -0.005em;
}

.hero-big .lede {
	font-family: var(--font-serif-reading);
	font-size: 15px;
	line-height: 1.45;
	color: rgba(250,250,247,0.86);
	max-width: 48ch;
	margin: 0;
}

.hero-big .time { color: rgba(250,250,247,0.6); display: block; margin-top: 14px; }

.hero-sm {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.hero-sm .img {
	/* `display: block` is load-bearing: `the_thumb()` renders the wrapper as
	   `<a class="img">` (inline by default). Without explicit block the box
	   collapses for the no-thumbnail placeholder (`<a>` empty + inline → 0×0)
	   and the row loses its grid lockstep.

	   1.4.3 — reverted from 1.1.6's `flex: 1 0 162px` (which let the thumb
	   grow with the row) back to a fixed `aspect-ratio: 16 / 10`. The
	   growth behaviour aligned `.time` to the row bottom but left the
	   kicker / h2 floating at different vertical offsets across the
	   three hero-sm cards in the same row — images of slightly different
	   intrinsic ratios produced slightly different growth, dragging the
	   text block up or down. Pinning the thumb to a strict 16:10 forces
	   every hero-sm card to start its text block at the same Y and
	   guarantees the three kickers line up. The hole between lede and
	   `.time` (1.1.5 symptom) reappears when the row is much taller than
	   the natural hero-sm content, but kicker misalignment was the more
	   visible defect; `margin-top: auto` on `.time` keeps the date
	   anchored at the row foot so the result is a deliberate vertical
	   gap rather than a misaligned card. `flex: none` keeps the box
	   stable inside the flex column (no shrink, no grow). */
	display: block;
	width: 100%;
	aspect-ratio: 16 / 10;
	flex: none;
	margin-bottom: 12px;
	overflow: hidden;
}

.hero-sm .img img,
.u-card .img img,
.u-card-big .img img,
.tech-top-card .img img,
.tech-sm .img img,
.cultura-block .img img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* 1.5.1 — Enforce `object-fit: cover` on every featured-image wrapper
   the theme emits. Editorial uploads come in arbitrary aspect ratios
   (4:3, 3:2, 21:9, vertical); without explicit cover, images of a
   ratio that doesn't match the slot leave visible empty space inside
   the card. The base rule above already sets cover but without
   `!important`, so a plugin / legacy / Larra figure CSS that resets
   `object-fit` (or the browser's default of `fill`) was leaking
   through on apaisada photos. This block forces the contract for
   every container of class `.img` that lives inside a known card or
   block wrapper.

   Scope is deliberately card-only: single-post's featured image
   container is `.featured-img` (not `.img`), so it falls outside
   these selectors and keeps its natural aspect-ratio render. All
   containers listed below have a defined size (aspect-ratio or
   min-height) — verified during 1.5.1 audit — so cover has a box to
   fill. The `article .img img` selector covers any future card whose
   thumb wrapper uses the shared `.img` class without explicitly
   listing the parent block. */
.hero .img img,
.hero-big .img img,
.hero-sm .img img,
.hero-extra .img img,
.u-card .img img,
.u-card-big .img img,
.tech-top-card .img img,
.tech-sm .img img,
.cultura-block .img img,
.row-3 .img img,
.row-6 .img img,
.row-rev .img img,
article.hero-sm .img img,
article .img img {
	width: 100% !important;
	height: 100% !important;
	object-fit: cover !important;
	object-position: center center !important;
	display: block !important;
}

/* 1.5.3 — Larra plugin envelope, scoped per card type. The 1.5.2 rule
   (a single generic `.img .larra-thumbnail` block) collapsed the
   hero-big slot because hero-big's `.img` is a flex item with
   `flex: 1; min-height: 380px` and its child `<img>` is `position:
   absolute; inset: 0` — forcing the figure between them to
   `width/height: 100%; position: relative` broke the flex chain that
   was holding the slot open. Per-card-type selectors keep hero-big
   completely out of scope; the inner img keeps natural flow
   (`position: static`, the default) so the existing per-slot
   `aspect-ratio` rules of each card define the box and `cover`
   handles overflow inside it. */
.hero-sm .img .larra-thumbnail,
.u-card .img .larra-thumbnail,
.u-card-big .img .larra-thumbnail,
.tech-top-card .img .larra-thumbnail,
.tech-sm .img .larra-thumbnail,
.cultura-block .img .larra-thumbnail {
	display: block !important;
	width: 100% !important;
	height: 100% !important;
	margin: 0 !important;
	padding: 0 !important;
}

.hero-sm .img .larra-thumbnail img,
.u-card .img .larra-thumbnail img,
.u-card-big .img .larra-thumbnail img,
.tech-top-card .img .larra-thumbnail img,
.tech-sm .img .larra-thumbnail img,
.cultura-block .img .larra-thumbnail img {
	width: 100% !important;
	height: 100% !important;
	object-fit: cover !important;
	object-position: center center !important;
	display: block !important;
}

.hero-sm h2 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 17px;
	line-height: 1.18;
	letter-spacing: -0.012em;
	margin: 8px 0 6px;
}

.hero-sm h2 a { color: inherit; }
.hero-sm h2 a:hover { color: var(--color-accent); }

.hero-sm .lede {
	font-family: var(--font-serif-reading);
	font-size: 14px;
	line-height: 1.5;
	color: var(--color-ink-muted);
	margin: 0 0 8px;
	max-width: 34ch;
}

.hero-sm .time { margin-top: auto; padding-top: 10px; }

@media (max-width: 900px) { .hero { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .hero { grid-template-columns: 1fr; } .hero-big .img { min-height: 280px; } }


/* ------------------------------------------------------------
   11. EDITORIAL BAR
   ------------------------------------------------------------ */
.ed-bar-wrap {
	max-width: var(--w, 1060px);
	margin: var(--gap-row) auto 0;
	padding: 0 var(--pad);
}

.ed-bar {
	display: grid;
	grid-template-columns: auto 1fr auto;
	gap: 20px;
	align-items: center;
	padding: 20px 24px;
	background: var(--color-bg-subtle);
	border-left: 4px solid var(--color-accent);
}

.ed-ava {
	width: 40px;
	height: 40px;
	border-radius: 50%;
	flex-shrink: 0;
	object-fit: cover;
}

.ed-content { min-width: 0; }

.ed-content .meta-line {
	display: flex;
	align-items: baseline;
	gap: 12px;
	flex-wrap: wrap;
}

.ed-content h4 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 20px;
	line-height: 1.18;
	letter-spacing: -0.015em;
	margin: 6px 0 0;
}

.ed-content h4 a { color: inherit; }
.ed-content h4 a:hover { color: var(--color-accent); }

.ed-more {
	font-family: var(--font-sans);
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-accent);
	flex-shrink: 0;
	border-bottom: 1px solid var(--color-accent);
	padding-bottom: 2px;
	text-decoration: none;
}

.ed-more:hover { color: var(--color-accent-ink); border-color: var(--color-accent-ink); }


/* ------------------------------------------------------------
   12. SECTION HEADING (.sh)
   ------------------------------------------------------------ */
.sh {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	padding-bottom: 10px;
	border-bottom: var(--rule-thick);
	margin-bottom: 20px;
}

.sh h3 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 28px;
	letter-spacing: -0.02em;
	line-height: 1;
	margin: 0;
	color: var(--color-ink);
	font-variation-settings: "opsz" 144;
}

.sh a.more {
	font-family: var(--font-sans);
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-ink);
	border-bottom: 1px solid var(--color-ink);
	padding-bottom: 2px;
	text-decoration: none;
}

.sh a.more:hover { color: var(--color-accent); border-color: var(--color-accent); }


/* ------------------------------------------------------------
   13. U-CARD (universal news card)
   ------------------------------------------------------------ */
.u-card {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.u-card > .img {
	width: 100%;
	aspect-ratio: 16 / 10;
	margin-bottom: 12px;
	overflow: hidden;
	display: block;
}

.u-card > .kicker { margin-bottom: 6px; }

.u-card h4 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 19px;
	line-height: 1.2;
	letter-spacing: -0.015em;
	margin: 4px 0 8px;
	font-variation-settings: "opsz" 72;
}

.u-card h4 a { color: inherit; text-decoration: none; }
.u-card h4 a:hover { color: var(--color-accent); }

.u-card p {
	font-family: var(--font-serif-reading);
	font-size: 15px;
	line-height: 1.55;
	color: var(--color-ink-muted);
	margin: 4px 0 0;
	text-wrap: pretty;
}

.u-card .time { margin-top: auto; padding-top: 10px; display: block; }


/* ------------------------------------------------------------
   14. U-CARD-BIG (feature card with dark overlay)
   ------------------------------------------------------------ */
.u-card-big {
	position: relative;
	min-height: 320px;
	overflow: hidden;
	display: flex;
	align-items: flex-end;
}

.u-card-big .img {
	position: absolute;
	inset: 0;
	background: linear-gradient(135deg, #2A2825 0%, #3A3732 50%, #26231F 100%);
}

/* Overlay lives in Section 50 (unified pattern). */

.u-card-big .body {
	position: relative;
	z-index: 2;
	padding: 22px;
	color: var(--color-bg);
}

.u-card-big h4 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 22px;
	line-height: 1.15;
	letter-spacing: -0.018em;
	margin: 10px 0 6px;
	color: var(--color-bg);
	/* 2.7.14 — `max-width: 24ch` removed so long headlines can use
	   the full image width; `text-wrap: balance` handles line breaks. */
}

.u-card-big h4 a { color: inherit; }
.u-card-big h4 a:hover { opacity: 0.88; }

.u-card-big p {
	font-family: var(--font-serif-reading);
	font-size: 14px;
	line-height: 1.5;
	color: rgba(250,250,247,0.85);
	margin: 6px 0 0;
	max-width: 42ch;
}

.u-card-big .time { color: rgba(250,250,247,0.6); display: block; margin-top: 12px; }


/* ------------------------------------------------------------
   15. SECTIONS (.sect) + ROWS (.row-3 / .row-rev / .row-6)
   ------------------------------------------------------------ */
.sect {
	max-width: var(--w, 1060px);
	margin: var(--gap-row) auto 0;
	padding: 0 var(--pad);
}

.row-3 {
	display: grid;
	/* auto-fit: standard u-cards (16:9 thumb + headline + lede) need ~280px
	   minimum to stay legible; below that the lede gets cramped. */
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	gap: 24px;
	align-items: stretch;
	margin-bottom: 24px;
}

.row-3:last-child { margin-bottom: 0; }

.row-rev {
	display: grid;
	grid-template-columns: 1fr 1fr 1fr 1.5fr;
	gap: 24px;
	align-items: stretch;
	margin-top: 24px;
}

.row-6 {
	display: grid;
	/* auto-fit: u-card-compact (1:1 dark thumb + overlay title) reads at
	   ~140px even in a tight grid; anything smaller breaks the title. */
	grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
	gap: 20px;
	align-items: stretch;
	margin-top: 28px;
	padding-top: 24px;
	border-top: var(--rule-faint);
}

/* Row-6 sizing lives with the u-card--dark block at Section 50;
   rule kept empty here to document the override chain. */

@media (max-width: 900px) {
	.row-3 { grid-template-columns: repeat(2, 1fr); }
	.row-rev { grid-template-columns: repeat(2, 1fr); }
	.row-6 { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 600px) {
	.row-3, .row-rev { grid-template-columns: 1fr; }
	.row-6 { grid-template-columns: repeat(2, 1fr); }
	.u-card-big { min-height: 260px; }
}


/* ------------------------------------------------------------
   16. MOST READ (.masleido-*)
   ------------------------------------------------------------ */
.masleido-grid {
	display: grid;
	/* auto-fit: ranked items need room for the large numeral + title.
	   1.1.2 — bumped from 200px to 240px so the title has more room
	   and the numerals don't crowd it on common viewport widths. */
	grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
	gap: 16px;
	align-items: stretch;
}

.masleido-item {
	display: flex;
	align-items: flex-start;
	gap: 14px;
	padding: 18px 16px;
	background: var(--color-bg-subtle);
	transition: background 0.15s;
	min-width: 0;
	text-decoration: none;
	color: inherit;
}

.masleido-item:hover { background: #E9E7DE; }

.masleido-num {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 40px;
	line-height: 0.88;
	color: var(--color-accent);
	letter-spacing: -0.035em;
	flex-shrink: 0;
	font-variation-settings: "opsz" 144;
}

.masleido-txt {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 15px;
	line-height: 1.22;
	letter-spacing: -0.01em;
	color: var(--color-ink);
	padding-top: 4px;
	min-width: 0;
}

@media (max-width: 900px) { .masleido-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .masleido-grid { grid-template-columns: 1fr; } }


/* ------------------------------------------------------------
   18. TECHNOLOGY
   ------------------------------------------------------------ */
.tech-top {
	display: grid;
	/* auto-fit: tech-top-card is a feature card with a 16:9 dark hero and
	   overlay copy; 360px is the minimum at which the headline doesn't
	   crowd the kicker. With _count=1 the single card stretches full-width. */
	grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
	gap: 24px;
	margin-bottom: 24px;
	align-items: stretch;
}

.tech-top-card {
	position: relative;
	min-height: 280px;
	overflow: hidden;
	display: flex;
	align-items: flex-end;
}

.tech-top-card .img {
	position: absolute;
	inset: 0;
	background: linear-gradient(135deg, #2A2825 0%, #3A3732 50%, #26231F 100%);
}

/* Overlay lives in Section 50 (unified pattern). */

.tech-top-card .body {
	position: relative;
	z-index: 2;
	padding: 22px;
	color: var(--color-bg);
	width: 100%;
}

.tech-top-card h4 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 22px;
	line-height: 1.15;
	letter-spacing: -0.018em;
	color: var(--color-bg);
	margin: 10px 0 6px;
	/* 2.7.14 — `max-width: 24ch` removed so long headlines fill
	   the full image width of the tech-top feature card. */
}

.tech-top-card h4 a { color: inherit; }
.tech-top-card h4 a:hover { opacity: 0.88; }

.tech-top-card .time { color: rgba(250,250,247,0.6); display: block; margin-top: 10px; }

.tech-bottom {
	display: grid;
	/* auto-fit: when the section count drops below 4, the remaining cards
	   stretch instead of leaving empty tracks. minmax 220px keeps the
	   16:10 thumbnail readable. */
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 20px;
	align-items: stretch;
}

.tech-sm { display: flex; flex-direction: column; min-width: 0; }

.tech-sm .img {
	/* See `.hero-sm .img` — `display: block` mirrors the u-card rule and is
	   required for the no-thumbnail `<a class="img ph">` placeholder. */
	display: block;
	width: 100%;
	aspect-ratio: 16 / 10;
	margin-bottom: 10px;
	overflow: hidden;
}

.tech-sm > .kicker { margin-bottom: 6px; }

.tech-sm h4 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 16px;
	line-height: 1.22;
	letter-spacing: -0.012em;
	margin: 4px 0 6px;
}

.tech-sm h4 a { color: inherit; text-decoration: none; }
.tech-sm h4 a:hover { color: var(--color-accent); }

.tech-sm p {
	font-family: var(--font-serif-reading);
	font-size: 13px;
	line-height: 1.5;
	color: var(--color-ink-muted);
	margin: 0;
}

.tech-sm .time { margin-top: auto; padding-top: 8px; display: block; }

@media (max-width: 900px) {
	.tech-top { grid-template-columns: 1fr; }
	.tech-bottom { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 600px) { .tech-bottom { grid-template-columns: 1fr; } }


/* ------------------------------------------------------------
   19. CULTURE
   ------------------------------------------------------------ */
.cultura-blocks {
	display: grid;
	/* auto-fit: cultura blocks are denser (thumb + title + bullets), so a
	   240px floor reads better than the finance/tech 220 px. */
	grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
	gap: 20px;
	align-items: stretch;
	margin-top: 24px;
}

.cultura-block {
	background: var(--color-bg-subtle);
	padding: 18px;
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.cultura-block-hd {
	font-family: var(--font-sans);
	font-size: 10px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.18em;
	color: var(--color-accent);
	padding-bottom: 8px;
	margin-bottom: 12px;
	border-bottom: 1px solid var(--color-accent);
}

.cultura-block > .img {
	width: 100%;
	aspect-ratio: 16 / 9;
	margin-bottom: 10px;
	overflow: hidden;
}

.cultura-block h4 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 15px;
	line-height: 1.22;
	letter-spacing: -0.01em;
	margin: 2px 0 10px;
}

.cultura-block h4 a { color: inherit; text-decoration: none; }
.cultura-block h4 a:hover { color: var(--color-accent); }

.cultura-bullets {
	margin-top: auto;
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding-top: 10px;
}

.cultura-bullet {
	display: flex;
	align-items: baseline;
	gap: 8px;
	font-family: var(--font-serif-reading);
}

.cultura-bullet::before {
	content: "";
	width: 4px;
	height: 4px;
	border-radius: 50%;
	background: var(--color-accent);
	flex-shrink: 0;
	position: relative;
	top: -2px;
}

.cultura-bullet a {
	font-size: 13px;
	line-height: 1.4;
	color: var(--color-ink-muted);
	text-decoration: none;
}

.cultura-bullet a:hover { color: var(--color-accent); }

@media (max-width: 900px) { .cultura-blocks { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .cultura-blocks { grid-template-columns: 1fr; } }


/* ------------------------------------------------------------
   20. NEWSLETTER BAR
   ------------------------------------------------------------ */
.nlbar-wrap { max-width: var(--w, 1060px); margin: var(--gap-row) auto 0; padding: 0 var(--pad); }

.nlbar-in {
	display: grid;
	grid-template-columns: auto 1fr auto;
	gap: 28px;
	align-items: center;
	padding: 28px 32px;
	background: var(--color-bg-subtle);
}

.nl-icon {
	width: 56px;
	height: 56px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--color-ink);
	color: var(--color-bg);
	flex-shrink: 0;
}

.nl-icon svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 1.5; }

.nl-txt { min-width: 0; }

.nl-label {
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.16em;
	color: var(--color-accent);
}

.nl-title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 22px;
	line-height: 1.15;
	letter-spacing: -0.018em;
	margin-top: 4px;
	color: var(--color-ink);
	max-width: 28ch;
}

.nl-desc {
	font-family: var(--font-serif-reading);
	font-size: 14px;
	color: var(--color-ink-muted);
	margin-top: 4px;
	font-style: italic;
}

.nl-right { flex-shrink: 0; min-width: 0; }

.nl-form {
	display: flex;
	flex-direction: column;
	gap: 12px;
	width: 100%;
}

.nl-form input[type="email"] {
	padding: 14px 16px;
	border: 1px solid var(--color-ink);
	font-family: var(--font-sans);
	font-size: 14px;
	outline: none;
	width: 100%;
	min-width: 0;
	color: var(--color-ink);
	background: var(--color-bg);
}

.nl-form input[type="email"]::placeholder { color: var(--color-ink-soft); }

.nl-form input[type="email"]:focus { outline: 2px solid var(--color-ink); outline-offset: -3px; }

.nl-form button {
	padding: 12px 22px;
	min-height: 44px;
	width: 100%;
	background: var(--color-accent);
	color: var(--color-bg);
	border: 1px solid var(--color-accent);
	font-family: var(--font-sans);
	font-size: 12px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.12em;
}

.nl-form button:hover { background: var(--color-accent-ink); border-color: var(--color-accent-ink); }

@media (min-width: 640px) {
	.nl-form { flex-direction: row; gap: 0; }
	.nl-form input[type="email"] { flex: 1; width: auto; border-right: none; }
	.nl-form button { width: auto; }
}

.nl-check {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	margin-top: 10px;
	font-family: var(--font-sans);
	font-size: 11px;
	color: var(--color-ink-soft);
	line-height: 1.4;
	max-width: 40ch;
}

.nl-check input { margin-top: 2px; accent-color: var(--color-accent); flex-shrink: 0; }
.nl-check .nl-check-txt { flex: 1; min-width: 0; }
.nl-check a { color: var(--color-accent); text-decoration: underline; text-underline-offset: 2px; }

@media (max-width: 900px) {
	.nlbar-in { grid-template-columns: 1fr; gap: 18px; }
	.nl-icon { width: 48px; height: 48px; }
}


/* ------------------------------------------------------------
   22. ARTICLE (single.php)
   ------------------------------------------------------------ */
.article-header { margin-bottom: var(--space-8); }

.article-title {
	font-family: var(--font-display);
	font-size: var(--fs-display);
	font-weight: 500;
	line-height: var(--lh-tight);
	letter-spacing: var(--tracking-tight);
	margin: var(--space-3) 0;
	font-variation-settings: "opsz" 144;
}

.article-lede {
	font-family: var(--font-serif-reading);
	font-size: var(--fs-lead);
	line-height: var(--lh-reading);
	color: var(--color-ink-muted);
	margin: var(--space-4) 0 var(--space-6);
}

.article-meta {
	font-family: var(--font-sans);
	font-size: var(--fs-meta);
	color: var(--color-ink-soft);
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	align-items: baseline;
}

.article-meta__author a {
	color: var(--color-ink);
	text-decoration: none;
	border-bottom: 1px solid var(--color-rule-faint);
}

.article-meta__sep { color: var(--color-ink-faint); }

.article-figure { margin: 0 0 var(--space-8); }

.article-figure__caption {
	font-family: var(--font-sans);
	font-size: var(--fs-meta);
	color: var(--color-ink-soft);
	margin-top: var(--space-2);
}

.article-tags {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	padding: 0;
	margin: var(--space-8) 0 0;
	list-style: none;
	font-family: var(--font-sans);
	font-size: var(--fs-meta);
}

.article-tags li a {
	display: inline-block;
	padding: 2px 8px;
	border: var(--rule-faint);
	text-decoration: none;
	color: var(--color-ink-muted);
}

.article-tags li a:hover { border-color: var(--color-accent); color: var(--color-accent); }


/* ------------------------------------------------------------
   23. INDEX / BLOG HEADER
   Used by `index.php` when the site has a separate "Posts page"
   configured in Settings → Reading. Every other archive-like
   view (category, tag, author, date, search, 404) uses the
   `.section-head` / `.section-title` / `.section-lede` vocabulary
   from section 41. The three `.archive-*` siblings that used to
   sit here — `.archive-description` (never emitted) and
   `.search-query` (never emitted) — were removed in 2.7.7 after
   confirming no live consumer; the two below stay because
   `index.php` still emits them in its Posts-page header branch.
   ------------------------------------------------------------ */
.archive-header {
	max-width: var(--w, 1060px);
	margin: var(--gap-row) auto 0;
	padding: 0 var(--pad) var(--space-4);
	border-bottom: var(--rule-thick);
}

.archive-title {
	font-family: var(--font-display);
	font-size: var(--fs-h1);
	font-weight: 500;
	margin: 0;
	letter-spacing: var(--tracking-tight);
	font-variation-settings: "opsz" 144;
}

/* Archives use the section framework: a top-level .sect wrapper with a
   row-3 grid of u-cards, matching the front-page pattern. */


/* ------------------------------------------------------------
   24. (removed — `.error-404__*` BEM block from an earlier
   architecture; dropped in 2.7.7 after confirming `404.php`
   renders through `.section-head` / `.section-title` /
   `.section-lede` / `.section-meta` instead, all styled by
   section 41.)
   ------------------------------------------------------------ */


/* ------------------------------------------------------------
   25. SIDEBAR / WIDGETS
   ------------------------------------------------------------ */
.widget-area {
	max-width: var(--w, 1060px);
	margin: var(--space-12) auto 0;
	padding: var(--space-8) var(--pad) 0;
	border-top: var(--rule-thick);
}

.widget { margin-bottom: var(--space-8); }

.widget-title {
	font-family: var(--font-sans);
	font-size: var(--fs-meta);
	font-weight: 700;
	letter-spacing: var(--tracking-caps);
	text-transform: uppercase;
	margin: 0 0 var(--space-3);
	padding-bottom: var(--space-2);
	border-bottom: var(--rule-hair);
}


/* ------------------------------------------------------------
   26. (removed — dead code)

   2.9.2b — the pre-2.7.18 `.pagination` block that used to live
   here (base styles, `.nav-links` centering, `.page-numbers`
   chip styling) was leftover from the 1.0.0 custom pagination
   system. The 2.7.18 rebuild in section 47 below supersedes
   every rule that used to live here:

     - `.pagination` base → section 47 rule (L3413).
     - `.pagination .nav-links` → section 47 rule (matches the
       `<div class="nav-links">` that `the_posts_pagination()`
       emits on the blog index).
     - `.pagination .page-numbers` chip styling → section 47's
       `.pagination a.page-numbers, .pagination span.page-numbers`
       rules (L3449), which target the same anchors / spans
       with equal specificity and later source order.

   The orphan `.pagination .nav-links { justify-content: center }`
   was the cause of the 2.9.2 regression where blog-index
   pagination stayed centred despite the section 47 update — the
   old rule was the only one covering the `<div class="nav-links">`
   shape, and my 2.9.2 flex-start change only touched the
   `<ul class="page-numbers">` shape used on archive pages.
   Deleting the entire block removes the conflict and lets
   section 47 govern both shapes. See Technical Lesson #26
   for the general principle about cleaning up dead code.
   ------------------------------------------------------------ */



/* ------------------------------------------------------------
   28. BUTTONS (.btn — used by 404 and welcome)
   ------------------------------------------------------------ */
.btn {
	display: inline-block;
	font-family: var(--font-sans);
	font-size: var(--fs-small);
	font-weight: 600;
	padding: var(--space-3) var(--space-6);
	text-decoration: none;
	border: 1px solid var(--color-ink);
	background: var(--color-ink);
	color: var(--color-ink-inverse);
	border-radius: var(--radius-1);
	transition: background 0.15s ease, color 0.15s ease;
}

.btn:hover { background: var(--color-accent); border-color: var(--color-accent); }

.btn--ghost { background: transparent; color: var(--color-ink); }

.btn--ghost:hover { background: var(--color-ink); color: var(--color-ink-inverse); }


/* ------------------------------------------------------------
   29. PWA INSTALL BANNER (rendered by CalDir_PWA)
   ------------------------------------------------------------ */
.caldir-install-banner {
	position: fixed;
	z-index: 70;
	left: 16px;
	right: 16px;
	bottom: calc(16px + env(safe-area-inset-bottom, 0));
	padding: 14px 18px;
	display: grid;
	grid-template-columns: 1fr auto;
	gap: 14px;
	align-items: center;
	background: var(--color-bg);
	color: var(--color-ink);
	border: 1px solid var(--color-rule);
	box-shadow: 0 18px 40px -20px rgba(0, 0, 0, 0.25);
	font-family: var(--font-sans);
}

.caldir-install-banner[hidden] { display: none; }

.caldir-install-banner__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.caldir-install-banner__title { font-weight: 700; font-size: 14px; }
.caldir-install-banner__desc { font-size: 12px; color: var(--color-ink-muted); }

.caldir-install-banner__actions { display: flex; gap: 6px; flex-shrink: 0; }

.caldir-install-banner__install,
.caldir-install-banner__dismiss {
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	padding: 10px 14px;
	min-height: 44px;
	border: 1px solid currentColor;
	background: transparent;
	cursor: pointer;
}

.caldir-install-banner__install {
	background: var(--color-accent);
	border-color: var(--color-accent);
	color: var(--color-bg);
}

.caldir-install-banner__install:hover {
	background: var(--color-accent-ink);
	border-color: var(--color-accent-ink);
}

.caldir-install-banner__dismiss { color: var(--color-ink-soft); }
.caldir-install-banner__dismiss:hover { color: var(--color-ink); }

@media (min-width: 640px) {
	.caldir-install-banner {
		left: auto;
		right: 24px;
		bottom: calc(24px + env(safe-area-inset-bottom, 0));
		max-width: 420px;
	}
}


/* ============================================================
   31. (removed — reading-progress bar retired in 2.7.3)
   ============================================================ */


/* ============================================================
   32. BREADCRUMBS (minimal — 2.7.3, left-aligned in 2.7.16)
   Two-step trail "Portada / Categoría" in sans 13 px, ink-muted,
   no uppercase, natural letter-spacing. The current post title is
   intentionally not in the visible crumb (already the <h1>).
   Separator was "›" (single right-pointing quotation) until
   2.7.16; switched to "/" for a newspaper-masthead feel
   (NYT, Guardian, El País all use a slash trail).
   ============================================================ */
.crumbs {
	/* 2.7.17 — defensive left-align chain: width:100% + margin-inline
	   separated from margin-block so the flex box always fills the
	   editorial column (1060 px) regardless of the flex-parent's
	   cross-axis policy. `align-self: start` overrides any
	   `align-items: center` an ancestor might ever set. The explicit
	   `justify-content: flex-start` + `text-align: left` (applied in
	   2.7.16) were insufficient on their own because they could not
	   override the parent flex-stretch fallback when the content was
	   narrower than the max-width. */
	width: 100%;
	max-width: var(--w, 1060px);
	margin-block: 0;
	margin-inline: auto;
	padding: 16px var(--pad) 0;
	align-self: start;
	font-family: var(--font-sans);
	font-size: 13px;
	letter-spacing: 0;
	text-transform: none;
	color: var(--color-ink-muted);
	display: flex;
	align-items: center;
	justify-content: flex-start;
	text-align: left;
	gap: 8px;
	flex-wrap: wrap;
}

.crumbs a {
	color: var(--color-ink-muted);
	text-decoration: none;
}

.crumbs a:hover { color: var(--color-accent); }

.crumbs .sep {
	color: var(--color-ink-faint);
	opacity: 0.7;
	/* 2.7.16 — "/" sets visually denser than "›"; nudge the
	   spacing slightly wider so the trail reads as three
	   balanced tokens rather than a compressed string. */
	margin: 0 2px;
}

.crumbs .cur {
	color: var(--color-ink-muted);
	max-width: 40ch;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

@media (max-width: 768px) {
	.crumbs { padding: 14px 16px 0; font-size: 12px; }
}


/* ============================================================
   33. POST HERO (single article header — WIDE per 2.7.3)
   ============================================================ */
.post-hero {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 40px var(--pad) 32px;
}

.post-hero .kicker { margin-bottom: 20px; font-size: 12px; }

.post-title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: clamp(2rem, 4.5vw + 0.5rem, 3.75rem);
	line-height: 1.05;
	letter-spacing: -0.022em;
	color: var(--color-ink);
	margin: 0 0 24px;
	font-variation-settings: "opsz" 144;
	/* 1.10.22 — `text-wrap: balance` eliminado. Hacía que el título se
	   distribuyera en un rectángulo equilibrado más estrecho que su
	   contenedor `.post-hero` (que comparte `max-width: var(--w, 1060px)` con
	   la foto destacada). Sin balance, el wrap es natural y cada línea
	   se extiende hasta el borde derecho del ancho editorial, igualando
	   visualmente el ancho de la foto. */
}

.post-title em {
	font-style: italic;
	font-weight: 400;
	font-variation-settings: "opsz" 144;
}

.post-lede {
	font-family: var(--font-serif-reading);
	font-style: italic;
	font-weight: 400;
	font-size: clamp(1.125rem, 0.6vw + 1rem, 1.375rem);
	line-height: 1.45;
	color: var(--color-ink-muted);
	margin: 0 0 32px;
	text-wrap: pretty;
}

.byline {
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 18px 0;
	border-top: var(--rule-faint);
	border-bottom: var(--rule-faint);
	font-family: var(--font-sans);
}

.byline-avatar {
	width: 44px;
	height: 44px;
	border-radius: 50%;
	flex-shrink: 0;
	border: 1px solid var(--color-rule-faint);
	object-fit: cover;
}

.byline-main { flex: 1; min-width: 0; }

.byline-author {
	font-size: 14px;
	font-weight: 600;
	color: var(--color-ink);
	line-height: 1.3;
}

.byline-author a { color: inherit; text-decoration: none; }
.byline-author a:hover { color: var(--color-accent); }

.byline-meta {
	margin-top: 3px;
	font-size: 12px;
	color: var(--color-ink-soft);
	letter-spacing: 0.04em;
	line-height: 1.4;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
}

.byline-meta .dot,
.section-meta .dot,
.card-meta .dot {
	width: 3px;
	height: 3px;
	border-radius: 50%;
	background: var(--color-ink-faint);
	display: inline-block;
}


/* ============================================================
   34. (removed — inline share buttons retired in 2.7.3)
   ============================================================ */
@media (max-width: 600px) {
	.byline { flex-wrap: wrap; gap: 12px; }
	.post-hero { padding: 28px 20px 24px; }
}


/* ============================================================
   35. FEATURED IMAGE (editorial width, natural aspect ratio)
   ============================================================ */
.featured {
	/* 2.9.5b — REVERTED from 2.9.5's viewport-wide full-bleed.
	   The featured image rejoins the editorial band (1060 px)
	   centred in the viewport, matching the rest of the single-
	   post layout (breadcrumb, post-hero, byline, article-body,
	   author-box, related). No more `100vw` full-bleed trick;
	   no more `calc(50% - 50vw)` negative margins. The image
	   now reads as part of the editorial composition, not as a
	   cinematic header that visually precedes it.

	   2.9.5b also moves this block in `single.php` to sit ABOVE
	   `.post-hero` — the reader sees image → kicker → title →
	   excerpt → byline → body. Featured-image-first matches the
	   pattern of the homepage hero card and prevents the title
	   from being visually subordinated to an oversized photo. */
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 0 var(--pad);
}

.featured-img {
	/* 2.9.5b — `aspect-ratio: 21 / 9` removed: the featured slot
	   no longer forces a cinemascope crop. The container adopts
	   the natural aspect ratio of the underlying `<img>` via
	   `width: 100%` + the img's `height: auto` below; the
	   `max-height: 500 px` cap on the img prevents tall portraits
	   from dominating the viewport. Background gradient stays as
	   a fallback for broken / slow-loading images — when the img
	   is in flight or missing, the gradient fills whatever natural
	   height the slot would have had. */
	width: 100%;
	background: linear-gradient(135deg, #2A2825 0%, #3A3732 50%, #26231F 100%);
}

.featured-img img {
	/* 2.9.5b — render at natural aspect ratio up to a 500 px
	   height cap. When the image's intrinsic ratio at 100 %
	   container width would exceed 500 px tall, `max-height`
	   clamps the element box and `object-fit: cover` centres
	   the image content inside the reduced box (cropping evenly
	   top and bottom for landscapes, left and right for tall
	   portraits). Below the cap the image renders naturally
	   with no cropping.

	   `position: absolute` / `inset: 0` from pre-2.9.5 removed:
	   no longer needed because the parent no longer forces a
	   fixed aspect ratio — the img flows in normal block
	   context and its box dictates the container's height. */
	width: 100%;
	height: auto;
	max-height: 500px;
	object-fit: cover;
	display: block;
}

/* 1.4.0 — Featured-image caption work reverted (1.3.x retired).
   Single now renders `the_post_thumbnail()` inside the legacy
   `<figure class="featured"><div class="featured-img">…</div></figure>`
   wrapper without any caption logic; the `.featured` / `.featured-img`
   rules in §35 above handle the wrap and aspect cap.

   Larra plugin still envelops `<img>` in `<figure class="larra-thumbnail">
   <figcaption class="larra-thumbnail-caption">…</figcaption></figure>`
   when the attachment has a caption. Inside cards (`<a class="img">…`
   anchor) Larra's caption paints as a dark overlay band and competes
   with the editorial card design — hide it. The single-post caption is
   left untouched: whatever Larra paints there is the accepted look. */
.img .larra-thumbnail-caption {
	display: none;
}

/* 1.4.1 — Style the caption text Larra paints on the dark band over
   the single-post featured image. Cards are excluded by the
   `.img .larra-thumbnail-caption { display: none }` rule above
   (higher specificity wins on the `display` axis, so cards stay
   hidden; these typography rules only affect single, where the band
   is visible). `!important` because Larra's plugin CSS sets the
   colour and padding from its own scope. */
.larra-thumbnail-caption {
	color: #FAF9F5 !important;
	text-align: right !important;
	padding: 7px 18px !important;
	font-size: 12.5px !important;
	line-height: 1.4 !important;
}


/* ============================================================
   36. ARTICLE BODY
   ============================================================ */
.article-body {
	/* 1.10.48 — Default: ancho de lectura (660px), centrado. Aplicable al
	   single article (que vive dentro de `.single-main`; el override de
	   abajo desactiva el centrado para que el grid lo coloque).
	   1.10.49 — Las páginas estáticas (`.page-article .article-body`)
	   reciben `--w` (1020px) más abajo, porque no tienen sidebar y la
	   columna de lectura se ve "perdida" en el centro del viewport. */
	max-width: var(--measure);
	margin: 0 auto;
	padding: 40px var(--pad) 48px;
	font-family: var(--font-serif-reading);
	font-size: 18px;
	line-height: 1.65;
	color: var(--color-ink);
	user-select: text;
	-webkit-user-select: text;
}

.article-body p {
	margin: 0 0 24px;
	text-wrap: pretty;
}

.article-body a {
	color: var(--color-ink);
	text-decoration: underline;
	text-decoration-color: var(--color-accent);
	text-decoration-thickness: 1px;
	text-underline-offset: 4px;
	transition: color 140ms ease, text-decoration-color 140ms ease;
}

.article-body a:hover {
	color: var(--color-accent);
	text-decoration-thickness: 2px;
}

.article-body h2 {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: clamp(1.5rem, 1vw + 1.1rem, 1.875rem);
	line-height: 1.2;
	letter-spacing: -0.015em;
	color: var(--color-ink);
	margin: 48px 0 16px;
	font-variation-settings: "opsz" 72;
	/* 1.10.23 — `text-wrap: balance` eliminado, mismo motivo que en
	   `.post-title`: estrechaba visualmente el subapartado, dejándolo
	   en un rectángulo equilibrado en lugar de extenderlo hasta el
	   borde derecho del ancho de lectura. */
}

.article-body h2 em { font-weight: 400; }

/* H3 = ladillo: small sans caps with bottom rule. */
.article-body h3 {
	font-family: var(--font-sans);
	font-weight: 700;
	font-size: 13px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-ink);
	margin: 40px 0 16px;
	padding-bottom: 8px;
	border-bottom: var(--rule-hair);
	display: inline-block;
	padding-right: 40px;
}

.article-body ul,
.article-body ol {
	margin: 0 0 24px;
	padding-left: 0;
	list-style: none;
	counter-reset: list-counter;
}

.article-body ul li,
.article-body ol li {
	position: relative;
	padding-left: 28px;
	margin-bottom: 10px;
	text-wrap: pretty;
}

.article-body ul li::before {
	content: "";
	position: absolute;
	left: 6px;
	top: 0.72em;
	width: 10px;
	height: 1px;
	background: var(--color-accent);
}

.article-body ol li { counter-increment: list-counter; }

.article-body ol li::before {
	content: counter(list-counter) ".";
	position: absolute;
	left: 0;
	top: 0;
	font-family: var(--font-display);
	font-weight: 500;
	color: var(--color-accent);
	font-size: 1em;
	font-variation-settings: "opsz" 72;
	width: 24px;
}

.article-body em { font-style: italic; }
.article-body strong { font-weight: 600; color: var(--color-ink); }

/* Drop cap on the first paragraph of a post (editors opt in with
   a <span class="dropcap"> inside the first <p>). */
.article-body .dropcap {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 5.4em;
	line-height: 0.82;
	float: left;
	padding: 6px 14px 0 0;
	margin: 8px 0 0 0;
	color: var(--color-ink);
	font-variation-settings: "opsz" 144;
	letter-spacing: -0.04em;
}

/* Blockquote: red rule + italic reading serif. */
.article-body blockquote {
	margin: 32px 0;
	padding: 8px 0 8px 28px;
	border-left: 3px solid var(--color-accent);
	font-family: var(--font-serif-reading);
	font-style: italic;
	font-size: 19px;
	line-height: 1.55;
	color: var(--color-ink-muted);
}

.article-body blockquote p { margin-bottom: 12px; }
.article-body blockquote p:last-child { margin-bottom: 0; }

.article-body blockquote cite {
	display: block;
	margin-top: 10px;
	font-family: var(--font-sans);
	font-style: normal;
	font-size: 12px;
	letter-spacing: 0.08em;
	color: var(--color-ink-soft);
}

/* Pull quote: huge italic display quote, Atlantic-style. */
.article-body .pullquote,
.pullquote {
	margin: 48px 0;
	padding: 0;
	border: 0;
	position: relative;
}

.article-body .pullquote::before,
.pullquote::before {
	content: "\201C";
	display: block;
	font-family: var(--font-display);
	font-weight: 400;
	font-style: italic;
	font-size: 140px;
	line-height: 0.7;
	color: var(--color-accent);
	margin-left: -8px;
	margin-bottom: 4px;
	font-variation-settings: "opsz" 144;
	user-select: none;
}

.article-body .pullquote p,
.pullquote p {
	font-family: var(--font-display);
	font-weight: 400;
	font-style: italic;
	font-size: clamp(1.625rem, 2vw + 0.6rem, 2.25rem);
	line-height: 1.2;
	color: var(--color-ink);
	margin: 0 0 16px;
	letter-spacing: -0.015em;
	font-variation-settings: "opsz" 144;
	text-wrap: balance;
}

.article-body .pullquote cite,
.pullquote cite {
	display: block;
	font-family: var(--font-sans);
	font-style: normal;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-ink-soft);
	margin-top: 4px;
}

.article-body .pullquote cite::before,
.pullquote cite::before { content: "—  "; color: var(--color-accent); }

/* Code: inline + block. */
.article-body code {
	font-family: var(--font-mono);
	font-size: 0.88em;
	background: var(--color-bg-subtle);
	padding: 2px 6px;
	border: 1px solid var(--color-rule-faint);
	color: var(--color-ink);
	letter-spacing: 0;
}

.article-body pre {
	margin: 24px 0;
	padding: 20px 22px;
	background: var(--color-bg-subtle);
	border: 1px solid var(--color-rule-faint);
	border-left: 3px solid var(--color-accent);
	overflow-x: auto;
	font-family: var(--font-mono);
	font-size: 13.5px;
	line-height: 1.55;
	color: var(--color-ink-muted);
}

.article-body pre code {
	background: transparent;
	border: 0;
	padding: 0;
	font-size: inherit;
	color: inherit;
}

/* Figures inside the article body. */
.article-body figure { margin: 32px 0; }

.article-body figure img {
	/* 2.9.5 — natural aspect ratio, no crop. The forced
	   `aspect-ratio: 16 / 9` + `object-fit: cover` from 1.0.0 →
	   2.9.4 cropped every content image into a landscape box,
	   which was especially wrong for portrait photos, screenshots
	   and illustrations. Editors paste an image expecting to see
	   its full content; the 16 / 9 crop silently trimmed the
	   top / bottom of portrait images and distorted the visual
	   intent. Removed in 2.9.5. Image renders at its natural
	   proportions within the `max-width: var(--measure)` cap
	   from the 2.9.4 block above. */
	width: 100%;
	height: auto;
	display: block;
}

.article-body figcaption {
	margin-top: 10px;
	font-family: var(--font-sans);
	font-size: 13px;
	line-height: 1.45;
	color: var(--color-ink-soft);
}

.article-body figcaption .credit {
	color: var(--color-ink-faint);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	font-size: 11px;
	margin-left: 6px;
}

/* Gutenberg align-wide → extend to --measure-wide, centred on the
   reading column. Uses the `margin-inline: calc(50% - 50vw)` trick
   to escape the narrow parent without declaring `width: 100vw` —
   100vw on iOS Safari includes the scrollbar area and causes a
   horizontal scroll on the main document. */
.article-body figure.alignwide,
.article-body .wp-block-image.alignwide,
.article-body .alignwide {
	max-width: var(--measure-wide);
	margin-left: auto;
	margin-right: auto;
}

/* Gutenberg align-full → bleed to viewport edges using the same
   margin-calc trick (no width: 100vw). */
.article-body figure.alignfull,
.article-body .wp-block-image.alignfull,
.article-body .alignfull {
	max-width: none;
	margin-left: calc(50% - 50vw);
	margin-right: calc(50% - 50vw);
	margin-top: 48px;
	margin-bottom: 48px;
}

/* 2.9.5 — `.article-body .alignfull img { aspect-ratio: 21 / 9 }`
   removed. Two reasons: (a) 2.9.4 already neutralises
   `.alignfull` figures inside `.article-body` (they render at
   `var(--measure)` not viewport-bleed), so a `21 / 9` aspect
   forced on the inner img would produce an oddly wide letterbox
   inside a reading-width figure rather than the cinemascope
   hero the original rule intended; and (b) 2.9.5's general
   rule for inline images is "natural aspect, no crop" — forced
   aspect-ratios on content images are retired across the
   board. The featured image at the top of the post keeps its
   21 / 9 crop via `.featured-img` (different selector, outside
   this scope). */


/* ============================================================
   2.9.4 — ARTICLE BODY content images at reading width

   Content images in `.article-body` / `.entry-content` are
   capped at the reading-column width (`var(--measure)` =
   660 px ≈ 72 ch — the same measure the surrounding text
   uses). Images join the reader's focal column rather than
   breaking out to `.alignwide` (860 px) or `.alignfull`
   (viewport) as Gutenberg defaults would suggest. Keeps the
   article's visual rhythm tight: images read as continuations
   of the argument, not as typographic interruptions.

   Product decision: content images do NOT break out. If a
   specific image deserves a wider treatment, the editor
   promotes it to the **featured image** slot (which renders
   at the article's `<main>`-level 1440 px `.featured` container,
   outside this rule's scope) or uses a **gallery** (which
   renders its own grid via the WP block layout).

   What the rule targets:
     - `<img>` inside `.article-body` / `.entry-content`.
     - `<figure>` wrappers + Gutenberg `.wp-block-image`
       wrappers + legacy `.wp-caption`.
     - `.alignwide` / `.alignfull` FIGURES are neutralised
       here (max-width pinned to measure, the viewport bleed
       of alignfull is undone via `margin-inline: auto`) for
       images inside `.article-body`. The classes stay usable
       for non-image blocks.

   What the rule does NOT target:
     - `.featured-img` / `.featured` — outside `.article-body`,
       stays at its 1440 px header cap.
     - `.wp-block-gallery` / `.gallery` and their inner items —
       higher-specificity revert below restores gallery layout.
     - `.wp-block-embed` / bare `<iframe>` — handled in 2.9.3
       at full editorial width.

   Specificity note: `.article-body .wp-block-image` is
   `(0, 2, 1)`, same as `.article-body figure.alignwide` /
   `.alignfull`. Source order wins; 2.9.4 comes later than
   the .alignwide/.alignfull figure rules above, so the cap
   applies. The `.wp-block-gallery *` revert below uses
   `(0, 3, 1)` to override the cap on gallery items only.
   ============================================================ */

/* 1 — Cap images and figures at the reading measure. */
.entry-content img,
.article-body img {
	max-width: var(--measure);
	margin-inline: auto;
	display: block;
	height: auto;
}

.entry-content figure,
.entry-content .wp-block-image,
.entry-content .wp-caption,
.article-body figure,
.article-body .wp-block-image,
.article-body .wp-caption {
	max-width: var(--measure);
	margin-inline: auto;
}

/* 2 — Neutralise .alignwide / .alignfull on figures inside
   `.article-body`. The classes normally push figures to 860
   or viewport-bleed; with content images capped at measure,
   keeping wider figure wrappers would leave an empty gutter
   on each side of the image (figure at 860, image at 660,
   160 px of empty background). Pin these figure wrappers to
   measure so the figure + image render as one coherent
   reading-width block. */
.entry-content figure.alignwide,
.entry-content .wp-block-image.alignwide,
.entry-content .alignwide,
.entry-content figure.alignfull,
.entry-content .wp-block-image.alignfull,
.entry-content .alignfull,
.article-body figure.alignwide,
.article-body .wp-block-image.alignwide,
.article-body .alignwide,
.article-body figure.alignfull,
.article-body .wp-block-image.alignfull,
.article-body .alignfull {
	max-width: var(--measure);
	margin-left: auto;
	margin-right: auto;
}

/* 3 — Gallery revert. `.wp-block-gallery` (and legacy
   `.gallery`) have their own column layout that the 660 px
   cap would break. Higher specificity (two classes in the
   descendant path) wins over rule (1). */
.entry-content .wp-block-gallery,
.entry-content .gallery,
.article-body .wp-block-gallery,
.article-body .gallery {
	max-width: none;
}

.entry-content .wp-block-gallery img,
.entry-content .wp-block-gallery .wp-block-image,
.entry-content .wp-block-gallery figure,
.entry-content .gallery img,
.entry-content .gallery figure,
.article-body .wp-block-gallery img,
.article-body .wp-block-gallery .wp-block-image,
.article-body .wp-block-gallery figure,
.article-body .gallery img,
.article-body .gallery figure {
	max-width: none;
	margin-inline: 0;
}


/* ============================================================
   2.9.3 — ARTICLE BODY video embeds

   oEmbed YouTube / Vimeo / etc. and raw-HTML iframe pastes in
   single posts break out of `.article-body`'s 660 px reading-
   column (`max-width: var(--measure)`) and render at the full
   editorial band width (`var(--w, 1060px) = 1060 px`), or at the
   viewport-minus-padding on narrow viewports — whichever is
   smaller. Centred via `position: relative` + `left: 50%` +
   `translateX(-50%)` rather than the `.alignfull` margin-inline
   trick, because margin-inline math interacts badly with a
   `max-width` cap (the element's width can't satisfy both the
   margin equation and the cap; browsers fall back to unpredictable
   rendering across engines).

   The `position: relative` shift reserves the element's
   original-position flow box (still inside `.article-body`'s
   660 px measure, so subsequent paragraphs resume flow
   immediately below) and paints the visual box at the
   parent-centre, offset by half the element's own width.
   Depends on `html { overflow-x: hidden }` (2.6.20) to clip
   the visual overflow at viewport edges; without that clip,
   the page would horizontal-scroll when the embed's visual
   box extends past the narrow parent.

   Product decision: videos are NOT grayscaled when
   `body.caldir-grayscale` is active. The grayscale brand
   signature applies to editorial images only (photos,
   featured images, avatars); video content preserves its
   original colour. No `filter` rule here. See the 2.7.23
   escape-hatches list (L4838+) for the equivalent image
   enumeration — videos are absent from it by design.
   ============================================================ */

/* Outer wrapper — Gutenberg-wrapped embeds. Handles
   `<figure class="wp-block-embed">` and its typed variants
   (`.is-type-video`, `.wp-block-embed-youtube`,
   `.wp-block-embed-vimeo`, etc.). */
.article-body .wp-block-embed,
.article-body figure.wp-block-embed {
	position: relative;
	left: 50%;
	transform: translateX(-50%);
	width: min(var(--w, 1060px), calc(100vw - var(--pad) * 2));
	max-width: none;
	margin: 32px 0;
}

/* Inner wrapper — Gutenberg's `.wp-block-embed__wrapper`.
   Declares the aspect ratio so the absolutely-positioned
   iframe inside has a known box to fill. */
.article-body .wp-block-embed__wrapper {
	position: relative;
	aspect-ratio: 16 / 9;
	width: 100%;
}

.article-body .wp-block-embed__wrapper iframe {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
	display: block;
}

/* Bare `<iframe>` children of `.article-body` — raw HTML paste
   or edge cases where oEmbed doesn't wrap in a `.wp-block-embed`
   figure. Same breakout + 16/9 aspect as the Gutenberg case. */
.article-body > iframe,
.article-body > p > iframe {
	position: relative;
	left: 50%;
	transform: translateX(-50%);
	width: min(var(--w, 1060px), calc(100vw - var(--pad) * 2));
	max-width: none;
	aspect-ratio: 16 / 9;
	height: auto;
	border: 0;
	display: block;
	margin: 32px 0;
}

@media (max-width: 600px) {
	.article-body { padding: 28px 20px 36px; font-size: 17px; }
	.article-body .dropcap { font-size: 4.2em; padding-right: 10px; }
	.article-body .pullquote::before,
	.pullquote::before { font-size: 100px; }
	.article-body h2 { margin-top: 36px; }
}


/* ============================================================
   37. POST END (tags)
   (`.post-end-mark` + its `.glyph` child — the P-letter ornament
   flanked by two hairlines — was dropped in 2.7.5. The configurable
   `.article-end-divider` from 2.7.3 Phase F is the single closing
   mark now; the legacy block had shipped in parallel by accident.)
   ============================================================ */
.post-end {
	/* 1.10.48 — Default: centrado (igual que `.article-body`). */
	max-width: var(--measure);
	margin: 0 auto;
	padding: 0 var(--pad) 40px;
}

.post-tags {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 10px;
	font-family: var(--font-sans);
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.post-tags .t-label {
	color: var(--color-ink-faint);
	font-weight: 600;
	letter-spacing: 0.14em;
	margin-right: 4px;
}

.post-tags a {
	color: var(--color-ink-muted);
	padding: 4px 10px;
	border: 1px solid var(--color-rule-faint);
	background: var(--color-bg);
	text-decoration: none;
	transition: color 140ms ease, border-color 140ms ease;
}

.post-tags a:hover { color: var(--color-accent); border-color: var(--color-accent); }


/* ============================================================
   38. AUTHOR BOX (WIDE — aligns with the post-hero + related
   columns per the 2.7.3 asymmetric reading layout)
   ============================================================ */
.author-box {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 32px var(--pad);
	border-top: var(--rule-hair);
	border-bottom: var(--rule-hair);
	display: grid;
	grid-template-columns: 88px 1fr auto;
	align-items: center;
	gap: 24px;
}

.author-avatar {
	width: 88px;
	height: 88px;
	border-radius: 50%;
	flex-shrink: 0;
	border: 1px solid var(--color-rule-faint);
	object-fit: cover;
}

.author-meta .kicker { font-size: 10px; margin-bottom: 6px; }

.author-name {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 24px;
	letter-spacing: -0.015em;
	line-height: 1.1;
	color: var(--color-ink);
	margin: 0 0 8px;
	font-variation-settings: "opsz" 72;
}

.author-bio {
	font-family: var(--font-serif-reading);
	font-style: italic;
	font-size: 15px;
	line-height: 1.5;
	color: var(--color-ink-muted);
	margin: 0;
	max-width: 55ch;
}

.author-more {
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-accent);
	white-space: nowrap;
	text-decoration: none;
	align-self: end;
	padding-bottom: 6px;
}

.author-more:hover { color: var(--color-accent-ink); }

@media (max-width: 600px) {
	.author-box {
		grid-template-columns: 64px 1fr;
		padding: 24px 20px;
		gap: 16px;
	}
	.author-avatar { width: 64px; height: 64px; }
	.author-name { font-size: 20px; }
	.author-more { grid-column: 1 / -1; align-self: start; padding-bottom: 0; padding-top: 4px; }
}


/* ============================================================
   39. RELATED ARTICLES (single + 404)
   ============================================================ */
.related {
	max-width: var(--w, 1060px);
	margin: 56px auto 0;
	padding: 0 var(--pad);
}

.related-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	margin-bottom: 24px;
	padding-bottom: 12px;
	border-bottom: var(--rule-thick);
}

.related-title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 22px;
	color: var(--color-ink);
	margin: 0;
	letter-spacing: -0.015em;
	font-variation-settings: "opsz" 72;
}

.related-more {
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-ink-soft);
	text-decoration: none;
}

.related-more:hover { color: var(--color-accent); }

.related-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 32px;
	align-items: stretch;
}

.related-grid .u-card {
	display: flex;
	flex-direction: column;
	text-decoration: none;
	color: inherit;
	transition: opacity 140ms ease;
}

.related-grid .u-card:hover { opacity: 0.85; }

.u-card-img {
	aspect-ratio: 4 / 3;
	margin-bottom: 14px;
	display: block;
	position: relative;
	overflow: hidden;
}

.u-card-img img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.related-grid .u-card .kicker { margin-bottom: 8px; font-size: 10px; }

.u-card-title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 20px;
	line-height: 1.18;
	letter-spacing: -0.015em;
	color: var(--color-ink);
	margin: 0 0 10px;
	font-variation-settings: "opsz" 72;
	text-wrap: balance;
}

.u-card-title em { font-weight: 400; }

.u-card-meta {
	font-family: var(--font-sans);
	font-size: 12px;
	color: var(--color-ink-soft);
	letter-spacing: 0.04em;
	margin-top: auto;
}

@media (max-width: 900px) {
	.related-grid { grid-template-columns: repeat(2, 1fr); gap: 24px; }
	.related-grid > :nth-child(3) { display: none; }
}

@media (max-width: 600px) {
	.related { margin-top: 36px; padding: 0 20px; }
	.related-grid { grid-template-columns: 1fr; gap: 28px; }
	.related-grid > :nth-child(3) { display: flex; }
	.u-card-title { font-size: 19px; }
}


/* ============================================================
   40. (removed — comments disabled site-wide in 2.7.3)
   ============================================================ */


/* ============================================================
   41. ARCHIVE — section head
   Left-aligned in 2.7.16. Until then the block relied on the
   browser default `text-align: left` inside a centered 1060 px
   column; the explicit declaration below encodes the intent so
   any future child-theme / plugin override that inherits from
   a centered ancestor cannot accidentally pull the kicker,
   title, lede and metrics back to centre. The border-bottom
   spans the full `max-width: var(--w, 1060px)` box so the divider
   lines up with the archive `<main>` edges below.
   ============================================================ */
.section-head {
	/* 2.7.17 — defensive left-align chain (kept).
	   2.7.18 — the `border-bottom` that used to live on this rule
	   is emitted via a `::after` pseudo-element below so the divider
	   respects the horizontal padding and aligns with the content
	   edges (kicker / title / lede / meta), not with the outer box.
	   Before 2.7.18 the border spanned the full 1060 px column while
	   the content was indented 20 px on each side by `--pad`, leaving
	   a visible 40 px "overhang" at both ends. */
	width: 100%;
	max-width: var(--w, 1060px);
	margin-block: 0;
	margin-inline: auto;
	padding: 40px var(--pad) 28px;
	align-self: start;
	text-align: left;
	position: relative;
}

.section-head::after {
	content: "";
	position: absolute;
	left: var(--pad);
	right: var(--pad);
	bottom: 0;
	border-bottom: var(--rule-thick);
	pointer-events: none;
}

.section-head .kicker {
	font-size: 12px;
	margin-bottom: 18px;
	/* Kicker is `display: inline-block` (see section 6); the defensive
	   `align-self` below keeps it at the cross-axis start should a
	   future refactor wrap it in a flex container. */
	align-self: flex-start;
}

.section-title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: clamp(2.5rem, 5.5vw, 4.5rem);
	line-height: 0.98;
	letter-spacing: -0.028em;
	color: var(--color-ink);
	/* 2.7.17 — explicit `margin-inline: 0` overrides any UA default
	   that could auto-centre a constrained block. `text-align: left`
	   locks the inline content regardless of ancestor inheritance. */
	margin: 0 0 16px;
	margin-inline: 0;
	text-align: left;
	font-variation-settings: "opsz" 144;
	text-wrap: balance;
}

.section-title em {
	font-style: italic;
	font-weight: 400;
}

.section-lede {
	font-family: var(--font-serif-reading);
	font-style: italic;
	font-size: clamp(1rem, 0.4vw + 0.95rem, 1.25rem);
	line-height: 1.45;
	color: var(--color-ink-muted);
	max-width: 58ch;
	/* 2.7.17 — explicit `margin-inline-start: 0` prevents the
	   `max-width: 58ch` + `margin: 0 0 20px` combination from
	   floating the lede toward the centre on ancestor chains that
	   force `margin: 0 auto` elsewhere. */
	margin: 0 0 20px;
	margin-inline-start: 0;
	text-align: left;
	text-wrap: pretty;
}

.section-meta {
	font-family: var(--font-sans);
	font-size: 12px;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--color-ink-soft);
	display: flex;
	flex-wrap: wrap;
	gap: 8px 14px;
	align-items: center;
	/* 2.7.17 — explicit `justify-content: flex-start` so the meta
	   chips ("N artículos", "N firmas", "Actualizado …") stay
	   packed to the left of the flex row. Default is `flex-start`
	   per spec, but the declaration guards against any future
	   `justify-content: center` inheritance / override. */
	justify-content: flex-start;
}

.section-meta .update { color: var(--color-accent); font-weight: 600; }

/* Fase 1 — "Cal Dir en los 500 pueblos": llamada a hacerse corresponsal.
   2026-08-05 — se mudó a la barra superior, junto al logo (ver
   `.topbar-corresponsal` más abajo, cerca de `.btn-sub`) — este hueco ya
   no la usa. Historial de las dos versiones anteriores (franja pesada
   tipo titular de sección, luego franja roja a todo el ancho) en el
   comentario de CalDir_Municipios::render_cta_corresponsal(). */


/* ============================================================
   42. ARCHIVE — subnav
   ============================================================ */
.subnav {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 18px var(--pad);
	display: flex;
	flex-wrap: wrap;
	gap: 4px 2px;
	font-family: var(--font-sans);
	border-bottom: var(--rule-faint);
	overflow-x: auto;
	scrollbar-width: none;
}

.subnav::-webkit-scrollbar { display: none; }

.subnav a {
	font-size: 12px;
	font-weight: 500;
	letter-spacing: 0.04em;
	color: var(--color-ink-muted);
	padding: 6px 14px 6px 0;
	white-space: nowrap;
	text-decoration: none;
}

.subnav a.on { color: var(--color-accent); font-weight: 700; }
.subnav a:hover { color: var(--color-accent); }

.subnav .sep {
	color: var(--color-ink-faint);
	padding-right: 14px;
	user-select: none;
}


/* ============================================================
   43. ARCHIVE — shared card atom + placeholder gradients
   ============================================================ */

/* 2.7.9 — Scope the root rule to `main.archive`. A bare `.archive`
   selector collides with WordPress's automatic `body_class()`,
   which adds the `archive` class to every archive view (category,
   tag, author, date). That collision was giving `<body>` itself a
   `max-width: var(--w, 1060px); margin: 0 auto` and boxing the topbar
   (which has been a direct body child since 2.7.8) with a cream
   gutter on ultra-wide viewports. Element-type scoping
   (`main.archive`) keeps the rule on the `<main>` emitted by
   `CalDir_Archive::render_variant()` and by `404.php`, without
   ever matching the `<body>`. Descendant rules below
   (`.archive .card`, `.archive .pagination`, etc.) do not need
   rescoping — their own children only exist inside the `<main>`
   in practice, so the redundant body-ancestor match produces
   identical output. */
main.archive {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 32px var(--pad) 40px;
}

.ph {
	background:
		linear-gradient(135deg, #E8E4DA 0%, #DAD4C6 100%);
	border: 1px solid var(--color-rule-faint);
	position: relative;
	overflow: hidden;
	display: block;
}

.ph--dark { background: linear-gradient(135deg, #2A2825 0%, #3A3732 50%, #26231F 100%); }
.ph--warm { background: linear-gradient(135deg, #C9A961 0%, #A6873E 100%); }
.ph--cool { background: linear-gradient(135deg, #3A5A6E 0%, #2A4254 100%); }
.ph--rust { background: linear-gradient(135deg, #8B3A2E 0%, #5A261F 100%); }
.ph--sage { background: linear-gradient(135deg, #7A8A6A 0%, #4E5B43 100%); }

.archive .card {
	display: flex;
	flex-direction: column;
	gap: 12px;
	transition: opacity 140ms ease;
}

.archive .card:hover { opacity: 0.88; }

.archive .card .kicker { font-size: 10px; margin-bottom: 0; }
.archive .card .kicker a { color: inherit; text-decoration: none; }

.card-title {
	font-family: var(--font-display);
	font-weight: 500;
	color: var(--color-ink);
	margin: 0;
	letter-spacing: -0.015em;
	line-height: 1.18;
	font-variation-settings: "opsz" 72;
	text-wrap: balance;
}

.card-title a { color: inherit; text-decoration: none; }
.card-title a:hover { color: var(--color-accent); }
.card-title em { font-weight: 400; }

.card-dek {
	font-family: var(--font-serif-reading);
	font-size: 15px;
	line-height: 1.45;
	color: var(--color-ink-muted);
	margin: 0;
	text-wrap: pretty;
}

.card-meta {
	font-family: var(--font-sans);
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--color-ink-soft);
	display: flex;
	flex-wrap: wrap;
	gap: 6px 10px;
	align-items: center;
	margin-top: auto;
}

.card-meta .author {
	color: var(--color-ink-muted);
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: none;
	font-size: 12px;
}

/* Archive card images: anchor + inner <img>. */
.archive .card .ph img,
.archive .card a.ph img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}


/* ============================================================
   44. ARCHIVE VARIANT A — PYRAMID
   ============================================================ */
.pyramid {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: 32px var(--grid-gap);
	row-gap: 40px;
}

.pyramid .hero {
	grid-column: span 12;
	display: grid;
	grid-template-columns: 7fr 5fr;
	gap: 32px;
	align-items: start;
	padding-bottom: 32px;
	border-bottom: var(--rule-faint);
}

.pyramid .hero .hero-img { aspect-ratio: 16 / 10; }
.pyramid .hero .hero-body { padding-top: 4px; }

.pyramid .hero .card-title {
	font-size: clamp(1.75rem, 2.2vw + 0.9rem, 2.75rem);
	line-height: 1.05;
	letter-spacing: -0.02em;
	margin-bottom: 16px;
}

.pyramid .hero .card-dek {
	font-family: var(--font-serif-reading);
	font-style: italic;
	font-size: 17px;
	line-height: 1.5;
	color: var(--color-ink-muted);
	margin-bottom: 18px;
}

.pyramid .mid { grid-column: span 6; }

.pyramid .mid .mid-img {
	aspect-ratio: 16 / 10;
	margin-bottom: 14px;
}

.pyramid .mid .card-title {
	font-size: 22px;
	line-height: 1.18;
	margin-bottom: 8px;
}

.pyramid .mid .card-dek { font-size: 14px; line-height: 1.5; }

.pyramid .std { grid-column: span 3; }

.pyramid .std .std-img {
	aspect-ratio: 4 / 3;
	margin-bottom: 10px;
}

.pyramid .std .card-title {
	font-size: 16px;
	line-height: 1.22;
	margin-bottom: 6px;
}

.pyramid .std-row-top {
	grid-column: span 12;
	border-top: var(--rule-faint);
	padding-top: 40px;
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 32px;
}

.pyramid .std-row-top .std { grid-column: auto; }

@media (max-width: 900px) {
	.pyramid .hero { grid-template-columns: 1fr; }
	.pyramid .mid { grid-column: span 12; }
	.pyramid .std { grid-column: span 6; }
	.pyramid .std-row-top { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 600px) {
	.pyramid .hero .card-title { font-size: 28px; }
	.pyramid .std { grid-column: span 12; }
	.pyramid .std-row-top { grid-template-columns: 1fr; gap: 24px; }
	.pyramid .mid .mid-img { aspect-ratio: 3 / 2; }
	.pyramid { row-gap: 28px; }
	.pyramid .hero { padding-bottom: 24px; }
}


/* ============================================================
   45. ARCHIVE VARIANT B — RIVER
   ============================================================ */
.river {
	max-width: 820px;
	margin: 0 auto;
	border-top: var(--rule-faint);
}

.river-item {
	display: grid;
	grid-template-columns: 220px 1fr;
	gap: 28px;
	padding: 28px 0;
	border-bottom: var(--rule-faint);
	align-items: start;
}

.river-item .r-img { aspect-ratio: 4 / 3; }

.river-body {
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.river-body .kicker { font-size: 10px; margin-bottom: 0; }

.river-body .card-title {
	font-size: clamp(1.25rem, 0.6vw + 1.1rem, 1.5rem);
	line-height: 1.2;
	letter-spacing: -0.015em;
}

.river-body .card-dek {
	font-size: 15px;
	line-height: 1.5;
	color: var(--color-ink-muted);
}

.river-body .card-meta { margin-top: 4px; }

@media (max-width: 600px) {
	.river-item { grid-template-columns: 100px 1fr; gap: 16px; padding: 20px 0; }
	.river-item .r-img { aspect-ratio: 1 / 1; }
	.river-body .card-title { font-size: 17px; }
	.river-body .card-dek { display: none; }
}


/* ============================================================
   46. ARCHIVE VARIANT C — GRID DENSE
   ============================================================ */
.grid-dense {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 40px var(--grid-gap);
}

.grid-dense .card { min-height: 100%; }

.grid-dense .card .gd-img {
	aspect-ratio: 16 / 10;
	margin-bottom: 14px;
}

.grid-dense .card .card-title {
	font-size: 19px;
	line-height: 1.2;
	margin-bottom: 8px;
}

.grid-dense .card .card-dek {
	font-size: 14px;
	line-height: 1.5;
	color: var(--color-ink-muted);
}

@media (max-width: 900px) { .grid-dense { grid-template-columns: repeat(2, 1fr); gap: 32px 24px; } }
@media (max-width: 600px) { .grid-dense { grid-template-columns: 1fr; gap: 28px; } }


/* =====================================================
   2.9.0 — ARCHIVE DEFENSIVE CSS

   A commercial WordPress theme has to survive any
   content its site owner throws at it — including the
   WP Theme Unit Test fixtures (200-character German
   compound-word titles, posts with no thumbnail,
   novella-length excerpts). The three archive layouts
   (pyramid, river, grid) were designed against the
   ideal case; this block adds the CSS defences that
   keep them readable and aligned under real editorial
   traffic.

   Note on the 2.6.15 global safety net. That rule
   (`:where([class*="-card"] h2, h3, h4)` at L4498)
   clamps titles at 6 lines on any element whose class
   CONTAINS `-card` as a substring. It catches `.u-card`,
   `.tech-top-card`, `.u-card-big`, etc. — but NOT the
   bare `.card` class used in the archives, because
   `.card` has no hyphen prefix. So the archive cards
   fall through the global net and need their own
   per-variant clamps below. The tighter line counts
   here (2–4 lines depending on hierarchy level) reflect
   the archive cards' narrower column widths and tighter
   visual rhythm.

   Changes captured in this block:

     1. Title line-clamps per hierarchy level.
     2. Excerpt (`.card-dek`) line-clamps per level.
     3. Overflow guards (`min-width: 0; overflow:
        hidden`) on every card variant + `word-break`
        on titles for URLs and unbreakable compound
        words.
     4. `height: 100%` on `.archive .card` so
        `margin-top: auto` on `.card-meta` anchors the
        metadata to the bottom of a grid-row-stretched
        card (Lesson #25 recipe).
     5. `.ph` placeholder aspect-ratio already resolves
        through the per-variant rules (`.hero-img`,
        `.mid-img`, `.std-img`, `.r-img`, `.gd-img`)
        applied to the same `.ph` anchor element — no
        new rule needed. The `.ph { display: block; }`
        from L2986 preserves aspect-ratio on empty
        (no-thumbnail) placeholders via CSS's default
        block-level aspect-ratio behaviour.
     6. Mobile relaxation (< 600 px) where `.std` goes
        full-width and deserves an extra line of title
        breathing room.

   See Technical Lesson #29.
   ===================================================== */

/* ---- (1) Title line-clamps ---------------------------- */
.pyramid .hero .card-title,
.pyramid .mid .card-title,
.pyramid .std .card-title,
.river-body .card-title,
.grid-dense .card .card-title {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-overflow: ellipsis;
}

.pyramid .hero .card-title    { -webkit-line-clamp: 4; }
.pyramid .mid .card-title     { -webkit-line-clamp: 3; }
.pyramid .std .card-title     { -webkit-line-clamp: 2; }
.river-body .card-title       { -webkit-line-clamp: 3; }
.grid-dense .card .card-title { -webkit-line-clamp: 3; }

/* ---- (2) Excerpt line-clamps -------------------------- */
.pyramid .hero .card-dek,
.pyramid .mid .card-dek,
.pyramid .std .card-dek,
.river-body .card-dek,
.grid-dense .card .card-dek {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-overflow: ellipsis;
}

.pyramid .hero .card-dek      { -webkit-line-clamp: 3; }
.pyramid .mid .card-dek       { -webkit-line-clamp: 2; }
.pyramid .std .card-dek       { -webkit-line-clamp: 2; }
.river-body .card-dek         { -webkit-line-clamp: 2; }
.grid-dense .card .card-dek   { -webkit-line-clamp: 2; }

/* ---- (3) Overflow guards ------------------------------ */
.pyramid .hero,
.pyramid .mid,
.pyramid .std,
.river-item,
.grid-dense .card {
	min-width: 0;
	overflow: hidden;
}

.card-title {
	word-break: break-word;
}

/* ---- (4) Card height normalisation -------------------- */
/* Applies to every archive card variant via the shared
   `.card` class. `.archive .card` already declares
   `display: flex; flex-direction: column` + `.card-meta
   { margin-top: auto }`; the missing piece was `height:
   100%` so the flex column stretches to the grid row
   height (which `align-items: stretch`, the grid default,
   gives us for free on `.std-row-top` and the
   `.grid-dense` container). See Technical Lesson #25. */
.archive .card {
	height: 100%;
}

/* ---- (6) Mobile relaxation for `.std` ----------------- */
/* At < 600 px `.std` goes full-width (`grid-column: span
   12`). The 2-line clamp that worked in a 3-column row
   is too tight when the card has the full viewport width
   to breathe. Relax to 3 lines. */
@media (max-width: 600px) {
	.pyramid .std .card-title { -webkit-line-clamp: 3; }
}


/* ============================================================
   47. ARCHIVE — numeric pagination (rebuilt in 2.7.18)

   Uses the stock WordPress `paginate_links( type: 'list' )`
   output. Markup shape:

     <nav class="pagination">
       <ul class="page-numbers">
         <li><a class="prev page-numbers">← Anterior</a></li>
         <li><a class="page-numbers" href>1</a></li>
         <li><span class="page-numbers current">2</span></li>
         …
         <li><a class="next page-numbers">Siguiente →</a></li>
       </ul>
     </nav>

   The previous custom `.pg-prev` / `.pg-num` / `.pg-ellipsis` /
   `.pg-total` system was removed: one unfamiliar vocabulary
   fewer for child themes and plugins to learn.
   ============================================================ */
.pagination {
	/* 2.9.2d — explicit `width: 100%` to defeat flex-shrink-to-fit.
	   `.pagination` lives inside an ancestor chain that passes
	   through `#page.site { display: flex; flex-direction:
	   column }`. Block elements inside a flex-column ancestor can
	   collapse to their intrinsic content width (the chips' total
	   ~333 px here) in some layout contexts, despite being inside
	   a non-flex intermediate parent like `.site-main` or
	   `main.archive`. Without `width: 100%` declared explicitly,
	   `max-width: var(--w, 1060px)` never kicks in because the used width
	   is already below the cap. With `width: 100%`, the used
	   width is the parent's content-box width, `max-width` caps
	   at 1060, and `margin: auto` centres the result. Same
	   pattern as Technical Lesson #18 documented for `.header`
	   in 2.7.27. */
	width: 100%;
	max-width: var(--w, 1060px);
	margin: 48px auto 0;
	padding: 28px var(--pad) 0;
	border-top: var(--rule-thick);
	/* 2.9.2 — left-aligned pagination. Previously centred, which
	   pulled the numbered links into the middle of the band below
	   a full-width separator rule. Editorial convention reads
	   cleaner with numbers flush against the left edge (where the
	   reader's eye lands after scanning the article list above);
	   the separator rule still spans the full `--w` band because
	   `.pagination` keeps `max-width: var(--w, 1060px)` + `margin: auto`. */
	text-align: left;
	/* 2.9.2b — font-family + font-size relocated from the retired
	   section 26 block. They were the only properties of that
	   block not already shadowed by the 2.7.18 section 47 rebuild,
	   so the deletion of section 26 would have dropped them
	   without this restoration. */
	font-family: var(--font-sans);
	font-size: var(--fs-small);
}

/* 2.9.2c — the 2.9.2b breakout rule (`.archive .pagination`
   with negative `margin-inline` + `max-width: none`) is
   retired. It solved one problem — making the border-top span
   the full 1060 px editorial band on archive pages by breaking
   out of `main.archive`'s 20 px horizontal padding — but
   introduced a worse one: on viewports narrower than ~1100 px
   (where `main.archive` fills the viewport width instead of
   hitting its 1060 cap), the negative margins pushed the
   pagination to the viewport edges. At 800 px viewport the
   pagination border stretched flush against the browser's
   chrome; at 400 px the numbers started at the absolute left
   of the viewport, outside the editorial content rhythm.
   Editorial convention reads better with the pagination
   contained to the same visual column as the cards above,
   even at the cost of being ~40 px narrower than the
   topbar / masthead / section-head border rules (which DO
   stretch to 1060 because they are not inside
   `main.archive`'s padded content-box). 2.9.2c accepts that
   asymmetry: the separator rule matches the cards-above, not
   the editorial-band-extrema. Numbers stay left-flush within
   the container. */

.pagination ul.page-numbers,
.pagination .nav-links {
	display: inline-flex;
	flex-wrap: wrap;
	/* 2.9.2 + 2.9.2b — `justify-content: flex-start` (was `center`).
	   Applied to both shapes of pagination emit:

	     - `paginate_links( type: 'list' )` used by
	       `CalDir_Archive::render_pagination()` emits
	       `<nav class="pagination"><ul class="page-numbers">`.
	     - `the_posts_pagination()` used by `index.php` emits
	       `<nav class="navigation pagination"><div class="nav-links">`.

	   Both share the same container (`.pagination`) and the same
	   numbered-link descendants (`.page-numbers`), but differ in
	   the intermediate wrapper — `<ul>` vs `<div>`. 2.9.2 targeted
	   only the `<ul>` variant; 2.9.2b combines both selectors so
	   blog-index pagination stops centring its inner flex row. */
	justify-content: flex-start;
	align-items: center;
	gap: 8px;
	list-style: none;
	padding: 0;
	margin: 0;
}

.pagination li { margin: 0; }

.pagination a.page-numbers,
.pagination span.page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 36px;
	padding: 6px 12px;
	font-family: var(--font-sans);
	font-size: 14px;
	font-weight: 500;
	color: var(--color-ink);
	text-decoration: none;
	border: 1px solid var(--color-rule-faint);
	border-radius: 2px;
	transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}

.pagination a.page-numbers:hover {
	background: var(--color-bg-subtle);
	border-color: var(--color-rule);
}

.pagination span.page-numbers.current {
	background: var(--color-ink);
	color: var(--color-ink-inverse);
	border-color: var(--color-ink);
	font-weight: 700;
}

.pagination span.page-numbers.dots {
	border: none;
	color: var(--color-ink-muted);
	background: transparent;
}

.pagination a.prev,
.pagination a.next {
	padding: 6px 16px;
	font-weight: 600;
	letter-spacing: 0.02em;
}


/* ============================================================
   48. PAGE template helpers
   ============================================================ */
.page-hero {
	padding-top: 32px;
	padding-bottom: 20px;
	border-bottom: var(--rule-faint);
	margin-bottom: 24px;
}


/* ============================================================
   49. MOBILE APP CHROME — sticky top + bottom nav + overlays
   Only active under 900 px. Disabled entirely by prefers-reduced-
   motion (the transforms are replaced with no-ops).
   ============================================================ */

/* ------ Bottom tab bar -------------------------------------- */
.bottom-nav { display: none; }

@media (max-width: 899.98px) {
	body.has-bottom-nav {
		padding-bottom: calc(60px + env(safe-area-inset-bottom, 0));
	}

	.bottom-nav {
		/* Responsive to item count: theme ships 3 tabs by default,
		   plugins can append more via `caldir_bottom_nav_items` and the
		   grid distributes them evenly without a CSS edit. */
		display: grid;
		grid-auto-flow: column;
		grid-auto-columns: minmax(0, 1fr);
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		width: 100%;
		/* 1.6.1 — z-index raised from 60 to 1000 so the bar wins over
		   any plugin overlay (Yoast / share bars / cookie banners) that
		   may sit at 100–999. */
		z-index: 1000;
		/* 1.7.3 — Opaque background. The earlier `rgba(250,250,247,0.92)`
		   plus the `@supports` override that switched to
		   `backdrop-filter: blur(14px)` produced a frosted-glass effect
		   that let the article underneath bleed through the bar — on
		   iPhone (both Safari and Chrome iOS) the reader perceived the
		   bottom-nav as cutting off the article text mid-line. A solid
		   `--color-bg` removes the bleed-through entirely; the bar reads
		   as a flat, decisive surface. The blur block is retired. */
		background: var(--color-bg);
		border-top: var(--rule-faint);
		padding-bottom: env(safe-area-inset-bottom, 0);
		padding-top: 4px;
		min-height: calc(60px + env(safe-area-inset-bottom, 0));
		-webkit-tap-highlight-color: transparent;

		/* 1.6.1 — iOS Safari / Chrome iOS fix. When the browser chrome
		   auto-hides on scroll the visual viewport resizes, and
		   `position: fixed; bottom: 0` sometimes lags one repaint
		   behind the new edge — the bar floats with a sliver of feed
		   visible underneath. Promoting the element to its own
		   compositing layer with `translateZ(0)` forces the browser to
		   re-anchor `bottom: 0` against the live visual viewport on
		   every frame instead of the cached layout viewport.
		   `-webkit-transform` keeps older WebKit happy; the duplicate
		   declaration costs nothing because both resolve to the same
		   identity matrix. */
		transform: translateZ(0);
		-webkit-transform: translateZ(0);
	}

	.bn-item {
		position: relative;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 3px;
		font-family: var(--font-sans);
		font-size: 9px;
		font-weight: 500;
		letter-spacing: 0.08em;
		text-transform: uppercase;
		color: var(--color-ink-soft);
		padding: 6px 4px;
		background: transparent;
		border: 0;
		text-decoration: none;
		cursor: pointer;
		transition: color 200ms ease, transform 160ms ease;
		-webkit-touch-callout: none;
		user-select: none;
	}

	.bn-item svg {
		width: 22px;
		height: 22px;
		stroke: currentColor;
		fill: none;
		stroke-width: 1.6;
		stroke-linecap: round;
		stroke-linejoin: round;
	}

	.bn-item .bn-label { line-height: 1; }

	.bn-item.on {
		color: var(--color-accent);
		font-weight: 600;
	}

	.bn-item:active {
		transform: scale(0.94);
	}

	/* 1.10.52 — Tab "APP" (install). El JS lo oculta con `[hidden]`
	   cuando la app ya está instalada (display-mode: standalone). */
	.bn-item--install[hidden] { display: none; }
}

/* iOS install-instructions modal (1.10.52). Aparece cuando el usuario
   pulsa el tab APP en iOS Safari (que no soporta beforeinstallprompt).
   Centrado, fondo oscurecido, panel claro con 3 pasos. */
.caldir-ios-install-modal {
	position: fixed;
	inset: 0;
	z-index: 9999;
	display: flex;
	align-items: center;
	justify-content: center;
}
.caldir-ios-install-modal[hidden] { display: none; }
.caldir-ios-install-modal__backdrop {
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, 0.55);
}
.caldir-ios-install-modal__panel {
	position: relative;
	background: var(--color-bg, #fff);
	color: var(--color-ink, #1d2327);
	max-width: 360px;
	width: calc(100% - 32px);
	padding: 28px 24px 24px;
	border-radius: 12px;
	box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
	font-family: var(--font-sans);
}
.caldir-ios-install-modal__title {
	font-family: var(--font-display);
	font-size: 22px;
	font-weight: 500;
	margin: 0 0 8px;
	letter-spacing: -0.01em;
}
.caldir-ios-install-modal__lede {
	font-size: 14px;
	line-height: 1.45;
	color: var(--color-ink-soft, #50575e);
	margin: 0 0 18px;
}
.caldir-ios-install-modal__steps {
	margin: 0 0 22px;
	padding-left: 22px;
	font-size: 14px;
	line-height: 1.5;
}
.caldir-ios-install-modal__steps li { margin-bottom: 8px; }
.caldir-ios-install-modal__close {
	display: block;
	width: 100%;
	padding: 12px 16px;
	background: var(--color-accent, #2271b1);
	color: #fff;
	border: 0;
	border-radius: 6px;
	font-family: var(--font-sans);
	font-size: 14px;
	font-weight: 600;
	cursor: pointer;
}
.caldir-ios-install-modal__close:hover { filter: brightness(1.08); }

/* ------ Safe-area shell ------------------------------------
   Sticky-on-scroll chrome (body.has-sticky-chrome + chrome-compact
   + chrome-hidden) was removed in 2.7.4. CalDir renders its top
   chrome as a static masthead on every viewport; only the fixed
   bottom-nav persists while scrolling. The JS scroll listener,
   the Customizer toggle, and the ~85 lines of sticky CSS that
   used to live here were dropped at the same time. */
html, body { overscroll-behavior-y: none; }

/* ------ Overlays (Sections + Search) ----------------------- */
.caldir-overlay {
	border: 0;
	padding: 0;
	margin: 0;
	width: 100vw;
	max-width: 100vw;
	height: 100dvh;
	max-height: 100dvh;
	background: rgba(250, 250, 247, 0.96);
	color: var(--color-ink);
	font-family: var(--font-sans);
	overflow: auto;
}

.caldir-overlay:not([open]) { display: none; }

@supports ((-webkit-backdrop-filter: blur(10px)) or (backdrop-filter: blur(10px))) {
	.caldir-overlay {
		background: rgba(250, 250, 247, 0.86);
		-webkit-backdrop-filter: blur(24px) saturate(1.6);
		backdrop-filter: blur(24px) saturate(1.6);
	}
}

.caldir-overlay::backdrop {
	background: transparent;
}

.caldir-overlay__head {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: calc(16px + env(safe-area-inset-top, 0)) 20px 12px;
	border-bottom: var(--rule-faint);
	position: sticky;
	top: 0;
	background: inherit;
}

.caldir-overlay__head .kicker {
	font-size: 12px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-ink-soft);
	margin: 0;
}

.caldir-overlay__head .caldir-overlay__close {
	margin-left: auto;
	width: 44px;
	height: 44px;
	font-size: 28px;
	line-height: 1;
	background: transparent;
	border: 0;
	color: var(--color-ink);
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}

.caldir-overlay__body {
	padding: 16px 20px calc(40px + env(safe-area-inset-bottom, 0));
}

/* ---- Sections overlay ---- */
.caldir-overlay--sections .caldir-overlay__menu {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 4px;
}

.caldir-overlay--sections .caldir-overlay__menu li {
	border-bottom: var(--rule-faint);
}

.caldir-overlay--sections .caldir-overlay__menu a {
	display: block;
	padding: 18px 8px;
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 24px;
	line-height: 1.1;
	letter-spacing: -0.015em;
	color: var(--color-ink);
	text-decoration: none;
	font-variation-settings: "opsz" 72;
}

.caldir-overlay--sections .caldir-overlay__menu .current-menu-item > a,
.caldir-overlay--sections .caldir-overlay__menu a:hover {
	color: var(--color-accent);
}

/* ---- Search overlay ---- */
.caldir-overlay--search .caldir-overlay__form {
	display: flex;
	align-items: center;
	gap: 10px;
	flex: 1;
	min-width: 0;
}

.caldir-overlay--search .caldir-overlay__form input[type="search"] {
	flex: 1;
	min-width: 0;
	padding: 14px 12px;
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 22px;
	line-height: 1.1;
	letter-spacing: -0.01em;
	border: 0;
	outline: 0;
	background: transparent;
	color: var(--color-ink);
	font-variation-settings: "opsz" 72;
}

.caldir-overlay--search .caldir-overlay__submit {
	width: 44px;
	height: 44px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: transparent;
	border: 0;
	color: var(--color-ink-soft);
	cursor: pointer;
}

.caldir-overlay--search .caldir-overlay__submit svg { width: 20px; height: 20px; }

.caldir-overlay__results {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0;
}

.caldir-overlay__results li { border-bottom: var(--rule-faint); }

.caldir-overlay__results a {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 16px 8px;
	text-decoration: none;
	color: var(--color-ink);
}

.caldir-overlay__results .r-title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 18px;
	line-height: 1.2;
	letter-spacing: -0.015em;
}

.caldir-overlay__results .r-type {
	font-family: var(--font-sans);
	font-size: 11px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-ink-soft);
}

.caldir-overlay__empty {
	padding: 48px 24px;
	text-align: center;
	font-family: var(--font-sans);
	font-size: 13px;
	color: var(--color-ink-faint);
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

/* When an overlay is open, lock background scroll. */
body.has-overlay-open {
	overflow: hidden;
}

/* ------ Reduced-motion override ----------------------------
   The sticky-chrome transform overrides that used to live here
   disappeared in 2.7.4 with the sticky feature itself. The
   bottom-nav and overlays keep their `transition: none` so
   readers who opt into reduced motion get no animated appearance. */
@media (prefers-reduced-motion: reduce) {
	.bn-item { transition: none !important; }
	.caldir-overlay { transition: none !important; }
}


/* ============================================================
   49b. HAMBURGER NAV + LATERAL DRAWER
   Opt-in via Customizer "Mobile navigation style: Hamburger toggle".
   The drawer slides in from the right on tap; the masthead itself
   stays static (sticky-on-scroll chrome was dropped in 2.7.4).
   ============================================================ */

.nav-hamburger {
	display: none;
	align-items: center;
	gap: 10px;
	padding: 12px 16px;
	background: transparent;
	border: 0;
	font-family: var(--font-sans);
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-ink);
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}

.nav-hamburger__bars {
	display: inline-flex;
	flex-direction: column;
	justify-content: space-between;
	width: 22px;
	height: 14px;
}

.nav-hamburger__bars span {
	display: block;
	height: 2px;
	width: 100%;
	background: currentColor;
}

@media (max-width: 899.98px) {
	html[data-nav-style="hamburger"] .nav-in { display: none; }
	html[data-nav-style="hamburger"] .nav-hamburger { display: inline-flex; }
	html[data-nav-style="hamburger"] .nav { padding: 0; }
	html[data-nav-style="hamburger"] .nav > .nav-hamburger {
		width: 100%;
		justify-content: flex-start;
		border-top: var(--rule-faint);
		border-bottom: var(--rule-hair);
	}
}

/* Drawer shell. Uses classic CSS transitions (not <dialog>) so the
   slide-in animation works identically across browsers. */
.nav-drawer {
	position: fixed;
	inset: 0;
	z-index: 80;
	pointer-events: none;
}

.nav-drawer[hidden] { display: none; }

.nav-drawer__backdrop {
	position: absolute;
	inset: 0;
	background: rgba(26, 26, 26, 0.55);
	opacity: 0;
	transition: opacity 240ms ease-out;
	pointer-events: auto;
}

.nav-drawer.is-open .nav-drawer__backdrop { opacity: 1; }

.nav-drawer__panel {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	width: min(320px, 85vw);
	padding-right: env(safe-area-inset-right, 0);
	padding-top: env(safe-area-inset-top, 0);
	padding-bottom: env(safe-area-inset-bottom, 0);
	background: var(--color-bg);
	box-shadow: -16px 0 40px -20px rgba(0, 0, 0, 0.32);
	transform: translateX(100%);
	transition: transform 240ms cubic-bezier(.2, .7, .2, 1);
	pointer-events: auto;
	display: flex;
	flex-direction: column;
	overflow-y: auto;
}

.nav-drawer.is-open .nav-drawer__panel { transform: translateX(0); }

.nav-drawer__head {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 16px 20px 12px;
	border-bottom: var(--rule-faint);
}

.nav-drawer__head .kicker {
	font-size: 11px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--color-ink-soft);
}

.nav-drawer__close {
	margin-left: auto;
	width: 44px;
	height: 44px;
	font-size: 28px;
	line-height: 1;
	background: transparent;
	border: 0;
	color: var(--color-ink);
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}

.nav-drawer__body { padding: 12px 20px 24px; }

.nav-drawer__menu {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.nav-drawer__menu li { border-bottom: var(--rule-faint); }

.nav-drawer__menu a {
	display: block;
	padding: 14px 4px;
	font-family: var(--font-sans);
	font-size: 14px;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--color-ink);
	text-decoration: none;
}

.nav-drawer__menu .current-menu-item > a,
.nav-drawer__menu a:hover { color: var(--color-accent); }

.nav-drawer__menu .sub-menu {
	list-style: none;
	margin: 0 0 8px;
	padding: 0 0 0 12px;
	border-left: 2px solid var(--color-rule-faint);
}

.nav-drawer__menu .sub-menu a {
	padding: 10px 4px;
	font-size: 12px;
	font-weight: 500;
	color: var(--color-ink-muted);
}

body.has-drawer-open {
	overflow: hidden;
}

@media (min-width: 900px) {
	/* Never show the drawer infrastructure on desktop, regardless of
	   the Customizer nav-style preference. */
	.nav-drawer, .nav-hamburger { display: none !important; }
}

@media (prefers-reduced-motion: reduce) {
	.nav-drawer__panel,
	.nav-drawer__backdrop { transition: none !important; }
}


/* ============================================================
   50. VIEW TRANSITIONS (cards → article)
   Only active when <html> has `.vt-on` (set by mobile-chrome.js
   based on the Customizer toggle). Browsers without support
   ignore the rules silently.
   ============================================================ */

@supports (view-transition-name: none) {
	html.vt-on .post-title {
		view-transition-name: caldir-article;
	}
	html.vt-on .u-card-title,
	html.vt-on .card-title {
		view-transition-name: var(--vt-name, none);
	}
}

@media (prefers-reduced-motion: reduce) {
	html.vt-on .post-title,
	html.vt-on .u-card-title,
	html.vt-on .card-title {
		view-transition-name: none;
	}
}


/* ============================================================
   51. PRINT
   ============================================================ */
@media print {
	.topbar, .ft, .widget-area, .pagination,
	.nav, .skip-link, .hdr-ad, .hdr-r, .nlbar-wrap,
	.caldir-install-banner, .bottom-nav, .caldir-overlay { display: none !important; }
	.wrap, .header, .sect, .hero, .ed-bar-wrap { padding: 0; max-width: none; }
	.article-body a { text-decoration: underline; }
}


/* ============================================================
   55. IMAGE-WITH-TITLE CARD — unified defensive pattern.

   All cards that place a white headline over a photo share the
   same contrast treatment:

     1. `filter: brightness(0.85)` on the inner <img> darkens the
        image by 15 %. Imperceptible on dark press photography,
        rescues light shots (snow, white sky, pastel backgrounds)
        that would otherwise blow out the white title.
     2. A unified `::after` **solid** overlay on the card applies
        `rgba(0, 0, 0, 0.55)` edge-to-edge. Up to 2.7.15 this was
        a bottom-anchored `linear-gradient` (0.75 → 0.55 → 0 at
        60 %) which only protected titles sitting at the bottom
        of the image; any card that placed the kicker or dek
        higher up (or that rendered on a light / medium-tone
        photo) lost contrast. A flat 55 % black covers the full
        image area and guarantees WCAG contrast for every piece
        of text inside `.img-content` regardless of where in the
        card it lives. If a future brief calls for more image
        visibility lower the alpha to 0.45; if light-image
        headlines still fail contrast raise it to 0.65.
     3. `text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4)` on every
        title reinforces contrast locally; kept from the gradient
        era because it costs nothing and still helps on the
        darkest band of the photograph.

   Applies to every dark-overlay card the theme ships: hero-big,
   u-card-big, tech-top-card, and u-card--dark (row-6). Any future
   dark card should opt in by adding its selector here, not by
   inventing a new overlay.
   ============================================================ */

/* 1. Darken the underlying image ---------------------------- */
.hero-big .img img,
.u-card-big .img img,
.tech-top-card .img img,
.u-card--dark .u-card__img {
	filter: brightness(0.85);
	transition: filter 0.3s ease;
}

/* 2. Unified solid overlay (flat rgba) ---------------------- */
.hero-big::after,
.u-card-big::after,
.tech-top-card::after,
.u-card--dark::after {
	content: "";
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, 0.55);
	pointer-events: none;
	z-index: 1;
}

/* 3. White title + defensive text-shadow -------------------- */
.hero-big h2,
.u-card-big h4,
.tech-top-card h4,
.u-card--dark .u-card__title {
	color: #fff;
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
	position: relative;
	z-index: 2;
}

/* 4. Line-clamp — truncate long headlines with ellipsis so the
      overlay card never blows past its height. Number of lines
      scales with card size. `overflow: hidden` + `-webkit-box` is
      the only cross-browser line-clamp that ships to production. */
.hero-big h2 {
	display: -webkit-box;
	-webkit-line-clamp: 4;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-overflow: ellipsis;
	line-height: 1.15;
}

.u-card-big h4 {
	display: -webkit-box;
	-webkit-line-clamp: 4;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-overflow: ellipsis;
	line-height: 1.2;
}

.tech-top-card h4 {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-overflow: ellipsis;
	line-height: 1.2;
}

.u-card--dark .u-card__title {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-overflow: ellipsis;
	line-height: 1.2;
}


/* ============================================================
   56. ROW-6 `.u-card--dark` BEM layout.

   Dense 1:1 grid at the tail of Latest News. Each card is a
   positioned container; the thumb fills it, the body (white title)
   anchors bottom-left. The shared overlay above handles contrast.
   ============================================================ */
.u-card--dark {
	position: relative;
	aspect-ratio: 1 / 1;
	overflow: hidden;
	display: block;
	min-width: 0;
	background: var(--color-ink);
}

.u-card--dark .u-card__link {
	position: absolute;
	inset: 0;
	display: block;
	color: inherit;
	text-decoration: none;
}

.u-card--dark .u-card__thumb {
	position: absolute;
	inset: 0;
}

.u-card--dark .u-card__img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* No-thumbnail fallback: diagonal-stripe placeholder. */
.u-card--dark .u-card__thumb--placeholder {
	background:
		repeating-linear-gradient(135deg, #2A2825 0, #2A2825 10px, #3A3732 10px, #3A3732 20px);
}

.u-card--dark .u-card__body {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	padding: 16px;
	z-index: 2;
}

.u-card--dark .u-card__title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 18px;
	line-height: 1.2;
	letter-spacing: -0.015em;
	margin: 0;
	font-variation-settings: "opsz" 96;
}

.u-card--dark:hover .u-card__img { filter: brightness(0.75); }


/* ============================================================
   99. MASTHEAD — LITERAL COPY OF FASE 1 (Portada-v2.html).

   Values reproduced verbatim from
     design/Prensa Prototipo/Portada-v2.html, lines 247–328.

   Nine earlier sub-phases tried to interpret the Fase 1 masthead
   from the rendered screenshot and drifted further with every
   pass. 2.6.10 stops the loop by copying the design file's
   inline <style> verbatim. The only deliberate deviation is the
   class name `.hdr-slot` (instead of `.ad-box`) so AdBlock /
   uBlock Origin do not hide the ad rail on visitors' browsers.
   ============================================================ */
/* Edge-to-edge three-column grid: logo at the container's left edge,
   ad slot centred, clock/date at the right edge. 1fr/auto/1fr lets
   the middle track size to content (exactly 468 px for the ad box)
   while the two 1fr tracks split the leftover space evenly — which,
   combined with justify-self, pushes the lateral items all the way
   to their respective edges. Progressive collapse below: 1100 drops
   the ad, 600 drops the clock, the logo is the one anchor that
   stays on every viewport. */
/* 2.7.25 — masthead layout reverted to CSS Grid after 2.7.24's
   flex experiment clustered the three blocks near the viewport
   centre instead of distributing them edge-to-edge across the
   editorial band. The flex version used `flex: 1 1 auto` +
   `max-width: 500px` on `.hdr-ad` so the ad capped at 500 px and
   `justify-content: space-between` distributed the leftover space
   — which, combined with the legacy 2.6.16 `margin-right: auto`
   defence on `.logo-area`, produced a cluster instead of the
   intended edge anchoring.

   Grid solves it declaratively: three columns (`auto 1fr auto`)
   place the logo at column 1 (hugging the band's left edge), the
   1fr middle track absorbs all free space (so its `justify-self:
   center` content sits exactly in the middle), and the clock sits
   at column 3 (hugging the band's right edge). No amount of
   logo / clock intrinsic-width variation shifts the centred
   middle column.

   Priority ordering from 2.7.24 is preserved: clock first to
   collapse at < 900 px, ad second at < 600 px, logo always
   visible with per-breakpoint height caps. */
/* 2.7.27 — explicit `width: 100%` to defeat the flex-column
   shrink-to-fit that 2.7.25 inherited silently. `.header` is a
   direct child of `#page.site`, which is `display: flex;
   flex-direction: column`. Flex items default to `align-self:
   stretch` on the cross axis, but when the item also declares
   `margin: 0 auto` on the cross axis, the auto margins absorb
   the free space BEFORE stretch applies — which neutralises
   stretch and leaves the item sized to its intrinsic width
   (`max-content` of the `auto 1fr auto` grid, where the 1fr
   track collapses to min-content because nothing is forcing the
   container wider). Consequence: `.header` rendered at ~937 px
   instead of the declared 1060 px cap, visibly narrower than
   `.hero` (which lives inside `<main>`, outside `.site`'s flex
   column, so block-flow's default `width: auto` naturally fills
   its containing block).

   Fix: `width: 100%` states explicitly "fill the cross axis";
   `max-width: var(--w, 1060px)` caps the stretch at 1060 px;
   `margin-inline: auto` centres the 1060 px inside the viewport.
   The three together produce the same outcome `.hero` already
   had via block flow — edge-anchored logo, centred ad, edge-
   anchored clock. Technical Lesson #18 documents the flex-item-
   auto-margin pitfall. */
.header {
	/* 1.10.72 — max-width/centrado retirados: `.site` ya fija ese
	   mismo ancho una sola vez (ver arriba). `.header` es ahora
	   simplemente el 100% de esa caja.
	   1.10.74 — `min-width: 0` es imprescindible: `.header` es un
	   elemento flex (hijo de `.site`, que es `display:flex`), y por
	   defecto un elemento flex tiene `min-width: auto` — el mismo
	   problema que ya se corrigió en `.site`, pero un nivel más
	   abajo. Sin esto, cualquier contenido interno más ancho que la
	   pantalla (el logo, la fecha) podía forzar a `.header` — y por
	   tanto a `.site` entero — a desbordar el viewport en móvil. */
	width: 100%;
	min-width: 0;
	/* 1.10.70 — bottom padding subido de 20px a 32px: separa el
	   bloque del logo/fecha del menú, que antes quedaba pegado. */
	padding: 28px var(--pad) 32px;
	display: grid;
	/* 2026-08-02 — REDISEÑO CAL DIR: logo centrado, no a la
	   izquierda. `1fr auto 1fr` en vez de `auto 1fr auto` — el
	   logo pasa a ser la columna central de ancho automático,
	   el hueco de publicidad y la fecha/hora ocupan los dos
	   flancos con el MISMO `1fr` cada uno. Al repartirse el
	   espacio sobrante a partes iguales entre columnas `1fr`
	   (con independencia de si esa columna tiene contenido o
	   está vacía), el logo queda matemáticamente centrado tanto
	   si el hueco de publicidad está presente como si no —
	   por eso ya no hace falta la regla de colapso a 2 columnas
	   que tenía el tema original cuando `.hdr-ad` no se emitía. */
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: 24px;
}

.header .logo-area {
	grid-column: 2;
	justify-self: center;
	min-width: 0;
}

.header .hdr-ad {
	grid-column: 1;
	justify-self: start;
	min-width: 0;
	max-width: 500px;
	width: 100%;
	display: flex;
	justify-content: flex-start;
	align-items: center;
}

.header .hdr-r {
	grid-column: 3;
	justify-self: end;
	text-align: right;
	white-space: nowrap;
}

/* 2026-08-04 — ficha de municipio: cabecera de 2 filas, las dos a todo
   el ancho del periódico y apiladas — ya no hay columnas. Fila 1 = solo
   el nombre del pueblo (el logo y la fecha/hora se fueron: el logo vive
   ahora en `.topbar-logo`, la fecha/hora se oculta del todo en estas
   páginas — ver header.php). Fila 2 = "COMARCA DE ___", pequeño, justo
   debajo del nombre. */
.header.header--municipio {
	grid-template-columns: 1fr;
	grid-template-rows: auto auto;
	grid-template-areas:
		"titular"
		"comarca";
	row-gap: 4px;
}

.header--municipio .hdr-municipio-titular { grid-area: titular; min-width: 0; }

.hdr-municipio-titular__nombre {
	font-family: var(--font-display);
	/* Mismo tratamiento que `.wordmark` (peso 700 real + trazo fino
	   simulando 900, ver comentario de `.wordmark` más abajo) — el
	   nombre del pueblo es el titular del periódico en esta página, no
	   una etiqueta secundaria.
	   Sin `font-size` fijo aquí a propósito: `data-caldir-fit` (ver
	   CalDir_Municipios::render_header_titular()) lo calcula entero en
	   JS, creciendo o encogiendo hasta ocupar el ancho exacto disponible
	   — un valor fijo en CSS competiría con eso. El único momento en
	   que se ve el tamaño "de fábrica" del navegador es el instante
	   antes de que el script corra; como el script corre en cuanto el
	   DOM de este bloque existe (no espera a `window.onload`), ese
	   parpadeo es imperceptible en la práctica. */
	font-weight: 700;
	-webkit-text-stroke: 1px currentColor;
	text-transform: uppercase;
	letter-spacing: -0.01em;
	line-height: 0.98;
	white-space: nowrap;
	margin: 0;
	color: var(--color-ink);
}

/* 2026-08-05 — una sola línea "Comarca de X · Provincia de Y" (antes
   eran dos: "Comarca de X" en mayúsculas pequeñas, y debajo "Provincia
   de Y · código INE Z · N artículos" en itálica — Javi pidió quitar el
   código INE y el nº de artículos por ruido de más, y fundir las dos
   líneas en una, quedándose con la tipografía itálica de la segunda
   (`.section-lede`), que le parecía la más elegante de las dos. */
.hdr-municipio-comarca {
	justify-self: start;
	grid-area: comarca;
	margin: 0;
	font-family: var(--font-serif-reading);
	font-style: italic;
	font-size: 0.8rem;
	color: var(--color-ink-muted);
}

/* Logo pequeño en la barra superior, solo en fichas de municipio — ver
   `.topbar-logo` en header.php. `.tax-municipio` lo añade WordPress
   automáticamente vía body_class() en cualquier archivo de esta
   taxonomía, así que no hace falta ninguna clase nueva calculada a
   mano. La franja se ensancha un poco (8px → 12px de relleno vertical)
   para que el logo respire — en el resto de páginas queda exactamente
   como estaba. */
.tax-municipio .topbar-in { padding-top: 12px; padding-bottom: 12px; }

.topbar-logo {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	height: 22px;
}
.topbar-logo img,
.topbar-logo .custom-logo {
	display: block;
	height: 100%;
	width: auto;
	max-width: none;
	object-fit: contain;
	border-radius: 3px;
}
.topbar-logo a {
	display: flex;
	align-items: center;
	height: 100%;
}
.topbar-logo__fallback {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 100%;
	aspect-ratio: 1 / 1;
	background: var(--color-ink);
	color: var(--color-ink-inverse);
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 13px;
	border-radius: 3px;
}

/* Uploaded custom logos get capped heights per breakpoint so they
   never crowd the ad / clock blocks. Text wordmark keeps its own
   `font-size` sizing (see `.wordmark`). The defensive list covers
   CalDir's own `.logo-area`, plus the WordPress core fallback
   classes `.site-logo` and `.site-branding` in case a child theme
   emits those. */
.header .logo-area img,
.site-logo img,
.site-branding img {
	max-height: 72px;
	width: auto;
	height: auto;
	display: block;
}

/* Tablet — el logo centrado es el elemento fijo del masthead; la
   fecha/hora y el hueco de publicidad son utilidad secundaria y se
   sueltan primero para dejarle todo el ancho al logo. Grid
   colapsa a una única columna centrada, mismo criterio que móvil. */
@media (max-width: 899.98px) {
	.header {
		grid-template-columns: auto;
		justify-content: center;
	}
	.header .hdr-ad,
	.header .hdr-r { display: none; }
	.header .logo-area {
		grid-column: 1;
		justify-self: center;
	}
	.header .logo-area img,
	.site-logo img,
	.site-branding img { max-height: 56px; }
	/* Ficha de municipio: incluso apilado, sigue siendo solo 2 filas
	   (titular / comarca) — el logo y la fecha/hora ya no viven en esta
	   cabecera a ningún ancho (ver comentario de escritorio arriba), así
	   que aquí no hay nada que "soltar", solo centrar. Selector
	   compuesto `.header.header--municipio` para igualar la
	   especificidad de la regla de escritorio y poder machacarla aquí. */
	.header.header--municipio {
		grid-template-columns: auto;
		grid-template-areas:
			"titular"
			"comarca";
		row-gap: 6px;
	}
	.hdr-municipio-titular { text-align: center; }
	.hdr-municipio-comarca { justify-self: center; text-align: center; margin-top: 2px; }
}

/* Móvil — mismo criterio que tablet, logo centrado, padding más
   ajustado al ritmo móvil. */
@media (max-width: 599.98px) {
	.header {
		grid-template-columns: auto;
		justify-content: center;
		padding: 18px 16px 14px;
	}
	.header .hdr-ad,
	.header .hdr-r { display: none; }
	.header .logo-area {
		grid-column: 1;
		justify-self: center;
		text-align: center;
		margin-left: auto;
		margin-right: auto;
	}
	.header .logo-area .logo-row,
	.header .logo-area .logo-custom,
	.header .logo-area .site-branding,
	.header .logo-area .site-logo {
		justify-content: center;
		align-items: center;
		text-align: center;
		margin-left: auto;
		margin-right: auto;
	}
	.header .logo-area img,
	.site-logo img,
	.site-branding img { max-height: 48px; }
	.header.header--municipio {
		grid-template-columns: auto;
		grid-template-areas:
			"titular"
			"comarca";
		row-gap: 4px;
	}
	.hdr-municipio-titular { text-align: center; }
	.hdr-municipio-comarca { justify-self: center; text-align: center; margin-top: 2px; }
	/* 2.8.1 — mobile scales the Customizer-chosen base sizes
	   proportionally. 0.85× matches the old 40 → 34 ratio; 0.9×
	   on the tagline keeps it slightly larger relative to the
	   wordmark than on desktop so it stays legible on narrow
	   viewports. */
	.wordmark { font-size: calc(var(--wordmark-size, 40px) * 0.85); }
	.logo-tag { font-size: calc(var(--tagline-size, 13px) * 0.9); }
}

.hdr-ad {
	display: flex;
	justify-content: center;
	align-items: center;
}

.hdr-ad .hdr-slot {
	width: 468px;
	height: 60px;
	background: var(--color-bg-subtle);
	border: 1px solid var(--color-rule-faint);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 10px;
	color: var(--color-ink-faint);
	text-transform: uppercase;
	letter-spacing: 0.08em;
	font-family: var(--font-sans);
}

.logo-area {
	flex-shrink: 0;
	min-width: 0;
	/* 2.8.7c — `.logo-area` reverts to a plain block. The
	   icon + wordmark pair now lives inside `.site-brand`
	   (below), which owns its own flex-row + align-items:
	   stretch relationship. `.logo-area` stays a simple
	   grid-cell wrapper so its height is purely content-driven
	   and doesn't interact with `.header`'s grid row height
	   (which is dictated by the 60 px ad slot and the
	   date/clock block — larger than the text block). */
}

/* =====================================================
   2.8.7 + 2.8.7b + 2.8.7c — SQUARE LOGO ICON
   inside `.site-brand`

   DOM contract:

     .logo-area
     └── .site-brand            (flex row, align-items: stretch)
         ├── .site-brand__icon  (anchor → img or fallback)
         └── .site-brand__text  (flex column — wordmark + tagline)

   Why `.site-brand` exists (2.8.7c). Pre-2.8.7c the icon and
   `.logo-wordmark-wrap` were direct children of `.logo-area`,
   which is itself a grid cell of `.header`. `.header`'s grid
   row height is the max of the three cells' heights (logo,
   468×60 ad, date+clock) — larger than the text block on most
   installs. Putting the icon + text flex relationship at the
   `.logo-area` level meant the icon's height was inheriting
   the grid row height rather than the text block height.
   Moving the pair inside `.site-brand` isolates the flex
   context to just what needs it; `.logo-area` stays content-
   height as a grid cell and stops contaminating the geometry.

   Class names. The wordmark + tagline pair keeps its 2.8.1
   classes (`.wordmark`, `.logo-tag`). The new `.site-brand__text`
   replaces the 2.8.1 `.logo-wordmark-wrap` as the column
   wrapper (the rule below is the rename; content classes
   preserved to avoid cascading renames across the Customizer
   partials, footer.php, and the typography-size rules from
   Technical Lesson #20).

   Icon geometry (2.8.7b, unchanged in 2.8.7c).

     - Fallback (`.site-brand__icon-fallback`) forces
       `aspect-ratio: 1 / 1`, so its width equals its height →
       the anchor shows as a square.
     - Uploaded image (`.site-brand__icon img`) has
       `height: 100%; width: auto`, preserving the image's
       natural aspect. Width adapts to the image rather than
       cropping a fixed square, so logos with wordmarks-
       inside-glyph don't get decapitated.

   Colour: `.site-brand__icon-fallback` uses `var(--color-ink)`
   (#141413) for the background and `var(--color-bg)` (#F5F4ED
   parchment) for the letter. Newsreader 500 keeps the glyph
   consistent with the wordmark it sits next to. Font-size of
   the letter = 55 % of the square's side, via a CSS container
   query (`container-type: size` + `cqh`). Container queries
   are stable in every modern browser (Chrome 105+, Safari 16+,
   Firefox 110+).

   Accessibility: the anchor carries an `aria-label` emitted
   from PHP ("Ir a la portada de {site-name}"), the fallback
   span is `aria-hidden="true"` so screen readers announce
   the anchor label rather than the letter glyph.
   ===================================================== */

.site-brand {
	display: flex;
	flex-direction: row;
	align-items: stretch;
	gap: 10px;
	min-width: 0;
}

.site-brand__text {
	display: flex;
	flex-direction: column;
	justify-content: center;
	line-height: 1;
	min-width: 0;
}

.site-brand__icon {
	flex: 0 0 auto;
	align-self: stretch;
	display: flex;
	align-items: stretch;
	text-decoration: none;
	/* 2.8.7e — explicit dimensions derived from the Customizer
	   wordmark-size custom property. Before 2.8.7e the anchor
	   had no width declaration; its width was supposed to come
	   from the fallback child's aspect-ratio (1 / 1 × height),
	   which in turn came from flex-stretch of the parent. That
	   child-derived width chain resolved unpredictably in some
	   layouts — browsers would give the anchor content-derived
	   width (≈ letter width) while painting the fallback at
	   aspect-ratio-derived width, producing a visual box wider
	   than the layout box. The overflow painted over the
	   adjacent wordmark / tagline.
	   The fix is to give the anchor explicit, definite
	   dimensions. 1.45 × the wordmark size ≈ the text block
	   height at default settings (40 px title → 58 px square),
	   and scales with the admin's wordmark-size choice without
	   depending on flex-stretch resolution. The square now
	   reserves its width correctly in the `.site-brand` flex
	   row; the adjacent text block starts at `58px + 10px gap`
	   from the logo-area's left edge, not at zero. */
	width: calc(var(--wordmark-size, 40px) * 1.45);
	height: calc(var(--wordmark-size, 40px) * 1.45);
}

.site-brand__icon img {
	/* 2.8.7e — fixed 100 % × 100 % of the now-definite anchor,
	   with `object-fit: contain` so non-square uploads letterbox
	   cleanly inside the square without crop or distortion. The
	   2.8.7d `width: auto` approach relied on the anchor having
	   no explicit width and deriving it from the image's aspect
	   — now that the anchor is a fixed square, `contain` is the
	   correct primitive: preserve aspect, fit inside the box.
	   The 2.8.7d `max-height: 100%` defence against oversized
	   uploads is now redundant (the anchor caps both dimensions)
	   so it's removed. */
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
	border-radius: 4px;
}

.site-brand__icon-fallback {
	/* 2.8.7e — fallback dimensions match the parent anchor
	   (100 % × 100 %) and the letter's `font-size` is explicit,
	   derived from the same Customizer wordmark-size custom
	   property as the anchor. 0.82 × the wordmark gives a
	   compact editorial monogram proportion: at the default
	   40 px title the letter renders at ~33 px inside the ~58 px
	   square (57 % ratio), which matches the historic
	   letter-on-square ratio of monogram branding. The
	   `aspect-ratio: 1 / 1` + `container-type: size` + `55cqh`
	   approach from 2.8.7b / 2.8.7d is retired: with an
	   explicit parent box (the anchor), the fallback doesn't
	   need to compute its own aspect or query its own size. */
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	padding: 0;
	background: var(--color-ink);
	color: var(--color-bg);
	font-family: var(--font-display);
	font-weight: 500;
	font-size: calc(var(--wordmark-size, 40px) * 0.82);
	line-height: 1;
	letter-spacing: -0.02em;
	font-variation-settings: "opsz" 144;
	border-radius: 4px;
	/* Prevent selection / click-through grief on the fallback
	   glyph; the anchor carries the semantics. */
	user-select: none;
}

/* 2.8.2 — `.logo-row` and `.logo-mark` rules removed. Both
   targeted elements that no longer exist in `header.php`. The
   `.logo-mark` "P square" (44 × 44 dark square rendered behind
   the first character of `bloginfo('name')`) was retired when
   the floating "P" over the wordmark was identified as a visual
   bug post-revert. The `.logo-row` flex wrapper around
   `.logo-mark` + the wordmark column no longer has a purpose
   now that the left child is gone; the new markup lives
   directly inside `.logo-area` as a single `.logo-wordmark-wrap`
   flex column (rule above). Defensive `.header .logo-area
   .logo-row` selectors elsewhere in this file (2.6.16 + 2.7.25
   blocks) are kept as historical scaffolding for child themes
   that might still emit the old structure — they are inert
   against the current DOM and cost nothing. See Technical
   Lesson #22 on post-revert residue cleanup. */

.wordmark {
	font-family: var(--font-display);
	/* 1.4.2 — wordmark thickened to match the original "CalDir" PNG
	   logo treatment now that the icon sits next to it. `font-weight:
	   900` requests the heaviest cut available; `-webkit-text-stroke`
	   adds a quarter-pixel stroke as a typographic crutch when the
	   loaded variant tops out below 900 (the display family ships up
	   to ~800), so the wordmark reads as a true display weight rather
	   than plain bold. */
	font-weight: 900;
	-webkit-text-stroke: 0.6px currentColor;
	/* 2.8.1 — size driven by the Customizer setting
	   `caldir_wordmark_size` (24–56 px), injected as
	   `--wordmark-size` on `:root` by `caldir_logo_sizes_inline_css()`
	   in functions.php. The `40px` fallback in `var()` keeps the
	   rule rendering correctly when the inline `<style>` is absent
	   (emergency print, block editor where the enqueue hook does
	   not fire). */
	font-size: var(--wordmark-size, 40px);
	letter-spacing: -0.035em;
	line-height: 1.05;
	color: var(--color-ink);
	font-variation-settings: "opsz" 144;
	margin: 0;
	display: block;
}

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

.logo-tag {
	/* 1.4.2 — tagline switched from italic serif-reading to upright
	   sans for a cleaner, more technical lockup under the thickened
	   wordmark. `--color-ink-muted` (charcoal) replaces the earlier
	   `--color-ink-soft` (olive) so the line reads slightly darker
	   without losing its "discreet sub-line" status. Mayúsculas /
	   letter-spacing del HTML del operador se conservan. */
	font-family: var(--font-sans);
	font-style: normal;
	/* 2.8.1 — size driven by the Customizer setting
	   `caldir_tagline_size` (10–20 px). Same pattern as `.wordmark`. */
	font-size: var(--tagline-size, 13px);
	line-height: 1.2;
	color: var(--color-ink-muted);
	margin: 3px 0 0;
	letter-spacing: 0.01em;
}

.hdr-r {
	display: flex;
	align-items: center;
	gap: 18px;
	justify-self: end;
}

.hdr-dt { text-align: right; }

.hdr-date {
	font-family: var(--font-sans);
	font-size: 11px;
	color: var(--color-ink-soft);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	line-height: 1.35;
}

.hdr-clock {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 36px;
	line-height: 1;
	color: var(--color-ink);
	letter-spacing: -0.025em;
	font-variation-settings: "opsz" 144;
	margin-top: 2px;
}

/* 2.7.25 — colon pulse tuned down after 2.7.24 read as too
   inquisitive. Rate slowed to 1.6 s (from 1 s) and minimum
   opacity raised to 0.7 (from 0.3), so the animation reads as a
   gentle editorial hint rather than a blinking cursor. The colon
   is still wrapped in a persistent `<span class="clock-colon">`
   by `assets/js/main.js`; hour and minute text nodes stay intact
   across ticks so the CSS animation keeps running without
   restarts. `prefers-reduced-motion` switches the pulse off to
   satisfy OS-level motion-reduction preferences. */
.clock-colon {
	display: inline-block;
	animation: caldir-clock-colon-pulse 1.6s ease-in-out infinite;
}

@keyframes caldir-clock-colon-pulse {
	0%, 100% { opacity: 1; }
	50%      { opacity: 0.7; }
}

@media (prefers-reduced-motion: reduce) {
	.clock-colon {
		animation: none;
		opacity: 1;
	}
}



/* ============================================================
   999. GLOBAL SAFETY NET — post titles (2.6.15).

   Last line of defence against catastrophic editorial accidents:
     - A title pasted with 1500 chars ("copied the whole body by
       mistake") must never break layout. Hard cap 6 lines.
     - A title that is one unbreakable 80-char word (URL, class
       name, technical jargon) must never overflow horizontally.

   The selector list is wrapped in `:where()` so its specificity
   is (0, 0, 0). That means every specific rule shipped by earlier
   sections (hero-big 4 / u-card-big 4 / tech-top-card 3 /
   row-6 3) still wins its own line-clamp on specificity. This
   block only fills the cards that lack a targeted clamp, and adds
   the word-break / hyphens defences on top regardless.
   ============================================================ */

:where(
	.u-card h2, .u-card h3, .u-card h4, .u-card__title,
	.hero-big h2,
	.u-card-big h4, .u-card-big h2,
	.tech-top-card h4, .tech-top-card h2,
	.row-6 .u-card__title,
	[class*="-card"] h2,
	[class*="-card"] h3,
	[class*="-card"] h4,
	.frontpage-section h2,
	.frontpage-section h3,
	.frontpage-section h4
) {
	display: -webkit-box;
	-webkit-line-clamp: 6;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-overflow: ellipsis;
	word-break: break-word;
	overflow-wrap: break-word;
	hyphens: auto;
}


/* ============================================================
   2.6.16 — LOGO ANCHORED TO START ON ALL BREAKPOINTS.

   On tablet (600-1099 px) and mobile (<600 px) the wordmark was
   reported as centred instead of pinned to the left edge of the
   editorial container. The 2.6.11 grid sets `justify-self: start`
   but something in the cascade (likely WP core's `.custom-logo-link`
   centering when a custom logo is used, or a plugin rule) kept
   pulling the block toward the middle.

   This block is the last line of defence: `!important` pins both
   the grid item's `justify-self` and the inner flex row's
   `justify-content` to start on every viewport. If a future audit
   proves the environmental override is gone, the `!important`
   modifiers can be dropped.
   ============================================================ */

.header .logo-area {
	justify-self: start !important;
	text-align: left;
	margin-left: 0;
	margin-right: auto;
}

.header .logo-area .logo-row,
.header .logo-area .logo-row-inner,
.header .logo-area .logo-custom {
	justify-content: flex-start;
	align-items: center;
	text-align: left;
}

.header .wordmark,
.header .logo-tag {
	text-align: left;
	margin-left: 0;
	margin-right: auto;
}

/* 2.7.25 — the grid-template-columns declarations that used to
   live in these media queries were removed; the new 2.7.25 block
   above defines the column layout per breakpoint (auto 1fr auto
   at desktop, auto 1fr at tablet, auto at mobile). What remains
   here is only the `justify-self` safety net that pins the logo
   to the start of its column regardless of environmental CSS
   (WordPress core's `.custom-logo-link` centering, plugin rules)
   — still worth keeping as a last line of defence. */
@media (min-width: 900px) {
	.header .logo-area { justify-self: start !important; }
	.header .hdr-r     { justify-self: end !important; }
}

@media (min-width: 600px) and (max-width: 899.98px) {
	.header .logo-area { justify-self: start !important; }
}

@media (max-width: 599.98px) {
	.header .logo-area { justify-self: start !important; }
}






/* ============================================================
   2.6.20 — MINIMAL HTML CAP.

   Surgical outcome after 2.6.17/2.6.18/2.6.19:
     - Root cause of mobile horizontal scroll was `.alignfull` /
       `.article-body .alignwide` / `.alignfull` declaring
       `width: 100vw`. On iOS Safari 100vw includes the scrollbar
       area; on desktop Chrome / Firefox / Safari the same rule
       adds the scrollbar width to the document, triggering a
       lateral scroll. Every in-flow `width: 100vw` is now
       replaced by the classic `margin-inline: calc(50% - 50vw)`
       breakout pattern, which extends the block to viewport
       edges through margins instead of width. No scrollbar
       double-count, no overflow.
     - The `.caldir-overlay` 100vw rule is kept: the overlay is
       a `<dialog>` opened with `.showModal()`, so it is
       `position: fixed` at render time and 100vw there is inert.

   With the culprits fixed, a single `html { overflow-x: hidden }`
   is enough as a defensive cap. No rules on body, no rules on
   `.site` / `.page` / wrappers, no mobile media override. The
   viewport keeps full control over the document's vertical
   scroll.
   ============================================================ */

html {
	overflow-x: hidden;
}


/* ============================================================
   2.6.21 — RESPONSIVE GRIDS (consolidated).

   Every front-page grid either already collapsed to 1fr on mobile
   or did not. This block consolidates the rules so every grid
   reaches a 1-column layout below 600 px, and every dense grid
   sits at 2-3 columns on tablets (600-899 px). Redundant with
   earlier per-section @media but the source-order wins and leaves
   no grid stranded at a wide layout below 600 — the documented
   root of any leftover mobile body-overflow after 2.6.20.
   ============================================================ */

@media (max-width: 599.98px) {
	.hero,
	.row-3,
	.row-rev,
	.row-6,
	.masleido-grid,
	.tech-top,
	.tech-bottom,
	.cultura-blocks,
	.pyramid,
	.pyramid .hero,
	.pyramid .std-row-top,
	.related-grid,
	.grid-dense {
		grid-template-columns: 1fr;
		gap: 24px;
	}

	/* Uniform 16 px horizontal padding on the main shell so the
	   editor can never leak content past the viewport with an
	   inline style or a wide block. */
	.header,
	.topbar,
	.site-main,
	.ft,
	.nav {
		padding-left: 16px;
		padding-right: 16px;
		box-sizing: border-box;
	}
}

@media (min-width: 600px) and (max-width: 899.98px) {
	.row-6 {
		grid-template-columns: repeat(3, 1fr);
	}

	.masleido-grid,
	.tech-bottom,
	.cultura-blocks {
		grid-template-columns: 1fr 1fr;
	}

	.row-3,
	.row-rev {
		grid-template-columns: 1fr 1fr;
	}
}


/* 2.6.23 — Nav responsive fix at source.

   Root cause (audited in 2.6.23 pre-commit): the base `.nav` has
   `max-width: 1060px` which is larger than any mobile viewport,
   and `.nav-in` has `justify-content: space-between` which
   conflicts with an overflowing flex rail. Six previous patches
   (2.6.17 → 2.6.22) tried to contain the symptom with
   `overflow: hidden` on wrappers, each regressing either
   desktop scroll or the nav underline. Fixed at source: shrink
   `.nav` to the viewport on mobile and switch `.nav-in` to
   `flex-start` packing below desktop so the `overflow-x: auto`
   rail on the base rule handles the rest. No new `overflow`
   anywhere, no `!important`, no wrapper containment. */
@media (max-width: 599.98px) {
	.nav {
		max-width: 100%;
		padding: 0 16px;
	}
}

@media (max-width: 899.98px) {
	.nav-in {
		justify-content: flex-start;
	}
}


/* 2.6.25 — scrollbar-gutter only on desktop (iOS WebKit fix).
   `scrollbar-gutter: stable` reserves ~15 px on the inline-end side
   of <html> even when there's no scrollbar. On iOS Safari 17.4+ that
   reservation is honoured but scrollbars are overlay (no layout
   space needed), so the reservation ends up stealing 15 px from the
   content area and shifting the centred document ~7-8 px to the
   left. The effect was invisible in 2.6.23 because the sticky
   backdrop-filter on topbar/nav masked the edge with blur; after
   2.6.24 (sticky disabled by default) it became plainly visible.
   Fix: only reserve the gutter on desktop, where real scrollbars
   exist and the layout benefit is real. */
@media (min-width: 900px) {
	html {
		scrollbar-gutter: stable;
	}
}


/* 2.7.2 — Remove editorial measure caps on mobile where card
   is the only column. Keep caps on desktop for typographic
   measure (45-75 chars/line). */
@media (max-width: 599.98px) {
	.hero-big .lede,
	.hero-sm .lede,
	.u-card-big p {
		max-width: none;
	}
}


/* ============================================================
   2.7.3 Phase F — ARTICLE END DIVIDER (configurable).
   Ornamental separator rendered at the bottom of the article body,
   just before the author box. Configured from Customizer →
   Apariencia del artículo. Three modes: symbol (default "• • •"),
   image (user upload), none. Width stays inside the reading column
   so the closing flourish aligns with the body text, not with the
   wide header/footer frame.
   ============================================================ */
.article-end-divider {
	max-width: 72ch;
	margin: 48px auto;
	padding: 0 var(--pad);
	display: flex;
	align-items: center;
	gap: 16px;
}

.article-end-divider__line {
	flex: 1;
	height: 1px;
	background: var(--color-rule-faint);
}

.article-end-divider__mark {
	font-family: var(--font-display);
	font-size: 18px;
	color: var(--color-accent);
	letter-spacing: 0.2em;
	line-height: 1;
	white-space: nowrap;
}

.article-end-divider__mark img {
	max-height: 24px;
	width: auto;
	display: block;
}


/* ============================================================
   2.7.6 — CONTENT-NONE / NO RESULTS

   Emitted by `content-none.php` when an archive (category, tag,
   author, date, search) returns zero posts. Until 2.7.6 this
   block shipped unstyled, so a "Nada encontrado" response sat
   flush against the `.site-footer` with only the 48 px footer
   margin above it. Rhythm mirrors `.section-head`:
   container clamped to `--w`, same lateral padding, generous
   vertical padding so the state reads as a proper editorial
   page, not as a terminal error.
   ============================================================ */
.no-results {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 48px var(--pad) 80px;
}

.no-results__header {
	margin: 0 0 20px;
}

.no-results__title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: clamp(28px, 4vw, 40px);
	line-height: 1.1;
	letter-spacing: -0.02em;
	color: var(--color-ink);
	margin: 0 0 16px;
	font-variation-settings: "opsz" 144;
	text-wrap: balance;
}

.no-results__body {
	max-width: 60ch;
}

.no-results__body p {
	font-family: var(--font-serif-reading);
	font-size: 17px;
	line-height: 1.6;
	color: var(--color-ink-muted);
	margin: 0 0 24px;
}

.no-results__body a {
	color: var(--color-accent);
	text-decoration: underline;
	text-underline-offset: 3px;
}


/* ============================================================
   2.7.23 — BRAND SIGNATURE: EDITORIAL GRAYSCALE

   Reintroduced as a more targeted version of the 2.7.18 brand
   signature that was reverted in 2.7.22. The 2.7.18 approach
   used `body.caldir-grayscale img { filter: grayscale(100%) }`
   — a universal selector over `<img>` — and relied on per-case
   escape hatches. Five sub-phases of regressions later (2.7.19
   → 2.7.22) it became clear the universal selector costs more
   than it saves: every icon wrapper, every decorative image,
   every plugin-emitted `<img>` becomes a regression vector.

   The 2.7.23 fix inverts the approach: **enumerate the
   editorial contexts** where grayscale IS intended instead of
   trying to subtract everything that is not. The selector list
   below covers every image wrapper the theme emits for
   editorial content (cards, featured figures, article body,
   avatars), explicitly leaving every other `<img>` untouched.
   Synthetic placeholders (`.ph` without `<img>`), SVG icons
   inline with `currentColor`, uploaded brand logos, ad slots
   and anything marked `.no-grayscale` stay in full colour by
   construction, with `!important` escape hatches as a
   defensive belt-and-braces for plugin-injected markup.

   See Technical Lesson #14 in ROADMAP for the enumeration
   rationale. Halftone overlay is NOT reintroduced — the
   2.7.18 → 2.7.22 arc established that the decorative pattern
   has too many variance surfaces (synthetic placeholders,
   Gutenberg alignwide/alignfull, service-worker cache) for a
   CSS-only approach to be maintainable.
   ============================================================ */

/* 1 — enumerated editorial image contexts. Every image wrapper
      CalDir renders for editorial content appears here. */
body.caldir-grayscale .hero-big img,
body.caldir-grayscale .hero-sm img,
body.caldir-grayscale .u-card img,
body.caldir-grayscale .u-card-big img,
body.caldir-grayscale .u-card-sm img,
body.caldir-grayscale .u-card-img img,
body.caldir-grayscale .masleido img,
body.caldir-grayscale .card img,
body.caldir-grayscale .ph img,
body.caldir-grayscale .img img,
body.caldir-grayscale .featured img,
body.caldir-grayscale .featured-img img,
body.caldir-grayscale .article-body img,
body.caldir-grayscale .wp-post-image,
body.caldir-grayscale figure.wp-block-image img,
body.caldir-grayscale .post-author-card img,
body.caldir-grayscale .author-bio img,
body.caldir-grayscale .avatar,
body.caldir-grayscale img.avatar {
	filter: grayscale(100%);
	/* 2.8.8 — transición suave para el "revelar color" al pasar el
	   ratón por encima (ver @media (hover: hover) más abajo). */
	transition: filter 320ms ease;
}

/* 2 — merge grayscale with the section-55 brightness filter on
      overlay cards. Section 55 declares `filter: brightness(0.85)`
      on five selectors at specificity higher than the enumerated
      list above; without this override the brightness filter
      replaces the grayscale (CSS `filter` is winner-takes-all,
      not additive). Same selector list as section 55 prefixed
      with `body.caldir-grayscale` → wins. */
body.caldir-grayscale .hero-big .img img,
body.caldir-grayscale .u-card-big .img img,
body.caldir-grayscale .tech-top-card .img img,
body.caldir-grayscale .u-card--dark .u-card__img {
	filter: grayscale(100%) brightness(0.85);
	transition: filter 320ms ease;
}

/* 3 — section-hero backgrounds (CSS `background-image` instead
      of `<img>`). Kept small: only the four documented section
      heroes from the front-page layout. No generic
      `[style*="background-image"]` selector — lesson from 2.7.19:
      too aggressive, caught unrelated decorative backgrounds. */
body.caldir-grayscale .cult-big .img,
body.caldir-grayscale .tec-big .img {
	filter: grayscale(100%);
	transition: filter 320ms ease;
}

/* 4 — escape hatches. `!important` because these elements may
      sit inside wrappers matched by rule 1 (e.g. an ad slot
      emitting `<img>` inside `.article-body`), and the filter
      must not apply regardless of the surrounding markup.
      `.site-brand__icon img` (2.8.7) is listed explicitly even
      though `.logo-area img` already covers it transitively —
      the explicit selector makes the intent obvious and survives
      any future refactor that lifts the icon out of `.logo-area`.
      The CSS fallback (`.site-brand__icon-fallback`) is already
      monochrome (ink on parchment) so it does not participate
      in this list. */
body.caldir-grayscale .logo-area img,
body.caldir-grayscale .site-logo img,
body.caldir-grayscale .site-branding img,
body.caldir-grayscale .site-brand__icon img,
body.caldir-grayscale .ad-slot img,
body.caldir-grayscale .publicidad img,
body.caldir-grayscale [class*="ad-"] img,
body.caldir-grayscale .no-grayscale,
body.caldir-grayscale .no-grayscale img,
body.caldir-grayscale img.no-grayscale {
	filter: none !important;
}

/* ============================================================
   2.8.8 — REVELAR COLOR AL PASAR EL RATÓN / AL TOCAR

   Petición de Javi (2026-07-30): "me gusta que las fotos salgan en
   blanco y negro, pero que al pinchar o pasar el ratón por encima
   recupere su color". Dos caminos distintos a propósito:

     - Escritorio (`hover: hover`): puro CSS, sin JS. Pasar el
       ratón por encima de CUALQUIER foto en blanco y negro la
       revela en color, con la misma transición añadida arriba.
     - Móvil/táctil: la mayoría de estas fotos son enlaces al
       artículo (tarjetas de portada, relacionados...) — si el
       primer toque revelara el color en vez de navegar, el lector
       tendría que tocar dos veces para entrar a leer. Se descartó
       a propósito (ver conversación del proyecto). El toque para
       revelar color se limita a `.featured-img` (la foto de
       cabecera del propio artículo, que NO es un enlace y por
       tanto no compite con nada) — la clase `.is-color-revealed`
       la añade/quita `assets/js/main.js` al tocarla, solo en
       dispositivos sin hover real.
   ============================================================ */

@media (hover: hover) {
	/* "tarjeta": pasar el ratón por la tarjeta entera revela su imagen. */
	body.caldir-grayscale .hero-big:hover img,
	body.caldir-grayscale .hero-sm:hover img,
	body.caldir-grayscale .u-card:hover img,
	body.caldir-grayscale .u-card-sm:hover img,
	body.caldir-grayscale .u-card-img:hover img,
	body.caldir-grayscale .masleido:hover img,
	body.caldir-grayscale .card:hover img,
	body.caldir-grayscale .ph:hover img,
	body.caldir-grayscale .img:hover img,
	body.caldir-grayscale .featured:hover img,
	body.caldir-grayscale .featured-img:hover img,
	body.caldir-grayscale .post-author-card:hover img,
	body.caldir-grayscale .author-bio:hover img,
	/* "imagen suelta": dentro de un cuerpo de artículo hay varias
	   imágenes seguidas — revelar la tarjeta entera revelaría todas
	   a la vez, así que aquí se pasa el ratón por la imagen misma. */
	body.caldir-grayscale .article-body img:hover,
	body.caldir-grayscale .wp-post-image:hover,
	body.caldir-grayscale figure.wp-block-image img:hover,
	body.caldir-grayscale .avatar:hover,
	body.caldir-grayscale img.avatar:hover {
		filter: grayscale(0%);
	}

	/* las de fondo con overlay de texto en blanco (hero-big/u-card-big/
	   tech-top-card/u-card--dark) mantienen el atenuado — si se quitara
	   del todo el texto superpuesto perdería legibilidad. Solo se
	   quita el blanco y negro, no el brightness(0.85). */
	body.caldir-grayscale .hero-big:hover .img img,
	body.caldir-grayscale .u-card-big:hover .img img,
	body.caldir-grayscale .tech-top-card:hover .img img,
	body.caldir-grayscale .u-card--dark:hover .u-card__img {
		filter: brightness(0.85);
	}

	/* fondos de sección (background-image, no <img>). */
	body.caldir-grayscale .cult-big:hover .img,
	body.caldir-grayscale .tec-big:hover .img {
		filter: none;
	}
}

/* móvil/táctil: toque en la foto de cabecera del artículo (no es un
   enlace, ver docblock arriba). La clase la gestiona main.js. */
body.caldir-grayscale .featured-img.is-color-revealed img {
	filter: grayscale(0%);
}

@media (prefers-reduced-motion: reduce) {
	body.caldir-grayscale .hero-big img,
	body.caldir-grayscale .hero-sm img,
	body.caldir-grayscale .u-card img,
	body.caldir-grayscale .u-card-big img,
	body.caldir-grayscale .u-card-sm img,
	body.caldir-grayscale .u-card-img img,
	body.caldir-grayscale .masleido img,
	body.caldir-grayscale .card img,
	body.caldir-grayscale .ph img,
	body.caldir-grayscale .img img,
	body.caldir-grayscale .featured img,
	body.caldir-grayscale .featured-img img,
	body.caldir-grayscale .article-body img,
	body.caldir-grayscale .wp-post-image,
	body.caldir-grayscale figure.wp-block-image img,
	body.caldir-grayscale .post-author-card img,
	body.caldir-grayscale .author-bio img,
	body.caldir-grayscale .avatar,
	body.caldir-grayscale img.avatar,
	body.caldir-grayscale .hero-big .img img,
	body.caldir-grayscale .u-card-big .img img,
	body.caldir-grayscale .tech-top-card .img img,
	body.caldir-grayscale .u-card--dark .u-card__img,
	body.caldir-grayscale .cult-big .img,
	body.caldir-grayscale .tec-big .img {
		transition: none;
	}
}

/* 2.8.3 — The "2.8.1 — TOPBAR PLAY BUTTON" block that used to
   follow this comment was removed when the ambient-audio feature
   was retired as a product decision. Every CSS rule, keyframes
   declaration, `@media (prefers-reduced-motion)` guard and the
   docblock header that described them have been stripped. The
   feature's `.topbar-play-button` / `.topbar-play-icon` selectors
   no longer appear anywhere in the theme — confirmed by
   `rg 'topbar-play-' theme/` returning 0 hits. See Technical
   Lesson #23 on retiring multi-layer features cleanly. */


/* =====================================================
   2.8.4 — CARD EQUAL-HEIGHT CONSOLIDATION

   Every front-page grid (`.hero`, `.row-3`, `.row-rev`,
   `.row-6`, `.masleido-grid`, `.tech-top`, `.tech-bottom`,
   `.cultura-blocks`) already declares
   `align-items: stretch` in its own section above. This
   block codifies the per-card contract that makes the
   stretch visible:

     1. Each card stretches to its grid cell's height via
        `height: 100%` (defensive — `align-items: stretch`
        already does this, but explicit declaration
        survives future grid-template changes and aliases).
     2. Each card is `display: flex; flex-direction:
        column` so its internal rhythm (img / kicker /
        title / excerpt / time) flows top-to-bottom.
     3. The `<time class="time">` element inside each
        non-overlay card carries `margin-top: auto` so it
        anchors to the bottom of the card regardless of
        how much text sits above it. Dates across a row
        line up horizontally.

   Overlay cards (`.hero-big`, `.u-card-big`,
   `.tech-top-card`) use a different pattern:
   they are `display: flex; align-items: flex-end` on the
   outer card, with an absolutely-positioned `.img` as
   the background and the content `.body` as a flex item
   anchored to the bottom. The date inside `.body` stacks
   with the title naturally; no `margin-top: auto` is
   needed because the whole body is already at the card
   foot. That existing pattern is preserved.

   See Technical Lesson #25. */

.hero-big,
.hero-sm,
.u-card,
.u-card-big,
.u-card--dark,
.tech-top-card,
.tech-sm,
.cultura-block {
	height: 100%;
}

/* Non-overlay cards — anchor the time to the bottom. The
   overlay cards (hero-big / u-card-big / tech-top-card)
   are excluded; their body is already bottom-aligned via
   `align-items: flex-end` on the card. */
.u-card .time,
.hero-sm .time,
.tech-sm .time {
	margin-top: auto;
}


/* ============================================================
   60. OPINIÓN + FIRMA (1.7.0)

   Cubre:
     - Layout `opinion` de portada (`.opinion-block / .opinion-grid /
       .opinion-card`).
     - single-opinion.php (`.single-opinion .kicker--opinion`,
       `.byline--opinion`, `.author-box`, `.opinion-more`).
     - archive-opinion.php (zonas A/B/C).
     - author.php (`.firma-archive / .firma-hero /
       .firma-featured / .firma-list / .article-item`).

   Paleta y tipografía heredadas del theme — `--w`, `--pad`,
   `--color-bg`, `--color-ink`, `--color-accent`, `--font-serif-
   display`, `--font-sans`. No se inventan tokens nuevos.
   ============================================================ */

/* -------- Layout `opinion` en portada -------- */
.opinion-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	gap: 32px;
	align-items: start;
}

.opinion-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 12px;
	text-align: center;
	color: var(--color-ink);
	text-decoration: none;
	transition: opacity 0.15s ease;
}

.opinion-card:hover {
	opacity: 0.86;
}

.opinion-card__portrait {
	width: 200px;
	aspect-ratio: 1 / 1;
	overflow: hidden;
	background: transparent;
}

.opinion-card__portrait img,
.opinion-card__portrait .opinion-card__avatar,
.opinion-card__portrait .avatar {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	border-radius: 0;
}

.opinion-card__author {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 22px;
	line-height: 1.15;
	color: var(--color-ink);
}

.opinion-card__title {
	font-family: var(--font-display);
	font-style: italic;
	font-weight: 400;
	font-size: 15px;
	line-height: 1.35;
	color: var(--color-ink-soft);
	max-width: 22ch;
}


/* -------- single-opinion.php -------- */
.single-opinion .kicker--opinion {
	display: inline-block;
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.16em;
	color: var(--color-accent);
	margin-bottom: 14px;
}

.byline--opinion .byline-avatar--firma {
	width: 48px;
	height: 48px;
	border-radius: 50%;
	object-fit: cover;
	display: block;
}

/* 1.9.3 — Author box, final layout.
   Outer: grid auto/1fr (avatar + body). Body: flex column gap 16px.
   First row of the body is `.author-box__header`, itself a
   `space-between` flex row: `.author-box__heading` (kicker + name)
   on the left, `.author-box__more` pill on the right. Second row is
   the bio at FULL WIDTH (no right slot reserved). The pill version
   of the more-link gives it visual weight matching the boceto:
   border + radius 999px + uppercase mono + hover invierte (fondo
   accent / texto paper). */
.author-box {
	max-width: var(--w, 1060px);
	margin: 48px auto 0;
	padding: 32px var(--pad);
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 24px;
	align-items: start;
	border-top: 1px solid var(--color-rule-faint, #d8d4ca);
	border-bottom: 1px solid var(--color-rule-faint, #d8d4ca);
}

.author-box__portrait img,
.author-box__portrait .avatar {
	width: 96px;
	height: 96px;
	border-radius: 50%;
	object-fit: cover;
	display: block;
	flex-shrink: 0;
}

.author-box__body {
	display: flex;
	flex-direction: column;
	gap: 16px;
	min-width: 0;
}

.author-box__header {
	display: flex;
	justify-content: space-between;
	align-items: flex-start;
	gap: 20px;
	width: 100%;
}

.author-box__heading {
	display: flex;
	flex-direction: column;
	gap: 6px;
	min-width: 0;
}

.author-box__kicker {
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-accent);
}

.author-box__name {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 28px;
	line-height: 1.1;
	letter-spacing: -0.015em;
	margin: 0;
	color: var(--color-ink);
}

.author-box__more {
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.10em;
	text-transform: uppercase;
	color: var(--color-accent);
	text-decoration: none;
	padding: 10px 20px;
	border: 1px solid var(--color-rule-faint, #d8d4ca);
	border-radius: 999px;
	white-space: nowrap;
	flex-shrink: 0;
	transition: color 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}

.author-box__more:hover,
.author-box__more:focus-visible {
	border-color: var(--color-accent);
	background: var(--color-accent);
	color: var(--color-bg);
	outline: none;
}

.author-box__bio {
	font-family: var(--font-serif-reading);
	font-style: italic;
	font-size: 16px;
	line-height: 1.55;
	color: var(--color-ink-soft);
	margin: 0;
	width: 100%;
}

.author-box__bio p {
	margin: 0 0 0.8em;
}

.author-box__bio p:last-child {
	margin-bottom: 0;
}

@media (max-width: 599.98px) {
	.author-box {
		grid-template-columns: 1fr;
		gap: 16px;
		padding: 24px var(--pad);
		text-align: left;
	}
	.author-box__portrait img,
	.author-box__portrait .avatar {
		width: 72px;
		height: 72px;
	}
	.author-box__name {
		font-size: 24px;
	}
	.author-box__header {
		flex-direction: column;
		gap: 12px;
	}
	.author-box__more {
		align-self: flex-start;
	}
}

.opinion-more {
	max-width: var(--w, 1060px);
	margin: 32px auto 0;
	padding: 0 var(--pad);
}

.opinion-more__title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 22px;
	line-height: 1.15;
	margin: 0 0 16px;
	color: var(--color-ink);
}

.opinion-more__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 20px;
}

.opinion-more__item {
	margin: 0;
}

.opinion-more__link {
	display: flex;
	gap: 12px;
	color: var(--color-ink);
	text-decoration: none;
	transition: opacity 0.15s ease;
}

.opinion-more__link:hover {
	opacity: 0.86;
}

.opinion-more__thumb {
	width: 120px;
	flex-shrink: 0;
}

.opinion-more__thumb img {
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	display: block;
}

.opinion-more__headline {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 16px;
	line-height: 1.2;
	margin: 0 0 6px;
}

.opinion-more__date {
	font-family: var(--font-sans);
	font-size: 11px;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--color-ink-faint);
}


/* -------- archive-opinion.php -------- */
.opinion-archive {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 0 var(--pad);
}

.opinion-archive__head {
	margin-bottom: 32px;
}

.opinion-archive__zone {
	margin-bottom: 56px;
}

.opinion-archive__zone-title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 28px;
	line-height: 1.15;
	margin: 0 0 20px;
	color: var(--color-ink);
}

/* Zona A — 4 destacados con autor único. */
.opinion-archive__featured-grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 28px;
}

.opinion-featured {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
	text-align: center;
	text-decoration: none;
	color: var(--color-ink);
	transition: opacity 0.15s ease;
}

.opinion-featured:hover {
	opacity: 0.86;
}

.opinion-featured__portrait {
	width: 200px;
	aspect-ratio: 1 / 1;
	overflow: hidden;
}

.opinion-featured__portrait img,
.opinion-featured__portrait .avatar {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.opinion-featured__author {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 22px;
	line-height: 1.15;
}

.opinion-featured__title {
	font-family: var(--font-serif-reading);
	font-size: 14px;
	line-height: 1.4;
	color: var(--color-ink-soft);
	max-width: 22ch;
}

/* Zona B — 10 últimas, 2 columnas. */
.opinion-archive__recent-grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 20px 32px;
}

.opinion-recent__link {
	display: flex;
	gap: 14px;
	align-items: flex-start;
	color: var(--color-ink);
	text-decoration: none;
	transition: opacity 0.15s ease;
}

.opinion-recent__link:hover {
	opacity: 0.86;
}

.opinion-recent__avatar img,
.opinion-recent__avatar .avatar {
	width: 50px;
	height: 50px;
	border-radius: 50%;
	object-fit: cover;
	display: block;
	flex-shrink: 0;
}

.opinion-recent__body {
	flex: 1;
	min-width: 0;
}

.opinion-recent__title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 18px;
	line-height: 1.2;
	margin: 0 0 6px;
	color: var(--color-ink);
}

.opinion-recent__meta {
	font-family: var(--font-sans);
	font-size: 11px;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--color-ink-faint);
	display: flex;
	gap: 6px;
}

.opinion-recent__sep {
	color: var(--color-ink-faint);
}

/* Zona C — todos los firmantes (mosaico). */
.opinion-archive__signers-grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
	gap: 24px 20px;
}

.opinion-signer__link {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
	text-align: center;
	color: var(--color-ink);
	text-decoration: none;
	transition: opacity 0.15s ease;
}

.opinion-signer__link:hover {
	opacity: 0.86;
}

.opinion-signer__portrait {
	width: 140px;
	aspect-ratio: 1 / 1;
	overflow: hidden;
}

.opinion-signer__portrait img,
.opinion-signer__portrait .avatar {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.opinion-signer__name {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 16px;
	line-height: 1.2;
}

.opinion-signer__count {
	font-family: var(--font-sans);
	font-size: 11px;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--color-ink-faint);
}


/* -------- author.php (perfil del firmante) -------- */
.firma-archive {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 0 var(--pad);
}

.firma-breadcrumb {
	font-family: var(--font-sans);
	font-size: 11px;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--color-ink-faint);
	margin: 20px 0 28px;
	display: flex;
	gap: 8px;
	align-items: center;
}

.firma-breadcrumb a {
	color: inherit;
	text-decoration: none;
}

.firma-breadcrumb a:hover {
	color: var(--color-accent);
}

.firma-hero {
	/* 1.10.57 — Composición foto-izquierda compacta.
	   1.10.58 — Foto a 220px (antes 280): la versión 280 era visualmente
	   demasiado dominante respecto al peso tipográfico del nombre + bio.
	   Añadido `padding-bottom + border-bottom` para crear un divisor
	   horizontal claro entre el hero y la sección "Última columna".
	   Margin-bottom subido a 48px para más respiración. */
	display: grid;
	grid-template-columns: 220px 1fr;
	gap: 48px;
	align-items: center;
	margin-bottom: 48px;
	padding-bottom: 40px;
	border-bottom: 1px solid var(--color-rule-faint, #d8d4ca);
}

.firma-hero__meta {
	min-width: 0;
}

.firma-hero__kicker {
	display: inline-block;
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--color-accent);
	margin-bottom: 14px;
}

.firma-hero__name {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: clamp(48px, 6vw, 78px);
	line-height: 0.98;
	letter-spacing: -0.025em;
	color: var(--color-ink);
	margin: 0 0 20px;
}

.firma-hero__bio {
	font-family: var(--font-serif-reading);
	font-size: 17px;
	line-height: 1.55;
	color: var(--color-ink-muted);
	padding-top: 16px;
	border-top: 1px solid var(--color-rule-faint, #d8d4ca);
}

.firma-hero__bio p {
	margin: 0 0 10px;
}

/* 1.10.59 — Portrait cuadrado, sin recorte. Pensado para PNG
   transparente con ilustración de retrato (líneas) que se integra
   con el fondo crema de la página. `object-fit: contain` evita el
   recorte que hacía `cover`, mostrando la imagen completa siempre.
   Fondo transparente → la página se ve a través del PNG. */
.firma-hero__portrait {
	aspect-ratio: 1 / 1;
	background: transparent;
}
.firma-hero__portrait img {
	width: 100%;
	height: 100%;
	object-fit: contain;
	display: block;
}

/* 1.10.57 — Responsive: bajo 720px el hero colapsa a 1 columna con
   foto centrada arriba (160px) y bloque texto debajo. */
@media (max-width: 720px) {
	.firma-hero {
		grid-template-columns: 1fr;
		gap: 24px;
		padding-bottom: 32px;
		margin-bottom: 32px;
	}
	.firma-hero__portrait {
		width: 160px;
		justify-self: center;
	}
	.firma-hero__name {
		text-align: center;
	}
	.firma-hero__kicker {
		display: block;
		text-align: center;
	}
}

.firma-section-title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 22px;
	line-height: 1.15;
	margin: 0 0 18px;
	color: var(--color-ink);
}

.firma-featured {
	margin: 0 0 48px;
}

.firma-featured__article {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 28px;
	align-items: center;
}

.firma-featured__thumb img {
	width: 100%;
	height: auto;
	aspect-ratio: 16 / 10;
	object-fit: cover;
	display: block;
}

.firma-featured__headline {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: clamp(28px, 3.6vw, 40px);
	line-height: 1.1;
	letter-spacing: -0.015em;
	margin: 0 0 12px;
}

.firma-featured__headline a {
	color: var(--color-ink);
	text-decoration: none;
}

.firma-featured__headline a:hover {
	color: var(--color-accent);
}

.firma-featured__lede {
	font-family: var(--font-serif-reading);
	font-size: 16px;
	line-height: 1.55;
	color: var(--color-ink-muted);
	margin: 0 0 14px;
}

.firma-featured__date {
	font-family: var(--font-sans);
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--color-ink-faint);
}

.firma-list {
	margin-bottom: 48px;
}

.firma-list__items {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 28px;
}

.article-item__link {
	display: flex;
	gap: 24px;
	color: var(--color-ink);
	text-decoration: none;
	align-items: flex-start;
	transition: opacity 0.15s ease;
}

.article-item__link:hover {
	opacity: 0.86;
}

.article-item__thumb {
	width: 220px;
	flex-shrink: 0;
}

.article-item__thumb img {
	width: 100%;
	height: auto;
	aspect-ratio: 16 / 10;
	object-fit: cover;
	display: block;
}

.article-item__body {
	flex: 1;
	min-width: 0;
}

.article-item__title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: 24px;
	line-height: 1.15;
	margin: 0 0 8px;
}

.article-item__lede {
	font-family: var(--font-serif-reading);
	font-size: 15px;
	line-height: 1.5;
	color: var(--color-ink-muted);
	margin: 0 0 8px;
	max-width: 60ch;
}

.article-item__date {
	font-family: var(--font-sans);
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--color-ink-faint);
}

.firma-empty {
	font-family: var(--font-serif-reading);
	font-size: 17px;
	color: var(--color-ink-muted);
	padding: 32px 0;
	text-align: center;
}


/* -------- Responsive collapse -------- */
@media (max-width: 899.98px) {
	.opinion-archive__featured-grid {
		grid-template-columns: repeat(2, 1fr);
	}
	.opinion-archive__recent-grid {
		grid-template-columns: 1fr;
	}
	.firma-hero {
		grid-template-columns: 1fr;
	}
	.firma-featured__article {
		grid-template-columns: 1fr;
	}
	.author-box {
		flex-direction: column;
	}
	.article-item__link {
		flex-direction: column;
	}
	.article-item__thumb {
		width: 100%;
	}
}

@media (max-width: 599.98px) {
	.opinion-archive__featured-grid {
		grid-template-columns: 1fr;
	}
	.opinion-card__portrait,
	.opinion-featured__portrait {
		width: 160px;
	}
	.opinion-signer__portrait {
		width: 110px;
	}
}


/* ============================================================
   61. PRIMARY MENU — SUBMENUS (1.7.5)

   The primary menu (`#primary-menu.nav-menu` inside `<nav class=
   "nav">`) supports one level of children. WP emits the nested
   `<ul class="sub-menu">` once `wp_nav_menu()` is called with
   `depth: 0` (see header.php). Parent items carry the
   `.menu-item-has-children` class automatically.

   Desktop (≥ 769 px): the submenu is absolutely positioned below
   the parent and revealed on `:hover` / `:focus-within`.
   Mobile (≤ 768 px): the hamburger drawer (`.nav-drawer__menu`)
   already styles its own submenus elsewhere; the inline horizontal
   menu collapses to a tap-to-toggle via the `.is-open` class set
   by main.js. The mobile rules below are scoped to `.nav .nav-menu`
   so the drawer's separate rule set is untouched.
   ============================================================ */

.nav-menu .menu-item-has-children {
	position: relative;
}

.nav-menu .sub-menu {
	list-style: none;
	margin: 0;
	padding: 0;
	display: none;
	/* 1.7.9 — `position: fixed` (not absolute) deliberately. Reason:
	   `.nav-in` carries `overflow-x: auto; overflow-y: hidden` for
	   its horizontal scrolling rail (section 9, L519-520). CSS spec
	   makes `overflow: hidden` clip ALL descendants — including
	   `position: absolute` ones that visually exit the padding box —
	   independently of which ancestor they're anchored to. So an
	   absolute submenu, no matter how it's positioned, gets clipped
	   the moment it crosses the bottom edge of `.nav-in`. The 1.7.7
	   attempt to fix the misalignment by making the `<li>` parent
	   `position: relative` HID the submenu entirely, because it
	   re-anchored to a node inside the clipping container.
	   `position: fixed` is the only positioning that escapes
	   ancestor overflow, because it's anchored to the viewport. The
	   downside is that scrolling the page while the submenu is open
	   leaves it floating in place — acceptable trade-off because the
	   submenu only lives during the brief hover/focus window.
	   `top` / `left` / `right` are inlined by main.js on every
	   `mouseenter` of the parent link, sized from
	   `getBoundingClientRect()`. */
	position: fixed;
	min-width: 220px;
	background: var(--color-bg);
	border: 1px solid var(--color-rule-faint);
	box-shadow: 0 8px 24px -12px rgba(0, 0, 0, 0.18);
	z-index: 1001;
}

.nav-menu .menu-item-has-children:hover > .sub-menu,
.nav-menu .menu-item-has-children:focus-within > .sub-menu,
.nav-menu .menu-item-has-children.is-open > .sub-menu {
	display: block;
}

.nav-menu .sub-menu li {
	margin: 0;
	display: block;
}

.nav-menu .sub-menu a {
	display: block;
	padding: 10px 16px;
	color: var(--color-ink);
	text-decoration: none;
	font-family: var(--font-sans);
	font-size: 13px;
	line-height: 1.4;
	letter-spacing: 0.02em;
	transition: background 0.15s ease, color 0.15s ease;
}

.nav-menu .sub-menu a:hover,
.nav-menu .sub-menu a:focus-visible {
	background: var(--color-bg-warm, rgba(0, 0, 0, 0.04));
	color: var(--color-accent);
}

/* Optional visual cue on the parent (a small chevron) so the
   reader knows the item has children. Renders only on desktop —
   the mobile drawer has its own affordance. */
@media (min-width: 769px) {
	.nav-menu .menu-item-has-children > a::after {
		content: "▾";
		display: inline-block;
		margin-left: 4px;
		font-size: 0.7em;
		opacity: 0.55;
		transition: transform 0.15s ease;
	}
	.nav-menu .menu-item-has-children:hover > a::after,
	.nav-menu .menu-item-has-children:focus-within > a::after {
		transform: rotate(180deg);
	}
}

/* Mobile (≤ 768 px) — inline collapse. The submenu drops the
   absolute positioning and renders below the parent in flow when
   the parent gets `.is-open` (set by main.js on tap). The
   hamburger drawer at `.nav-drawer__menu` is a separate surface
   and keeps its own rules. */
@media (max-width: 768px) {
	.nav .nav-menu .sub-menu {
		position: static;
		min-width: 0;
		border: 0;
		box-shadow: none;
		background: transparent;
	}
	.nav .nav-menu .menu-item-has-children:hover > .sub-menu,
	.nav .nav-menu .menu-item-has-children:focus-within > .sub-menu {
		/* On mobile, hover/focus-within fire on touch in unhelpful
		   ways (the first tap targets the link, not the parent <li>).
		   Defer entirely to `.is-open` from main.js. */
		display: none;
	}
	.nav .nav-menu .menu-item-has-children.is-open > .sub-menu {
		display: block;
	}
}

/* 1.7.9 — When the reader is on a page the parent points to (e.g.
   `/regiones/`), WordPress flags the parent `<li>` with
   `current-menu-item` / `current_page_item` / etc. The theme's
   section-9 rules (L571 + L588) use a descendant selector that
   reaches every `<a>` inside that `<li>` — including the seven `<a>`
   that live in the open `.sub-menu`. The result is the submenu
   children rendered in accent green plus a 3 px `::after` underline
   bar across each one. 1.7.7 tried `display: none` on that pseudo
   but Firefox left a flicker residual when the mouse moved between
   items. Using `content: none` (instead of `display: none`)
   removes the pseudo from the box tree entirely — there's nothing
   left to render, transition or animate. The color override below
   wins on specificity ((0,4,1) vs the original (0,3,1)). */
.nav-in li.current-menu-item .sub-menu a,
.nav-in li.current_page_item .sub-menu a,
.nav-in li.current-menu-parent .sub-menu a,
.nav-in li.current-menu-ancestor .sub-menu a,
.nav-in li.current-cat .sub-menu a {
	color: var(--color-ink);
	font-weight: 500;
}

.nav-in li.current-menu-item .sub-menu a:hover,
.nav-in li.current_page_item .sub-menu a:hover,
.nav-in li.current-menu-parent .sub-menu a:hover,
.nav-in li.current-menu-ancestor .sub-menu a:hover,
.nav-in li.current-cat .sub-menu a:hover {
	color: var(--color-accent);
}

.nav-in li.current-menu-item .sub-menu a::after,
.nav-in li.current_page_item .sub-menu a::after,
.nav-in li.current-menu-parent .sub-menu a::after,
.nav-in li.current-menu-ancestor .sub-menu a::after,
.nav-in li.current-cat .sub-menu a::after {
	content: none;
}

/* 1.7.10 — Hide the "Regiones" parent (and its submenu) from the
   horizontal masthead strip on mobile (≤ 768 px). Editorial
   decision: the JS-positioned `position: fixed` submenu of 1.7.9
   works in desktop but, with "Regiones" sitting near the right
   edge of the scrollable strip on phones, the panel floats
   horizontally beside the parent and overflows the viewport
   (last item gets clipped). Mobile readers reach the seven
   geo-tags through the "Secciones" surface in the footer-style
   bottom nav, which already renders them as a clean indented
   vertical list. So we just hide the strip entry on small screens.

   Selector uses `:has()` (Baseline since late 2023, Safari 15.4+,
   Chrome 105+, Firefox 121+) so the rule auto-targets whichever
   `<li>` carries a link pointing at `/regiones/` without needing
   the editor to tag the menu item manually. `href*="/regiones/"`
   includes the trailing slash to avoid false matches against any
   future "/regiones-de-X/" page. */
@media (max-width: 768px) {
	.nav-in #primary-menu > li.menu-item-has-children:has(> a[href*="/regiones/"]),
	.nav-in .nav-menu > li.menu-item-has-children:has(> a[href*="/regiones/"]) {
		display: none !important;
	}
}


/* ============================================================
   62. PAGE — /regiones/ (1.7.6)

   Hub visual de 7 tarjetas continentales. Renderiza desde
   `page-regiones.php` (auto-attached por slug). Toda la cascada
   está bajo `body.page-regiones` para que NO contamine
   `.entry-content`, `.article-body` ni ninguna otra superficie
   del theme — y para que NO se la pisen los selectores
   genéricos del editor de bloques. El color de cada continente
   se inyecta vía el custom property `--region-color` en cada
   `<li>` (PHP), de modo que el CSS no tiene que enumerar 7
   selectores: una sola regla con `var(--region-color)` resuelve
   los tres puntos donde el color aparece (borde izquierdo,
   título del continente y CTA).

   Defensive `text-decoration: none` (sin `!important`) porque
   `body.page-regiones .page-regiones__card` tiene specificity
   (0,3,0), suficiente para ganar al underline genérico del
   theme. La regla del card no se cruza con `.entry-content a`
   porque la página /regiones/ no usa esa clase.
   ============================================================ */

body.page-regiones .page-regiones {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 40px var(--pad) 80px;
}

body.page-regiones .page-regiones__head {
	margin-bottom: 32px;
}

body.page-regiones .page-regiones__title {
	font-family: var(--font-display);
	font-weight: 500;
	font-size: clamp(40px, 5vw, 60px);
	line-height: 1;
	letter-spacing: -0.02em;
	color: var(--color-ink);
	margin: 0;
	text-transform: none;
}

body.page-regiones .page-regiones__grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
	gap: 20px;
}

body.page-regiones .page-regiones__item {
	margin: 0;
	padding: 0;
}

body.page-regiones .page-regiones__card {
	display: block;
	height: 100%;
	padding: 28px 28px 24px;
	background: #fafafa;
	border: 1px solid var(--color-rule-faint, #e5e3dd);
	border-left: 4px solid var(--region-color, var(--color-accent));
	color: var(--color-ink);
	text-decoration: none;
	transition: background 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}

body.page-regiones .page-regiones__card:hover,
body.page-regiones .page-regiones__card:focus-visible {
	background: #ffffff;
	box-shadow: 0 8px 24px -12px rgba(0, 0, 0, 0.18);
	transform: translateY(-3px);
	text-decoration: none;
	outline: none;
}

body.page-regiones .page-regiones__name {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: 22px;
	line-height: 1.2;
	letter-spacing: -0.01em;
	color: var(--region-color, var(--color-ink));
	margin: 0 0 12px;
	/* Override any inherited uppercase from the theme's editorial
	   h2 conventions — the continent name reads as a proper noun. */
	text-transform: none;
}

body.page-regiones .page-regiones__desc {
	font-family: var(--font-serif-reading);
	font-size: 15px;
	line-height: 1.55;
	color: #3a3a3a;
	margin: 0 0 18px;
}

body.page-regiones .page-regiones__cta {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--region-color, var(--color-accent));
}

body.page-regiones .page-regiones__arrow {
	display: inline-block;
	transition: transform 0.2s ease;
}

body.page-regiones .page-regiones__card:hover .page-regiones__arrow,
body.page-regiones .page-regiones__card:focus-visible .page-regiones__arrow {
	transform: translateX(4px);
}

@media (max-width: 639.98px) {
	body.page-regiones .page-regiones__grid {
		grid-template-columns: 1fr;
	}
	body.page-regiones .page-regiones {
		padding: 28px var(--pad) 56px;
	}
}


/* ============================================================
   63. PAGE TEMPLATE — "Lienzo libre" (1.8.0)

   Lienzo neutro para páginas con HTML+CSS autocontenido. Renderiza
   header y footer del tema pero deja el cuerpo SIN estilos editoriales
   (sin contenedor estrecho de `.entry-content`, sin pintar enlaces /
   headings / listas / párrafos con las reglas que el theme aplica a
   contenido editorial). Sólo se neutraliza lo que el tema impone
   agresivamente — un `:all initial` rompería demasiado (font-family
   del body, herencia de color de texto, etc.).

   Activado por `template-lienzo.php` (Template Name: "Lienzo libre"),
   asignable desde Atributos de página → Plantilla.
   ============================================================ */

.lienzo-libre {
	width: 100%;
	max-width: 100%;
	margin: 0;
	padding: 0;
	background: inherit;
}

/* Reset de enlaces: el operador controla el color/decoración desde
   su HTML inline. Aplica a todos los `<a>` dentro del lienzo, sea
   cual sea el wrap del operador. */
.lienzo-libre a {
	color: inherit;
	text-decoration: none;
	border-bottom: none;
}

.lienzo-libre a:hover {
	color: inherit;
}

/* Reset de headings — el theme aplica font-family display, weight,
   letter-spacing, etc. Aquí se hereda para que el HTML inline
   defina su propia tipografía. */
.lienzo-libre h1,
.lienzo-libre h2,
.lienzo-libre h3,
.lienzo-libre h4,
.lienzo-libre h5,
.lienzo-libre h6 {
	font-family: inherit;
	font-weight: inherit;
	font-size: inherit;
	line-height: inherit;
	letter-spacing: inherit;
	color: inherit;
	margin: 0;
	padding: 0;
}

/* Reset de listas: el theme pinta viñetas + ::before custom para
   `ul li` y `ol li` dentro de `.entry-content`/`.article-body`. El
   lienzo no usa esas clases, pero los selectores genéricos del
   theme (h1-h6, p, etc.) podrían heredar del body. Neutralizamos
   por defensa. */
.lienzo-libre ul,
.lienzo-libre ol {
	list-style: none;
	margin: 0;
	padding: 0;
}

.lienzo-libre li {
	margin: 0;
	padding: 0;
}

.lienzo-libre p {
	margin: 0;
	padding: 0;
}

/* Neutralizar la barra `::after` que las reglas current-menu-item /
   current_page_item / etc. del nav pintan en el `<a>` cuando el
   wrap exterior es `.nav-in`. Si la página activa el lienzo está
   marcada como current y el operador inserta `<a>` con URL
   coincidente con un menú, el theme podría imprimir la barra. Sin
   `!important` no gana: la regla original tiene specificity
   (0,4,1) y el reset aquí (0,2,1). Con `!important` el reset
   neutraliza el pseudo sin animación residual (`content: none`
   también funciona; ambas formas evitan el flicker). */
.lienzo-libre a::after {
	display: none !important;
}

/* 1.8.2 — Inner wrap. Hereda el ancho editorial del tema (`--w`,
   1060 px) y el padding lateral (`--pad`, 20 px) de las variables
   compartidas, los mismos tokens que usan `.sect`, `.nav`,
   `.featured`, `.site-footer-inner`, `.opinion-archive` y demás
   superficies. Cualquier página con plantilla Lienzo queda
   automáticamente alineada con el resto del sitio sin que el
   editor tenga que recordar el ancho exacto en el HTML pegado;
   si el theme cambia `--w` en el futuro, todas las páginas
   Lienzo se ajustan sin tocar el contenido. `box-sizing:
   border-box` para que el padding no sume sobre el max-width.

   Recomendación al operador: el HTML pegado dentro del Lienzo
   debería NO declarar su propio `max-width` — el wrap exterior
   ya lo aporta. */
.lienzo-libre__inner {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 0 var(--pad);
	box-sizing: border-box;
}


/* ============================================================
   64. INTERACTIONS — heart + share-on-X (1.9.0, reorganised 1.9.1)

   1.9.1 layout decisions:

   - Inside the article (single.php / single-opinion.php) the heart
     and the share-on-X button live INSIDE the `.byline` row,
     pushed to the right edge by `.byline-actions { margin-left:
     auto }`. The standalone `.interaction-bar` strip from 1.9.0
     is retired.
   - The share-on-X button is now icon-only (no "Compartir" label),
     same visual weight as the heart pill — both pills inline next
     to the author info.
   - On cards (homepage + archives) the heart counter renders as
     an inline-flex badge BETWEEN the headline and the lede, not
     down at the meta row. `.card-interactions-inline` is the
     selector; non-interactive (the card link is the click target).
     The previous `.card-interactions` selector (emitted by
     `the_time_el()` in 1.9.0) is retired.

   Tokens: `--color-rule-faint`, `--color-ink-soft`,
   `--color-accent`, `--color-ink-faint`, `--font-sans`.
   ============================================================ */

/* Actions slot inside the article's byline row. `margin-left: auto`
   pushes it to the right edge of the existing `.byline` flex
   container, so neither the avatar (first item) nor the
   `.byline-main` column have to move. */
.byline-actions {
	display: flex;
	gap: 10px;
	align-items: center;
	flex-shrink: 0;
	margin-left: auto;
}

.interaction-heart {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 8px 14px;
	background: transparent;
	border: 1px solid var(--color-rule-faint);
	border-radius: 999px;
	font-family: var(--font-sans);
	font-size: 13px;
	font-weight: 600;
	color: var(--color-ink-soft);
	cursor: pointer;
	transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
	line-height: 1;
}

.interaction-heart:hover,
.interaction-heart:focus-visible {
	border-color: var(--color-accent);
	color: var(--color-accent);
	outline: none;
}

.interaction-heart:disabled {
	cursor: default;
}

.interaction-heart svg {
	width: 18px;
	height: 18px;
	transition: fill 0.2s ease, stroke 0.2s ease;
}

.interaction-heart.is-liked {
	border-color: var(--color-accent);
	color: var(--color-accent);
}

.interaction-heart.is-liked .heart-icon path {
	fill: var(--color-accent);
	stroke: var(--color-accent);
}

/* Inline interaction badge on every card. Sits between the
   headline and the lede via `CalDir_Frontpage::the_card_
   interactions_inline()`. No border, no chip — just icon + count
   in a discreet grey. Non-interactive (`cursor: default`); the
   tooltip arrives via the `title` attribute on the span. */
.card-interactions-inline {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	margin: 8px 0;
	font-family: var(--font-sans);
	font-size: 12px;
	letter-spacing: 0.02em;
	color: var(--color-ink-faint);
	cursor: default;
}

.card-interactions-inline svg {
	width: 13px;
	height: 13px;
	fill: var(--color-ink-faint);
}

@media (max-width: 599.98px) {
	.byline-actions {
		gap: 8px;
	}
	.interaction-heart {
		padding: 6px 12px;
		font-size: 12px;
	}
}

/* Share bar (1.10.63) — icon-only pills, same visual weight and hover
   behaviour as `.interaction-heart` (border-color + color → accent),
   just square instead of an elongated pill since there's no count
   label to make room for. Lives inside `.byline-actions`, right next
   to the heart — see inc/partials/share-bar.php. */
.share-bar {
	display: flex;
	align-items: center;
	gap: 6px;
}

.share-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 34px;
	height: 34px;
	border: 1px solid var(--color-rule-faint);
	border-radius: 999px;
	color: var(--color-ink-soft);
	transition: color 0.2s ease, border-color 0.2s ease;
}

.share-btn:hover,
.share-btn:focus-visible {
	border-color: var(--color-accent);
	color: var(--color-accent);
	outline: none;
}

.share-btn svg {
	width: 16px;
	height: 16px;
}

@media (max-width: 599.98px) {
	.share-btn {
		width: 30px;
		height: 30px;
	}
	.share-btn svg {
		width: 14px;
		height: 14px;
	}
}


/* === 1.10.43 — Tema block (HTML libre) === */
.tema-block { margin-bottom: var(--space-section, 48px); }
.tema-block__html { width: 100%; }
.tema-block__html > :first-child { margin-top: 0; }
.tema-block__html > :last-child { margin-bottom: 0; }
.tema-block__html img,
.tema-block__html video,
.tema-block__html iframe { max-width: 100%; height: auto; }

/* === 1.11.0 — Front-page canvas (12-col grid) === */
.fp-canvas {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: minmax(0, auto);
  grid-auto-flow: row dense;
  gap: var(--space-section, 48px) 32px;
  margin-bottom: var(--space-section, 48px);
  align-items: start;
  max-width: var(--w, 1060px);
  margin-left: auto;
  margin-right: auto;
  padding: 0 var(--pad);
}
.fp-cell { min-width: 0; }
.fp-cell--w12 { grid-column: span 12; }
.fp-cell--w9  { grid-column: span 9; }
.fp-cell--w8  { grid-column: span 8; }
.fp-cell--w6  { grid-column: span 6; }
.fp-cell--w4  { grid-column: span 4; }
.fp-cell--w3  { grid-column: span 3; }
.fp-cell--w2  { grid-column: span 2; }
.fp-cell--h1  { grid-row: span 1; }
.fp-cell--h2  { grid-row: span 2; }

/* 2026-08-03 — divisor "Portada de Cal Dir" que taxonomy-municipio.php
   pone encima de la portada normal reutilizada (ver
   CalDir_Frontpage::render_blocks()) debajo de las noticias propias del
   municipio. `.sh` normalmente hereda su ancho de un `.sect` padre; aquí
   se le da el mismo ancho/margen que ya usa `.fp-canvas` justo debajo,
   para que ambos queden alineados. */
.municipio-portada__sh {
  max-width: var(--w, 1060px);
  margin-left: auto;
  margin-right: auto;
  padding: 0 var(--pad);
  margin-top: var(--space-section, 48px);
}

/* Neutralizar la auto-contención del .sect cuando vive dentro de una
   .fp-cell. Fuera del canvas (single posts, archivos) el .sect sigue
   intacto con su `max-width: var(--w, 1060px); margin: 0 auto; padding: 0 var(--pad)`.
   Dentro del canvas el contenedor `.fp-canvas` asume esa contención;
   los .sect anidados deben colapsar a 0 para que la rejilla 12-col
   posicione correctamente las cells. Sin este reset, los bloques de
   ancho parcial empujan su contenido al borde izquierdo de la viewport. */
.fp-cell > .sect {
  max-width: none;
  margin: 0;
  padding: 0;
}

/* Aire vertical antes del section header cuando vive dentro del canvas.
   El gap del .fp-canvas controla el espacio entre cells, pero el header
   del .sect dentro de cada cell necesita su propio aire arriba para que
   el título no quede pegado al borde superior del bloque. */
.fp-cell > .sect > .sh:first-child {
  margin-top: 8px;
}

/* Responsive: bajo 900px el canvas colapsa a 1 col y los cells fluyen
   apilados, ignorando spans de width y height (un h2 pasa a h1 visual). */
@media (max-width: 900px) {
  .fp-canvas {
    display: block;
  }
  .fp-canvas > .fp-cell {
    margin-bottom: var(--space-section, 48px);
  }
  .fp-canvas > .fp-cell:last-child {
    margin-bottom: 0;
  }
}

/* === 1.10.x — news3 + masleido width-aware === */

/* news3 dentro de cells parciales: el auto-fit con minmax(280px,...) ya
   colapsa correctamente (a w8 sale 2 cols, a w6 sale 2 cols apretadas,
   a w4 sale 1 col). Bajamos el minmax solo para w6 y w4 donde quedaría
   demasiado ancho una sola columna. */
.fp-cell--w6 .row-3,
.fp-cell--w4 .row-3 {
  grid-template-columns: 1fr;
  gap: 20px;
}

/* En cells parciales el header del bloque (título + "Ver todo →") puede
   quedar apretado; reducir el tamaño del título proporcionalmente. */
.fp-cell--w8 .sect > .sh h3,
.fp-cell--w6 .sect > .sh h3 {
  font-size: clamp(1.1rem, 1.3vw, 1.3rem);
}
.fp-cell--w4 .sect > .sh h3,
.fp-cell--w3 .sect > .sh h3 {
  font-size: clamp(0.95rem, 1.1vw, 1.1rem);
}

/* masleido dentro de cells parciales: el minmax(240px,...) original lo
   dejamos; auto-fit hace su trabajo a w8 (3 cols), w6 (2 cols), w4 (1-2
   cols según viewport), w3 (1 col). Solo afinamos el tamaño del numeral
   en cells muy estrechos para que no domine la card. */
.fp-cell--w4 .masleido-num,
.fp-cell--w3 .masleido-num {
  font-size: 30px;
}
.fp-cell--w4 .masleido-txt,
.fp-cell--w3 .masleido-txt {
  font-size: 14px;
}

/* En cells parciales el padding interno del item de masleido también se
   reduce para no desperdiciar espacio horizontal. */
.fp-cell--w4 .masleido-item,
.fp-cell--w3 .masleido-item {
  padding: 14px 12px;
  gap: 10px;
}

/* === 1.11+ — Kripton block (columna vertical de opinión) === */
.kripton-block { display: flex; flex-direction: column; }
.kripton-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.kripton-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 0;
  border-bottom: 1px solid var(--color-rule-faint);
}
.kripton-item:first-child { padding-top: 0; }
.kripton-item:last-child { border-bottom: none; padding-bottom: 0; }

.kripton-item__body {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.kripton-item__author {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-soft);
  text-decoration: none;
}
.kripton-item__author:hover { color: var(--color-accent); }

.kripton-item__title {
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 1.2vw, 1.2rem);
  font-weight: 500;
  line-height: 1.2;
  margin: 0;
  letter-spacing: -0.01em;
}
.kripton-item__title a { color: inherit; text-decoration: none; }
.kripton-item__title a:hover { color: var(--color-accent); }

.kripton-item__portrait {
  flex: 0 0 48px;
  display: block;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--color-rule-faint);
}
.kripton-item__portrait img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* === 1.11+ — Xenon block (destacado con foto) === */
.xenon-block { display: flex; flex-direction: column; }

/* === 1.11+ — Radio block (lista de titulares) === */
.radio-block { display: flex; flex-direction: column; }
.radio-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.radio-list__item {
  margin: 0;
  padding: 14px 0;
  border-bottom: 1px solid var(--color-rule-faint);
}
.radio-list__item:first-child { padding-top: 0; }
.radio-list__item:last-child { border-bottom: none; padding-bottom: 0; }

.radio-headline { margin: 0; }
.radio-headline__title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 500;
  line-height: var(--lh-snug);
  letter-spacing: var(--tracking-tight);
  margin: 0;
  color: var(--color-ink);
}
.radio-headline__title a { color: inherit; text-decoration: none; }
.radio-headline__title a:hover { color: var(--color-accent); }

/* === 1.10.67 — Titular block (titular gigante, portada excepcional) === */
/* 1.10.71 le puso un margin-top propio a este bloque porque `.nav` no
   aportaba ningún aire hacia el contenido. 1.10.72 mueve ese aire a
   `.nav` (margin-bottom, universal para toda plantilla), así que el
   margen especial de aquí ya sobra — se retira para no duplicar el
   hueco. */
.titular-block { display: flex; flex-direction: column; }
.titular { display: flex; flex-direction: column; }

/* El titular más grande de portada hoy es hero-big h2 a 30px fijos
   (véase línea ~691). "Al menos el doble" exige un suelo de 60px que
   NUNCA baje con clamp() — el mínimo del clamp es ese suelo, no un
   valor de partida que el motor pueda recortar. */
.titular__heading {
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 2vw + 1.1rem, 3.25rem); /* 36px → 52px, más grande que los 30px de hero-big sin dominar la portada */
  font-weight: 500;
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  margin: 0 0 28px;
  text-wrap: balance;
  color: var(--color-ink);
}
.titular__heading a { color: inherit; text-decoration: none; }
.titular__heading a:hover { color: var(--color-accent); }

/* Dos columnas: imagen pequeña a la izquierda, entradilla a la derecha. */
.titular__row {
  display: flex;
  align-items: flex-start;
  gap: 28px;
}

/* 1.10.71 — ancho doblado (220px → 440px) sin variar la altura; la
   imagen ocupaba demasiado protagonismo respecto a la entradilla, así
   que se ensancha para reducir el hueco de texto en vez de agrandarla
   en las dos dimensiones. aspect-ratio 8/3 = 440/165, la misma altura
   que daba el 4/3 anterior a 220px. */
.titular__img {
  display: block;
  flex: 0 0 440px;
  width: 440px;
  aspect-ratio: 8 / 3;
  overflow: hidden;
}

.titular__body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Mismo tratamiento que el resto de entradillas de portada
   (hero-big/hero-sm .lede): 15px, serif de lectura, gris tinta
   apagado. Antes usaba --fs-lead (18-22px) y desentonaba con el
   resto de la portada. */
.titular .lede {
  font-family: var(--font-serif-reading);
  font-size: 15px;
  line-height: 1.45;
  color: var(--color-ink-muted);
  margin: 0;
}

/* 1.10.71 — fecha + contador de "me gusta" bajo la entradilla. */
.titular__meta {
  display: flex;
  align-items: center;
  gap: 14px;
}
.titular__meta .card-interactions-inline { margin: 0; }

/* Cell de media anchura (6/12): se recorta el techo para que no
   desborde una columna más estrecha. */
.fp-cell--w6 .titular__heading {
  font-size: clamp(2.25rem, 1.8vw + 0.9rem, 2.75rem);
}

/* Bajo 640px la columna de imagen ya no cabe junto a la entradilla sin
   apretarla en exceso: la fila pasa a apilarse. */
@media (max-width: 640px) {
  .titular__row {
    flex-direction: column;
  }
  .titular__img {
    width: 100%;
    flex: none;
    /* El 8/3 del layout a dos columnas queda demasiado plano a
       ancho completo apilado; en móvil vuelve a una proporción de
       foto normal. */
    aspect-ratio: 16 / 9;
  }
}

/* === 1.10.76 — Ecosistema Cal Dir (franja de cabecera + bloque
   de barra lateral). Tarjetas claras del mismo papel que el resto del
   tema; cada módulo se distingue por un filete de color arriba y una
   etiqueta en píldora, no por un fondo oscuro. === */
.eco-strip {
  /* `.eco-strip` es hijo directo de `.site` (display:flex, columna) —
     igual que `.header`/`.nav`, necesita `min-width: 0` explícito
     para poder encogerse en móvil por debajo del contenido de sus
     tarjetas (ver la odisea de 1.10.70-75 sobre este mismo problema
     con `.header`/`.nav`). */
  min-width: 0;
  padding: 0 var(--pad) 22px;
}
.eco-strip__head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 10px;
}
.eco-strip__head span {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-ink-faint);
}
.eco-strip__head::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--color-rule-faint);
}

.eco-strip__grid {
  display: grid;
  /* `minmax(0, 1fr)`, no `1fr` a secas — mismo motivo que arriba: sin
     el `minmax(0, …)` las columnas del grid no se encogen por debajo
     del min-content de la tarjeta más ancha. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}

.eco-card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  min-height: 96px;
  min-width: 0;
  padding: 14px 16px 15px;
  border-radius: 2px;
  border: 1px solid var(--color-rule-faint);
  border-top: 3px solid transparent;
  color: var(--color-ink);
  text-decoration: none;
  overflow: hidden;
  isolation: isolate;
  transition: box-shadow 140ms ease, transform 140ms ease;
}
.eco-card:hover {
  box-shadow: 0 10px 22px -14px rgba(20, 20, 19, 0.35);
  transform: translateY(-1px);
}
.eco-card__glyph {
  position: absolute;
  right: -10px;
  bottom: -16px;
  z-index: 0;
  pointer-events: none;
  opacity: 0.09;
}
.eco-card__glyph svg { width: 80px; height: 80px; display: block; }

.eco-card__eyebrow {
  display: inline-flex;
  align-self: flex-start;
  padding: 3px 9px;
  border-radius: 20px;
  font-family: var(--font-sans);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  z-index: 1;
}
.eco-card__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 17px;
  line-height: 1.15;
  z-index: 1;
}
.eco-card__desc {
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1.4;
  color: var(--color-ink-soft);
  max-width: 30ch;
  z-index: 1;
}
.eco-card__cta {
  font-family: var(--font-sans);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-top: 4px;
  z-index: 1;
}

/* icon-chip: oculto por defecto — solo se usa en la barra lateral
   (siempre) y en la ficha mini de móvil (ver @media más abajo); en la
   tarjeta de escritorio el identificador visual es el glyph de fondo. */
.eco-card__icon-chip { display: none; }

.eco-card--globo { border-top-color: #ff4d9d; }
.eco-card--globo .eco-card__eyebrow { background: rgba(255, 77, 157, 0.14); color: #c2277a; }
.eco-card--globo .eco-card__cta { color: #c2277a; }
.eco-card--globo .eco-card__glyph { color: #ff4d9d; }
.eco-card--globo .eco-card__icon-chip { background: rgba(255, 77, 157, 0.14); color: #c2277a; }

.eco-card--espana { border-top-color: #d4922a; }
.eco-card--espana .eco-card__eyebrow { background: rgba(212, 146, 42, 0.16); color: #a3691a; }
.eco-card--espana .eco-card__cta { color: #a3691a; }
.eco-card--espana .eco-card__glyph { color: #d4922a; }
.eco-card--espana .eco-card__icon-chip { background: rgba(212, 146, 42, 0.16); color: #a3691a; }

.eco-card--redac { border-top-color: var(--color-accent); }
.eco-card--redac .eco-card__eyebrow { background: rgba(150, 47, 102, 0.12); color: #7a2653; }
.eco-card--redac .eco-card__cta { color: #7a2653; }
.eco-card--redac .eco-card__glyph { color: var(--color-accent); }
.eco-card--redac .eco-card__icon-chip { background: rgba(150, 47, 102, 0.12); color: #7a2653; }

/* --- versión compacta (barra lateral, antes de "Lo más leído") --- */
.eco-stack-block .eco-strip__head { margin-bottom: 12px; }
.eco-stack { display: flex; flex-direction: column; gap: 10px; }

.eco-card--compact {
  flex-direction: row;
  align-items: center;
  /* 1.10.77 — `.eco-card` base fija `justify-content: center` para
     centrar el bloque de texto EN VERTICAL cuando la tarjeta es una
     columna (versión de cabecera). Al pasar aquí a fila sin resetear
     esta propiedad, "centrar en el eje" pasó a significar centrar en
     HORIZONTAL — el texto quedaba descolocado en vez de a la
     izquierda. Sin este reset era el bug que se veía en producción. */
  justify-content: flex-start;
  gap: 14px;
  min-height: 0;
  padding: 13px 16px 13px 14px;
  /* El filete de color pasa de arriba (card vertical) a la izquierda
     (card horizontal) — mismo lenguaje, orientación coherente con la
     fila. */
  border-top: 1px solid var(--color-rule-faint);
  border-left: 3px solid transparent;
}
.eco-card--compact .eco-card__icon-chip {
  width: 38px;
  height: 38px;
  /* Cuadrado redondeado en vez de círculo: lee más a "ficha de
     producto editorial" que a avatar genérico. */
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  z-index: 1;
}
.eco-card--compact .eco-card__icon-chip svg { width: 19px; height: 19px; }
.eco-card--compact .eco-card__txt {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  min-width: 0;
  z-index: 1;
}
.eco-card--compact .eco-card__eyebrow { margin: 0; }
.eco-card--compact .eco-card__title { font-size: 14.5px; line-height: 1.25; }

.eco-card--globo.eco-card--compact { border-left-color: #ff4d9d; }
.eco-card--espana.eco-card--compact { border-left-color: #d4922a; }
.eco-card--redac.eco-card--compact { border-left-color: var(--color-accent); }

/* 1.10.78 — en móvil, tres tarjetas completas apiladas (título +
   descripción + CTA cada una) se comían la pantalla entera antes de
   llegar a una sola noticia — nada aceptable en un periódico. Bajo
   640px la fila de 3 se queda en fila (no se apila), pero cada
   tarjeta colapsa a una ficha mini: icono + etiqueta corta, sin
   descripción ni CTA. Mismo contenido, mucho menos alto. */
@media (max-width: 640px) {
  .eco-strip { padding-bottom: 18px; }
  .eco-strip__head { margin-bottom: 8px; }

  .eco-strip__grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
  }

  .eco-card {
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    text-align: center;
    gap: 7px;
    min-height: 0;
    padding: 12px 6px 10px;
  }

  .eco-card__glyph,
  .eco-card__desc,
  .eco-card__cta {
    display: none;
  }

  .eco-card__icon-chip {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 8px;
  }
  .eco-card__icon-chip svg { width: 17px; height: 17px; }

  /* La etiqueta pasa de "badge" secundario a ser el nombre principal
     de la ficha — dos líneas cortas centradas bajo el icono. El
     selector repite `.eco-card--globo` etc. porque esas reglas (fuera
     de este media query) tienen la misma especificidad y, al no
     pertenecer a un media query, ganarían el empate por orden de
     aparición si aquí solo se apuntara `.eco-card__eyebrow` a secas. */
  .eco-card--globo .eco-card__eyebrow,
  .eco-card--espana .eco-card__eyebrow,
  .eco-card--redac .eco-card__eyebrow {
    background: none;
    padding: 0;
    font-size: 10px;
    line-height: 1.25;
    white-space: normal;
  }

  .eco-card__title { display: none; }
}

/* --- 1.10.80 — tira mini para [caldir_herramientas], pensada
   para respirar dentro del cuerpo de un artículo (no una barra
   entera): icono + nombre corto, sin descripción. --- */
.eco-inline {
  margin: 24px 0;
  border: 1px solid var(--color-rule-faint);
  border-top: 3px solid var(--color-accent);
  border-radius: 2px;
  overflow: hidden;
  background: var(--color-bg-subtle);
}
.eco-inline .eco-strip__head {
  padding: 12px 16px 0;
  margin-bottom: 0;
}
.eco-inline__row {
  display: flex;
  align-items: stretch;
}
.eco-inline__item {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 12px 8px 14px;
  text-decoration: none;
  text-align: center;
  border-right: 1px solid var(--color-rule-faint);
  transition: background 140ms ease;
}
.eco-inline__item:last-child { border-right: none; }
.eco-inline__item:hover { background: var(--color-bg-warm); }
.eco-inline__icon {
  width: 30px;
  height: 30px;
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.eco-inline__icon svg { width: 15px; height: 15px; }
.eco-inline__label {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--color-ink);
}
.eco-inline__item--globo .eco-inline__icon { background: rgba(255, 77, 157, 0.14); color: #c2277a; }
.eco-inline__item--espana .eco-inline__icon { background: rgba(212, 146, 42, 0.16); color: #a3691a; }
.eco-inline__item--redac .eco-inline__icon { background: rgba(150, 47, 102, 0.12); color: #7a2653; }

/* ============================================================
   1.10.25 — Single article: 2-column layout (body + sidebar)
   ============================================================ */
.single-layout {
	display: grid;
	/* 1.10.26 — sidebar usa `minmax(300px, 1fr)` para estirarse hasta
	   el borde derecho del wrapper. Antes era `300px` fijo: como
	   660 + 40 (gap) + 300 = 1000 < 1060 (--w), quedaban 60px sin
	   asignar a la derecha y la línea del bloque "LO MÁS LEÍDO" no
	   se alineaba con la línea superior del header. */
	grid-template-columns: minmax(0, var(--measure)) minmax(300px, 1fr);
	gap: 40px;
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 0 var(--pad);
	align-items: start;
}

/* Columna principal: hereda los anchos internos de `.article-body`,
   `.post-end`, etc. — todos esos ya usan `max-width: var(--measure)`
   y `margin: 0`, por lo que se alinean al borde izquierdo del slot. */
.single-main { min-width: 0; }
/* 1.10.48 — Dentro del `.single-main` el body se alinea al borde
   izquierdo del slot del grid (no centrado), porque el sidebar a la
   derecha completa la composición. Override del `margin: 0 auto`
   default que ahora aplica a páginas estáticas.
   También resetea paddings horizontales: el layout ya aporta
   `padding: 0 var(--pad)` al wrapper, no hay que duplicarlo dentro. */
.single-main .article-body,
.single-main .post-end {
	margin-left: 0;
	margin-right: 0;
	padding-left: 0;
	padding-right: 0;
}

/* 1.10.49 — Páginas estáticas (`page.php` → article.page-article):
   ensanchar el body de 660px (medida de lectura) a 1020px (ancho
   editorial). Las páginas legales/regiones/etc. no tienen sidebar
   ni necesitan la columna estrecha; quedan visualmente perdidas
   centradas en el viewport. Con `--w` ocupan el mismo ancho que
   el resto de la página. */
.page-article .article-body {
	max-width: var(--w, 1060px);
}

/* Sidebar: sticky para que acompañe el scroll del cuerpo (las piezas
   más largas son las que más necesitan que el sidebar esté siempre
   visible). Si el contenido del sidebar supera la altura del viewport,
   el `top` evita que se solape con el header. */
.single-sidebar {
	min-width: 0;
	position: sticky;
	top: 24px;
	display: flex;
	flex-direction: column;
	gap: 32px;
}

.single-sidebar__block {
	padding-bottom: 24px;
	border-bottom: 1px solid var(--color-rule-faint);
}
.single-sidebar__block:last-child { border-bottom: none; padding-bottom: 0; }

.single-sidebar__title {
	font-family: var(--font-sans);
	font-size: 11px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--color-ink);
	margin: 0 0 14px;
	padding-bottom: 8px;
	border-bottom: 2px solid var(--color-ink);
}

/* --- Lo más leído --- */
.single-toplist__items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.single-toplist__item {
	display: grid;
	grid-template-columns: 32px 1fr;
	gap: 10px;
	align-items: start;
	padding-bottom: 14px;
	border-bottom: 1px solid var(--color-rule-faint);
}
.single-toplist__item:last-child { border-bottom: none; padding-bottom: 0; }
.single-toplist__rank {
	font-family: var(--font-display);
	font-size: 24px;
	font-weight: 500;
	line-height: 1;
	color: var(--color-accent);
}
.single-toplist__body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.single-toplist__title {
	/* 1.10.27 — alineado con `.masleido-txt`, el patrón ya establecido
	   del theme para títulos de cards numeradas: 15px / 1.22 / -0.01em.
	   13px (`--fs-meta`) en serif quedaba aplastado: los serifs a
	   tamaño pequeño pierden definición de detalles. */
	font-family: var(--font-display);
	font-size: 15px;
	font-weight: 500;
	line-height: 1.22;
	letter-spacing: -0.01em;
	margin: 0;
	color: var(--color-ink);
}
.single-toplist__title a { color: inherit; text-decoration: none; }
.single-toplist__title a:hover { color: var(--color-accent); }
.single-toplist__count {
	font-family: var(--font-sans);
	font-size: 11px;
	color: var(--color-ink-faint);
}

/* --- Ad slot (1:1) ---
   1.10.28 — sustituye a los antiguos bloques `.single-newsletter*` y
   `.single-support*`. Sin borde inferior porque el slot ya es una caja
   visualmente cerrada. El placeholder gris se ve mientras nadie hookee
   `caldir_sidebar_ad_square`; cuando llega contenido inyectado, la
   etiqueta "PUBLICIDAD" queda por debajo (z-index: 0) y el ad ocupa el
   100% del slot por encima. */
.single-ad {
	border-bottom: none !important;
	padding-bottom: 0 !important;
}
.single-ad__slot {
	position: relative;
	aspect-ratio: 1 / 1;
	width: 100%;
	background: var(--color-bg-subtle);
	border: 1px solid var(--color-rule-faint);
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
}
.single-ad__slot > * {
	max-width: 100%;
	max-height: 100%;
}
.single-ad__label {
	position: absolute;
	font-family: var(--font-sans);
	font-size: 10px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.12em;
	color: var(--color-ink-faint);
	z-index: 0;
}
/* Si un script de ads inyecta su iframe/img dentro del slot, queda
   por encima del label automáticamente (z-index implicit por orden DOM
   + position: relative del padre). El label sirve solo como fallback
   visible cuando el slot está vacío. */

/* Responsive: bajo 1000px sidebar cae debajo del cuerpo, sin sticky. */
@media (max-width: 1000px) {
	.single-layout {
		grid-template-columns: 1fr;
		gap: 48px;
		max-width: var(--measure);
	}
	.single-sidebar {
		position: static;
		top: auto;
	}
}

/* ============================================================
   1.10.40 — Page template "Página con bloques"
   ============================================================ */
.page-bloques__header {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 48px var(--pad) 24px;
}
.page-bloques__title {
	font-family: var(--font-display);
	font-size: clamp(2rem, 4.5vw + 0.5rem, 3.75rem);
	font-weight: 500;
	line-height: var(--lh-tight);
	letter-spacing: var(--tracking-tight);
	color: var(--color-ink);
	margin: 0;
}
.page-bloques__intro {
	max-width: var(--measure);
	margin: 0 auto;
	padding: 0 var(--pad) 32px;
	font-family: var(--font-serif-reading);
	font-size: var(--fs-body);
	line-height: var(--lh-reading);
	color: var(--color-ink-muted);
}
.page-bloques__intro p { margin: 0 0 16px; }
.page-bloques__intro p:last-child { margin-bottom: 0; }

/* ============================================================
   1.10.47 — Footer (estilo El Confidencial, 5 columnas)
   ============================================================ */
.ft {
	background: #0a0a0a;
	color: #b0b0b0;
	margin-top: var(--space-section, 48px);
	font-family: var(--font-sans);
	font-size: 15px;
	line-height: 1.4;
	/* 1.10.74 — `min-width: 0`: mismo motivo que `.header`/`.nav`/
	   `.site-main` (flex item de `.site`). */
	min-width: 0;
}
.ft__inner {
	max-width: var(--w, 1060px);
	margin: 0 auto;
	padding: 40px var(--pad) 24px;
}

/* ── Utility bar (logo + enlaces de utilidad) ── */
.ft__utility {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 32px;
	padding-bottom: 24px;
	border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.ft__brand {
	flex-shrink: 0;
	text-decoration: none;
	color: #fff;
}
.ft__brand-logo {
	display: block;
	max-height: 36px;
	width: auto;
}
.ft__brand-wordmark {
	font-family: var(--font-display);
	font-size: 24px;
	font-weight: 500;
	letter-spacing: -0.01em;
	color: #fff;
}
.ft__utility-menu {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 32px;
	flex: 1 1 auto;
}
.ft__utility-menu li { margin: 0; }
.ft__utility-menu a {
	color: #fff;
	text-decoration: none;
	font-size: 14px;
	font-weight: 500;
	letter-spacing: 0.005em;
	transition: color 0.15s ease;
}
.ft__utility-menu a:hover { color: #b0b0b0; }

/* ── 5 columnas iguales ── */
.ft__cols {
	display: grid;
	grid-template-columns: repeat(5, 1fr);
	gap: 32px;
	padding: 40px 0 32px;
}
.ft__group {
	min-width: 0;
}
.ft__group-title {
	font-family: var(--font-sans);
	font-size: 16px;
	font-weight: 600;
	color: #fff;
	margin: 0 0 24px;
	letter-spacing: 0.005em;
}
.ft__group-menu {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 12px;
}
.ft__group-menu li { margin: 0; }
.ft__group-menu a {
	color: #888;
	text-decoration: none;
	font-size: 14px;
	transition: color 0.15s ease;
}
.ft__group-menu a:hover { color: #fff; }

/* ── Bottom bar (copyright + legales en horizontal) ── */
.ft__bottom {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 16px 24px;
	padding-top: 24px;
	border-top: 1px solid rgba(255, 255, 255, 0.08);
	font-size: 13px;
}
.ft__copyright {
	margin: 0;
	color: #888;
	flex: 1 1 auto;
}
.ft__legal-menu {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 24px;
}
.ft__legal-menu li { margin: 0; }
.ft__legal-menu a {
	color: #888;
	text-decoration: none;
	font-size: 13px;
	transition: color 0.15s ease;
}
.ft__legal-menu a:hover { color: #fff; }

/* Responsive */
@media (max-width: 1100px) {
	.ft__cols { grid-template-columns: repeat(3, 1fr); gap: 36px; }
}
@media (max-width: 800px) {
	.ft__cols { grid-template-columns: repeat(2, 1fr); gap: 32px; }
}
/* 1.10.54 — Móvil: ocultar las columnas anteriores a la última
   (Especiales, Categorías, Firmas, Regiones…) y dejar visible solo
   la última (Aviso legal + Manifiesto + …). El bottom-bar con
   copyright + menú legal sigue debajo como cierre del footer.
   El selector `:not(:last-child)` apunta automáticamente a la última
   columna renderizada — si alguna está vacía y el PHP la salta, sigue
   funcionando bien sin reescribir el CSS. */
@media (max-width: 600px) {
	.ft__cols { grid-template-columns: 1fr; gap: 0; }
	.ft__group:not(:last-child) { display: none; }
	.ft__inner { padding: 28px var(--pad) 20px; }
	.ft__utility {
		flex-direction: column;
		align-items: flex-start;
		gap: 16px;
		padding-bottom: 20px;
	}
	.ft__utility-menu { gap: 16px 20px; }
	.ft__bottom { padding-top: 20px; gap: 12px 18px; }
	.ft__legal-menu { gap: 12px 18px; }
	.ft__copyright { font-size: 12px; }
	.ft__legal-menu a { font-size: 12px; }
}
