@import "tokens-YJLIESf.css";

/* ---------- Fonts (self-hosted, see /DESIGN.md) ---------- */

@font-face {
	font-family: 'IBM Plex Sans';
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/ibm-plex-sans/ibm-plex-sans-latin-400-normal-dCzU-sz.woff2") format('woff2');
}

@font-face {
	font-family: 'IBM Plex Sans';
	font-style: normal;
	font-weight: 500;
	font-display: swap;
	src: url("../fonts/ibm-plex-sans/ibm-plex-sans-latin-500-normal-mSIWrNY.woff2") format('woff2');
}

@font-face {
	font-family: 'IBM Plex Sans';
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url("../fonts/ibm-plex-sans/ibm-plex-sans-latin-600-normal-pA5FLX3.woff2") format('woff2');
}

@font-face {
	font-family: 'IBM Plex Mono';
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/ibm-plex-mono/ibm-plex-mono-latin-400-normal-78NWkmx.woff2") format('woff2');
}

@font-face {
	font-family: 'IBM Plex Mono';
	font-style: normal;
	font-weight: 500;
	font-display: swap;
	src: url("../fonts/ibm-plex-mono/ibm-plex-mono-latin-500-normal-QJNNIVc.woff2") format('woff2');
}

/* ---------- Reset ---------- */

*,
*::before,
*::after {
	box-sizing: border-box;
}

body {
	margin: 0;
}

img,
svg {
	display: block;
	max-width: 100%;
}

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		transition-duration: 0.01ms !important;
	}
}

/* ---------- Base typography ---------- */

body {
	background: var(--color-bg);
	color: var(--color-ink);
	font-family: var(--font-sans);
	font-size: var(--font-size-body);
	line-height: 1.5;
	-webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
	font-family: var(--font-sans);
	font-weight: 600;
	line-height: 1.2;
	margin: 0 0 var(--space-3);
	text-wrap: balance;
}

h1 {
	font-size: var(--font-size-h1);
}
h2 {
	font-size: var(--font-size-h2);
}
h3 {
	font-size: var(--font-size-h3);
}

p {
	margin: 0 0 var(--space-4);
	max-width: 65ch;
}

a {
	color: var(--color-accent);
}

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

.text-muted {
	color: var(--color-ink-muted);
}

.caption {
	font-size: var(--font-size-caption);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--color-ink-muted);
}

.font-mono {
	font-family: var(--font-mono);
}

.tabular-nums {
	font-variant-numeric: tabular-nums;
}

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

/* ---------- Layout ---------- */

.app-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	height: 60px;
	padding: 0 var(--space-5);
	background: var(--color-surface);
	border-bottom: 1px solid var(--color-border);
}

.app-header__brand {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	font-weight: 600;
	color: var(--color-ink);
	text-decoration: none;
}

.app-header__nav {
	display: flex;
	align-items: center;
	gap: var(--space-5);
}

/* Los enlaces de la barra llevaban estos valores repetidos en línea en cada uno;
   al pintarse ahora en bucle desde una sola lista de secciones, pasan a ser clases. */
.app-header__link {
	text-decoration: none;
	color: var(--color-ink-muted);
	font-weight: 400;
}

.app-header__link:hover {
	color: var(--color-ink);
}

.app-header__link.is-current {
	color: var(--color-ink);
	font-weight: 500;
}

.app-header__actions {
	display: flex;
	align-items: center;
	gap: var(--space-3);
}

.page {
	padding: var(--space-6) var(--space-5);
}

.page-work {
	max-width: 720px;
	margin: 0 auto;
	padding: var(--space-6) var(--space-5);
}

.page-header {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	margin-bottom: var(--space-5);
}

.stack {
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
}

.cluster {
	display: flex;
	align-items: center;
	gap: var(--space-3);
}

.login-shell {
	min-height: calc(100vh - 60px);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--space-5);
}

.login-card {
	width: 100%;
	max-width: 360px;
}

.login-card h1 {
	font-size: var(--font-size-h2);
	margin-bottom: var(--space-1);
}

/* ---------- Buttons ---------- */

.btn {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	height: 40px;
	padding: 0 var(--space-4);
	border-radius: var(--radius-sm);
	border: 1px solid transparent;
	font-family: var(--font-sans);
	font-size: var(--font-size-small);
	font-weight: 500;
	cursor: pointer;
	text-decoration: none;
	transition: background-color 0.12s ease, border-color 0.12s ease;
}

