/* AITOI — componentes canónicos. Todo con prefijo `ui-` para no chocar con el CSS legado
   (aitoi.css) mientras las pantallas se migran. Solo tokens de design_tokens.css; el catálogo
   vivo con el markup de cada uno está en /estilo/. Se carga después de aitoi.css a propósito:
   en un empate de especificidad gana el canon. */

/* ===== Botones =====
   <button class="ui-boton ui-primario">, .ui-secundario, .ui-peligro, .ui-fantasma; .ui-chico;
   .ui-boton-icono (cuadrado, solo SVG + aria-label). Las acciones son botones, nunca enlaces de texto;
   un <a> que lleva a otra página puede vestirse de botón con estas mismas clases. */
.ui-boton { display: inline-flex; align-items: center; justify-content: center; gap: var(--espacio-2); box-sizing: border-box;
  height: var(--altura-control); padding: 0 var(--espacio-4); margin: 0; border: 1px solid transparent; border-radius: var(--radio-sm);
  font: inherit; font-size: var(--texto-sm); font-weight: var(--peso-negrita); line-height: 1; text-decoration: none; white-space: nowrap;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: background-color var(--transicion-rapida), border-color var(--transicion-rapida), color var(--transicion-rapida), box-shadow var(--transicion-rapida); }
.ui-boton svg { width: 1.125em; height: 1.125em; flex: none; }
.ui-boton:focus-visible { outline: 2px solid var(--acento); outline-offset: 2px; }
.ui-boton:disabled, .ui-boton[aria-disabled=true] { opacity: .55; cursor: not-allowed; }
/* Texto largo (una confirmación que dice exactamente qué pasa): se parte en renglones en vez de salirse de la página en móvil. */
.ui-boton.ui-largo { white-space: normal; height: auto; min-height: var(--altura-control); max-width: 100%; padding-top: var(--espacio-2); padding-bottom: var(--espacio-2); line-height: var(--interlineado-corto); text-align: center; }
.ui-boton.ui-primario { background: var(--acento); border-color: var(--acento); color: var(--acento-inverso); }
.ui-boton.ui-primario:hover:not(:disabled) { background: var(--acento-hover); border-color: var(--acento-hover); color: var(--acento-inverso); }
.ui-boton.ui-secundario { background: var(--superficie); border-color: var(--borde-fuerte); color: var(--texto); box-shadow: var(--sombra-sm); }
.ui-boton.ui-secundario:hover:not(:disabled) { background: var(--superficie-2); border-color: var(--borde-fuerte); color: var(--texto); }
.ui-boton.ui-peligro { background: var(--peligro); border-color: var(--peligro); color: var(--texto-inverso); }
.ui-boton.ui-peligro:hover:not(:disabled) { background: var(--peligro-hover); border-color: var(--peligro-hover); color: var(--texto-inverso); }
.ui-boton.ui-peligro:focus-visible { outline-color: var(--peligro); }
.ui-boton.ui-fantasma { background: transparent; border-color: transparent; color: var(--texto-2); }
.ui-boton.ui-fantasma:hover:not(:disabled) { background: var(--superficie-2); color: var(--texto); }
.ui-boton.ui-chico { height: var(--altura-control-chica); padding: 0 var(--espacio-3); font-size: var(--texto-xs); }
.ui-boton.ui-boton-icono { width: var(--altura-control); padding: 0; }
.ui-boton.ui-boton-icono.ui-chico { width: var(--altura-control-chica); }
.ui-boton.ui-boton-icono.ui-fantasma.ui-peligroso:hover:not(:disabled) { background: var(--peligro-tinte); color: var(--peligro-texto); }
.ui-boton[aria-busy=true] { cursor: progress; opacity: .8; }
.ui-boton .ui-spinner { width: 1em; height: 1em; box-sizing: border-box; border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%; animation: ui-girar .8s linear infinite; }
@keyframes ui-girar { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .ui-boton .ui-spinner { animation: none; } }
/* Fila de acciones al pie de un formulario o al frente de una tabla. */
.ui-acciones { display: flex; flex-wrap: wrap; gap: var(--espacio-2); align-items: center; margin: var(--espacio-6) 0 0; }
.ui-acciones.ui-derecha { justify-content: flex-end; }

/* ===== Etiquetas (badges) =====
   <span class="ui-etiqueta ui-exito">: .ui-exito, .ui-aviso, .ui-peligro, .ui-info, .ui-neutra. Con .ui-punto lleva un punto de color. */
.ui-etiqueta { display: inline-flex; align-items: center; gap: var(--espacio-1); height: 1.5rem; padding: 0 var(--espacio-2); box-sizing: border-box;
  border: 1px solid var(--borde); border-radius: var(--radio-pildora); background: var(--superficie-2); color: var(--texto-2);
  font-size: var(--texto-xs); font-weight: var(--peso-negrita); line-height: 1; white-space: nowrap; }
