/* =========================================================================
   ESTAVILLO — REstimator Design System: capa propia
   -------------------------------------------------------------------------
   ÚNICO archivo de este directorio escrito a mano: tokens.css y doc.css los
   genera tools/build-ds.mjs y se sobrescriben en cada build.

   Acá va sólo lo que la integración necesita y el Design System no trae:
     1. blindaje contra el CSS global de Kadence y de .es-page dentro de .re-doc;
     2. la barra mínima de salida (fallback cuando el header está apagado);
     3. el estilo de las previews de pantalla (que dejaron de ser iframes);
     4. los arreglos de overflow horizontal en mobile;
     5. el offset del rail sticky cuando el header institucional está activo;
     6. el ensanchado del contenedor en pantallas grandes;
     7. el bloque "System evolution" que cierra el documento.

   NO se rediseña nada del Design System: no se tocan colores, tipografía,
   espaciado, jerarquía ni el rail.
   ========================================================================= */

/* -------------------------------------------------------------------------
   0. Página
   ------------------------------------------------------------------------- */

/* scroll-behavior vive en el elemento que scrollea de verdad (<html>), no en
   el wrapper. Es lo que hace que los anchors del rail se deslicen en vez de
   saltar. Mismo criterio de :has() ya usado en pages-home.css. */
html:has(> body.es-ds-page) {
	scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
	html:has(> body.es-ds-page) {
		scroll-behavior: auto;
	}
}

/* -------------------------------------------------------------------------
   GEOMETRÍA DE LA PÁGINA
   -------------------------------------------------------------------------
   Un solo juego de números para las CUATRO franjas de esta página: el header
   institucional, la navegación mínima, el documento y el footer. Antes cada
   una traía su propia grilla —el chrome del portfolio centrado a 1140px, el
   documento pegado a la izquierda con un rail de 264px— y la página se leía
   como dos sitios pegados: la marca ESTAVILLO arrancaba en un sitio y el rail
   de REstimator en otro, sin ninguna alineación en común.

   Los valores NO son nuevos: son los que el Design System ya usa.
     --es-ds-rail      264px  = .doc { grid-template-columns: 264px … }
     --es-ds-rail-pad   22px  = .rail (12px) + .rail-brand (10px), o sea la x
                                donde empieza el texto de la marca del rail
     --es-ds-gutter     40px  = --re-s8, el padding lateral de .pad y de .hero
     --es-ds-content          = el max-width de .pad

   Con esto, la marca del header cae exactamente sobre la marca del rail, y la
   navegación y el footer terminan exactamente donde termina el contenido. */

body.es-ds-page {
	margin: 0;
	background: var(--re-bg);

	--es-ds-rail: 264px;
	--es-ds-rail-pad: 22px;
	--es-ds-gutter: 40px;
	--es-ds-content: 1180px;
	--es-ds-topbar-h: 48px;
}

/* Aprovechamiento de pantallas grandes.
   Medido antes de tocar nada: `.pad{max-width:1180px}` es un tope fijo y sin
   margin:auto, así que a 1600px quedaban 156px muertos a la derecha y a 1920px
   quedaban 476px, con todo el documento pegado a la izquierda.

   La legibilidad del texto NO depende de este contenedor: el propio Design
   System ya acota la prosa por su cuenta (`p.body{max-width:78ch}`,
   `.lede{max-width:76ch}`), independientemente de lo ancho que sea el padre.
   Por eso alcanza con ensanchar el contenedor: los párrafos siguen igual de
   angostos y son las grillas, las tarjetas, las tablas, las demos de
   componentes y las Screen Examples —todas fluidas, sin tope propio— las que
   se llevan el ancho extra.

   La fórmula es fluida y no por saltos, y está construida para no poder
   empeorar nada:
     max()  — nunca por debajo del ancho de hoy, así ningún viewport queda más
              angosto que antes;
     calc() — reserva el rail (264px) y deja ~100px de margen a la derecha;
     min()  — tope duro, para que en un monitor muy ancho las tablas y las
              grillas no se estiren sin límite.

   Vive en la variable y no en `.pad`, así el header, el footer y la barra
   mínima se ensanchan con el documento en vez de quedarse atrás. */
@media (min-width: 1500px) {
	body.es-ds-page {
		--es-ds-content: max(1180px, min(1720px, calc(100vw - 364px)));
	}
}

.re-doc .pad {
	max-width: var(--es-ds-content);
}

/* El hero no tiene el padding de .pad, así que su contenido se acota al mismo
   ancho MENOS ese padding: los dos terminan alineados al mismo ancho de texto. */
.re-doc .hero .in {
	max-width: calc(var(--es-ds-content) - 2 * var(--es-ds-gutter));
}

/* -------------------------------------------------------------------------
   1. Blindaje contra Kadence y contra .es-page
   -------------------------------------------------------------------------
   Esta página imprime wp_head(), así que el CSS global del tema padre se
   carga igual aunque no se use ninguno de sus componentes. Kadence estila
   elementos genéricos por nombre —y en particular pinta <button> en
   :hover/:focus con color propio: está documentado en vivo en
   case-figure-lightbox.css, donde un botón redondo se ponía azul—. El
   Design System usa botones, inputs, tablas y headings por todos lados, así
   que el riesgo es real y no teórico.

   :where() mantiene la especificidad en 0, de modo que cualquier regla real
   del DS (que siempre tiene al menos una clase) le gana sin necesidad de
   !important. Los !important quedan sólo donde hay que ganarle a una regla
   de Kadence con especificidad propia — mismo criterio ya usado en el
   lightbox.

   AHORA HAY UN SEGUNDO FRENTE. Desde que el header/footer institucionales son
   opcionales en esta página, el documento vive dentro de <div class="es-page">
   — que base.css documenta como ancestro necesario del header sticky, y donde
   están los resets que el chrome del portfolio necesita. Eso abre una vía de
   contaminación que antes no existía, y contra la que :where() NO sirve:

     .es-page h1, .es-page h2, .es-page h3 { color: var(--es-ink); }

   tiene especificidad (0,1,1); `.re-doc :where(h2)` tiene (0,1,0), o sea que
   PIERDE. Los <h2> de sección del DS no declaran color propio (su regla
   `.re-doc .sec-h h2` sólo fija tamaño y margen), así que habrían pasado de
   --re-ink (#f2f3f4, blanco frío) a --es-ink (#ebe7df, crema): medido, no
   supuesto.

   Por eso el bloque de abajo repite esos dos resets con selectores REALES, a
   especificidad (0,1,1) — exactamente la misma que .es-page. El desempate lo
   decide el orden de carga, y ese orden está garantizado por las dependencias
   declaradas en inc/enqueue.php: es-ds-overrides → es-ds-doc → es-ds-tokens →
   es-components → … → es-base. doc-overrides.css siempre llega último.

   NO se sube más la especificidad a propósito: con (0,2,1) —p. ej.
   `.re-doc.re-root h1`— este reset empataría con las reglas propias del Design
   System que sí fijan color, como `.re-doc .hero h1 { color:#fff }`, y al
   cargar después se las comería. (0,1,1) le gana a .es-page y pierde contra
   cualquier regla del DS con dos clases, que es justo lo que se busca. */

.re-doc h1,
.re-doc h2,
.re-doc h3,
.re-doc h4,
.re-doc h5,
.re-doc h6 {
	color: inherit;
}

/* El ::selection propio del sistema sólo declara background; sin esta línea,
   el color del texto seleccionado lo seguiría poniendo .es-page ::selection. */
.re-doc ::selection {
	color: inherit;
}

.re-doc :where(h1, h2, h3, h4, h5, h6, p, ul, ol, li, dl, dt, dd, figure, figcaption, blockquote) {
	margin: 0;
	padding: 0;
	font: inherit;
	color: inherit;
	letter-spacing: inherit;
	text-transform: none;
}

.re-doc :where(ul, ol) {
	list-style: none;
}

.re-doc :where(table) {
	border-collapse: collapse;
	border-spacing: 0;
}

.re-doc :where(th, td) {
	text-align: left;
	vertical-align: top;
}

.re-doc :where(a) {
	box-shadow: none;
	text-decoration: none;
}

/* Botones: el DS los usa como superficies de demo y como trigger del visor.
   Kadence les impone relleno, borde, sombra y color de foco propios. */
.re-doc :where(button) {
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	font: inherit;
	color: inherit;
	text-transform: none;
	appearance: none;
}

.re-doc button:hover,
.re-doc button:focus,
.re-doc button:focus-visible {
	box-shadow: none !important;
}