.btn-primary {
	background: var(--color-accent);
	color: var(--color-accent-contrast);
}

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

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

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

.btn-ghost {
	background: transparent;
	color: var(--color-accent);
	height: auto;
	padding: 0;
}

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

.btn-icon {
	width: 40px;
	padding: 0;
	justify-content: center;
	background: transparent;
	border-color: var(--color-border);
	color: var(--color-ink-muted);
}

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

/* ---------- Forms ---------- */

.field {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
}

/* Un grupo (etiqueta + control + ayuda) se separa 8px por dentro, así que necesita
   bastante más aire por fuera o el texto de ayuda queda casi equidistante entre su
   propio campo y el siguiente. Sumado al gap de 16px del contenedor da 24px, un
   ratio 3:1 que deja claro qué pertenece a qué. Va como margen —y no subiendo el gap
   de .stack— para no alterar el resto de composiciones que usan esa utilidad. */
.field + .field {
	margin-top: var(--space-2);
}

/* Salvo cuando los campos van en horizontal (p. ej. inicio/fin de ensayo), donde el
   margen superior los desalinearía. */
.cluster > .field + .field {
	margin-top: 0;
}

.field label {
	font-size: var(--font-size-small);
	font-weight: 500;
}

.field .help {
	font-size: var(--font-size-caption);
	color: var(--color-ink-muted);
}

.field input,
.field select,
.field textarea {
	height: 40px;
	padding: 0 var(--space-3);
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-sm);
	color: var(--color-ink);
	font-family: var(--font-sans);
	font-size: var(--font-size-body);
}

.field textarea {
	height: auto;
	/* 6 líneas (24px de line-height + 24px de padding). Los textarea de la app llevan
	   texto largo de verdad y las 2 líneas por defecto del navegador obligan a escribir
	   por una rendija; la medida está tomada de la cita normativa real de Quesos, la más
	   larga del catálogo, que a este alto entra entera sin scroll. */
	min-height: 144px;
	padding: var(--space-3);
	/* Los controles de formulario no heredan el line-height del body. */
	line-height: 1.5;
	/* Solo vertical: redimensionar a lo ancho se sale de la tarjeta. */
	resize: vertical;
}

input[type='checkbox'],
input[type='radio'] {
	height: 16px;
	width: 16px;
	accent-color: var(--color-accent);
}

/* Keeps the visible control small while giving it a real ~40px hit area
   (native checkboxes ignore padding, so a wrapping label is what actually
   works) — see .claude/skills/interface-design. */
.hit-target {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	cursor: pointer;
}

.field input:focus,
.field select:focus,
.field textarea:focus {
	outline: 2px solid var(--color-accent);
	outline-offset: -1px;
	border-color: var(--color-accent);
}

.field-check {
	flex-direction: row;
	align-items: center;
	gap: var(--space-2);
}

/* ---------- Card ---------- */

.card {
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
	padding: var(--space-5);
}

/* Las páginas de detalle (informe, hoja de ruta) apilan tarjetas directamente dentro de
   <main>, que es un bloque sin gap, así que se tocaban entre sí: los bordes de dos
   tarjetas contiguas se leían como una sola caja partida. Mismo criterio que con los
   grupos de formulario — ver la excepción documentada en DESIGN.md. */
.card + .card {
	margin-top: var(--space-4);
}

/* Cuando el contenedor ya aporta separación (formularios apilados, p. ej. una tarjeta
   por muestra en la parte externa), la pone él y no hay que duplicarla. */
.stack > .card + .card,
.cluster > .card + .card {
	margin-top: 0;
}

/* ---------- Table ---------- */

.table-wrap {
	overflow-x: auto;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
}

table.table-data {
	width: 100%;
	border-collapse: collapse;
	background: var(--color-surface);
}

.table-data th,
.table-data td {
	padding: var(--space-3) var(--space-4);
	text-align: left;
	border-bottom: 1px solid var(--color-border);
	font-size: var(--font-size-small);
}

.table-data th {
	font-size: var(--font-size-caption);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--color-ink-muted);
	font-weight: 500;
	position: sticky;
	top: 0;
	background: var(--color-surface);
}

.table-data tbody tr:last-child td {
	border-bottom: none;
}