.ui-etiqueta.ui-exito { background: var(--exito-tinte); border-color: var(--exito-borde); color: var(--exito-texto); }
.ui-etiqueta.ui-aviso { background: var(--aviso-tinte); border-color: var(--aviso-borde); color: var(--aviso-texto); }
.ui-etiqueta.ui-peligro { background: var(--peligro-tinte); border-color: var(--peligro-borde); color: var(--peligro-texto); }
.ui-etiqueta.ui-info { background: var(--info-tinte); border-color: var(--info-borde); color: var(--info-texto); }
.ui-etiqueta.ui-punto::before { content: ""; width: .5em; height: .5em; border-radius: 50%; background: currentColor; }
.ui-conteo { display: inline-flex; align-items: center; justify-content: center; min-width: 1.5rem; height: 1.5rem; padding: 0 var(--espacio-2); box-sizing: border-box;
  border-radius: var(--radio-pildora); background: var(--acento); color: var(--acento-inverso); font-size: var(--texto-xs); font-weight: var(--peso-fuerte); line-height: 1; }
/* Con .ui-cero (no hay nadie) queda gris tenue: el acento solo cuando hay gente. */
.ui-conteo.ui-cero { background: var(--superficie-2); color: var(--texto-3); }
/* .ui-conteo--neutro: un total que no debe verse verde ni rojo (p. ej. retardos o faltas de la semana). Mismo contorno que .ui-etiqueta. */
.ui-conteo.ui-conteo--neutro { background: var(--superficie-2); color: var(--texto-2); border: 1px solid var(--borde); }

/* ===== Formularios =====
   <div class="ui-campo"><label>…</label><input class="ui-entrada">…<p class="ui-ayuda">…</p><p class="ui-error">…</p></div>
   .ui-campo.ui-con-error pinta el borde en peligro. .ui-casilla es la fila de checkbox/radio. .ui-fila pone campos lado a lado. */
.ui-campo { display: flex; flex-direction: column; gap: var(--espacio-1); margin: 0 0 var(--espacio-4); min-width: 0; }
.ui-campo > label, .ui-rotulo { font-size: var(--texto-sm); font-weight: var(--peso-negrita); color: var(--texto); margin: 0; }
.ui-campo .ui-opcional { font-weight: var(--peso-normal); color: var(--texto-3); }
input.ui-entrada, select.ui-entrada, textarea.ui-entrada { width: 100%; box-sizing: border-box; height: var(--altura-control); padding: 0 var(--espacio-3); margin: 0;
  border: 1px solid var(--borde-fuerte); border-radius: var(--radio-sm); background: var(--superficie); color: var(--texto); font: inherit; font-size: var(--texto-sm);
  transition: border-color var(--transicion-rapida), box-shadow var(--transicion-rapida); }
textarea.ui-entrada { height: auto; min-height: calc(var(--altura-control) * 2.2); padding: var(--espacio-2) var(--espacio-3); line-height: var(--interlineado); resize: vertical; }
select.ui-entrada { padding-right: var(--espacio-8); appearance: none; -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--texto-2) 50%), linear-gradient(135deg, var(--texto-2) 50%, transparent 50%);
  background-position: calc(100% - 1.05rem) 55%, calc(100% - .75rem) 55%; background-size: .3rem .3rem; background-repeat: no-repeat; }
.ui-entrada::placeholder { color: var(--texto-3); }
.ui-entrada:hover:not(:disabled) { border-color: var(--texto-3); }
.ui-entrada:focus, .ui-entrada:focus-visible { outline: none; border-color: var(--acento); box-shadow: var(--anillo); }
.ui-entrada:disabled { background: var(--superficie-2); color: var(--texto-3); cursor: not-allowed; }
.ui-entrada.ui-chica { height: var(--altura-control-chica); font-size: var(--texto-xs); }
.ui-campo.ui-con-error .ui-entrada { border-color: var(--peligro); }
.ui-campo.ui-con-error .ui-entrada:focus, .ui-campo.ui-con-error .ui-entrada:focus-visible { box-shadow: var(--anillo-error); }
.ui-ayuda { margin: 0; font-size: var(--texto-xs); color: var(--texto-2); }
.ui-error { margin: 0; font-size: var(--texto-xs); font-weight: var(--peso-medio); color: var(--peligro-texto); }
.ui-casilla { display: flex; align-items: flex-start; gap: var(--espacio-2); margin: 0 0 var(--espacio-3); font-size: var(--texto-sm); font-weight: var(--peso-normal); cursor: pointer; }
.ui-casilla input { width: 1rem; height: 1rem; margin: .2rem 0 0; flex: none; accent-color: var(--acento); }
.ui-casilla input:focus-visible { outline: 2px solid var(--acento); outline-offset: 2px; }
.ui-fila { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: var(--espacio-3) var(--espacio-4); }
.ui-fila > .ui-campo { margin-bottom: var(--espacio-4); }
.ui-formulario { max-width: 32rem; }
/* Grupo de casillas o radios con su leyenda: <fieldset class="ui-grupo"><legend class="ui-rotulo">…</legend>
   <div class="ui-grupo-opciones"><label class="ui-casilla">…</label>…</div><p class="ui-ayuda">…</p></fieldset>. Con .ui-apilado las
   opciones van una por renglón (radios con texto largo). */