.re-doc :where(input, select, textarea) {
	font: inherit;
	color: inherit;
}

/* El tema padre puede fijar un ancho máximo o un padding a los contenedores
   de contenido; el documento del DS maneja su propio layout de punta a punta. */
.re-doc {
	max-width: none;
	margin: 0;
	padding: 0;
}

/* -------------------------------------------------------------------------
   2. Navegación mínima (fallback sin header institucional)
   -------------------------------------------------------------------------
   Se imprime SÓLO cuando el header institucional está apagado para esta
   página. Es la única salida hacia afuera del documento, así que:

     · es sticky, como el header real. Antes no lo era —el argumento era que
       el rail ya navega— pero el rail navega DENTRO del documento: si esta
       barra se va con el scroll, a mitad del documento no queda ninguna
       manera de volver al caso. Al quedarse fija también deja de aparecer y
       desaparecer, que es lo que la hacía leer como una barra de debug;
     · usa tokens --re-*: pertenece a esta página, no al chrome del portfolio,
       y con los --es-* quedaba de un color que no era ni el del sitio ni el
       del documento;
     · se alinea a la grilla del documento (§ GEOMETRÍA), igual que el header
       institucional: el "volver" cae sobre la marca del rail y la atribución
       sobre el borde derecho del contenido;
     · 48px de alto contra los 66px del header institucional. Es deliberado:
       es la barra secundaria, y el rail arranca justo abajo (§5). */

.es-ds-topbar {
	position: sticky;
	top: 0;
	z-index: 80;
	height: var(--es-ds-topbar-h);
	background: color-mix(in srgb, var(--re-surface) 92%, transparent);
	-webkit-backdrop-filter: blur(12px);
	backdrop-filter: blur(12px);
	border-bottom: 1px solid var(--re-line);
	font-family: var(--re-font-sans, system-ui, sans-serif);
}

/* Sin color-mix (navegadores viejos) el fondo tiene que ser opaco igual, o la
   barra deja ver el documento pasando por debajo. */
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
	.es-ds-topbar {
		background: var(--re-surface);
	}
}

.admin-bar .es-ds-topbar {
	top: 32px;
}

@media (max-width: 782px) {
	.admin-bar .es-ds-topbar {
		top: 46px;
	}
}

.es-ds-topbar__in {
	position: relative;
	display: grid;
	grid-template-columns: var(--es-ds-rail) minmax(0, 1fr);
	align-items: center;
	height: 100%;
	gap: 0;
}

/* Misma continuación del borde del rail que en el header institucional: las
   dos barras tienen que leerse como la misma grilla que el documento. */
@media (min-width: 1101px) {
	.es-ds-topbar__in::before {
		content: "";
		position: absolute;
		left: calc(var(--es-ds-rail) - 1px);
		top: 0;
		bottom: -1px;
		width: 1px;
		background: var(--re-line);
		pointer-events: none;
	}
}

.es-ds-topbar a {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	font-size: 12.5px;
	line-height: 1.2;
	letter-spacing: 0.01em;
	text-decoration: none;
	transition: color 160ms ease;
}

.es-ds-topbar__back {
	padding-left: var(--es-ds-rail-pad);
	color: var(--re-ink-2);
	/* min-width:0 es obligatorio: sin él un ítem no baja del ancho de su
	   contenido y el text-overflow nunca llega a activarse. */
	min-width: 0;
}

