/*
 * QAInex — hoja complementaria.
 *
 * theme.json fija color, tipografía y espacios. Aquí va solo lo que theme.json
 * no puede expresar: estados, medios, pseudo-elementos y el panel a pantalla
 * completa que da su carácter a la portada.
 *
 * Tres reglas de gobierno:
 *   1. El color es estado, nunca decoración. Verde, ámbar y azul solo marcan la
 *      madurez de una línea; nada más en el sitio los usa.
 *   2. Blanco sobre negro, y una sola regla de 1px para separar. Sin sombras,
 *      sin degradados de adorno, sin bordes redondeados.
 *   3. Las mayúsculas son para rótulos y titulares condensados. El texto
 *      corrido va en caja mixta, que es lo que se lee de corrido.
 */

/* 1. Base ================================================================= */

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

/*
 * clip y no hidden, y la diferencia no es sutil.
 *
 * overflow:hidden convierte al elemento en CONTENEDOR DE SCROLL, y
 * animation-timeline: view() se ancla al contenedor de scroll mas cercano. Si
 * ese contenedor no se desplaza nunca, el progreso de la animacion queda
 * clavado en un valor fijo. overflow:clip recorta igual pero no crea contenedor,
 * asi que view() sube hasta el documento, que es lo que si se desplaza.
 */
body {
	text-rendering: optimizeLegibility;
	-webkit-font-smoothing: antialiased;
	overflow-x: clip;
}

/* El foco tiene que verse sobre negro y sobre fotografía. Doble trazo: blanco
   por dentro, negro por fuera, así sobrevive a cualquier fondo. */
:where(a, button, input, textarea, select, summary, [tabindex]):focus-visible {
	outline: 2px solid #fff;
	outline-offset: 2px;
	box-shadow: 0 0 0 4px #000;
}

::selection { background: #fff; color: #000; }

/* 2. Enlace para saltar al contenido ====================================== */

.qnxb-saltar {
	position: absolute;
	left: -9999px;
	z-index: 999;
	padding: 0.85rem 1.4rem;
	background: #fff;
	color: #000;
	font-family: var(--wp--preset--font-family--mono);
	font-size: var(--wp--preset--font-size--menudo);
	text-transform: uppercase;
	letter-spacing: 0.12em;
}

.qnxb-saltar:focus {
	left: 1rem;
	top: 1rem;
}

/* 3. Cabecera ============================================================= */

/*
 * Flota sobre la fotografía, sin fondo, como en un sitio de una sola imagen a
 * pantalla completa. Al bajar se vuelve sólida: encima de un panel claro un
 * menú transparente deja de leerse.
 */
/*
 * Va fija, no pegajosa. position:sticky se rompe cuando un ancestro tiene
 * overflow-x:hidden, y el body lo necesita para contener los paneles a sangre.
 * Fija además es el comportamiento correcto aquí: la cabecera nunca se va.
 */
.qnxb-cabecera {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 100;
	transition: background-color 0.25s ease, border-color 0.25s ease;
	border-bottom: 1px solid transparent;
	/* En móviles con muesca la barra no puede meterse bajo el sistema. */
	padding-top: env(safe-area-inset-top, 0);
}

.qnxb-cabecera.esta-desplazada {
	background: rgba(0, 0, 0, 0.88);
	border-bottom-color: var(--wp--preset--color--linea);
}

@supports (backdrop-filter: blur(10px)) {
	.qnxb-cabecera.esta-desplazada {
		background: rgba(0, 0, 0, 0.62);
		backdrop-filter: blur(12px) saturate(140%);
	}
}

/* Con transparencia reducida el desenfoque desaparece y el fondo se vuelve
   sólido: si no, el texto del menú queda sobre una foto en movimiento. */
@media (prefers-reduced-transparency: reduce) {
	.qnxb-cabecera.esta-desplazada {
		background: #000;
		backdrop-filter: none;
	}
}

/* Los enlaces del menú: mayúsculas menudas con mucho espaciado. A ese tamaño
   el tracking amplio es lo que las vuelve legibles. */
.qnxb-cabecera .wp-block-navigation {
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.6875rem;
	text-transform: uppercase;
	letter-spacing: 0.16em;
}

.qnxb-cabecera .wp-block-navigation-item__content {
	color: var(--wp--preset--color--humo);
	padding-block: 0.5rem;
	transition: color 0.18s ease;
}

.qnxb-cabecera .wp-block-navigation-item__content:hover,
.qnxb-cabecera .current-menu-item .wp-block-navigation-item__content {
	color: #fff;
	text-decoration: none;
}

/* El menú desplegable en móvil ocupa la pantalla; que herede el negro. */
.wp-block-navigation__responsive-container.is-menu-open {
	background: #000;
	padding: 1.5rem;
}

.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
	font-size: 1.25rem;
	letter-spacing: 0.1em;
}

/* 4. Panel a pantalla completa =========================================== */

/*
 * La pieza central. Cada línea de trabajo ocupa una pantalla entera, con la
 * imagen al fondo y el texto abajo, apoyado en el borde inferior.
 *
 * svh y no vh: en el móvil vh cuenta la barra de direcciones que se retrae, así
 * que el panel queda más alto que la pantalla y el texto se corta.
 */
/*
 * Grid y no flex en columna, a propósito.
 *
 * WordPress centra el contenido dando a cada bloque max-width y
 * margin-inline:auto. En un contenedor flex en columna ese margen automático
 * cae sobre el eje transversal, que encoge el hijo al ancho de su contenido y
 * lo centra: el texto deja de apoyarse en el borde izquierdo y la retícula del
 * sitio se rompe justo en los paneles. En grid el margen automático se comporta
 * como en flujo normal y la columna queda donde corresponde.
 */
.is-style-panel-completo {
	position: relative;
	display: grid;
	/*
	 * minmax(0, 1fr) y no la pista implicita por defecto.
	 *
	 * Una pista "auto" crece hasta el max-content de su hijo mas ancho. Con los
	 * botones en mayusculas y espaciado amplio, a 400 %% de zoom ese max-content
	 * supera la pantalla, la pista se estira, y todo lo que va a width:100 %%
	 * hereda ese ancho. Medido: la seccion de contacto se salia 388 px, y
	 * overflow:clip lo ocultaba en vez de dejar desplazarse: texto cortado e
	 * inalcanzable. El minimo en 0 obliga a la pista a caber y el contenido a
	 * reajustarse.
	 */
	grid-template-columns: minmax(0, 1fr);
	/* clip, no hidden: ver la nota del body. Con hidden, view() se anclaba a
	   este panel y la aparicion al bajar quedaba congelada. Medido: un parrafo
	   se quedaba en opacidad 0,85 para siempre. */
	overflow: clip;
	/*
	 * Sin fotografía, el panel NO ocupa la pantalla entera y el texto va
	 * centrado en vertical. Una pantalla completa de negro con el texto pegado
	 * abajo no se lee como sobriedad, se lee como una imagen que no cargó.
	 */
	min-height: min(44rem, 86svh);
	align-content: center;
}