.ui-grupo { min-width: 0; margin: 0 0 var(--espacio-4); padding: 0; border: 0; }
.ui-grupo > legend { padding: 0; margin: 0 0 var(--espacio-2); }
.ui-grupo-opciones { display: flex; flex-wrap: wrap; gap: var(--espacio-2) var(--espacio-4); margin: 0 0 var(--espacio-2); }
.ui-grupo-opciones > .ui-casilla { margin: 0; }
.ui-grupo-opciones.ui-apilado { flex-direction: column; }
/* Formulario que no cabe en la columna angosta (listas y grupos largos): el ancho del contenido, sin pasarse de la página. */
.ui-formulario.ui-ancho { max-width: 48rem; }
/* Formulario solo en la página (entrar, registrar la empresa): centrado en la columna. */
.ui-formulario.ui-centrado { margin-left: auto; margin-right: auto; }
/* Filtros de una lista: <form class="ui-fila ui-filtros"> con .ui-campo y el botón, todo en una línea y alineado a la base de
   los campos. Una acción aparte (otro formulario, p. ej. un POST) va en .ui-filtros-aparte, dentro de .ui-barra-filtros junto
   a los filtros: su .ui-rotulo le da la misma altura de label, así su botón queda a la base de los campos; se va a la derecha
   si cabe y, si no, baja a su propia línea. Bajo 640 px todo se apila a lo ancho. */
.ui-barra-filtros { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--espacio-3) var(--espacio-6); margin: 0 0 var(--espacio-4); }
.ui-fila.ui-filtros { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--espacio-3); margin: 0 0 var(--espacio-4); }
.ui-barra-filtros > .ui-filtros { flex: 1 1 44rem; margin: 0; }  /* sin espacio para los filtros en una línea, la acción aparte baja */
.ui-filtros > .ui-campo { flex: 1 1 9rem; max-width: 20rem; margin: 0; }
.ui-filtros > .ui-boton { flex: none; }
.ui-filtros-aparte { display: flex; flex-direction: column; align-items: flex-start; gap: var(--espacio-1); max-width: 18rem; margin: 0 0 0 auto; }
@media (max-width: 640px) {
  .ui-filtros > .ui-campo { flex-basis: 100%; max-width: none; }
  .ui-filtros > .ui-boton, .ui-filtros-aparte, .ui-filtros-aparte .ui-boton { width: 100%; max-width: none; }
  .ui-filtros-aparte { margin-left: 0; }
}
/* Buscador en píldora: la lupa envía, la × (solo con búsqueda aplicada) la borra. El foco lo lleva la píldora. */
.ui-buscador { display: inline-flex; align-items: center; gap: var(--espacio-1); width: 18rem; max-width: 100%; height: var(--altura-control); box-sizing: border-box;
  padding: 0 var(--espacio-1); background: var(--superficie); border: 1px solid var(--borde-fuerte); border-radius: var(--radio-pildora);
  transition: border-color var(--transicion-rapida), box-shadow var(--transicion-rapida); }
.ui-buscador:hover { border-color: var(--texto-3); }
.ui-buscador:focus-within { border-color: var(--acento); box-shadow: var(--anillo); }
.ui-buscador input { flex: 1; min-width: 0; height: 100%; border: 0; outline: 0; margin: 0; padding: 0 var(--espacio-1); background: transparent; font: inherit; font-size: var(--texto-sm); color: var(--texto); }
.ui-buscador input::placeholder { color: var(--texto-3); }
.ui-buscador input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.ui-buscador .ui-boton { color: var(--texto-3); border-radius: 50%; }
.ui-buscador:focus-within .ui-boton, .ui-buscador .ui-boton:hover { color: var(--texto); }

/* ===== Tablas =====
   <div class="ui-tabla-marco"><div class="ui-tabla-barra">título · buscador · acciones</div><table class="ui-tabla">…</table></div>
   th.ui-ordenable lleva un <a> con la flecha; .ui-activo es la columna del orden. td.ui-celda-acciones lleva botones de icono.
   «Tablas con vida» (UBI-37): sin rejilla, renglones pares con un velo (--superficie-cebra) y divisores de --linea; el encabezado
   es una banda sobre --fondo. El acento no entra a las celdas: el nombre va en negritas y en --texto; el color lo ponen las
   cifras de incidencias (.ui-cifra) y las etiquetas de estado. Las flechas de orden son chevrones SVG (como los iconos de
   UBI-35); el glifo ↕ ▲ ▼ sigue en el HTML (aria-hidden) y aquí se pinta encima con una máscara. */