.es-ds-topbar__back-text {
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* :focus PLANO además de :focus-visible: sin él, un click con mouse dejaba el
   link en el `a:focus` del tema padre, que lo pinta de azul. Mismo criterio
   que .es-lightbox__close en case-figure-lightbox.css. */
.es-ds-topbar__back:hover,
.es-ds-topbar__back:focus,
.es-ds-topbar__back:focus-visible,
.es-ds-topbar__back:active {
	color: var(--re-ink);
}

.es-ds-topbar__arrow {
	font-size: 14px;
	line-height: 1;
	flex: none;
	color: var(--re-amber-ink);
}

/* La atribución cierra sobre el borde derecho del contenido, igual que la
   navegación del header institucional. */
/* La caja se alinea al INICIO de la columna y se acota igual que `.pad`; el
   texto se empuja a su borde derecho desde adentro. Con `justify-self:end` la
   caja se pegaba al borde del viewport y la atribución terminaba 100px más a
   la derecha que el contenido del documento. */
.es-ds-topbar__owner {
	justify-self: start;
	width: 100%;
	max-width: var(--es-ds-content);
	box-sizing: border-box;
	padding-right: var(--es-ds-gutter);
	justify-content: flex-end;
	color: var(--re-ink-4);
	font-size: 12px;
	flex: none;
}

.es-ds-topbar__owner:hover,
.es-ds-topbar__owner:focus,
.es-ds-topbar__owner:focus-visible,
.es-ds-topbar__owner:active {
	color: var(--re-ink-2);
}

/* El anillo lo termina imponiendo `.es-page a:focus-visible` de base.css, que
   lleva !important y la misma especificidad: esta regla nunca llegaba a
   pintarse en ámbar. Se la deja declarada —el offset y el radio sí son
   propios— pero con el token que realmente se ve. */
.es-ds-topbar a:focus-visible {
	outline: 2px solid var(--es-accent);
	outline-offset: 3px;
	border-radius: 3px;
}

/* Sin rail no hay columna que respetar: la barra vuelve a ser dos extremos. */
@media (max-width: 1100px) {
	.es-ds-topbar__in {
		display: flex;
		justify-content: space-between;
		gap: 16px;
	}

	.es-ds-topbar__back {
		padding-left: var(--re-s5);
	}

	.es-ds-topbar__owner {
		width: auto;
		padding-right: var(--re-s5);
	}
}

@media (max-width: 640px) {
	.es-ds-topbar__back {
		padding-left: var(--re-s4);
	}

	.es-ds-topbar__owner {
		padding-right: var(--re-s4);
	}
}

/* -------------------------------------------------------------------------
   2b. Header y footer institucionales: variante full-width de esta página
   -------------------------------------------------------------------------
   NO es un header nuevo ni otra marca: es EL header del portfolio, el mismo
   template-part que imprime el resto del sitio, con la geometría adaptada a
   esta página. Sólo cambian ancho y alineación; tipografía, color, altura,
   comportamiento sticky, menú mobile y switch de idioma quedan intactos.

   Qué se corrige. `.es-nav` es un `.es-container`: max-width 1140px, centrado,
   con padding lateral clamp(20px,4.5vw,48px). El documento, en cambio, arranca
   pegado a la izquierda con un rail de 264px. Medido a 1920px: la marca
   ESTAVILLO empezaba en x=438 y la marca del rail en x=22 — 416px de desfase
   entre dos elementos que el ojo lee como la misma columna. La navegación
   terminaba en x=1482 y el contenido del documento en x=1780.

   Acá el header pasa a la MISMA grilla del documento: [rail][área principal].
   La marca cae en la columna del rail, alineada con la marca del rail; la
   navegación cierra sobre el borde derecho del contenido. La franja sigue
   ocupando el 100% del viewport, como siempre.

   Se aplica sólo arriba de 1100px, que es donde el Design System mantiene el
   rail; por debajo el rail pasa a bloque estático y el header vuelve a su
   contenedor normal (con el mismo padding que toma el documento ahí, para que
   tampoco en tablet queden dos márgenes distintos). */

@media (min-width: 1101px) {
	body.es-ds-page .es-site-header .es-nav {
		position: relative;
		display: grid;
		grid-template-columns: var(--es-ds-rail) minmax(0, 1fr);
		align-items: center;
		gap: 0;
		max-width: none;
		padding-left: 0;
		padding-right: 0;
	}

	/* Continuación exacta del borde derecho del rail a través del header: es
	   lo que hace que las dos franjas se lean como una sola grilla y no como
	   dos barras superpuestas. -1px porque el borde del rail es interior
	   (box-sizing:border-box), o sea que ocupa 263→264. */
	body.es-ds-page .es-site-header .es-nav::before {
		content: "";
		position: absolute;
		left: calc(var(--es-ds-rail) - 1px);
		top: 0;
		bottom: -1px;
		width: 1px;
		background: var(--re-line);
		pointer-events: none;
	}

	body.es-ds-page .es-site-header .es-nav__brand {
		padding-left: var(--es-ds-rail-pad);
	}

	/* Mismo cálculo de caja que `.pad`: ancho tope + gutter. Así el borde
	   derecho de la navegación y el del contenido del documento son el mismo,
	   en cualquier viewport y con la fórmula fluida activa o no. */
	body.es-ds-page .es-site-header .es-nav__right {
		width: 100%;
		max-width: var(--es-ds-content);
		box-sizing: border-box;
		padding-left: var(--es-ds-gutter);
		padding-right: var(--es-ds-gutter);
		justify-content: flex-end;
	}

	/* El footer va después del documento, cuando el rail ya terminó: sus filas
	   se alinean con la columna de contenido —misma caja que `.pad`— y la
	   franja sigue siendo full-width. Mismo criterio que el header: una sola
	   geometría de punta a punta de la página. */
	body.es-ds-page .es-site-footer__inner {
		max-width: none;
		padding-left: 0;
		padding-right: 0;
	}

	body.es-ds-page .es-site-footer__inner > * {
		max-width: var(--es-ds-content);
		margin-left: var(--es-ds-rail);
		padding-left: var(--es-ds-gutter);
		padding-right: var(--es-ds-gutter);
		box-sizing: border-box;
	}
}

/* Tablet y abajo: sin rail, pero el chrome sigue tomando el mismo margen
   lateral que el documento (--re-s5 = 20px) en vez del clamp del portfolio,
   que ahí llega a 46px. */
@media (max-width: 1100px) {
	body.es-ds-page .es-site-header .es-nav,
	body.es-ds-page .es-site-footer__inner {
		max-width: none;
		padding-left: var(--re-s5);
		padding-right: var(--re-s5);
	}
}

/* -------------------------------------------------------------------------
   2c. Breadcrumb contextual (sólo con el header institucional activo)
   -------------------------------------------------------------------------
   Es el breadcrumb INSTITUCIONAL: el mismo template-part y las mismas clases
   que usan el Case Study y las páginas fijas, así que el color, el separador,
   el hover y el truncado del último nivel ya vienen de site.css —que en esta
   página se carga junto con el header— y acá no se define ningún componente
   nuevo.

   Lo único propio es la geometría. El partial envuelve la lista en un
   `.es-container` (max-width 1140px, centrado), que dentro del documento
   dejaría el breadcrumb desalineado del contenido. Se neutraliza y se le da el
   mismo padding lateral que `.hero .in`, que es su vecino inmediato: los dos
   viven en la columna principal, así que "Proyectos" arranca exactamente donde
   arranca el eyebrow del hero.

   La tipografía la fuerza el documento (`.re-doc` fija font-family en la
   raíz), así que se devuelve la del portfolio: el breadcrumb pertenece al
   chrome, no al Design System. */

.re-doc .es-breadcrumbs {
	font-family: var(--es-sans, system-ui, sans-serif);
	padding: var(--re-s6) var(--es-ds-gutter) 0;
}

.re-doc .es-breadcrumbs .es-container {
	max-width: none;
	padding-left: 0;
	padding-right: 0;
}

@media (max-width: 1100px) {
	/* Sin rail, el documento toma --re-s5 de margen lateral. */
	.re-doc .es-breadcrumbs {
		padding-left: var(--re-s5);
		padding-right: var(--re-s5);
	}
}

/* Si faltara el partial del documento (theme instalado sin ds/). */
.es-ds-missing {
	padding: 64px 24px;
	color: var(--re-ink-3, #8b857e);
	font-family: var(--re-font-sans, system-ui, sans-serif);
	text-align: center;
}

/* -------------------------------------------------------------------------
   3. Previews de pantalla (§08 Screen Examples)
   -------------------------------------------------------------------------
   Las tarjetas dejaron de ser <div> con un <iframe> escalado y pasaron a ser
   <figure> con un <button> que abre el visor compartido del portfolio.

   TODA la tarjeta es el botón, cabecera incluida. Antes la cabecera (nombre ·
   archivo · "Ampliar") era HERMANA del botón, y como el visor se dispara por
   delegación —`e.target.closest('[data-es-zoom-trigger]')`— un click sobre la
   palabra "Ampliar" no encontraba trigger y no abría nada. Con la cabecera
   adentro hay un único elemento interactivo, nativo, que cubre la tarjeta
   entera; el teclado no cambia porque sigue siendo el mismo <button>.

   La caja y el borde los sigue poniendo el CSS del Design System (.shot,
   .shot .cap); acá sólo va lo que el markup nuevo necesita. */

.re-doc .shot .cap .zoom-hint {
	font-size: 11.5px;
	font-weight: 600;
	color: var(--re-amber-ink);
}

.re-doc button.vp:hover .zoom-hint {
	color: var(--re-amber-bright);
}

/* .vp era un contenedor; ahora es el botón que dispara el visor. */
.re-doc button.vp {
	display: block;
	width: 100%;
	text-align: left;
	cursor: zoom-in;
}

/* El recorte vive acá y no en el botón: la cabecera pasó a estar dentro del
   botón, así que un max-height en el botón le descontaría alto a la imagen.
   Las capturas son full-page y muy altas — la tarjeta muestra la franja
   superior, igual que hacía el iframe recortado, y el visor muestra la
   pantalla entera. */
.re-doc .vp-media {
	position: relative;
	display: block;
	max-height: 352px;
	overflow: hidden;
}

.re-doc .vp-media img {
	display: block;
	width: 100%;
	height: auto;
}

.re-doc .shot--mobile .vp-media {
	/* Las pantallas de mobile son verticales y entran completas. */
	max-height: none;
}

/* Afordancia de "ampliar": aparece en hover/foco, nunca tapa la captura. */
.re-doc .vp-expand {
	position: absolute;
	right: 10px;
	bottom: 10px;
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	border: 1px solid var(--re-line-2);
	border-radius: 999px;
	background: rgba(13, 14, 17, 0.82);
	color: var(--re-ink-2);
	opacity: 0;
	transform: translateY(4px);
	transition: opacity 160ms ease, transform 160ms ease, border-color 160ms ease, color 160ms ease;
	pointer-events: none;
}

.re-doc .vp-expand svg {
	width: 15px;
	height: 15px;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.7;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.re-doc button.vp:hover .vp-expand,
.re-doc button.vp:focus-visible .vp-expand {
	opacity: 1;
	transform: translateY(0);
	border-color: var(--re-amber);
	color: var(--re-amber);
}

/* El anillo de foco tiene que dibujarse HACIA ADENTRO.
   assets/css/base.css impone, para todo el sitio y con !important,
   `outline: 2px solid var(--es-accent) !important; outline-offset: 3px
   !important` sobre :focus-visible (es su red anti-Kadence, ver el comentario
   ahí). Con offset positivo el anillo cae fuera del botón y lo recorta
   `.shot{overflow:hidden}` del propio Design System: el foco de teclado
   quedaba invisible. Especificidad (0,3,1) contra (0,1,1) de la regla base,
   así que con !important de los dos lados gana esta. */
.re-doc button.vp:focus-visible {
	outline: 2px solid var(--es-accent) !important;
	outline-offset: -3px !important;
}

@media (prefers-reduced-motion: reduce) {
	.re-doc .vp-expand {
		transition: none;
		transform: none;
	}
}

/* La grilla de mobile muestra tres teléfonos: caben tres columnas. */
.re-doc .shots--mobile {
	grid-template-columns: repeat(3, 1fr);
}

/* Inventario de artefactos (ex-tabla de enlaces a .html locales). */
.re-doc .art-name {
	font-weight: 600;
	color: var(--re-ink-2);
}

.re-doc .art-kind {
	display: inline-block;
	padding: 2px 8px;
	border: 1px solid var(--re-line-2);
	border-radius: 999px;
	background: var(--re-surface-2);
	font-family: var(--re-font-mono);
	font-size: 10.5px;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--re-ink-3);
	white-space: nowrap;
}

/* -------------------------------------------------------------------------
   3b. Visor de pantallas
   -------------------------------------------------------------------------
   La contraparte visual de assets/js/ds-screen-viewer.js: una ventana de
   aplicación —barra de título arriba, lienzo scrolleable abajo— y no un
   lightbox de foto. El JS calcula los anchos; acá va la superficie.

   Todo con tokens --re-*: la ventana tiene que parecer parte del producto que
   está mostrando. */

.esv {
	width: 100%;
	max-width: 100%;
	max-height: 100%;
	height: 100%;
	margin: 0;
	padding: 0;
	border: 0;
	background: transparent;
	overflow: hidden;
}

.esv::backdrop {
	background: rgba(6, 7, 9, 0.88);
	-webkit-backdrop-filter: blur(3px);
	backdrop-filter: blur(3px);
}

/* La ventana se centra sola; el JS le fija el ancho. */
.esv__win {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	display: flex;
	flex-direction: column;
	max-width: calc(100% - 32px);
	max-height: calc(100% - 32px);
	border: 1px solid var(--re-line-2);
	border-radius: var(--re-r-lg);
	background: var(--re-surface);
	box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55);
	overflow: hidden;
}

.esv__bar {
	display: flex;
	align-items: center;
	gap: var(--re-s4);
	flex: none;
	padding: 0 var(--re-s3) 0 var(--re-s5);
	height: 46px;
	border-bottom: 1px solid var(--re-line);
	background: var(--re-surface-2);
	font-family: var(--re-font-sans, system-ui, sans-serif);
}

.esv__id {
	display: flex;
	align-items: baseline;
	gap: var(--re-s3);
	min-width: 0;
}

/* En una ventana angosta (una pantalla de mobile son 390px) el que cede es el
   metadato, no el nombre: el nombre es lo que identifica la pantalla. Con los
   dos al mismo flex-shrink, el título se recortaba a "Cal…" mientras el
   metadato conservaba su ancho. */
.esv__title {
	flex: 0 1 auto;
	min-width: 0;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--re-ink);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.esv__meta {
	flex: 0 50 auto;
	min-width: 0;
	font-family: var(--re-font-mono);
	font-size: 11px;
	color: var(--re-ink-4);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.esv__tools {
	display: flex;
	align-items: center;
	gap: 2px;
	margin-left: auto;
	flex: none;
}

.esv__level {
	min-width: 46px;
	text-align: center;
	font-family: var(--re-font-mono);
	font-variant-numeric: tabular-nums;
	font-size: 11px;
	color: var(--re-ink-3);
}

.esv__btn {
	display: grid;
	place-items: center;
	width: 30px;
	height: 30px;
	border: 0;
	border-radius: var(--re-r-sm);
	background: none;
	color: var(--re-ink-3);
	cursor: pointer;
	transition: background 140ms ease, color 140ms ease;
}

.esv__btn svg {
	width: 17px;
	height: 17px;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.7;
	stroke-linecap: round;
}

.esv__btn:hover:not(:disabled) {
	background: var(--re-surface);
	color: var(--re-ink);
}

.esv__btn:disabled {
	opacity: 0.35;
	cursor: default;
}

.esv__btn--close {
	margin-left: var(--re-s3);
}

.esv__btn:focus-visible {
	outline: 2px solid var(--es-accent) !important;
	outline-offset: -2px !important;
}

/* El lienzo: acá vive el scroll de la pantalla. */
.esv__scroll {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	overflow-x: hidden;
	background: var(--re-canvas);
	overscroll-behavior: contain;
}

/* Sólo con zoom por encima del ancho disponible. Al abrir nunca hay scroll
   horizontal: el JS arranca en min(ancho real, ancho disponible). */
.esv__scroll.is-wide {
	overflow-x: auto;
}

.esv__scroll:focus-visible {
	outline: 2px solid var(--es-accent) !important;
	outline-offset: -2px !important;
}

/* height:auto es lo que impide la deformación: el ancho lo fija el JS y el
   alto lo deduce el navegador del aspect-ratio real de la captura. */
.esv__img {
	display: block;
	width: 100%;
	height: auto;
	max-width: none;
}

@media (max-width: 640px) {
	.esv__bar {
		padding-left: var(--re-s4);
		height: 42px;
	}

	.esv__meta {
		display: none;
	}
}

/* -------------------------------------------------------------------------
   4. Overflow horizontal en mobile
   -------------------------------------------------------------------------
   Defecto real del documento original, medido antes de tocar nada: a 390px
   el scrollWidth daba 632px contra un clientWidth de 390. Las causas NO eran
   las previews (esas ya estaban contenidas) sino:

     a) cadenas largas sin corte en <span class="mono"> de §02 Components
        (nombres de export del namespace) — una llegaba a 564px de ancho
        dentro de una columna de 350px;
     b) las tarjetas de §10 Design → Implementation, que no encogían.

   Se arregla en el elemento que desborda, NUNCA con overflow-x:hidden en un
   ancestro: el rail del DS es position:sticky y este repo ya tiene
   documentado que un overflow en un ancestro rompe el sticky (misma razón
   por la que .es-page nunca lleva overflow-x). */

.re-doc .mono {
	overflow-wrap: anywhere;
}

/* Las celdas de tabla ya viven dentro de .scrollx y deben poder ser anchas:
   ahí el corte agresivo estorbaría la lectura de la tabla. */
.re-doc .scrollx .mono {
	overflow-wrap: normal;
}

@media (max-width: 1100px) {
	/* Ítems de grilla/flex: sin min-width:0 se niegan a bajar del ancho de su
	   contenido y empujan la página. */
	.re-doc .card,
	.re-doc .card-b,
	.re-doc .stage,
	.re-doc .lk,
	.re-doc .prin > *,
	.re-doc .chain > * {
		min-width: 0;
	}

	.re-doc .shots--mobile {
		grid-template-columns: 1fr 1fr;
	}
}

@media (max-width: 640px) {
	.re-doc .shots--mobile {
		grid-template-columns: 1fr;
	}

	/* El rail pasa a bloque estático arriba del contenido (regla del propio
	   DS). Con 12 ítems ocupa demasiado: se compacta en dos columnas para no
	   empujar el documento entero fuera de la primera pantalla. */
	.re-doc .rail {
		display: grid;
		grid-template-columns: 1fr 1fr;
		align-items: start;
	}

	.re-doc .rail-brand,
	.re-doc .rail-grp {
		grid-column: 1 / -1;
	}
}

/* -------------------------------------------------------------------------
   5. Rail persistente con el header institucional
   -------------------------------------------------------------------------
   El rail del Design System es `position:sticky; top:0; height:100vh`. Con el
   header institucional activo —que es sticky y ocupa la franja superior— el
   rail arrancaba tapado por él, y su alto de 100vh se pasaba del viewport por
   exactamente la altura del header: la metadata del pie del rail (.rail-foot,
   anclada con margin-top:auto) quedaba empujada fuera de la vista.

   El offset se resuelve con los MISMOS valores que ya usa .es-case-index en
   case-study.css, que tiene este problema resuelto desde antes: 66px de header
   (.es-nav), 98px con la admin bar de escritorio (66+32) y 112px con la de
   mobile (66+46). Se mantiene ese criterio en vez de introducir una variable
   nueva, para que haya un solo número que actualizar si el header cambia.

   Todo va gateado por .es-header-sticky (clase que ya imprime el propio tema
   vía body_class): con el header estático o apagado, el rail vuelve a 100vh
   sin ninguna regla extra. */

.es-header-sticky .re-doc .rail {
	top: 66px;
	height: calc(100vh - 66px);
}

.admin-bar.es-header-sticky .re-doc .rail {
	top: 98px;
	height: calc(100vh - 98px);
}

@media (max-width: 782px) {
	.admin-bar.es-header-sticky .re-doc .rail {
		top: 112px;
		height: calc(100vh - 112px);
	}
}

/* Mismo problema con la navegación mínima, que ahora también es sticky: sin
   esto el rail arrancaba tapado por ella y su pie se iba 48px fuera de la
   vista. La clase la imprime el template según qué barra se haya renderizado
   (ver templates/page-restimator-ds.php). */
.es-ds-minimal-nav .re-doc .rail {
	top: var(--es-ds-topbar-h);
	height: calc(100vh - var(--es-ds-topbar-h));
}

.admin-bar.es-ds-minimal-nav .re-doc .rail {
	top: calc(var(--es-ds-topbar-h) + 32px);
	height: calc(100vh - var(--es-ds-topbar-h) - 32px);
}

@media (max-width: 782px) {
	.admin-bar.es-ds-minimal-nav .re-doc .rail {
		top: calc(var(--es-ds-topbar-h) + 46px);
		height: calc(100vh - var(--es-ds-topbar-h) - 46px);
	}
}

/* Los anchors del rail tienen que caer por debajo de la barra, no atrás. */
.es-header-sticky .re-doc .sec {
	scroll-margin-top: 78px;
}

.admin-bar.es-header-sticky .re-doc .sec {
	scroll-margin-top: 110px;
}

.es-ds-minimal-nav .re-doc .sec {
	scroll-margin-top: calc(var(--es-ds-topbar-h) + 12px);
}

.admin-bar.es-ds-minimal-nav .re-doc .sec {
	scroll-margin-top: calc(var(--es-ds-topbar-h) + 44px);
}

/* La metadata del pie del rail se queda abajo SIEMPRE.
   `margin-top:auto` ya la manda al fondo cuando sobra alto, pero el rail es un
   scroller (`overflow-y:auto`) y en cuanto la lista de secciones no entra
   —viewport bajo, zoom de navegador, la barra sticky comiéndose 48 o 66px— el
   pie se iba con el scroll y cambiaba de posición mientras se lee el
   documento. Con `position:sticky` queda anclado al borde inferior del
   scroller pase lo que pase; el fondo opaco evita que el texto del rail se
   lea por debajo. */
.re-doc .rail-foot {
	position: sticky;
	bottom: 0;
	background: var(--re-surface);
}

/* Bajo 1100px el rail deja de ser sticky (regla del propio Design System) y
   pasa a ser un bloque arriba del contenido: ahí no compite con el header y
   no debe llevar ni offset ni alto calculado. */
@media (max-width: 1100px) {
	.es-header-sticky .re-doc .rail,
	.admin-bar.es-header-sticky .re-doc .rail {
		top: auto;
		height: auto;
	}
}

/* -------------------------------------------------------------------------
   6. Hero
   -------------------------------------------------------------------------
   El hero pasa a dos columnas: la izquierda es el texto de siempre y la
   derecha una composición con material real del sistema. Debajo, a ancho
   completo, las cuatro métricas como tarjetas.

   Qué se corrige. Medido a 1920px con el contenedor ya ensanchado a 1556:
   todo el peso caía en los primeros ~700px y quedaban ~800 vacíos a la
   derecha. Las métricas eran cuatro columnas separadas por hairlines, que a
   ese ancho se leían como una tabla a medio terminar.

   La composición NO es una imagen: son las clases reales de los especímenes
   del documento (.sw, .b, .st, .d-*) más dos capturas ya generadas. El texto
   sigue siendo texto —nítido a cualquier DPI y traducido por el mismo
   diccionario— y el único peso extra son esas dos previews. Ver
   heroComposition() en tools/build-ds.mjs. */

.re-doc .hero {
	position: relative;
	overflow: hidden;
}

/* Resplandor cálido detrás de la composición. Un solo radial-gradient, sin
   imagen: es lo que da profundidad a la esquina sin sumar una request. */
.re-doc .hero::before {
	content: "";
	position: absolute;
	top: -30%;
	right: -10%;
	width: 70%;
	height: 150%;
	background: radial-gradient(closest-side, rgba(224, 145, 30, 0.13), rgba(224, 145, 30, 0) 70%);
	pointer-events: none;
}

.re-doc .hero .in {
	position: relative;
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	grid-template-areas: "copy" "facts";
	gap: var(--re-s8) 0;
}

.re-doc .hero .hero-copy {
	grid-area: copy;
	min-width: 0;
}

.re-doc .hero .hero-art {
	grid-area: art;
	display: none;
}

.re-doc .hero .facts {
	grid-area: facts;
}

/* El degradé del H1 lo dibuja el texto, no una capa: color de respaldo
   primero, así un navegador sin background-clip:text sigue viendo blanco. */
.re-doc .hero h1 {
	max-width: 16ch;
	color: #fff;
	background: linear-gradient(102deg, #fff 38%, #9aa0aa 100%);
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
}

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
	.re-doc .hero h1 {
		background: none;
		-webkit-text-fill-color: #fff;
	}
}

/* ---------- las cuatro métricas, como tarjetas ---------- */

.re-doc .hero .facts {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--re-s4);
	max-width: none;
	margin-top: 0;
	border-top: 0;
}

/* `.re-doc .hero .facts div{border-top:1px}` del Design System matchea
   CUALQUIER div descendiente, no sólo la columna: dentro de una tarjeta eso
   volvía a dibujar una línea sobre el label, sobre el valor y sobre el pie —
   justo la "tabla a medio terminar" que esta pasada viene a sacar. Se apagan
   todas y la tarjeta pone su propio borde. */
.re-doc .hero .facts div {
	border-top: 0;
	padding-top: 0;
}

.re-doc .hero .facts .fact {
	border: 1px solid var(--re-charcoal-line);
	border-radius: var(--re-r-lg);
	background: rgba(255, 255, 255, 0.022);
	padding: var(--re-s5);
}

.re-doc .hero .facts .fact-i {
	display: grid;
	place-items: center;
	width: 40px;
	height: 40px;
	margin-bottom: var(--re-s4);
	border: 1px solid var(--re-amber-line);
	border-radius: var(--re-r);
	background: var(--re-amber-soft);
	color: var(--re-amber-bright);
}

.re-doc .hero .facts .fact-i svg {
	width: 19px;
	height: 19px;
}

.re-doc .hero .facts .k {
	font-size: 11px;
	letter-spacing: 0.08em;
	color: var(--re-on-dark-2);
}

.re-doc .hero .facts .v {
	font-size: 38px;
	line-height: 1.05;
	margin-top: 6px;
}

.re-doc .hero .facts .d {
	margin-top: 8px;
	line-height: 1.45;
}

/* Métrica de dos cifras ("5 desktop 3 mobile"): los números mantienen el peso
   de las otras métricas y las unidades van en texto chico, así el dato se
   entiende sin leer el pie. */
.re-doc .hero .facts .v--pair {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 0 7px;
	font-size: 14px;
	font-weight: 500;
	letter-spacing: 0;
	color: var(--re-on-dark-2);
}

.re-doc .hero .facts .v--pair .n {
	font-size: 38px;
	font-weight: 700;
	letter-spacing: -0.02em;
	color: var(--re-amber-bright);
}

.re-doc .hero .facts .v--pair .n:not(:first-child) {
	margin-left: 6px;
}

/* ---------- composición de la columna derecha ---------- */

.re-doc .hero .hc-stage {
	position: absolute;
	inset: 0;
	/* La composición sangra fuera de su columna a propósito: es lo que la
	   hace leer como una pila de material y no como una ilustración
	   encuadrada. El recorte lo pone .hero{overflow:hidden}, así que nunca
	   genera scroll horizontal. */
	transform: perspective(1600px) rotateX(6deg) rotateY(-15deg) rotate(2deg) scale(1.06);
	transform-origin: 50% 40%;
}

.re-doc .hero .hc-screen,
.re-doc .hero .hc-card {
	position: absolute;
	border: 1px solid var(--re-charcoal-line);
	border-radius: var(--re-r-lg);
	background: var(--re-surface);
	box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
	overflow: hidden;
}

/* Las capturas se recortan por arriba: se muestra la franja superior, que es
   donde vive la identidad de cada pantalla. */
.re-doc .hero .hc-screen img {
	display: block;
	width: 100%;
	height: auto;
}

.re-doc .hero .hc-screen--back {
	top: 2%;
	left: 46%;
	width: 62%;
	height: 62%;
	opacity: 0.72;
}

.re-doc .hero .hc-screen--front {
	top: 20%;
	left: 24%;
	width: 74%;
	height: 74%;
	opacity: 0.95;
}

.re-doc .hero .hc-card {
	padding: var(--re-s4);
	background: color-mix(in srgb, var(--re-surface) 88%, transparent);
	-webkit-backdrop-filter: blur(6px);
	backdrop-filter: blur(6px);
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
	.re-doc .hero .hc-card {
		background: var(--re-surface);
	}
}

.re-doc .hero .hc-card--type {
	top: 12%;
	left: -1%;
	width: 27%;
}

.re-doc .hero .hc-aa {
	font-size: 40px;
	font-weight: 600;
	line-height: 1;
	letter-spacing: -0.03em;
	color: var(--re-ink);
}

.re-doc .hero .hc-face {
	margin-top: 10px;
	font-size: 12px;
	font-weight: 500;
	color: var(--re-ink-2);
}

.re-doc .hero .hc-weights {
	display: flex;
	flex-direction: column;
	gap: 3px;
	margin-top: 10px;
	font-size: 11.5px;
	color: var(--re-ink-3);
}

.re-doc .hero .hc-weights span:nth-child(2) { font-weight: 500; }
.re-doc .hero .hc-weights span:nth-child(3) { font-weight: 600; }
.re-doc .hero .hc-weights span:nth-child(4) { font-weight: 700; }

.re-doc .hero .hc-card--color {
	top: 7%;
	left: 27%;
	width: 30%;
}

.re-doc .hero .hc-chips {
	display: flex;
	gap: 7px;
}

.re-doc .hero .hc-chip {
	width: 28px;
	height: 28px;
	border-radius: var(--re-r-sm);
	border: 1px solid var(--re-line-2);
	flex: none;
}

.re-doc .hero .hc-lab {
	margin-top: 11px;
	font-size: 11.5px;
	color: var(--re-ink-3);
}

/* El ancho tiene que dar para el par de botones a su tamaño real. Con 34% a
   1440 la tarjeta medía ~180px y los dos botones ~190: el `overflow:hidden` de
   la tarjeta cortaba "Secundario" al medio, que se lee como un bug y no como
   profundidad. 38% deja margen y el borde derecho sigue quedando antes del 55%
   donde arranca la tarjeta de estados. */
.re-doc .hero .hc-card--ctrl {
	top: 43%;
	left: 15%;
	width: 38%;
}

/* Red de seguridad: si algún idioma trae etiquetas más largas, los botones
   bajan de línea en vez de recortarse. */
.re-doc .hero .hc-btns {
	display: flex;
	flex-wrap: wrap;
	gap: 9px;
}

.re-doc .hero .hc-select {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-top: 11px;
	padding: 0 11px;
	height: var(--re-control-h);
	border: 1px solid var(--re-line-2);
	border-radius: var(--re-r-sm);
	background: var(--re-surface-2);
	font-size: 13px;
	color: var(--re-ink-4);
	/* El select tiene alto fijo (--re-control-h) como el componente real: si el
	   placeholder se parte en dos líneas se desborda de su propia caja. Se
	   trunca, que es lo que hace un select de verdad. */
	white-space: nowrap;
	overflow: hidden;
}

.re-doc .hero .hc-select svg {
	width: 15px;
	height: 15px;
	margin-left: auto;
	flex: none;
}

.re-doc .hero .hc-card--state {
	top: 36%;
	left: 55%;
	width: 30%;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 8px;
}

/* Los botones y los badges de la composición son los componentes REALES
   (.b/.b.acc/.st/.d-*): sólo se les quita el puntero, porque acá son
   decoración y no controles. */
.re-doc .hero .hc-card .b,
.re-doc .hero .hc-card .st {
	cursor: default;
}

@media (prefers-reduced-motion: reduce) {
	.re-doc .hero .hc-stage {
		transform: none;
	}
}

/* ---------- desktop: dos columnas ---------- */

@media (min-width: 1101px) {
	.re-doc .hero .in {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
		grid-template-areas:
			"copy art"
			"facts facts";
		column-gap: var(--re-s8);
		align-items: center;
	}

	.re-doc .hero .hero-art {
		display: block;
		position: relative;
		/* Alto derivado del ancho: la composición mantiene su proporción sin
		   depender de cuánto texto tenga la columna izquierda. */
		aspect-ratio: 16 / 11;
		min-height: 300px;
		max-height: 460px;
	}

	.re-doc .hero .facts {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}
}

/* Bajo 1101px el hero ya reduce el h1 a 38px (regla del propio DS) y las
   métricas pasan a 2×2 por el default de arriba. La composición no se
   intenta encajar: se saca. Es decoración, y comprimida a media columna
   dejaba de leerse como una pila de material — además de costar dos
   descargas arriba del fold en el viewport donde más caro sale. */
@media (max-width: 1100px) {
	.re-doc .hero h1 {
		max-width: 18ch;
	}

	.re-doc .hero .facts .v,
	.re-doc .hero .facts .v--pair .n {
		font-size: 32px;
	}
}

@media (max-width: 560px) {
	.re-doc .hero .facts {
		gap: var(--re-s3);
	}

	.re-doc .hero .facts .fact {
		padding: var(--re-s4);
	}

	.re-doc .hero .facts .fact-i {
		width: 32px;
		height: 32px;
		margin-bottom: var(--re-s3);
	}

	.re-doc .hero .facts .fact-i svg {
		width: 16px;
		height: 16px;
	}

	.re-doc .hero .facts .v,
	.re-doc .hero .facts .v--pair .n {
		font-size: 26px;
	}

	.re-doc .hero .facts .v--pair {
		font-size: 12.5px;
	}

	.re-doc .hero .facts .d {
		font-size: 11px;
	}
}
/* -------------------------------------------------------------------------
   6b. Leyenda del inventario (§09) y bloques de §08
   ------------------------------------------------------------------------- */

/* Los cuatro estados del inventario, como leyenda de la columna Estado. Antes
   iban encadenados por "·" en una sola línea de lede: toda la información
   estaba, pero leída de corrido sonaba a salida de un script. */
/* Dos columnas fijas y no auto-fit: con auto-fit entraban tres y el cuarto
   estado caía solo en una segunda fila, con el texto partido. En 2×2 los
   cuatro estados se leen como un bloque. */
.re-doc .ivt-legend {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--re-s3) var(--re-s7);
	margin: 0 0 var(--re-s6);
	max-width: 1060px;
}