.table-data tbody tr:hover {
	background: color-mix(in srgb, var(--color-accent) 4%, var(--color-surface));
}

/* Secondary line inside a table cell: a breakdown that qualifies the main value
   (e.g. the sample types behind a sample count). Deliberately not a pill — those are
   reserved for status — and not a tooltip, which hides the information from touch
   and from keyboard users. */
.table-data .cell-detail {
	display: block;
	margin-top: var(--space-1);
	font-family: var(--font-sans);
	font-size: var(--font-size-caption);
	color: var(--color-ink-muted);
	font-variant-numeric: normal;
}

/* Section header inside a results table: the water/pool report types split their
   determinations into EXAMEN ORGANOLÉPTICO / PARÁMETROS FÍSICO QUÍMICOS / PARÁMETROS
   MICROBIOLÓGICOS. Not sticky like the real <thead>, and slightly inset from the
   surface so it reads as a divider inside the table rather than a second header. */
.table-data tbody th.row-section {
	position: static;
	background: color-mix(in srgb, var(--color-ink) 4%, var(--color-surface));
	color: var(--color-ink);
	font-weight: 600;
}

.table-data tbody tr:hover th.row-section {
	background: color-mix(in srgb, var(--color-ink) 4%, var(--color-surface));
}

.table-data td.numeric,
.table-data th.numeric {
	text-align: right;
	font-family: var(--font-mono);
	font-variant-numeric: tabular-nums;
}

/* ---------- Tarjeta-enlace y rejilla de navegación ---------- */

/* Una tarjeta que además es un enlace: sin subrayado, hereda el color del texto y
   responde al puntero con el mismo tinte de acento al 4% que ya usan las filas de
   tabla. Sustituye a los estilos en línea que se repetían en cada tarjeta. */
.card-link,
.card-link:hover {
	display: block;
	text-decoration: none;
	/* Sin esto, el `a:hover` global teñiría de acento todo el texto de la tarjeta, no
	   solo su título; el cuerpo debe seguir leyéndose como texto. */
	color: inherit;
	transition: background-color 0.12s ease, color 0.12s ease;
}

.card-link:hover {
	background: color-mix(in srgb, var(--color-accent) 4%, var(--color-surface));
}

/* El título sí toma el acento: junto al tinte de fondo es lo que hace que la tarjeta
   entera se lea como un enlace y no como un panel de contenido. */
.card-link:hover h3 {
	color: var(--color-accent-hover);
}

.nav-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	gap: var(--space-4);
}

/* `.card + .card` separa tarjetas apiladas en flujo normal; dentro de una rejilla, que
   ya separa con `gap`, desplazaría hacia abajo todas menos la primera y las
   desalinearía. Se anula igual que dentro de .stack y .cluster (ver DESIGN.md). */
.nav-grid > .card + .card {
	margin-top: 0;
}

.nav-group + .nav-group {
	margin-top: var(--space-5);
}

.nav-group__title {
	font-size: var(--font-size-caption);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--color-ink-muted);
	font-weight: 500;
	margin: 0 0 var(--space-3);
}

/* ---------- Búsqueda y filtros de listado ---------- */

.listing-filters {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: var(--space-4);
	margin-bottom: var(--space-4);
}

/* El campo de texto se lleva el espacio sobrante; los desplegables se quedan en su
   ancho natural, que ya lo marca el contenido. */
.listing-filters__search {
	flex: 1 1 220px;
	min-width: 200px;
}

/* Anula el margen entre grupos de formulario apilados: aquí van en horizontal. */
.listing-filters .field + .field {
	margin-top: 0;
}

.listing-filters__active {
	margin-bottom: var(--space-4);
}

/* Criterio activo, con su propio aspa para quitarlo. Redondeo completo como los pills de
   estado: aquí también es semántico —es una etiqueta suelta, no un bloque— y además lo
   distingue de un botón. */
.filter-chip {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	padding: var(--space-1) var(--space-3);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-pill);
	font-size: var(--font-size-small);
	color: var(--color-ink);
	text-decoration: none;
	transition: background-color 0.12s ease, border-color 0.12s ease;
}

.filter-chip:hover {
	background: color-mix(in srgb, var(--color-accent) 6%, var(--color-surface));
	border-color: var(--color-accent);
	color: var(--color-ink);
}

/* Texto solo para lectores de pantalla: el aspa se ve, pero "×" no dice qué hace. */
.visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* ---------- Paginación ---------- */