:root {
  --icono-orden: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 9.5 5-5 5 5M7 14.5l5 5 5-5'/%3E%3C/svg%3E");
  --icono-orden-asc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 15 6-6 6 6'/%3E%3C/svg%3E");
  --icono-orden-desc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
.ui-tabla-marco { background: var(--superficie); border: 1px solid var(--borde); border-radius: var(--radio-md); box-shadow: var(--sombra-sm); overflow: hidden; }
.ui-tabla-barra { display: flex; flex-wrap: wrap; align-items: center; gap: var(--espacio-3); padding: var(--espacio-3) var(--espacio-4); border-bottom: 1px solid var(--linea); }
.ui-tabla-barra h1, .ui-tabla-barra h2, .ui-tabla-barra h3 { margin: 0; font-size: var(--texto-lg); font-weight: var(--peso-negrita); }  /* h1 cuando la tabla es la página (Empleados) */
.ui-tabla-barra .ui-relleno { flex: 1; }
.ui-tabla-marco .ui-desplazable { overflow-x: auto; position: relative; }  /* ancla de lo .ui-oculto de las celdas: sin ella se sale de la página en móvil */
.ui-desplazable { overflow-x: auto; position: relative; }  /* igual fuera del marco (catálogo de festivos): la tabla ancha no desborda la página */
.ui-tabla-marco > .ui-vacio { border: 0; border-radius: 0; }
.ui-tabla-marco + .ui-tabla-marco, .ui-paginacion + .ui-tabla-marco { margin-top: var(--espacio-6); }  /* dos tablas seguidas en una página (detalle de departamento) */  /* el marco ya pone el borde: sin búsqueda con resultados o sin filas */
.ui-tabla { width: 100%; border-collapse: collapse; background: var(--superficie); font-size: var(--texto-sm); }
.ui-tabla th, .ui-tabla td { text-align: left; padding: var(--espacio-3) var(--espacio-4); border: 0; border-bottom: 1px solid var(--linea); vertical-align: middle; }
.ui-tabla thead th { font-size: var(--texto-xs); font-weight: var(--peso-negrita); text-transform: uppercase; letter-spacing: var(--espaciado-etiqueta);
  color: var(--texto-2); background: var(--fondo); border-bottom-color: var(--borde); padding-top: var(--espacio-2); padding-bottom: var(--espacio-2); white-space: nowrap; }
.ui-tabla thead th.ui-partible { white-space: normal; }  /* encabezado largo de una columna angosta (cifras): puede ir en dos líneas */
.ui-tabla tbody tr { transition: background-color var(--transicion-rapida); }
.ui-tabla tbody tr:nth-child(even) { background: var(--superficie-cebra); }
.ui-tabla tbody tr:hover { background: var(--superficie-2); }
.ui-tabla tbody tr:last-child td { border-bottom: 0; }
.ui-tabla .ui-num { text-align: right; font-variant-numeric: tabular-nums; padding-left: var(--espacio-2); white-space: nowrap; }  /* alineada a la derecha: el aire va del lado del texto */
.ui-tabla th.ui-num { text-align: right; width: 1%; }  /* las cifras se encogen a su contenido; el sobrante va a las columnas de texto */
.ui-tabla th.ui-num.ui-ordenable a { flex-direction: row-reverse; }  /* la flecha de una cifra va a la izquierda: el rótulo queda alineado con los números */
.ui-tabla td.ui-principal { font-weight: var(--peso-negrita); color: var(--texto); }
.ui-tabla td.ui-principal a { color: inherit; text-decoration: none; }
.ui-tabla td.ui-principal a:hover, .ui-tabla td.ui-principal a:focus-visible { text-decoration: underline; text-underline-offset: .2em; }
.ui-tabla td.ui-secundaria { color: var(--texto-2); }
.ui-tabla th.ui-ordenable a { display: inline-flex; align-items: center; gap: var(--espacio-1); color: inherit; text-decoration: none; }
.ui-tabla th.ui-ordenable a:hover, .ui-tabla th.ui-ordenable a:focus-visible { color: var(--texto); }
.ui-tabla th.ui-ordenable .ui-orden { opacity: .4; font-weight: var(--peso-normal); }
.ui-tabla th.ui-ordenable a:hover .ui-orden { opacity: .8; }
.ui-tabla th.ui-ordenable.ui-activo a { color: var(--texto); }
.ui-tabla th.ui-ordenable.ui-activo .ui-orden { opacity: 1; color: var(--acento-texto); }  /* el orden es estado: la flecha activa lleva el acento */
/* La flecha: el glifo del HTML se apaga (font-size 0) y un chevrón del tamaño del rótulo toma su lugar. */
.ui-orden, main th.ordenable .orden-neutro, main th.ordenable .orden-activo { display: inline-flex; font-size: 0; line-height: 0; vertical-align: middle; }
.ui-orden::before, main th.ordenable .orden-neutro::before, main th.ordenable .orden-activo::before { content: ""; width: .875rem; height: .875rem; background: currentColor;
  -webkit-mask: var(--icono-orden) center / contain no-repeat; mask: var(--icono-orden) center / contain no-repeat; }
[aria-sort=ascending] .ui-orden::before, main th[aria-sort=ascending] .orden-activo::before { -webkit-mask-image: var(--icono-orden-asc); mask-image: var(--icono-orden-asc); }
[aria-sort=descending] .ui-orden::before, main th[aria-sort=descending] .orden-activo::before { -webkit-mask-image: var(--icono-orden-desc); mask-image: var(--icono-orden-desc); }
.ui-tabla th.ui-col-acciones, .ui-tabla td.ui-celda-acciones { width: 1%; white-space: nowrap; text-align: right; padding-top: var(--espacio-1); padding-bottom: var(--espacio-1); }
th.ui-col-acciones { position: relative; }  /* el <span class="ui-oculto"> del encabezado se queda dentro de la celda y no ensancha la página en móvil */
.ui-tabla td.ui-celda-acciones .ui-boton { color: var(--texto-3); }
.ui-tabla tr:hover td.ui-celda-acciones .ui-boton { color: var(--texto-2); }
.ui-tabla td.ui-celda-acciones .ui-boton:hover { color: var(--texto); }
.ui-tabla tr.ui-elegida { background: var(--acento-tinte); }
.ui-tabla-pie { display: flex; flex-wrap: wrap; align-items: center; gap: var(--espacio-3); padding: var(--espacio-3) var(--espacio-4); border-top: 1px solid var(--linea);
  font-size: var(--texto-xs); color: var(--texto-2); }
/* Cifra de incidencia en una celda .ui-num: <span class="ui-cifra ui-aviso">3</span> (retardos, por revisar), .ui-peligro (faltas).
   Un cero va como <span class="ui-cifra ui-cero">0</span>: misma caja sin fondo ni borde, gris tenue, para que los dígitos
   queden alineados con los de las pastillas y el cero no compita. */
.ui-cifra { display: inline-flex; align-items: center; justify-content: flex-end; min-width: 1.75rem; height: 1.375rem; padding: 0 var(--espacio-2); box-sizing: border-box;
  border: 1px solid var(--borde); border-radius: var(--radio-pildora); background: var(--superficie-2); color: var(--texto);
  font-size: var(--texto-xs); font-weight: var(--peso-negrita); font-variant-numeric: tabular-nums; line-height: 1; white-space: nowrap; vertical-align: middle; }
.ui-cifra.ui-aviso { background: var(--aviso-tinte); border-color: var(--aviso-borde); color: var(--aviso-texto); }
.ui-cifra.ui-peligro { background: var(--peligro-tinte); border-color: var(--peligro-borde); color: var(--peligro-texto); }
.ui-cifra.ui-info { background: var(--info-tinte); border-color: var(--info-borde); color: var(--info-texto); }
.ui-cifra.ui-cero { background: transparent; border-color: transparent; color: var(--texto-3); font-weight: var(--peso-normal); }
.ui-tabla .ui-tenue { color: var(--texto-3); }  /* «—» de un dato vacío */

/* ===== Tarjetas =====
   <section class="ui-tarjeta"><header class="ui-tarjeta-cabeza"><h2>…</h2></header><div class="ui-tarjeta-cuerpo">…</div><footer class="ui-tarjeta-pie">…</footer></section>
   .ui-dato es la tarjeta de cifra (tablero), estilo «Contorno» (B2, decisión de Alberto 2026-09-25): borde fino,
   sin sombra, rótulo en minúsculas y peso medio, cifra en negritas. Con .ui-aviso / .ui-peligro / .ui-info / .ui-exito
   es una tarjeta de alerta: fondo blanco, barra izquierda de 3 px del color de estado y cifra en ese color. */
.ui-tarjeta { background: var(--superficie); border: 1px solid var(--borde); border-radius: var(--radio-md); box-shadow: var(--sombra-sm); margin: 0 0 var(--espacio-4); }
.ui-tarjeta-cabeza { display: flex; flex-wrap: wrap; align-items: center; gap: var(--espacio-3); padding: var(--espacio-4) var(--espacio-5); border-bottom: 1px solid var(--linea); }
.ui-tarjeta-cabeza h1, .ui-tarjeta-cabeza h2, .ui-tarjeta-cabeza h3 { margin: 0; font-size: var(--texto-lg); font-weight: var(--peso-negrita); line-height: var(--interlineado-corto); }
.ui-tarjeta-cabeza .ui-relleno { flex: 1; }
.ui-tarjeta-cuerpo { padding: var(--espacio-5); }
.ui-tarjeta-cuerpo > :first-child { margin-top: 0; }
.ui-tarjeta-cuerpo > :last-child { margin-bottom: 0; }
.ui-tarjeta-pie { display: flex; flex-wrap: wrap; gap: var(--espacio-2); align-items: center; padding: var(--espacio-3) var(--espacio-5); border-top: 1px solid var(--linea);
  background: var(--superficie-2); border-radius: 0 0 var(--radio-md) var(--radio-md); }
.ui-tarjeta-pie.ui-derecha { justify-content: flex-end; }
.ui-tablero { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--espacio-3); margin: var(--espacio-4) 0; }
/* Mosaico: tarjetas de navegación en rejilla (portada de Configuración). <ol class="ui-mosaico"> si el orden importa
   (pasos), <ul> si no; cada <li class="ui-tarjeta"> con cabeza, cuerpo y pie con un solo botón. .ui-destacada marca
   la tarjeta que toca (borde y anillo del acento, sin relleno sólido). Una columna en móvil, sin scroll horizontal. */