@media (min-width: 860px) {
	.re-doc .ivt-legend {
		grid-template-columns: 1fr 1fr;
	}
}

.re-doc .ivt-legend > div {
	display: flex;
	align-items: baseline;
	gap: var(--re-s3);
}

.re-doc .ivt-legend dt {
	flex: none;
}

.re-doc .ivt-legend dd {
	font-size: 13px;
	color: var(--re-ink-3);
}

/* La exploración de color es una sola pieza y va a ancho completo: no es una
   pantalla más de la grilla de dos columnas. */
.re-doc .shots--single {
	grid-template-columns: 1fr;
	max-width: 760px;
}

/* Etiqueta de la tarjeta ("Exploración"): usa el mismo pill del inventario de
   artefactos, para no inventar un componente nuevo. */
.re-doc .shot .cap .art-kind {
	flex: none;
}

/* -------------------------------------------------------------------------
   7. System evolution (cierre del documento)
   -------------------------------------------------------------------------
   Reemplaza al registro "Needs review" como cierre público. Las 13 notas de
   auditoría siguen publicadas, pero dentro de un <details>: son material de
   trabajo interno y como cierre del documento se leían como una lista de
   defectos del producto.

   Usa los mismos tokens y el mismo lenguaje visual del Design System (fila con
   hairline, mono para el estado, ámbar racionado): no se inventa un componente
   nuevo. */