/*
 * Con fotografía sí: pantalla completa y texto apoyado en el borde inferior,
 * que es la disposición que hace funcionar una imagen a sangre.
 *
 * WordPress escribe la imagen de fondo como estilo en línea, así que este
 * selector distingue un panel con foto de uno sin ella sin pedirle a nadie que
 * recuerde marcar una casilla.
 */
.is-style-panel-completo[style*="background-image"] {
	min-height: 100svh;
	align-content: end;
}

/* Un panel sin altura completa, para bloques de lectura larga. */
.qnxb-panel-corto { min-height: min(46rem, 82svh); }

/*
 * Los hijos del panel van a todo el ancho y pegados al borde izquierdo.
 *
 * Hace falta decirlo explícitamente. WordPress centra el contenido con
 * margin-inline:auto, y un margen automático DESACTIVA el estirado tanto en
 * flex como en grid: el bloque se encoge al ancho de su hijo más ancho y se
 * planta en medio de la pantalla. Medido: la columna caía a 687px y arrancaba
 * en x=377 en vez de x=48.
 *
 * Anular el margen devuelve el bloque al borde. La medida de lectura no se
 * pierde: la fija cada párrafo por su cuenta, más abajo.
 */
.is-style-panel-completo > * {
	position: relative;
	z-index: 2;
	margin-inline: 0;
	max-width: none;
	width: 100%;
	/* La otra mitad del arreglo: sin esto un hijo se niega a bajar de su
	   min-content y vuelve a empujar la pista. */
	min-width: 0;
}

/*
 * Velo. El texto blanco sobre foto no se sostiene solo: según la zona de la
 * imagen el contraste cae por debajo de lo legible. El velo garantiza el piso
 * pase lo que pase con la fotografía que se cargue después desde el editor.
 */
.is-style-panel-completo::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(
		to top,
		rgba(0, 0, 0, 0.94) 0%,
		rgba(0, 0, 0, 0.78) 30%,
		rgba(0, 0, 0, 0.34) 62%,
		rgba(0, 0, 0, 0.12) 100%
	);
	pointer-events: none;
}

/* 5. Rótulos técnicos ===================================================== */

.is-style-rotulo,
.qnxb-rotulo {
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.6875rem;
	text-transform: uppercase;
	letter-spacing: 0.18em;
	color: var(--wp--preset--color--humo);
	display: flex;
	align-items: center;
	gap: 0.75rem;
}

/* La regla corta que antecede al rótulo. Marca dónde empieza el bloque sin
   gastar una línea de texto en decirlo. */
.is-style-rotulo::before,
.qnxb-rotulo::before {
	content: '';
	width: 1.75rem;
	height: 1px;
	background: currentColor;
	flex: none;
}

/* 6. Estados de madurez ================================================== */

/*
 * Principio VII hecho visible: nadie puede leer el sitio y confundir una línea
 * probada con una que todavía no lo está. Estos son los únicos lugares del tema
 * donde el color significa algo.
 */
.is-style-estado-validado,
.is-style-estado-desarrollo,
.is-style-estado-investigacion {
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.6875rem;
	text-transform: uppercase;
	letter-spacing: 0.14em;
	padding: 0.45rem 0.85rem;
	border: 1px solid currentColor;
	line-height: 1;
}

/* El punto delante no es adorno: repite el estado en forma, no solo en color,
   para quien no distingue verde de ámbar. */
.is-style-estado-validado::before,
.is-style-estado-desarrollo::before,
.is-style-estado-investigacion::before {
	content: '';
	width: 0.4rem;
	height: 0.4rem;
	background: currentColor;
	flex: none;
}

.is-style-estado-validado      { color: var(--wp--preset--color--validado); }
.is-style-estado-desarrollo    { color: var(--wp--preset--color--desarrollo); }
.is-style-estado-investigacion { color: var(--wp--preset--color--investigacion); }

/* Validado lleva el punto lleno y redondo; en desarrollo, hueco; investigación,
   cuadrado. Tres estados, tres formas distintas además del color. */
.is-style-estado-validado::before { border-radius: 50%; }

.is-style-estado-desarrollo::before {
	background: transparent;
	box-shadow: inset 0 0 0 1px currentColor;
	border-radius: 50%;
}

/* 7. Tipografía fina ===================================================== */

/* A tamaño display la condensada en mayúsculas se separa demasiado; se aprieta.
   Al revés en los rótulos, donde se abre. Cada tamaño lleva su tracking. */
h1 { letter-spacing: -0.012em; }
h2 { letter-spacing: -0.006em; }

/*
 * Todo lo que lleva tope de ancho necesita ademas margin-inline: 0 auto.
 *
 * WordPress pone margin-inline:auto a los hijos de un contenedor constrained.
 * En cuanto un parrafo tiene max-width menor que el contenedor, esos dos
 * margenes automaticos lo centran, y el texto se despega del borde donde se
 * apoya el titular. Fijar el margen izquierdo en 0 lo devuelve a la columna.
 */
.qnxb-bajada {
	font-size: var(--wp--preset--font-size--medio);
	color: var(--wp--preset--color--humo);
	max-width: 42ch;
	margin-inline: 0 auto;
	line-height: 1.6;
}

/*
 * Medida de lectura.
 *
 * El contenedor del sitio llega a 1500px, y a ese ancho una línea de texto pasa
 * de 200 caracteres: el ojo pierde el renglón al volver. El tope va en el
 * párrafo y no en el contenedor porque los titulares y las retículas sí deben
 * usar todo el ancho.
 */
main :where(section) p:not(.qnxb-bajada):not(.is-style-rotulo):not([class*="is-style-estado-"]):not(.qnxb-programa__texto):not([class*="qnxb-campo"]),
main :where(section) li {
	max-width: 74ch;
	margin-inline: 0 auto;
}

/* Los dígitos alineados verticalmente: comparar cifras en columna es la razón
   de ser de varias de estas listas. */
.qnxb-cifra,
.is-style-estado-validado,
.is-style-estado-desarrollo,
.is-style-estado-investigacion {
	font-variant-numeric: tabular-nums;
}