.ui-mosaico { list-style: none; margin: var(--espacio-3) 0 var(--espacio-8); padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr)); gap: var(--espacio-4); }
.ui-mosaico > .ui-tarjeta { margin: 0; display: flex; flex-direction: column; min-width: 0; }
.ui-mosaico > .ui-tarjeta > .ui-tarjeta-cuerpo { flex: 1; }
.ui-mosaico .ui-tarjeta-cabeza { gap: var(--espacio-2); }
.ui-mosaico .ui-tarjeta-cabeza h3 { min-width: 0; overflow-wrap: anywhere; }
.ui-mosaico > .ui-destacada { border-color: var(--acento-borde); box-shadow: var(--anillo); }
.ui-dato { background: var(--superficie); border: 1px solid var(--borde); border-radius: var(--radio-md); padding: var(--espacio-4) var(--espacio-5); box-shadow: none; min-width: 0; box-sizing: border-box; }
.ui-dato-rotulo { display: block; font-size: var(--texto-sm); font-weight: var(--peso-medio); color: var(--texto-2); }
.ui-dato-cifra { display: block; margin-top: var(--espacio-1); font-size: var(--texto-2xl); font-weight: var(--peso-fuerte); letter-spacing: -.01em; line-height: 1.1; color: var(--texto); font-variant-numeric: tabular-nums; }
.ui-dato-nota { display: block; margin-top: var(--espacio-1); font-size: var(--texto-xs); color: var(--texto-3); }
.ui-dato.ui-aviso, .ui-dato.ui-peligro, .ui-dato.ui-info, .ui-dato.ui-exito { border-left: 3px solid var(--dato-color); padding-left: calc(var(--espacio-5) - 2px); }
.ui-dato.ui-aviso { --dato-color: var(--aviso); }
.ui-dato.ui-peligro { --dato-color: var(--peligro); }
.ui-dato.ui-info { --dato-color: var(--info); }
.ui-dato.ui-exito { --dato-color: var(--exito); }
.ui-dato.ui-aviso .ui-dato-cifra, .ui-dato.ui-peligro .ui-dato-cifra, .ui-dato.ui-info .ui-dato-cifra, .ui-dato.ui-exito .ui-dato-cifra { color: var(--dato-color); }