.re-doc .ev-grid {
	display: grid;
	gap: 0;
	border: 1px solid var(--re-line);
	border-radius: var(--re-r-lg);
	overflow: hidden;
	margin-bottom: var(--re-s6);
}

.re-doc .ev {
	display: flex;
	align-items: baseline;
	gap: var(--re-s4);
	padding: var(--re-s4) var(--re-s5);
	background: var(--re-surface);
}

.re-doc .ev + .ev {
	border-top: 1px solid var(--re-line);
}

.re-doc .ev-name {
	font-size: 14.5px;
	font-weight: 600;
	color: var(--re-ink);
}

.re-doc .ev-state {
	margin-left: auto;
	flex: none;
	font-family: var(--re-font-mono);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	padding: 3px 9px;
	border-radius: 999px;
	border: 1px solid var(--re-line-2);
	color: var(--re-ink-3);
	white-space: nowrap;
}

/* En progreso / En desarrollo: ámbar, que es el único acento del sistema.
   Planificado se queda en neutro a propósito — todavía no es trabajo en curso. */
.re-doc .ev-state--progress {
	color: var(--re-amber-ink);
	border-color: var(--re-amber-line);
	background: var(--re-amber-soft);
}

.re-doc .ev-state--planned {
	color: var(--re-ink-4);
}