.qnxb-cifra {
	font-family: var(--wp--preset--font-family--mono);
	font-size: 1.25rem;
	color: #fff;
	display: block;
}

/*
 * La bajada de la portada, pegada al borde.
 *
 * Dentro de los paneles la bajada cuelga de un grupo en flujo normal y no hay
 * problema. En la PORTADA es hija directa del contenedor del cover, donde
 * WordPress impone margin-left:auto !important. Con un tope de 42ch eso la
 * centra y el titular queda solo en el borde izquierdo.
 *
 * Aqui el !important no es pereza: es la unica forma de responder a otro
 * !important del nucleo. El truco de anular el tope de ancho, que sirvio en los
 * paneles y en el formulario, no aplica: aqui el tope es justamente lo que se
 * quiere conservar.
 */
.wp-block-cover__inner-container > .qnxb-bajada {
	margin-inline: 0 auto !important;
}

/* 8. Fila de datos ======================================================= */

.qnxb-datos {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
	gap: 1.5rem clamp(1.5rem, 4vw, 3rem);
}

.qnxb-datos > * {
	border-left: 1px solid var(--wp--preset--color--linea);
	padding-left: 1rem;
	min-width: 0;
}

/* 9. Banda de programa =================================================== */

/*
 * Va debajo de la portada, en fila propia. Un logo ajeno dentro del titular se
 * lee como sello de respaldo del producto; abajo y separado se lee como lo que
 * es, un hecho verificable sobre la empresa.
 */
.qnxb-programa {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.9rem clamp(1.25rem, 3vw, 2rem);
	padding-top: 1.5rem;
	border-top: 1px solid var(--wp--preset--color--linea);
}

/* El SVG hereda currentColor, así que el logo sirve sobre cualquier fondo con
   un solo archivo. */
.qnxb-programa__logo {
	display: block;
	flex: none;
	width: clamp(6.5rem, 5rem + 5vw, 8.5rem);
	color: #fff;
}

.qnxb-programa__logo svg { display: block; width: 100%; height: auto; }

.qnxb-programa__texto {
	margin: 0;
	min-width: 0;
	font-size: var(--wp--preset--font-size--chico);
	color: var(--wp--preset--color--humo);
}

.qnxb-programa__texto strong {
	font-family: var(--wp--preset--font-family--mono);
	font-weight: 500;
	color: #fff;
	text-transform: uppercase;
	letter-spacing: 0.1em;
	font-size: 0.6875rem;
}

.qnxb-programa__texto span { display: block; margin-top: 0.25rem; }

/* 10. Detalles de interacción ============================================ */

/* El pulsado responde de inmediato: sin esto la interfaz parece no escuchar. */
.wp-element-button,
.wp-block-button__link {
	transition: background-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
	-webkit-tap-highlight-color: transparent;
}

.wp-element-button:active,
.wp-block-button__link:active { transform: translateY(1px); }

/*
 * Jerarquía entre botones.
 *
 * El principal va relleno; el secundario, con trazo. Dos botones idénticos uno
 * al lado del otro no son dos opciones, son una decisión que el visitante tiene
 * que tomar sin ninguna pista.
 *
 * El trazo va a 1px, no a los 2px del núcleo: a este tamaño de letra 2px pesa
 * más que el texto que enmarca.
 */
.wp-block-button.is-style-outline > .wp-block-button__link {
	background-color: transparent;
	color: #fff;
	border: 1px solid #fff;
	padding: 0.9rem 1.6rem;
}

.wp-block-button.is-style-outline > .wp-block-button__link:hover,
.wp-block-button.is-style-outline > .wp-block-button__link:focus-visible {
	background-color: #fff;
	color: #000;
}

/* Objetivos táctiles: 44px es el mínimo por debajo del cual se falla el toque.
   Los enlaces de menú son los que más cerca quedan del límite. */
@media (pointer: coarse) {
	.wp-block-navigation-item__content,
	.qnxb-programa__texto a {
		min-height: 44px;
		display: inline-flex;
		align-items: center;
	}
}

/* 11. Movimiento ========================================================= */

/*
 * Una sola curva para todo el sitio, y es de salida fuerte.
 *
 * Las curvas del navegador son demasiado blandas: a igual duración, una entrada
 * con ease-out se percibe más rápida que con ease-in, porque el ojo ve el
 * movimiento en el instante en que está mirando. ease-in retrasa justo ese
 * momento y la interfaz se siente lenta aunque el reloj diga lo mismo.
 */
:root {
	--qnxb-salida: cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes qnxb-aparece {
	from { opacity: 0; transform: translateY(1.5rem); }
	to   { opacity: 1; transform: none; }
}

/* Un velo que se abre de izquierda a derecha. Para los titulares condensados
   funciona mejor que un desvanecido: la letra aparece dibujándose. */
@keyframes qnxb-descubre {
	from { clip-path: inset(0 100% 0 0); opacity: 0.4; }
	to   { clip-path: inset(0 0 0 0);    opacity: 1; }
}

/* --- Entrada de la portada, una sola vez al cargar ----------------------- */

/*
 * Va con reloj, no con scroll: la portada ya está a la vista cuando la página
 * abre, así que no hay recorrido que la dispare.
 *
 * 700 ms es largo para una interfaz, pero esto se ve una vez por visita. La
 * regla de que nada pase de 300 ms aplica a lo que se repite cien veces al día,
 * no al telón que sube al entrar.
 */
.wp-block-cover__inner-container > * {
	animation: qnxb-aparece 700ms var(--qnxb-salida) both;
}

.wp-block-cover__inner-container > *:nth-child(1) { animation-delay: 80ms; }
.wp-block-cover__inner-container > *:nth-child(2) { animation-delay: 180ms; }
.wp-block-cover__inner-container > *:nth-child(3) { animation-delay: 280ms; }
.wp-block-cover__inner-container > *:nth-child(4) { animation-delay: 380ms; }
.wp-block-cover__inner-container > *:nth-child(5) { animation-delay: 480ms; }

/* El titular se descubre en vez de desvanecerse, y arranca antes que el resto
   porque es lo primero que se lee. */
.wp-block-cover__inner-container h1 {
	animation: qnxb-descubre 900ms var(--qnxb-salida) both;
	animation-delay: 140ms;
}

/* --- Aparición al bajar -------------------------------------------------- */

/*
 * Cada hijo se anima según SU propia posición en la pantalla, no con retardos
 * encadenados desde el padre. Así el escalonado sale del recorrido real: quien
 * baja despacio los ve entrar de a uno, quien baja de golpe los encuentra ya
 * puestos. Un retardo fijo hace esperar a quien ya llegó.
 *
 * El @supports importa: donde no hay líneas de tiempo de scroll, la declaración
 * se descarta y la animación no se aplica, así que el contenido queda visible.
 * Nunca un opacity 0 que dependa de una función que el navegador no tiene.
 */
@supports (animation-timeline: view()) {
	.qnxb-entra > * {
		animation: qnxb-aparece linear both;
		animation-timeline: view();
		animation-range: entry 8% cover 30%;
	}

	/* Los rótulos y los estados entran descubriéndose, igual que los titulares:
	   son piezas anchas y bajas, y ahí el barrido lateral se lee mejor. */
	.qnxb-entra > h2,
	.qnxb-entra > .is-style-rotulo {
		animation-name: qnxb-descubre;
	}

	/*
	 * La foto de la portada se desplaza más lento que la página.
	 *
	 * El scale(1.12) no es estético: sin él, al desplazar la imagen se
	 * asomarían los bordes del contenedor. El sobreancho da margen para mover.
	 */
	.wp-block-cover__image-background {
		animation: qnxb-fondo linear both;
		animation-timeline: view();
		animation-range: entry 0% exit 100%;
		will-change: transform;
	}

	@keyframes qnxb-fondo {
		from { transform: scale(1.12) translateY(-3%); }
		to   { transform: scale(1.12) translateY(3%); }
	}
}

/*
 * Con movimiento reducido se anula todo y queda el estado final.
 *
 * El animation-delay tiene que ir a cero también: con la duración en casi cero
 * pero el retardo intacto, el elemento se queda en su fotograma inicial —
 * invisible— durante el retardo, y parpadea. Es el error clásico de esta regla.
 */
@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: 0.001ms !important;
		animation-delay: 0s !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.001ms !important;
		scroll-behavior: auto !important;
	}

	.qnxb-entra,
	.qnxb-entra > *,
	.wp-block-cover__inner-container > *,
	.wp-block-cover__image-background {
		animation: none;
		opacity: 1;
		clip-path: none;
		transform: none;
	}
}