/* ===== Navegación =====
   .ui-pestanas: pestañas en línea (la activa con aria-current="page"); con conteo, un .ui-conteo--neutro (o .ui-cero) por
   pestaña, así el acento queda solo en la actual (segmentos de estado de Empleados, UBI-55/4). .ui-nav-lateral: lista vertical de secciones.
   .ui-migas: migas de pan. */
.ui-pestanas { display: flex; flex-wrap: wrap; gap: var(--espacio-1); margin: 0 0 var(--espacio-4); padding: 0; list-style: none; border-bottom: 1px solid var(--borde); }
.ui-pestanas a { display: inline-flex; align-items: center; gap: var(--espacio-2); height: var(--altura-control); padding: 0 var(--espacio-3); margin-bottom: -1px; border-bottom: 2px solid transparent;
  color: var(--texto-2); font-size: var(--texto-sm); font-weight: var(--peso-medio); text-decoration: none; transition: color var(--transicion-rapida), border-color var(--transicion-rapida); }
.ui-pestanas a:hover { color: var(--texto); }
.ui-pestanas a[aria-current] { color: var(--acento-texto); border-bottom-color: var(--acento); font-weight: var(--peso-negrita); }
.ui-nav-lateral { list-style: none; margin: 0; padding: 0; }
.ui-nav-lateral li { margin-bottom: 2px; }
.ui-nav-lateral a { display: block; padding: var(--espacio-2) var(--espacio-3); border-radius: var(--radio-sm); color: var(--texto-2); font-size: var(--texto-sm);
  font-weight: var(--peso-medio); text-decoration: none; transition: background-color var(--transicion-rapida), color var(--transicion-rapida); }
.ui-nav-lateral a:hover { background: var(--superficie-2); color: var(--texto); }
.ui-nav-lateral a[aria-current] { background: var(--acento-tinte); color: var(--acento-texto); font-weight: var(--peso-negrita); }
.ui-nav-titulo { font-size: var(--texto-xs); font-weight: var(--peso-negrita); text-transform: uppercase; letter-spacing: var(--espaciado-etiqueta); color: var(--texto-3);
  margin: var(--espacio-2) 0 var(--espacio-2) var(--espacio-3); }
.ui-migas { display: flex; flex-wrap: wrap; align-items: center; gap: var(--espacio-1); margin: 0 0 var(--espacio-3); padding: 0; list-style: none; font-size: var(--texto-sm); color: var(--texto-3); }
.ui-migas a { color: var(--texto-2); text-decoration: none; }
.ui-migas a:hover { color: var(--texto); text-decoration: underline; }
.ui-migas li + li::before { content: "/"; margin-right: var(--espacio-1); color: var(--texto-3); }
.ui-migas [aria-current] { color: var(--texto); font-weight: var(--peso-medio); }