/* --- disclosure de auditoría --- */

.re-doc .ev-audit {
	border-top: 1px solid var(--re-line);
	padding-top: var(--re-s5);
	margin-top: var(--re-s6);
}

.re-doc .ev-audit > summary {
	cursor: pointer;
	list-style: none;
	display: inline-flex;
	align-items: center;
	gap: 8px;
	font-family: var(--re-font-mono);
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--re-ink-4);
	transition: color 160ms ease;
}

.re-doc .ev-audit > summary::-webkit-details-marker {
	display: none;
}

/* Triángulo propio: el nativo no se puede estilar de forma consistente. */
.re-doc .ev-audit > summary::before {
	content: "";
	width: 0;
	height: 0;
	border-left: 5px solid currentColor;
	border-top: 4px solid transparent;
	border-bottom: 4px solid transparent;
	transition: transform 160ms ease;
}

.re-doc .ev-audit[open] > summary::before {
	transform: rotate(90deg);
}

.re-doc .ev-audit > summary:hover,
.re-doc .ev-audit > summary:focus,
.re-doc .ev-audit > summary:active,
.re-doc .ev-audit[open] > summary {
	color: var(--re-ink-2);
}

/* Igual que en la barra mínima: el anillo real lo pone base.css con
   !important; acá sólo viven el offset y el radio. */