.pagination {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	margin-top: var(--space-4);
}

/* El estado va en medio y empuja los dos controles a los extremos: la posición de
   «Anterior» y «Siguiente» no se mueve al cambiar de página, aunque el texto de en medio
   cambie de ancho. */
.pagination__status {
	flex: 1;
	margin: 0;
	text-align: center;
	font-size: var(--font-size-small);
}

/* Extremo de la lista: se deja el control visible pero inerte, en vez de esconderlo, para
   que los dos botones no bailen de sitio en la primera y la última página. */
.btn.is-disabled {
	opacity: 0.45;
	pointer-events: none;
}

/* ---------- Bloque plegable (<details> nativo) ---------- */

/* Agrupa un listado largo bajo cabeceras que se abren y cierran. Se apoya en <details>
   nativo: sin JavaScript, accesible por teclado y con búsqueda del navegador incluida
   en los que la soportan. */
.collapse {
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
	overflow: hidden;
}

.collapse + .collapse {
	margin-top: var(--space-3);
}

.collapse__summary {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	padding: var(--space-3) var(--space-4);
	cursor: pointer;
	font-weight: 500;
	/* Oculta el triángulo del navegador: abajo se dibuja uno propio que sí gira. */
	list-style: none;
}

.collapse__summary::-webkit-details-marker {
	display: none;
}

/* Triángulo dibujado con bordes, no un emoji ni un icono externo (ver DESIGN.md). */
.collapse__summary::before {
	content: '';
	flex: none;
	width: 0;
	height: 0;
	border-left: 5px solid var(--color-ink-muted);
	border-top: 4px solid transparent;
	border-bottom: 4px solid transparent;
	transition: transform 0.12s ease;
}

.collapse[open] > .collapse__summary::before {
	transform: rotate(90deg);
}

.collapse__summary:hover {
	background: color-mix(in srgb, var(--color-accent) 4%, var(--color-surface));
}

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

/* Contador o estado del grupo, alineado al extremo opuesto de la cabecera. */
.collapse__meta {
	margin-left: auto;
	font-size: var(--font-size-small);
	color: var(--color-ink-muted);
	font-weight: 400;
}

.collapse__body {
	border-top: 1px solid var(--color-border);
}

.collapse__empty {
	padding: var(--space-4);
	margin: 0;
	color: var(--color-ink-muted);
	font-size: var(--font-size-small);
}

/* ---------- Status pill ---------- */

.pill {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	height: 24px;
	padding: 0 var(--space-3);
	border-radius: var(--radius-pill);
	font-size: var(--font-size-caption);
	font-weight: 500;
	letter-spacing: 0.02em;
	white-space: nowrap;
}

.pill::before {
	content: '';
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: currentColor;
}

.pill-success {
	background: var(--color-success-bg);
	color: var(--color-success);
}

.pill-warning {
	background: var(--color-warning-bg);
	color: var(--color-warning);
}

.pill-critical {
	background: var(--color-critical-bg);
	color: var(--color-critical);
}

.pill-neutral {
	background: var(--color-bg);
	color: var(--color-ink-muted);
	border: 1px solid var(--color-border);
}

.pill-neutral::before {
	display: none;
}

/* ---------- Theme toggle ---------- */

.theme-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	border-radius: var(--radius-sm);
	border: 1px solid var(--color-border);
	background: transparent;
	color: var(--color-ink-muted);
	cursor: pointer;
}

.theme-toggle:hover {
	border-color: var(--color-accent);
	color: var(--color-accent);
}

/* ---------- Modal (native <dialog>, driven by confirm_controller.js) ---------- */

.modal {
	width: min(420px, calc(100vw - var(--space-6)));
	padding: var(--space-5);
	background: var(--color-surface);
	color: var(--color-ink);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
	/* A real floating element, so this is one of the few places we use a shadow. */
	box-shadow: var(--shadow-overlay);
}

.modal::backdrop {
	background: rgba(0, 0, 0, 0.45);
}

.modal-title {
	margin: 0 0 var(--space-3);
	font-size: var(--font-size-h3);
}

.modal-body {
	margin: 0 0 var(--space-5);
	color: var(--color-ink-muted);
}

.modal-actions {
	display: flex;
	justify-content: flex-end;
	gap: var(--space-3);
}