/* ===== Encabezado de página ===== */
.ui-pagina-cabeza { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--espacio-3) var(--espacio-4); margin: 0 0 var(--espacio-6); }
.ui-pagina-cabeza h1 { margin: 0; font-size: var(--texto-xl); font-weight: var(--peso-fuerte); line-height: var(--interlineado-corto); letter-spacing: -.01em; }
.ui-pagina-cabeza .ui-subtitulo { margin: var(--espacio-1) 0 0; font-size: var(--texto-sm); color: var(--texto-2); }
.ui-pagina-cabeza .ui-relleno { flex: 1; }
.ui-pagina-cabeza .ui-acciones { margin: 0; }

/* ===== Estados vacíos =====
   <div class="ui-vacio"><svg…><h3>…</h3><p>…</p><button class="ui-boton ui-primario">…</button></div> */
.ui-vacio { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--espacio-2); padding: var(--espacio-10) var(--espacio-6);
  background: var(--superficie); border: 1px dashed var(--borde-fuerte); border-radius: var(--radio-md); color: var(--texto-2); }
.ui-vacio > svg { width: 2.5rem; height: 2.5rem; color: var(--texto-3); margin-bottom: var(--espacio-1); }
.ui-vacio h3 { margin: 0; font-size: var(--texto-lg); font-weight: var(--peso-negrita); color: var(--texto); }
.ui-vacio p { margin: 0; max-width: 28rem; font-size: var(--texto-sm); }
.ui-vacio .ui-boton { margin-top: var(--espacio-3); }
.ui-vacio.ui-compacto { padding: var(--espacio-6) var(--espacio-4); border-style: solid; border-color: var(--borde); }

/* ===== Alertas =====
   <div class="ui-alerta ui-info" role="status"><svg…><div><strong>Título</strong> texto</div><button class="ui-boton ui-fantasma ui-boton-icono ui-chico ui-cerrar" aria-label="Cerrar">×</button></div>
   Colores: .ui-exito, .ui-aviso, .ui-peligro, .ui-info. Un error de formulario va con role="alert". */
.ui-alerta { display: flex; align-items: flex-start; gap: var(--espacio-3); padding: var(--espacio-3) var(--espacio-4); margin: 0 0 var(--espacio-4);
  border: 1px solid var(--borde); border-radius: var(--radio-md); background: var(--superficie-2); color: var(--texto); font-size: var(--texto-sm); line-height: var(--interlineado); }
.ui-alerta > svg { width: 1.25rem; height: 1.25rem; flex: none; margin-top: .1rem; color: var(--texto-2); }
.ui-alerta > div { flex: 1; min-width: 0; }
.ui-alerta strong { font-weight: var(--peso-negrita); }
/* Tinte-first: fondo tinte, borde derivado, icono y negritas en el sólido; el texto corrido queda en --texto. */
.ui-alerta.ui-exito strong { color: var(--exito-texto); }
.ui-alerta.ui-aviso strong { color: var(--aviso-texto); }
.ui-alerta.ui-peligro strong { color: var(--peligro-texto); }
.ui-alerta.ui-info strong { color: var(--info-texto); }
.ui-alerta .ui-cerrar { margin: -.35rem -.5rem -.35rem 0; }
.ui-alerta.ui-exito { background: var(--exito-tinte); border-color: var(--exito-borde); }
.ui-alerta.ui-exito > svg { color: var(--exito-texto); }
.ui-alerta.ui-aviso { background: var(--aviso-tinte); border-color: var(--aviso-borde); }
.ui-alerta.ui-aviso > svg { color: var(--aviso-texto); }
.ui-alerta.ui-peligro { background: var(--peligro-tinte); border-color: var(--peligro-borde); }
.ui-alerta.ui-peligro > svg { color: var(--peligro-texto); }
.ui-alerta.ui-info { background: var(--info-tinte); border-color: var(--info-borde); }
.ui-alerta.ui-info > svg { color: var(--info-texto); }
/* Trabajo en curso dentro de una alerta: el .ui-spinner ocupa el lugar del icono. Con avance medido de verdad (hecho/total
   que manda el servidor) va un <progress class="ui-progreso" max="total" value="hecho"> con el conteo en texto al lado;
   sin avance medible se queda solo el spinner (indeterminado): nunca un porcentaje inventado ni que avance con el tiempo. */
.ui-alerta > .ui-spinner { width: 1.25rem; height: 1.25rem; flex: none; margin-top: .1rem; box-sizing: border-box; border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%; color: var(--info-texto); animation: ui-girar .8s linear infinite; }
@media (prefers-reduced-motion: reduce) { .ui-alerta > .ui-spinner { animation: none; } }
.ui-alerta .ui-acciones { margin-top: var(--espacio-2); }
.ui-progreso { display: block; width: 100%; max-width: 28rem; height: .5rem; margin: var(--espacio-2) 0 var(--espacio-1); border: 0; border-radius: var(--radio-pildora);
  background: var(--superficie); box-shadow: inset 0 0 0 1px var(--borde-fuerte); color: var(--acento); overflow: hidden; -webkit-appearance: none; appearance: none; }