/* 12. Más contraste ====================================================== */

@media (prefers-contrast: more) {
	:root {
		--wp--preset--color--humo: #d8dadd;
		--wp--preset--color--linea: #6a6d72;
	}

	.is-style-panel-completo::before { background: rgba(0, 0, 0, 0.82); }
}

/* 13. Impresión ========================================================== */

/*
 * Una postulación se imprime o se exporta a PDF. En negro sobre negro no queda
 * nada legible, así que la versión impresa se invierte y se quitan los paneles
 * a pantalla completa, que en papel solo generan páginas en blanco.
 */
@media print {
	body {
		background: #fff !important;
		color: #000 !important;
	}

	.qnxb-cabecera,
	.wp-block-navigation,
	.wp-block-buttons { display: none !important; }

	.is-style-panel-completo {
		min-height: 0 !important;
		background-image: none !important;
		page-break-inside: avoid;
	}

	.is-style-panel-completo::before { display: none; }

	h1, h2, h3, p, li,
	.qnxb-cifra,
	.qnxb-programa__texto strong { color: #000 !important; }

	.qnxb-bajada,
	.qnxb-rotulo,
	.is-style-rotulo,
	.qnxb-programa__texto { color: #333 !important; }

	.qnxb-programa__logo { color: #000 !important; }

	/* Un enlace impreso sin su destino es un enlace perdido. */
	main a[href^="http"]::after {
		content: ' (' attr(href) ')';
		font-size: 0.75em;
		word-break: break-all;
	}
}

/* El cover del nucleo trae overflow:hidden y dentro va el fondo con
   desplazamiento lento; mismo motivo que arriba. */
.wp-block-cover { overflow: clip; }

/* 14. Formulario de contacto ============================================= */

/*
 * El tope de ancho va en el formulario interno, NO en este contenedor.
 *
 * WordPress aplica "margin-left: auto !important" a los hijos directos de un
 * contenedor constrained. Contra un !important no gana ni mas especificidad ni
 * el orden: la unica forma limpia de no pelearlo es que el hijo directo ocupe
 * todo el ancho, y que el tope de lectura lo ponga un elemento de mas adentro,
 * al que esa regla ya no alcanza. Medido: con el tope aqui, el formulario
 * arrancaba en x=352 en vez de x=48.
 */
.qnxb-form {
	margin-top: var(--wp--preset--spacing--40);
}

.qnxb-formulario,
.qnxb-aviso {
	max-width: 46rem;
}

.qnxb-formulario {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
	gap: 1.25rem clamp(1rem, 2vw, 1.75rem);
	padding-top: var(--wp--preset--spacing--30);
	border-top: 1px solid var(--wp--preset--color--linea);
}

/* El mensaje y el botón ocupan la fila entera: un textarea a media columna
   invita a escribir dos líneas, que no es lo que se pide. */
.qnxb-campo--ancho { grid-column: 1 / -1; }

.qnxb-campo {
	display: grid;
	gap: 0.45rem;
	margin: 0;
	min-width: 0;
}

.qnxb-campo label {
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.6875rem;
	text-transform: uppercase;
	letter-spacing: 0.14em;
	color: var(--wp--preset--color--humo);
}

.qnxb-campo__opcional {
	text-transform: none;
	letter-spacing: 0;
	opacity: 0.75;
}

/*
 * Los controles van con fondo propio y no transparentes.
 *
 * Un campo transparente sobre negro se apoya solo en su borde para existir, y
 * en cuanto el panel lleve fotografía de fondo deja de distinguirse de la
 * imagen. El fondo sólido lo hace legible pase lo que pase detrás.
 */
.qnxb-campo input,
.qnxb-campo textarea {
	box-sizing: border-box;
	width: 100%;
	min-width: 0;
	padding: 0.75rem 0.85rem;
	background: var(--wp--preset--color--grafito);
	color: #fff;
	border: 1px solid var(--wp--preset--color--linea);
	border-radius: 0;
	font-family: inherit;
	font-size: 1rem;
	line-height: 1.5;
	/* 16px es el umbral bajo el cual iOS hace zoom al enfocar un campo, y ese
	   zoom no se deshace solo: el visitante queda con la página ampliada. */
}

.qnxb-campo textarea {
	resize: vertical;
	min-height: 7rem;
}

/*
 * El borde se aclara al enfocar, pero NO se quita el outline.
 *
 * Un "outline: none" aquí deja al teclado sin indicador: el cambio de borde es
 * demasiado sutil para cumplir 2.4.7, y la regla global de foco deja de
 * aplicarse. Medido: cuatro campos quedaron sin indicador.
 */
.qnxb-campo input:focus,
.qnxb-campo textarea:focus {
	border-color: #fff;
}

.qnxb-campo input::placeholder,
.qnxb-campo textarea::placeholder { color: var(--wp--preset--color--humo); }

/* El borde del campo con error es 3:1 sobre el fondo, y además el mensaje se
   escribe: el color por sí solo no comunica nada a quien no lo distingue. */
.qnxb-campo input[aria-invalid="true"],
.qnxb-campo textarea[aria-invalid="true"] {
	border-color: var(--wp--preset--color--desarrollo);
}

.qnxb-campo__error {
	font-size: 0.8125rem;
	color: var(--wp--preset--color--desarrollo);
}

.qnxb-form__nota {
	grid-column: 1 / -1;
	margin: 0;
	font-size: 0.8125rem;
	color: var(--wp--preset--color--humo);
}

/*
 * Trampa para robots.
 *
 * Fuera de la pantalla y no display:none: los robots que interpretan estilos
 * saltan lo evidente. aria-hidden y tabindex="-1" la dejan fuera del alcance de
 * un lector de pantalla y del tabulador.
 */
.qnxb-trampa {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* Avisos tras el envío ---------------------------------------------------- */

.qnxb-aviso {
	padding: 1rem 1.15rem;
	margin-bottom: var(--wp--preset--spacing--30);
	border: 1px solid currentColor;
	font-size: var(--wp--preset--font-size--chico);
}

.qnxb-aviso p { margin: 0; }
.qnxb-aviso p + p { margin-top: 0.6rem; }
.qnxb-aviso a { color: inherit; text-decoration: underline; text-underline-offset: 0.2em; }

.qnxb-aviso--ok       { color: var(--wp--preset--color--validado); }
.qnxb-aviso--atencion { color: var(--wp--preset--color--desarrollo); }
.qnxb-aviso--error    { color: var(--wp--preset--color--desarrollo); }

/* El aviso recibe el foco al enviar, así que necesita indicador propio. */
.qnxb-aviso:focus-visible {
	outline: 2px solid #fff;
	outline-offset: 3px;
}

@media print {
	.qnxb-formulario { display: none !important; }
}

/* 15. Marca ============================================================== */

/*
 * La Q va delante del nombre en la cabecera y en el pie.
 *
 * mask-image + background-color: currentColor, no un <img>: así la marca toma
 * el color del texto y sirve sobre negro, sobre papel y en impresión con un
 * solo archivo. Un <img> sería un documento aparte donde currentColor cae a
 * negro y la marca desaparece sobre el fondo oscuro.
 */
.wp-block-site-title::before {
	content: '';
	display: inline-block;
	vertical-align: -0.14em;
	width: 0.92em;
	height: 0.92em;
	margin-right: 0.42em;
	background-color: currentColor;
	-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' fill='none' aria-hidden='true'%3E %3Ccircle cx='19' cy='19' r='13.5' stroke='currentColor' stroke-width='6'/%3E %3Cpath d='M21 21 L34 34' stroke='currentColor' stroke-width='6' stroke-linecap='round'/%3E %3C/svg%3E") center / contain no-repeat;
	mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' fill='none' aria-hidden='true'%3E %3Ccircle cx='19' cy='19' r='13.5' stroke='currentColor' stroke-width='6'/%3E %3Cpath d='M21 21 L34 34' stroke='currentColor' stroke-width='6' stroke-linecap='round'/%3E %3C/svg%3E") center / contain no-repeat;
}

/* Sin soporte de mask la marca se omite entera. Es preferible a dejar un
   cuadrado sólido delante del nombre, que es lo que quedaría. */
@supports not ((mask: none) or (-webkit-mask: none)) {
	.wp-block-site-title::before { display: none; }
}

/* 16. Segunda capa: el detalle que se despliega ========================== */

/*
 * Marco de decisión: ¿esto debe animarse?
 *
 * Un desplegable se abre unas pocas veces por visita, no cien veces al día, y
 * el cambio es de altura: sin transición el contenido aparece de golpe y la
 * página salta bajo el dedo. Sí se anima, y corto.
 *
 * Va con transición y no con @keyframes a propósito: una transición se puede
 * interrumpir y retomar desde donde va. Quien abre y cierra rápido ve el panel
 * devolverse desde su posición actual, no reiniciarse desde cero.
 */
.qnxb-detalle {
	margin-top: var(--wp--preset--spacing--20);
	max-width: 46rem;
	border-top: 1px solid var(--wp--preset--color--linea);
}

.qnxb-detalle summary {
	display: flex;
	align-items: center;
	gap: 0.7rem;
	padding: 0.9rem 0;
	cursor: pointer;
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.6875rem;
	text-transform: uppercase;
	letter-spacing: 0.16em;
	color: var(--wp--preset--color--humo);
	transition: color 160ms ease-out;
	list-style: none;
	-webkit-tap-highlight-color: transparent;
}

/* El triángulo del navegador no combina con nada; se reemplaza por un signo
   dibujado que gira. */
.qnxb-detalle summary::-webkit-details-marker { display: none; }
.qnxb-detalle summary::marker { content: ''; }

.qnxb-detalle summary:hover,
.qnxb-detalle summary:focus-visible { color: #fff; }

/* Una cruz que gira 45 grados hasta ser un aspa. Dos trazos, sin archivo. */
.qnxb-detalle summary::after {
	content: '';
	width: 0.75rem;
	height: 0.75rem;
	flex: none;
	background:
		linear-gradient(currentColor, currentColor) center / 100% 1px no-repeat,
		linear-gradient(currentColor, currentColor) center / 1px 100% no-repeat;
	transition: transform 220ms var(--qnxb-salida);
}

.qnxb-detalle[open] summary::after { transform: rotate(135deg); }

.qnxb-detalle[open] summary { color: #fff; }

.qnxb-detalle > *:not(summary) { margin-block: 0 1rem; }

/*
 * La apertura animada.
 *
 * interpolate-size permite transicionar hacia height:auto, que hasta hace poco
 * era imposible sin medir con JavaScript. Donde no exista, el desplegable abre
 * de golpe: pierde la transición, nunca el contenido.
 *
 * 260 ms: por encima de 300 un control que se usa varias veces se siente lento.
 */
:root { interpolate-size: allow-keywords; }

@supports selector(::details-content) {
	.qnxb-detalle::details-content {
		block-size: 0;
		overflow: hidden;
		opacity: 0;
		transition:
			block-size 260ms var(--qnxb-salida),
			opacity 200ms ease-out,
			content-visibility 260ms allow-discrete;
	}

	.qnxb-detalle[open]::details-content {
		block-size: auto;
		opacity: 1;
	}
}

/* 17. Detalles de interaccion (Emil Kowalski) ============================ */

/*
 * El pulsado encoge en vez de bajar.
 *
 * scale(0.97) escala también el texto y el borde del botón, así que la pieza
 * entera se comprime como un objeto físico. Un translateY solo la desliza, que
 * es un movimiento que ningún botón real hace.
 */
.wp-element-button:active,
.wp-block-button__link:active { transform: scale(0.97); }

/*
 * El subrayado del menú crece desde la izquierda.
 *
 * Va con scaleX sobre un pseudo-elemento y no animando border-bottom o width:
 * transform corre en el compositor, las otras dos obligan a recalcular la
 * disposición en cada cuadro.
 */
.qnxb-cabecera .wp-block-navigation-item__content {
	position: relative;
}

.qnxb-cabecera .wp-block-navigation-item__content::after {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0.15rem;
	height: 1px;
	background: currentColor;
	transform: scaleX(0);
	transform-origin: left;
	transition: transform 200ms var(--qnxb-salida);
}

.qnxb-cabecera .wp-block-navigation-item__content:hover::after { transform: scaleX(1); }

/* En el menú desplegado de móvil el subrayado sobra: ahí los enlaces son
   grandes y ya se distinguen solos. */
.is-menu-open .wp-block-navigation-item__content::after { display: none; }

/* 18. Remate tipografico ================================================= */

/*
 * balance reparte las palabras entre las líneas de un titular para que ninguna
 * quede sola al final. pretty hace lo mismo con el texto corrido, evitando la
 * palabra huérfana en el último renglón.
 */
h1, h2, h3, h4 { text-wrap: balance; }

.qnxb-bajada, .qnxb-detalle p { text-wrap: pretty; }

@media (prefers-reduced-motion: reduce) {
	.qnxb-detalle::details-content,
	.qnxb-detalle[open]::details-content {
		transition: none;
		block-size: auto;
		opacity: 1;
	}

	.qnxb-cabecera .wp-block-navigation-item__content::after { transition: none; }
}

/* A zoom alto la etiqueta del boton ya no cabe en una linea. Se le permite
   partir: dos lineas se leen, una linea cortada no. */
@media (max-width: 30rem) {
	.wp-element-button,
	.wp-block-button__link {
		white-space: normal;
		text-align: center;
	}

	.wp-block-buttons { width: 100%; }
	.wp-block-button { max-width: 100%; }
}

/* 19. Redes ============================================================== */

/*
 * Los iconos van en blanco, no en el color de cada marca.
 *
 * En este tema el color significa madurez de una línea. Una fila de logos en
 * azul de LinkedIn, degradado de Instagram y demás mete tres colores nuevos en
 * el pie y desarma esa lectura. En blanco leen como lo que son: enlaces.
 */
.qnxb-redes .wp-block-social-link {
	transition: opacity 160ms ease-out, transform 160ms var(--qnxb-salida);
}

.qnxb-redes .wp-block-social-link a {
	padding: 0.35rem;
	/* El icono del núcleo mide 24px; el área pulsable queda muy por debajo del
	   mínimo de 44px con el que se falla el toque en un teléfono. */
	min-width: 44px;
	min-height: 44px;
	display: flex;
	align-items: center;
	justify-content: center;
}

.qnxb-redes .wp-block-social-link:hover { opacity: 0.72; }

.qnxb-redes .wp-block-social-link:active { transform: scale(0.94); }

/* El bloque del núcleo dibuja su propio foco redondeado, que sobre negro casi
   no se ve. Se reemplaza por el mismo trazo doble del resto del sitio. */
.qnxb-redes .wp-block-social-link a:focus-visible {
	outline: 2px solid #fff;
	outline-offset: 2px;
	box-shadow: 0 0 0 4px #000;
}

@media print {
	/* En papel un icono sin su dirección no sirve de nada. */
	.qnxb-redes { display: none !important; }
}

/* 20. Fondos generados ==================================================== */

/*
 * Cada panel dibuja el problema del que habla, con SVG dentro del CSS.
 *
 * Sin fotografía y sin archivos: cero peticiones de red, cero licencias, y
 * nada que insinúe operaciones reales que no existen. Escala a cualquier
 * pantalla sin pesar más, que es lo que ninguna imagen de mapa de bits hace.
 *
 * Orden de capas, de abajo hacia arriba:
 *   ::after  → el dibujo          (z 0)
 *   ::before → el velo oscuro     (z 1)
 *   contenido                      (z 2)
 */
.qnxb-fondo--reticula::after {
	content: '';
	position: absolute;
	/* Sobra por arriba y por abajo para tener margen del desplazamiento lento
	   sin que asomen los bordes. */
	inset: -12% 0;
	z-index: 0;
	background-repeat: no-repeat;
	background-position: center;
	background-size: cover;
	pointer-events: none;
	/*
	 * Se desvanece hacia la esquina donde va el texto. El dibujo tiene que
	 * quedar detrás, no competir: en la zona del titular casi no se ve.
	 */
	-webkit-mask-image: radial-gradient(ellipse 110% 90% at 78% 22%, #000 20%, transparent 78%);
	mask-image: radial-gradient(ellipse 110% 90% at 78% 22%, #000 20%, transparent 78%);
}


/*
 * Retícula técnica de base, para los bloques de lectura.
 *
 * Va con gradientes y no con SVG: dos líneas de CSS pesan menos que cualquier
 * archivo y el navegador las repite sin decodificar nada.
 */
.qnxb-fondo--reticula::after {
	background-image:
		linear-gradient(rgba(255, 255, 255, 0.055) 1px, transparent 1px),
		linear-gradient(90deg, rgba(255, 255, 255, 0.055) 1px, transparent 1px);
	background-size: 76px 76px;
	background-position: 0 0;
	-webkit-mask-image: radial-gradient(ellipse 90% 80% at 80% 15%, #000 5%, transparent 70%);
	mask-image: radial-gradient(ellipse 90% 80% at 80% 15%, #000 5%, transparent 70%);
}

/*
 * Profundidad: el dibujo se desplaza más lento que la página.
 *
 * translateY y nada más: se resuelve en el compositor, sin recalcular la
 * disposición ni repintar. Animar background-position obligaría a repintar el
 * panel entero en cada cuadro.
 */
@supports (animation-timeline: view()) {
	.qnxb-fondo--reticula::after,
	.qnxb-trama {
		animation: qnxb-profundidad linear both;
		animation-timeline: view();
		animation-range: entry 0% exit 100%;
		will-change: transform;
	}

	@keyframes qnxb-profundidad {
		from { transform: translateY(-5%); }
		to   { transform: translateY(5%); }
	}
}

@media (prefers-reduced-motion: reduce) {
	.qnxb-fondo--reticula::after {
		animation: none;
		transform: none;
		will-change: auto;
	}
}

/* En papel el dibujo solo gasta tinta. */
@media print {
	.qnxb-fondo--reticula::after { display: none !important; }
}

/* 21. Soporte de los paneles con fondo ==================================== */

/*
 * Los paneles cortos no son .is-style-panel-completo, así que no traen ni
 * posición relativa ni recorte: sin esto el dibujo del ::after se escaparía
 * fuera del panel y se apilaría sobre la sección siguiente.
 */
.qnxb-fondo {
	position: relative;
	/* clip y no hidden: hidden crearía contenedor de scroll y volvería a
	   congelar las animaciones dirigidas por scroll. */
	overflow: clip;
}

/* El texto siempre por encima del dibujo. */
.qnxb-fondo > * { position: relative; z-index: 2; }

/* 22. Desplazamiento entre secciones ===================================== */

/*
 * El desplazamiento suave va condicionado a que no se haya pedido movimiento
 * reducido. Declararlo sin condición y anularlo después con !important funciona,
 * pero deja el navegador calculando un recorrido que nadie quiere ver.
 */
@media (prefers-reduced-motion: no-preference) {
	html { scroll-behavior: smooth; }
}

/*
 * Sin esto, cada ancla del menú deja el titular DEBAJO de la cabecera fija.
 * El navegador lleva la sección al borde superior de la ventana sin saber que
 * ahí hay 72 px de barra tapando. Es el defecto clásico de toda cabecera fija.
 */
html {
	scroll-padding-top: 5.5rem;
}

/* 23. Botones: el relleno que sube ====================================== */

/*
 * El fondo entra desde abajo con scaleY sobre un pseudo-elemento.
 *
 * scaleY se resuelve en el compositor. Animar height o background-position
 * obligaría a recalcular disposición o a repintar el botón en cada cuadro, y
 * son elementos que se recorren con el cursor a toda velocidad.
 *
 * El pseudo-elemento se pinta encima del fondo del botón pero debajo de su
 * texto, así que no hace falta tocar el color de fondo: el relleno lo tapa.
 */
.wp-block-button__link,
.wp-element-button {
	position: relative;
	overflow: hidden;
	isolation: isolate;
	transition: color 260ms var(--qnxb-salida), transform 100ms ease-out;
}

.wp-block-button__link::before,
.wp-element-button::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: -1;
	transform: scaleY(0);
	transform-origin: bottom;
	transition: transform 320ms var(--qnxb-salida);
}

/* Botón con trazo: sube el blanco, el texto pasa a negro. */
.wp-block-button.is-style-outline > .wp-block-button__link::before { background: #fff; }

.wp-block-button.is-style-outline > .wp-block-button__link:hover,
.wp-block-button.is-style-outline > .wp-block-button__link:focus-visible { color: #000; }

/*
 * Botón relleno: sube el negro y el texto pasa a blanco. Es el mismo gesto
 * invertido. Dos movimientos distintos en el mismo sitio se leen como dos
 * componentes distintos, y son el mismo.
 */
.wp-block-button:not(.is-style-outline) > .wp-block-button__link::before { background: #000; }

.wp-block-button:not(.is-style-outline) > .wp-block-button__link:hover,
.wp-block-button:not(.is-style-outline) > .wp-block-button__link:focus-visible { color: #fff; }

/* El teclado recibe exactamente el mismo trato que el cursor. */
.wp-block-button__link:hover::before,
.wp-block-button__link:focus-visible::before { transform: scaleY(1); }

@media (prefers-reduced-motion: reduce) {
	.wp-block-button__link::before,
	.wp-element-button::before { transition: none; }
}

/* 24. Menú de móvil ===================================================== */

/*
 * El icono del núcleo es un SVG fijo. Se sustituye por dos barras dibujadas con
 * pseudo-elementos: pesan cero y se pueden animar, que es lo que un SVG
 * incrustado por WordPress no permite.
 */
.wp-block-navigation__responsive-container-open svg,
.wp-block-navigation__responsive-container-close svg { display: none; }

.wp-block-navigation__responsive-container-open,
.wp-block-navigation__responsive-container-close {
	position: relative;
	width: 44px;
	height: 44px;
	color: #fff;
}

.wp-block-navigation__responsive-container-open::before,
.wp-block-navigation__responsive-container-open::after,
.wp-block-navigation__responsive-container-close::before,
.wp-block-navigation__responsive-container-close::after {
	content: '';
	position: absolute;
	left: 50%;
	top: 50%;
	width: 22px;
	height: 1.5px;
	margin-left: -11px;
	background: currentColor;
	transition: transform 260ms var(--qnxb-salida);
}

/* Cerrado: dos barras separadas. */
.wp-block-navigation__responsive-container-open::before { transform: translateY(-5px); }
.wp-block-navigation__responsive-container-open::after  { transform: translateY(5px); }

/* Abierto: las mismas dos barras cruzadas. */
.wp-block-navigation__responsive-container-close::before { transform: rotate(45deg); }
.wp-block-navigation__responsive-container-close::after  { transform: rotate(-45deg); }

.wp-block-navigation__responsive-container-open:hover::before { transform: translateY(-7px); }
.wp-block-navigation__responsive-container-open:hover::after  { transform: translateY(7px); }

@media (prefers-reduced-motion: reduce) {
	.wp-block-navigation__responsive-container-open::before,
	.wp-block-navigation__responsive-container-open::after,
	.wp-block-navigation__responsive-container-close::before,
	.wp-block-navigation__responsive-container-close::after { transition: none; }
}

/*
 * Velo para los paneles cortos con dibujo.
 *
 * Los paneles a pantalla completa ya traen el suyo. Los cortos no, y sin él la
 * garantía de legibilidad depende de dónde caiga el dibujo: el texto secundario
 * (#A8ABB0) sobre el punto más claro de la retícula da 3,87:1, por debajo del
 * mínimo de 4,5 para texto corrido.
 *
 * El velo hace que ese número no dependa de la suerte. Se concentra abajo a la
 * izquierda, que es donde vive el texto, y deja respirar el dibujo arriba a la
 * derecha.
 */
.qnxb-fondo:not(.is-style-panel-completo)::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient(
		to top right,
		rgba(0, 0, 0, 0.88) 0%,
		rgba(0, 0, 0, 0.62) 40%,
		rgba(0, 0, 0, 0.18) 75%,
		rgba(0, 0, 0, 0) 100%
	);
	pointer-events: none;
}

@media print {
	.qnxb-fondo:not(.is-style-panel-completo)::before { display: none !important; }
}

/* 25. Trama incrustada: el dibujo que se traza =========================== */

/*
 * La trama pasó de imagen de fondo a SVG dentro del documento.
 *
 * El motivo no es capricho: en la revisión de diseño quedó anotado que estos
 * dibujos casi no se percibían. La salida fácil era subirles la opacidad, que
 * los habría vuelto ruido detrás del texto. La salida buena es que se TRACEN al
 * entrar: el ojo detecta movimiento donde no detectaba una línea quieta, y el
 * dibujo puede quedarse tenue.
 *
 * Un background-image es opaco para el navegador; sus trazos no existen como
 * elementos. Incrustado, cada trazo es un nodo animable.
 */
.qnxb-trama {
	position: absolute;
	inset: -12% 0;
	width: 100%;
	height: 124%;
	z-index: 0;
	pointer-events: none;
	/* Se apaga hacia donde va el texto: el dibujo acompaña, no compite. */
	-webkit-mask-image: radial-gradient(ellipse 110% 90% at 78% 22%, #000 20%, transparent 78%);
	mask-image: radial-gradient(ellipse 110% 90% at 78% 22%, #000 20%, transparent 78%);
}

/*
 * vector-effect mantiene el grosor del trazo constante aunque el SVG se escale
 * para cubrir el panel. Sin esto, en una pantalla ancha las líneas engordan y el
 * dibujo pasa de sutil a pesado.
 */
.qnxb-trama path {
	stroke: rgba(255, 255, 255, 0.17);
	stroke-width: 1.5;
	vector-effect: non-scaling-stroke;
	fill: none;
}

.qnxb-trama .qnxb-guia { stroke: rgba(255, 255, 255, 0.07); }
.qnxb-trama .qnxb-tenue { stroke: rgba(255, 255, 255, 0.10); }
.qnxb-trama .qnxb-nodo { fill: rgba(255, 255, 255, 0.32); stroke: none; }

/*
 * El trazado.
 *
 * pathLength="1" en cada trazo hace que dasharray:1 y dashoffset:1 signifiquen
 * "sin dibujar" para todos por igual, sea una recta corta o una curva larga.
 * Sin él habría que medir cada geometría a mano.
 *
 * Va dentro de @supports: donde no existan las líneas de tiempo de scroll, el
 * dasharray NO se aplica y el dibujo aparece completo. Nunca un trazo escondido
 * dependiendo de una función que el navegador no tiene.
 */
@supports (animation-timeline: view()) {
	.qnxb-trama path {
		stroke-dasharray: 1;
		stroke-dashoffset: 1;
		animation: qnxb-traza linear both;
		animation-timeline: view();
		animation-range: entry 12% cover 42%;
	}

	@keyframes qnxb-traza {
		to { stroke-dashoffset: 0; }
	}

	/* Los nodos aparecen DESPUÉS de que las rutas llegan a ellos. El orden
	   cuenta la historia: primero el camino, después el punto de encuentro. */
	.qnxb-trama .qnxb-nodo {
		opacity: 0;
		animation: qnxb-nodo linear both;
		animation-timeline: view();
		animation-range: cover 34% cover 52%;
	}

	@keyframes qnxb-nodo {
		to { opacity: 1; }
	}
}

@media (prefers-reduced-motion: reduce) {
	.qnxb-trama path {
		stroke-dasharray: none;
		stroke-dashoffset: 0;
		animation: none;
	}

	.qnxb-trama .qnxb-nodo {
		opacity: 1;
		animation: none;
	}

	.qnxb-trama { animation: none; transform: none; }
}

@media print {
	.qnxb-trama { display: none !important; }
}

/* 26. Detalles de entrada =============================================== */

/*
 * La rayita del rótulo se traza de izquierda a derecha.
 *
 * Es el gesto más pequeño del sitio y el que más se repite: hay una antes de
 * cada rótulo. Con scaleX no cuesta nada —corre en el compositor— y convierte
 * una marca estática en el arranque de la línea de lectura.
 */
@supports (animation-timeline: view()) {
	.is-style-rotulo::before,
	.qnxb-rotulo::before {
		transform-origin: left;
		animation: qnxb-raya linear both;
		animation-timeline: view();
		animation-range: entry 10% cover 28%;
	}

	@keyframes qnxb-raya {
		from { transform: scaleX(0); }
		to   { transform: scaleX(1); }
	}
}

@media (prefers-reduced-motion: reduce) {
	.is-style-rotulo::before,
	.qnxb-rotulo::before {
		animation: none;
		transform: none;
	}
}

/*
 * El titular de la portada entra línea por línea.
 *
 * Antes se descubría entero de una vez. Dos líneas que llegan una tras otra se
 * leen en el orden en que están escritas, que es como se lee un titular.
 */
.qnxb-linea-titulo {
	display: block;
	animation: qnxb-descubre 900ms var(--qnxb-salida) both;
}

.qnxb-linea-titulo:nth-child(1) { animation-delay: 140ms; }
.qnxb-linea-titulo:nth-child(2) { animation-delay: 300ms; }

/* El h1 deja de animarse como bloque: ahora lo hacen sus líneas. */
.wp-block-cover__inner-container h1:has(.qnxb-linea-titulo) {
	animation: none;
}

@media (prefers-reduced-motion: reduce) {
	.qnxb-linea-titulo {
		animation: none;
		clip-path: none;
		opacity: 1;
	}
}

/*
 * Los iconos sociales del núcleo vienen en píldora.
 *
 * Todo el resto del sitio está a radio 0: dos formas redondas en el pie rompen
 * el bloqueo de silueta. WordPress las redondea por defecto incluso en el estilo
 * "solo logos", donde ni siquiera hay fondo que redondear.
 */
.wp-block-social-links .wp-social-link,
.wp-block-social-links .wp-social-link a {
	border-radius: 0;
}

/* Encabezado de listado (busqueda, archivo, entradas) ==================== */

.qnxb-encabezado-listado {
	display: grid;
	gap: var(--wp--preset--spacing--20);
	padding-bottom: var(--wp--preset--spacing--30);
	margin-bottom: var(--wp--preset--spacing--30);
	border-bottom: 1px solid var(--wp--preset--color--linea);
}

/* El titular de un listado no es el de la portada: alli grita, aqui solo
   tiene que decir donde esta uno. */
.qnxb-encabezado-listado h1 {
	font-size: var(--wp--preset--font-size--titulo);
}