.re-doc .ev-audit > summary:focus-visible {
	outline: 2px solid var(--es-accent);
	outline-offset: 3px;
	border-radius: 3px;
}

.re-doc .ev-audit > .body {
	margin-top: var(--re-s4);
}

.re-doc .ev-audit .stack {
	margin-top: var(--re-s4);
}

@media (prefers-reduced-motion: reduce) {
	.re-doc .ev-audit > summary,
	.re-doc .ev-audit > summary::before {
		transition: none;
	}
}

/* -------------------------------------------------------------------------
   12. Subnav del documento (≤1100px)
   -------------------------------------------------------------------------
   Debajo de 1100px el Design System suelta el rail sticky y lo convierte en un
   bloque estático. Con 12 secciones eso son ~560px de links antes del primer
   contenido: en un teléfono de 844px de alto, el documento empezaba fuera de
   la primera pantalla.

   La subnav lo reemplaza por una barra de una línea que dice en qué sección
   estás y despliega el resto. La construye assets/js/ds-restimator.js CLONANDO
   el rail, así que no hay una segunda lista que mantener, y el rail sólo se
   oculta cuando la subnav ya está montada (.es-ds-subnav-on en <body>): sin JS
   queda el bloque de siempre, que navega igual.

   1100px no es un breakpoint nuevo — es el que el propio DS ya usa. */

.es-ds-subnav-on .re-doc .rail {
	display: none;
}

.ds-subnav {
	display: none;
}

@media (max-width: 1100px) {
	.ds-subnav {
		display: block;
		position: sticky;
		/* Justo debajo del header institucional, con la misma convención de
		   66px que ya usan el rail y .es-case-index. */
		top: 66px;
		z-index: 60;
		background: color-mix(in srgb, var(--re-surface) 94%, transparent);
		-webkit-backdrop-filter: blur(12px);
		backdrop-filter: blur(12px);
		border-bottom: 1px solid var(--re-line);
	}

	@supports not (background: color-mix(in srgb, red 50%, transparent)) {
		.ds-subnav {
			background: var(--re-surface);
		}
	}

	/* Con la barra mínima en vez del header institucional. */
	.es-ds-minimal-nav .ds-subnav {
		top: var(--es-ds-topbar-h);
	}

	.admin-bar .ds-subnav {
		top: 98px;
	}

	.admin-bar.es-ds-minimal-nav .ds-subnav {
		top: calc(var(--es-ds-topbar-h) + 32px);
	}

	/* Los anchors tienen que caer debajo del header Y de la subnav. */
	.re-doc .sec,
	.es-header-sticky .re-doc .sec {
		scroll-margin-top: 122px;
	}

	.es-ds-minimal-nav .re-doc .sec {
		scroll-margin-top: calc(var(--es-ds-topbar-h) + 56px);
	}
}

@media (max-width: 782px) {
	.admin-bar .ds-subnav {
		top: 112px;
	}

	.admin-bar.es-ds-minimal-nav .ds-subnav {
		top: calc(var(--es-ds-topbar-h) + 46px);
	}
}

.ds-subnav__trigger {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	/* 52px: por encima del mínimo táctil de 44px que el propio sistema fija
	   en §07 para mobile. */
	min-height: 52px;
	padding: 0 var(--re-s5);
	border: 0;
	background: none;
	color: var(--re-ink);
	font-family: var(--re-font-sans);
	font-size: 14px;
	font-weight: 600;
	text-align: left;
	cursor: pointer;
}

.ds-subnav__num {
	font-family: var(--re-font-mono);
	font-size: 11px;
	font-weight: 600;
	color: var(--re-amber-ink);
	flex: none;
}

.ds-subnav__label {
	min-width: 0;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}

.ds-subnav__chev {
	display: grid;
	place-items: center;
	margin-left: auto;
	flex: none;
	color: var(--re-ink-3);
	transition: transform 160ms ease;
}

.ds-subnav__chev svg {
	width: 17px;
	height: 17px;
}

.ds-subnav__trigger[aria-expanded="true"] .ds-subnav__chev {
	transform: rotate(180deg);
}

/* Anillo de foco: el verde interactivo del portfolio (--es-accent), igual que
   el resto del sitio. El offset negativo es lo único propio — ver §16. */
.ds-subnav__trigger:focus-visible {
	outline: 2px solid var(--es-accent) !important;
	outline-offset: -3px !important;
}

/* El menú no ocupa media pantalla: se acota y scrollea por dentro. */
.ds-subnav__menu {
	max-height: min(58vh, 420px);
	overflow-y: auto;
	overscroll-behavior: contain;
	border-top: 1px solid var(--re-line);
	padding: var(--re-s2) 0 var(--re-s3);
	background: var(--re-surface);
}

.re-doc .ds-subnav__item {
	display: flex;
	align-items: baseline;
	gap: 11px;
	min-height: 44px;
	padding: 10px var(--re-s5);
	color: var(--re-ink-2);
	font-size: 14px;
	line-height: 1.35;
}

.re-doc .ds-subnav__item:hover {
	background: var(--re-surface-2);
	color: var(--re-ink);
}

.re-doc .ds-subnav__item.is-current {
	background: var(--re-surface-2);
	color: var(--re-ink);
	font-weight: 600;
	box-shadow: inset 3px 0 0 var(--re-amber);
}

.re-doc .ds-subnav__item:focus-visible {
	outline: 2px solid var(--es-accent) !important;
	outline-offset: -3px !important;
}

.ds-subnav__inum {
	font-family: var(--re-font-mono);
	font-size: 10.5px;
	color: var(--re-ink-4);
	min-width: 18px;
	flex: none;
}

.ds-subnav__itext {
	min-width: 0;
}

@media (prefers-reduced-motion: reduce) {
	.ds-subnav__chev {
		transition: none;
	}
}

/* -------------------------------------------------------------------------
   13. Breadcrumb en mobile
   -------------------------------------------------------------------------
   Tres niveles en 390px dejan el último apenas legible. El primero cede: con
   la subnav y el header arriba, "Work" ya está a un toque de distancia y el
   contexto que importa acá es REstimator → Design System. */

@media (max-width: 700px) {
	.re-doc .es-breadcrumbs__item:first-child {
		display: none;
	}

	.re-doc .es-breadcrumbs__item:nth-child(2)::before {
		content: none;
	}
}

/* -------------------------------------------------------------------------
   14. Visor en mobile: geometría y gesto
   -------------------------------------------------------------------------
   Nada de acá cambia el visor de desktop: son reglas de `pointer: coarse` y de
   viewport chico, más `touch-action`, que en un mouse no tiene efecto. */

/* `touch-action` es la mitad CSS del pinch (la otra está en
   assets/js/ds-screen-viewer.js).

   Al declarar los ejes de paneo explícitamente, el navegador deja de tratar el
   gesto de dos dedos como zoom DE LA PÁGINA — que es lo que hacía que en el
   iPhone se ampliara el documento de WordPress con el visor adentro — y se lo
   entrega al handler, que lo usa para escalar la captura. El paneo de un dedo
   lo sigue haciendo el navegador, nativo y con inercia:

     en fit  → pan-y      (la captura sólo desborda a lo alto)
     con zoom → pan-x pan-y (desborda en los dos ejes)

   Nunca `none`: eso mataría el scroll natural, que es la forma principal de
   recorrer una captura alta. */
.esv__scroll {
	touch-action: pan-y;
}

.esv__scroll.is-wide {
	touch-action: pan-x pan-y;
}

/* La hoja del visor se decide por PUNTERO y no por ancho: un teléfono en
   apaisado mide 844px de ancho y con un `max-width: 700px` se caía a la
   ventana centrada de desktop —barra despegada del borde y botones de 30px
   bajo el pulgar—. `pointer: coarse` es además la MISMA condición que usa el
   JS (coarse()), así que CSS y JS no pueden desincronizarse. */