.ui-progreso::-webkit-progress-bar { background: var(--superficie); box-shadow: inset 0 0 0 1px var(--borde-fuerte); border-radius: var(--radio-pildora); }
.ui-progreso::-webkit-progress-value { background: var(--acento); border-radius: var(--radio-pildora); transition: width var(--transicion); }
.ui-progreso::-moz-progress-bar { background: var(--acento); border-radius: var(--radio-pildora); }

/* ===== Modales =====
   <dialog class="ui-modal"><form method="dialog"><header class="ui-modal-cabeza"><h2>…</h2><button … class="ui-cerrar"></header>
   <div class="ui-modal-cuerpo">…</div><footer class="ui-modal-pie">botones</footer></form></dialog>. Se abre con dialog.showModal(). */
dialog.ui-modal { width: min(32rem, calc(100% - 2rem)); max-height: calc(100dvh - 2rem); box-sizing: border-box; padding: 0; margin: auto; border: 1px solid var(--borde);
  border-radius: var(--radio-lg); background: var(--superficie); color: var(--texto); box-shadow: var(--sombra-lg); overflow: hidden; }
dialog.ui-modal::backdrop { background: var(--velo); }
dialog.ui-modal > form { display: flex; flex-direction: column; max-height: calc(100dvh - 2rem); margin: 0; }
.ui-modal-cabeza { display: flex; align-items: center; gap: var(--espacio-3); padding: var(--espacio-4) var(--espacio-5); border-bottom: 1px solid var(--linea); }
.ui-modal-cabeza h2 { flex: 1; margin: 0; font-size: var(--texto-lg); font-weight: var(--peso-negrita); line-height: var(--interlineado-corto); }
.ui-modal-cabeza .ui-cerrar { margin: 0 calc(var(--espacio-2) * -1) 0 0; }
.ui-modal-cuerpo { padding: var(--espacio-5); overflow-y: auto; font-size: var(--texto-sm); }
.ui-modal-cuerpo > :first-child { margin-top: 0; }
.ui-modal-cuerpo > :last-child { margin-bottom: 0; }
.ui-modal-pie { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--espacio-2); padding: var(--espacio-3) var(--espacio-5); border-top: 1px solid var(--linea); background: var(--superficie-2); }
.ui-modal.ui-ancho { width: min(44rem, calc(100% - 2rem)); }

/* ===== Paginación =====
   <nav class="ui-paginacion" aria-label="Paginación"><a class="ui-boton ui-secundario ui-chico" rel="prev">…</a><span class="ui-paginas">…</span><a … rel="next">…</a></nav>
   Las páginas son <a class="ui-pagina">; la actual lleva aria-current="page". Un extremo sin página va como <span class="ui-boton … " aria-disabled="true">. */
.ui-paginacion { display: flex; flex-wrap: wrap; align-items: center; gap: var(--espacio-2); margin: var(--espacio-4) 0; font-size: var(--texto-sm); }
.ui-paginacion .ui-paginas { display: inline-flex; gap: 2px; }
.ui-paginacion .ui-pagina { display: inline-flex; align-items: center; justify-content: center; min-width: var(--altura-control-chica); height: var(--altura-control-chica);
  padding: 0 var(--espacio-2); border-radius: var(--radio-sm); color: var(--texto-2); font-weight: var(--peso-medio); text-decoration: none; font-variant-numeric: tabular-nums;
  transition: background-color var(--transicion-rapida), color var(--transicion-rapida); }
.ui-paginacion .ui-pagina:hover { background: var(--superficie-2); color: var(--texto); }
.ui-paginacion .ui-pagina[aria-current] { background: var(--acento-tinte); color: var(--acento-texto); font-weight: var(--peso-negrita); }
.ui-paginacion .ui-elipsis { display: inline-flex; align-items: center; min-width: var(--altura-control-chica); justify-content: center; color: var(--texto-3); }
.ui-paginacion .ui-resumen { color: var(--texto-2); font-size: var(--texto-xs); }
.ui-paginacion .ui-relleno { flex: 1; }

/* ===== Mapa =====
   <div class="ui-mapa"> es el lienzo de Leaflet (static/vendor/leaflet). isolation: isolate encierra los z-index de sus capas para que no tapen menús ni cuadros. */
.ui-mapa { height: 20rem; border: 1px solid var(--borde-fuerte); border-radius: var(--radio-md); overflow: hidden; isolation: isolate; background: var(--superficie-2); }
.ui-mapa.leaflet-container { font: inherit; font-size: var(--texto-xs); }
.ui-campo > .ui-acciones { margin-top: var(--espacio-2); }

/* ===== Texto ===== */
.ui-oculto { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.ui-texto-2 { color: var(--texto-2); }
.ui-texto-3 { color: var(--texto-3); }
.ui-chico-texto { font-size: var(--texto-xs); }
.ui-mono { font-family: var(--fuente-mono); font-size: .875em; }