@media (pointer: coarse) {
	/* dvh y no vh: en iOS Safari `vh` mide la ventana con la barra de URL
	   desplegada, así que con la barra colapsada el diálogo quedaba más alto
	   que la pantalla y la ventana se iba por abajo. El fallback en vh queda
	   primero para los navegadores sin dvh. */
	.esv {
		height: 100vh;
		height: 100dvh;
	}

	/* Hoja a pantalla completa, y no una ventana centrada que se redimensiona:
	   con el ancho y el alto fijos, la barra de título no se mueve mientras se
	   hace pinch. El JS deja de fijarle medidas en táctil (ver apply()). */
	.esv__win {
		top: 0;
		left: 0;
		transform: none;
		width: 100%;
		height: 100%;
		max-width: 100%;
		max-height: 100%;
		border: 0;
		border-radius: 0;
	}

	/* La barra respeta el notch y la barra de gestos. */
	.esv__bar {
		padding-top: env(safe-area-inset-top, 0px);
		height: calc(42px + env(safe-area-inset-top, 0px));
		padding-left: max(var(--re-s4), env(safe-area-inset-left, 0px));
		padding-right: max(var(--re-s3), env(safe-area-inset-right, 0px));
	}

	/* Controles a tamaño táctil: 30px era cómodo con mouse, no con el pulgar. */
	.esv__btn {
		width: 40px;
		height: 40px;
	}

	.esv__btn svg {
		width: 19px;
		height: 19px;
	}

	.esv__level {
		min-width: 42px;
		font-size: 12px;
	}

	.esv__scroll {
		padding-bottom: env(safe-area-inset-bottom, 0px);
	}
}

/* -------------------------------------------------------------------------
   15. Encabezado de sección en pantallas muy angostas
   -------------------------------------------------------------------------
   `.sec-h` es un flex de una línea con el contador empujado por margin-left:
   auto. A 320px el título y el contador no entran juntos y el contador se iba
   16px fuera del viewport (medido: right 350 contra un clientWidth de 320).
   Baja a su propia línea en vez de empujar la página. */

@media (max-width: 560px) {
	.re-doc .sec-h {
		flex-wrap: wrap;
	}

	.re-doc .sec-h .cnt {
		margin-left: 0;
		width: 100%;
	}
}

/* -------------------------------------------------------------------------
   16. Estados interactivos de los controles nuevos
   -------------------------------------------------------------------------
   En producción aparecían superficies AZULES al interactuar con la subnav y
   con el botón de cerrar del visor. No es un color de este proyecto: lo pone
   el tema padre, que estila <button> por nombre de elemento y le pinta
   background, border y box-shadow propios en :hover y en :focus. Es el mismo
   mecanismo ya documentado en vivo en dos lugares de este repo —el círculo
   azul de las flechas del índice del Case Study (case-figure-lightbox.css) y
   el texto azul de los botones sin clase (components.css)—, sólo que acá
   golpea a controles que se agregaron después.

   POR QUÉ NO ALCANZABA LO QUE YA HABÍA. Dos huecos distintos:

    · La armadura de §1 (`.re-doc button:hover/:focus`) sólo neutraliza
      `box-shadow`. Nunca tocó `background-color` ni `border-color`, así que
      el relleno del tema padre pasaba igual.
    · El visor vive FUERA de todo. `ds-screen-viewer.js` hace
      `document.body.appendChild(dialog)`, o sea que el <dialog> no está ni
      dentro de `.re-doc` ni dentro de `.es-page`: no le llega la armadura del
      documento NI la red de foco de base.css. Sin una regla propia, el
      <button> de cerrar quedaba a merced del tema padre — y como el JS le da
      el foco al abrir (`closeBtn.focus()`), el azul era lo primero que se veía.

   POR QUÉ NO SE ARREGLA EN LA RAÍZ. Poner `background: none !important` sobre
   `.re-doc button:hover` (0,1,1) le ganaría también a `.re-doc .b:hover`
   (0,2,1) de doc.css, que es el hover REAL de los botones de demostración del
   Design System: se apagarían los especímenes de §02 para arreglar la subnav.
   Por eso las reglas de abajo son por control, como ya lo hace
   `.es-lightbox__close` para este mismo problema.

   QUÉ COLOR. El verde interactivo del portfolio, sin inventar nada:
   `--es-accent` (+ `--es-accent-soft` para el relleno), los mismos tokens que
   usan `.es-btn`, los links y la red de foco de base.css. Siguen al
   Customizer como el resto del sitio. El patrón también es el de `.es-btn`:
   hover, focus-visible y active comparten superficie —fondo del acento con
   transparencia y borde del acento— y focus-visible SUMA el anillo. El
   `:focus` plano no dibuja anillo, igual que en todo el sitio, pero sí limpia
   el relleno del tema padre.

   `!important` en las propiedades que pinta el tema padre, y sólo en esas: sin
   él, `button:hover` (0,1,1) empata con `.esv__btn:hover` y decide el orden de
   carga, que contra un tema padre no está garantizado. Nunca `outline: none`:
   el anillo de foco se mantiene en los tres controles.
   ------------------------------------------------------------------------- */

/* ---------- subnav: el disparador ---------- */

.ds-subnav__trigger:hover,
.ds-subnav__trigger:focus,
.ds-subnav__trigger:focus-visible,
.ds-subnav__trigger:active {
	background: var(--es-accent-soft) !important;
	border-color: var(--es-accent) !important;
	box-shadow: none !important;
	color: var(--re-ink) !important;
}

/* El número y el chevron son hijos: sin esto heredarían el color de texto que
   el tema padre le pone al botón. */
.ds-subnav__trigger:hover .ds-subnav__chev,
.ds-subnav__trigger:focus .ds-subnav__chev,
.ds-subnav__trigger:active .ds-subnav__chev {
	color: var(--es-accent);
}

.ds-subnav__trigger:hover .ds-subnav__num,
.ds-subnav__trigger:focus .ds-subnav__num,
.ds-subnav__trigger:active .ds-subnav__num {
	color: var(--re-amber-ink);
}

/* ---------- subnav: los ítems del desplegable ---------- */

/* Son <a>, así que el riesgo acá es el `a:hover` del tema padre pintando el
   TEXTO de azul. Mismo verde, mismo patrón. */
.re-doc .ds-subnav__item:hover,
.re-doc .ds-subnav__item:focus,
.re-doc .ds-subnav__item:focus-visible,
.re-doc .ds-subnav__item:active {
	background: var(--es-accent-soft) !important;
	color: var(--re-ink) !important;
	box-shadow: none;
}

/* El ítem actual conserva su marca ámbar —es estado, no interacción— y la
   mantiene también mientras se lo recorre. */
.re-doc .ds-subnav__item.is-current,
.re-doc .ds-subnav__item.is-current:hover,
.re-doc .ds-subnav__item.is-current:focus,
.re-doc .ds-subnav__item.is-current:focus-visible {
	box-shadow: inset 3px 0 0 var(--re-amber);
}

/* ---------- visor: cerrar, − y + ---------- */

.esv__btn:hover:not(:disabled),
.esv__btn:focus:not(:disabled),
.esv__btn:focus-visible:not(:disabled),
.esv__btn:active:not(:disabled) {
	background: var(--es-accent-soft) !important;
	border: 1px solid var(--es-accent) !important;
	box-shadow: none !important;
	color: var(--re-ink) !important;
}

/* Un botón deshabilitado (− al ancho de ajuste, + al tope de zoom) no toma
   ningún estado: sin esta línea el tema padre se lo pintaba igual. */
.esv__btn:disabled,
.esv__btn:disabled:hover,
.esv__btn:disabled:focus {
	background: none !important;
	border-color: transparent !important;
	box-shadow: none !important;
	color: var(--re-ink-3) !important;
}

/* El <dialog> está fuera de .es-page, así que tampoco le llegó el
   `-webkit-tap-highlight-color: transparent` de base.css: en Android el toque
   dejaba un destello propio del navegador sobre los controles. */
.esv,
.esv button,
.esv [tabindex] {
	-webkit-tap-highlight-color: transparent;
}

/* ---------- visor: el lienzo ---------- */

/* Es focusable (tabindex=0) para recorrer la captura con el teclado, así que
   también es un control. No lleva superficie: sólo hay que impedir que el
   tema padre le dibuje su propio anillo en el :focus plano. El anillo verde de
   :focus-visible se declara junto al resto del visor. */
.esv__scroll:focus {
	outline-color: transparent;
	box-shadow: none !important;
}

/* ---------- disparador de las previews de pantalla ---------- */

/* `button.vp` ya trae su propio hover (el chip "Ampliar" del Design System) y
   ese no cambia: lo único que se corrige es el borde que le pinta el tema
   padre, invisible hoy porque el botón tiene border-width 0, pero que
   reaparecería con cualquier hoja que le devuelva un ancho. */
.re-doc button.vp:hover,
.re-doc button.vp:focus,
.re-doc button.vp:focus-visible,
.re-doc button.vp:active {
	background: none !important;
	border-color: transparent !important;
	box-shadow: none !important;
}
