/* ============================================================
   Contratistia — Estilos propios de la web.

   Regla dura: ni un color literal. Todo sale de los tokens de
   `estaticos/ds/tokens.css` (invariante 5 del sistema de diseño 4d3).
   Los archivos de `ds/` no se modifican nunca; lo que la web necesita
   de más vive aquí.

   Orden de carga: ds/tokens.css → ds/base.css → ds/components.css →
   css/fuentes.css → app.css

   Índice
     0. Las tres correcciones del sistema de diseño
     1. Tokens propios
     2. Utilidades y accesibilidad
     3. htmx: indicadores de carga
     4. Shell: cabecera, contenido y pie
     5. Cabecera de página
     6. Formularios
     7. Tablas
     8. Etiquetas de estado
     9. Avisos
    10. Migas
    11. Listado y tarjetas de licitación
    12. Panel de filtros plegable
    13. Paginación
    14. Diálogo
    15. Ficha y pares dato/valor
    16. Campo trampa (honeypot)
    17. Adaptación a pantallas pequeñas
    18. Autocompletado de CPV, notas y pie de formulario
    19. El esqueleto de la espera del alta
   ============================================================ */

/* ============================================================
   0. Las tres correcciones del sistema de diseño

   Son defectos conocidos del sistema, no de esta web: los archivos de
   `ds/` no se tocan, así que la corrección vive aquí. Las tres están
   pendientes de trasladar al sistema, porque afectan a cualquier
   proyecto que lo use.
   ============================================================ */

/* --- 1. El relleno a rayas del botón tapaba el texto ---

   En `ds/components.css` el pseudo-elemento del relleno se declara con
   `inset: 0` y, acto seguido, `left: -100%`. Como `inset` ya ha fijado
   `right: 0`, el ancho resultante es la suma de ambos: el doble del
   botón. El relleno no esperaba fuera al `hover`, sino que cubría el
   botón desde el primer momento y dejaba la etiqueta ilegible.

   Se le devuelve el ancho que la animación espera. */
.btn::before {
  right: auto;
  width: 100%;
}

/* --- 2. Contraste de `--accent-dark` como color de texto ---

   Los tres acentos oscuros del sistema se quedan entre 3,4:1 y 3,9:1
   sobre blanco, por debajo del 4,5:1 que pide WCAG AA para texto
   normal. Sobre bordes, rellenos e iconos sí valen (ahí el mínimo es
   3:1); sobre letras, no.

   Se oscurecen con `color-mix` —nunca con un hexadecimal nuevo, que
   rompería la invariante 5— hasta pasar de 5:1 conservando el tono.
   Los tokens resultantes están en el bloque 1. */
.btn { color: var(--verde-texto); }
.btn.accent-2 { color: var(--azul-texto); }
.btn.accent-3 { color: var(--coral-texto); }
/* El hover y la variante para fondos oscuros pintan sobre el relleno
   rayado, así que el texto vuelve a blanco. Sin esta regla, `.btn`
   ganaría por orden de carga y el texto desaparecería. */
.btn:hover,
.btn--light { color: var(--white); }

/* --- 3. El `<dialog>` salía pegado a la esquina superior izquierda ---

   El navegador centra un `<dialog>` abierto con `showModal()`
   dándole `margin: auto` en su propia hoja de estilo. El reset de
   `ds/base.css` —`*, *::before, *::after { margin: 0 }`— la gana por
   especificidad, y el diálogo aparece arriba a la izquierda.

   Se le devuelve el centrado. El resto de su aspecto está en el
   bloque 14. */
.dialogo { margin: auto; }

/* ============================================================
   1. Tokens propios

   Solo composiciones de los tokens del sistema: aquí no nace ningún
   color nuevo.
   ============================================================ */

:root {
  /* Semántica de estado. El color nunca viaja solo: siempre va con
     texto y, cuando cabe, con icono. */
  --ok: var(--pastel-green);
  --ok-fuerte: var(--dark-green);
  --info: var(--pastel-blue);
  --info-fuerte: var(--dark-blue);
  --atencion: var(--pastel-coral);
  --atencion-fuerte: var(--dark-coral);
  --error: var(--dark-coral);

  /* Acentos con contraste suficiente para pintar TEXTO (corrección 2). */
  --verde-texto: color-mix(in srgb, var(--dark-green) 80%, black);
  --azul-texto: color-mix(in srgb, var(--dark-blue) 80%, black);
  --coral-texto: color-mix(in srgb, var(--dark-coral) 80%, black);
  --error-fuerte: var(--coral-texto);

  /* El `--gray-mid` del sistema se queda en 3,5:1 sobre blanco: vale
     para bordes e iconos, no para etiquetas ni texto de ayuda. */
  --gris-texto: color-mix(in srgb, var(--gray-mid) 70%, black);

  /* Ritmo vertical de la aplicación (más densa que la web de marca). */
  --hueco-xs: 0.25rem;
  --hueco-s: 0.5rem;
  --hueco-m: 1rem;
  --hueco-l: 1.5rem;
  --hueco-xl: 2.5rem;

  /* Tipografía de aplicación, siempre con clamp(): la escala fluida del
     sistema es para la web de marca y aquí resulta grande. */
  --texto-app: clamp(0.9375rem, 0.9vw, 1rem);
  --texto-app-s: clamp(0.8125rem, 0.8vw, 0.875rem);
  --texto-titulo: clamp(1.5rem, 2.4vw, 2.25rem);
  --texto-tarjeta: clamp(1.0625rem, 1.3vw, 1.25rem);
  --texto-seccion: clamp(1.125rem, 1.5vw, 1.375rem);

  /* Anillo de foco único para toda la aplicación (WCAG 2.4.7). */
  --foco: 3px solid var(--dark-blue);

  /* Ancho de la columna de contenido. */
  --ancho-app: var(--container-wide);
}

/* ============================================================
   2. Utilidades y accesibilidad
   ============================================================ */

/* --- `hidden` gana siempre ---
   La regla `[hidden] { display: none }` del navegador pierde contra
   cualquier clase de esta hoja que fije `display`. El panel de filtros
   y los bloques vacíos se esconden con esa propiedad, así que aquí se
   le devuelve la última palabra. Es el único `!important` del archivo,
   y por eso está. */
[hidden] { display: none !important; }

/* --- Foco visible en todo lo que se puede usar con el teclado --- */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: var(--foco);
  outline-offset: 2px;
}

/* --- Texto solo para lectores de pantalla --- */
.sr-solo {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- Enlace de salto al contenido: visible solo al recibir el foco --- */
.saltar {
  position: absolute;
  top: var(--hueco-s);
  left: var(--hueco-s);
  z-index: 10001;
  padding: var(--hueco-s) var(--hueco-m);
  background: var(--white);
  border: var(--border-btn) solid var(--dark-blue);
  color: var(--gray-dark);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  text-decoration: none;
  translate: 0 -200%;
}
.saltar:focus { translate: 0 0; }

/* --- Iconos del sprite (macro `icono` de macros/app.j2) ---
   El color lo hereda del texto que acompañan: dentro del sprite los
   trazos van en `currentColor`. Son decorativos, nunca el único canal
   de información. */
.icono {
  width: 1.25em;
  height: 1.25em;
  flex: none;
  vertical-align: -0.22em;
  color: inherit;
}
.icono--s { width: 1em; height: 1em; }
.icono--l { width: 1.75em; height: 1.75em; }

/* --- Cifras: siempre IBM Plex Mono y con ancho fijo, para que las
       columnas de importes y fechas se puedan comparar de un vistazo --- */
.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* --- Marca: el rombo de 4d3 y el nombre, sin imagen (CSP estricta) --- */
.marca {
  display: inline-flex;
  align-items: baseline;
  gap: var(--hueco-s);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  letter-spacing: var(--tracking-mono);
  text-transform: uppercase;
  color: var(--gray-dark);
  text-decoration: none;
}
.marca-nombre { font-weight: 500; }
.rombo { color: var(--pastel-green); }

/* ============================================================
   3. htmx: indicadores de carga

   htmx tiene desactivada la inyección de sus propios estilos
   (`includeIndicatorStyles: false` en la meta `htmx-config`), porque
   la CSP no permite `style-src 'unsafe-inline'`. Las reglas van aquí.

   El indicador se oculta con `visibility` y no solo con `opacity`:
   así no lo lee un lector de pantalla cuando no hay nada en marcha.
   ============================================================ */

.htmx-indicator {
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--micro), visibility var(--micro);
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  visibility: visible;
  opacity: 1;
}

/* Mientras hay una petición en curso, el control no acepta más clics. */
.htmx-request[disabled],
button.htmx-request,
.htmx-request > button {
  pointer-events: none;
  opacity: 0.6;
}

/* Rombo giratorio en línea (macro `cargando`). Solo gira mientras hay
   una petición en marcha: parado no gasta nada y sigue diciendo lo que
   es por su forma, no por su movimiento. */
.cargando {
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  flex: none;
  border: 2px solid var(--pastel-green);
  border-right-color: var(--pastel-blue);
  border-bottom-color: var(--pastel-coral);
  rotate: 45deg;
  animation: spin 1.2s linear infinite;
  animation-play-state: paused;
}
.htmx-request .cargando,
.htmx-request.htmx-indicator .cargando,
.htmx-request .htmx-indicator .cargando { animation-play-state: running; }

.cargando-aviso {
  display: inline-flex;
  align-items: center;
  gap: var(--hueco-s);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}

/* ============================================================
   4. Shell: cabecera, contenido y pie
   ============================================================ */

body.app {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  background: var(--off-white);
  font-size: var(--texto-app);
}
/* Sin sesión no hay navegación que pegar arriba: la cabecera pública
   se queda quieta y la página se lee como una página. */
body.publica { background: var(--white); }

/* --- Cargador inicial ---
   El sistema lo trae entero en `ds/base.css` (`.loader`). Aquí solo se
   le añade la red de seguridad: si `app.js` no llega a cargarse, la
   cortina se retira sola a los 3 s en lugar de dejar la página tapada
   para siempre. `app.js` la retira antes, en cuanto la página termina
   de cargar. */
.loader {
  animation: retirarCargador var(--micro) ease 3s forwards;
}
@keyframes retirarCargador {
  to {
    opacity: 0;
    visibility: hidden;
  }
}
.loader.done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* --- Cabecera de la aplicación, pegada arriba --- */
.cabecera {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--white);
  border-bottom: var(--border-thin);
  transition: box-shadow var(--micro), background var(--micro);
}
/* `app.js` añade `.scrolled` en cuanto la página se desplaza. */
.cabecera.scrolled {
  background: color-mix(in srgb, var(--white) 88%, transparent);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-nav);
}
.cabecera-fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--hueco-m);
  max-width: var(--ancho-app);
  margin: 0 auto;
  padding: var(--hueco-s) var(--hueco-l);
}

/* Las secciones rotan la tricromía (invariante 1): la que está activa
   se marca con el acento que le toca, y también con el peso. */
.cabecera-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hueco-xs);
  list-style: none;
}
.cabecera-nav a {
  display: inline-flex;
  align-items: center;
  gap: var(--hueco-xs);
  padding: var(--hueco-s) var(--hueco-m);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  text-decoration: none;
  color: var(--gray-dark);
  border-bottom: 3px solid transparent;
  transition: border-color var(--micro), background var(--micro);
}
.cabecera-nav a:hover { background: var(--off-white); }
.cabecera-nav a[aria-current='page'] {
  border-bottom-color: var(--accent, var(--pastel-green));
  font-weight: 500;
}

/* --- Cabecera pública: la marca y la puerta de entrada ---
   Es la misma pieza sin las secciones de la aplicación. Lleva el
   filete grueso de acento abajo, que es el gesto con el que el
   sistema separa la portada del contenido. */
.cabecera--publica { border-bottom: 3px solid var(--pastel-green); }

/* --- Contenido --- */
.contenido {
  flex: 1;
  width: 100%;
  max-width: var(--ancho-app);
  margin: 0 auto;
  padding: var(--hueco-xl) var(--hueco-l);
}

/* --- Pie ---
   Reutiliza `.footer` del sistema (mono, mayúsculas, gris) y añade lo
   justo para que cierre una aplicación: fondo blanco y borde arriba. */
.pie {
  margin-top: auto;
  background: var(--white);
  border-top: var(--border-thin);
  padding: var(--hueco-l) var(--hueco-l);
}
.pie-enlaces {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hueco-m);
  list-style: none;
}
.pie-nota { text-transform: none; letter-spacing: 0; }

/* ============================================================
   5. Cabecera de página

   Lo que va dentro del bloque `seccion` de `base.html`: el número de
   sección del sistema (invariante 3), el título y las acciones.
   ============================================================ */

.pagina-cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--hueco-m);
  margin-bottom: var(--hueco-l);
  padding-bottom: var(--hueco-m);
  border-bottom: var(--border-thin);
}
.pagina-cabecera .section-number { margin-bottom: var(--hueco-xs); }
.pagina-titulo {
  font-size: var(--texto-titulo);
  font-weight: 300;
  line-height: 1.15;
  text-wrap: balance;
}
.pagina-cabecera h2 { margin-bottom: 0; font-size: var(--texto-titulo); }
.pagina-acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hueco-s);
}
.pagina-nota {
  max-width: 44rem;
  margin-bottom: var(--hueco-l);
  color: var(--gris-texto);
}

/* ============================================================
   6. Formularios
   ============================================================ */

.formulario {
  display: flex;
  flex-direction: column;
  gap: var(--hueco-m);
  max-width: 40rem;
}
.formulario--ancho { max-width: none; }

.campo {
  display: flex;
  flex-direction: column;
  gap: var(--hueco-xs);
}
.campo > label {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}
/* La marca de obligatorio: un asterisco para quien ve y un texto para
   quien no. Va en el acento coral, que es el que ya avisa. */
.campo-obligatorio {
  color: var(--coral-texto);
  margin-left: 0.15em;
}
.campo-ayuda {
  font-size: var(--texto-app-s);
  color: var(--gris-texto);
}
/* El error lleva icono y texto: el color acompaña, no informa solo. */
.campo-error {
  display: flex;
  align-items: flex-start;
  gap: var(--hueco-xs);
  font-size: var(--texto-app-s);
  color: var(--coral-texto);
}
.campo-error .icono { margin-top: 0.15em; }

.campo input,
.campo select,
.campo textarea {
  width: 100%;
  min-height: 2.75rem;
  padding: var(--hueco-s) var(--hueco-m);
  font-family: var(--font-sans);
  font-size: var(--texto-app);
  color: var(--gray-dark);
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
  transition: border-color var(--micro);
}
.campo input:hover,
.campo select:hover,
.campo textarea:hover { border-color: var(--gray-mid); }
.campo input::placeholder,
.campo textarea::placeholder { color: var(--gray-mid); }
.campo textarea {
  min-height: 6rem;
  resize: vertical;
  line-height: 1.5;
}
.campo select[multiple] { min-height: 8rem; }

/* Campo con error: borde de acento a la izquierda, además del texto. */
.campo--invalido input,
.campo--invalido select,
.campo--invalido textarea {
  border-color: var(--dark-coral);
  border-left-width: 3px;
}
.campo--invalido > label { color: var(--coral-texto); }

/* --- Una lista de casillas (`casillas_lista`) ---
   Para elegir varios valores de una lista larga: el territorio, el tipo de
   contrato, el estado. La usan el panel del listado, «Mi perfil», el perfil
   de empresa y el paso 3 del alta.

   `min-inline-size: 0` porque el `auto` que los `fieldset` traen de serie
   rompe las rejillas: la caja se niega a encogerse por debajo de su
   contenido. */
.campo--lista {
  min-inline-size: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

/* El título y su botón «Quitar», en la misma línea: el botón suelta esa
   lista y se lee pegado a lo que suelta. */
.campo-cabecera {
  display: flex;
  align-items: baseline;
  gap: var(--hueco-s);
}
.campo-titulo {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}

/* La caja de casillas: alto fijo para que varias listas en fila cuadren
   entre ellas. En pantalla estrecha manda el contenido (ver la media query
   de 900px). */
.casillas {
  display: flex;
  flex-direction: column;
  gap: var(--hueco-xs);
  height: 11rem;
  margin-top: var(--hueco-xs);
  padding: var(--hueco-s) var(--hueco-m);
  overflow-y: auto;
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
}
.casillas:focus-within { border-color: var(--gray-mid); }

/* Un grupo de la lista (las comunidades del territorio), plegable. */
.casillas-grupo { border: 0; }
.casillas-grupo > summary {
  padding: var(--hueco-xs) 0;
  font-family: var(--font-mono);
  font-size: var(--texto-app-s);
  color: var(--gray-dark);
  cursor: pointer;
}
.casillas-grupo > .campo-casilla { margin-left: var(--hueco-m); }

/* --- El botón «Quitar» de una lista de casillas ---
   Al lado del título de su campo, no en una esquina: pegado a lo que suelta
   se entiende sin leerlo dos veces. */
.campo-quitar {
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--azul-texto);
  text-decoration: underline;
}
.campo-quitar:hover { text-decoration: none; }

/* Una lista de casillas con error: el título y la caja, en coral, como los
   demás campos. */
.campo--invalido .campo-titulo { color: var(--coral-texto); }
.campo--invalido .casillas {
  border-color: var(--dark-coral);
  border-left-width: 3px;
}

.campo-rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: var(--hueco-m);
}

/* Casilla: la etiqueta va en su tamaño de lectura, no en mono, porque
   es una frase y no el nombre de un campo. */
.campo-casilla {
  display: flex;
  align-items: flex-start;
  gap: var(--hueco-s);
  font-size: var(--texto-app-s);
  cursor: pointer;
}
.campo-casilla input {
  width: 1.15rem;
  height: 1.15rem;
  min-height: 0;
  margin-top: 0.2rem;
  flex: none;
  accent-color: var(--dark-green);
}
.campo-casilla a { color: var(--azul-texto); }

/* Botón secundario: el hermano discreto del `.btn` en fondos claros.

   El sistema trae `.btn--light`, pero esa variante **solo** cambia el
   color del texto a blanco: está pensada para ir sobre el relleno
   rayado o sobre una sección oscura, y sobre blanco deja el texto
   invisible. Aquí hace falta lo contrario —un botón que se lea bien y
   que no compita con el principal—, así que el secundario va con el
   borde y el texto en gris y se aclara al pasar por encima. Se usan
   tokens del sistema: ni un hexadecimal nuevo. */
.btn--secundario {
  border-color: var(--gray-mid);
  color: var(--gray-dark);
}
.btn--secundario:hover {
  /* El relleno rayado del hover usaría `--accent` (verde): aquí se
     apaga, porque un secundario no debe encenderse al pasar por encima. */
  color: var(--gray-dark);
  background: var(--gray-light);
}
.btn--secundario::before { content: none; }

/* Botones: variantes densas del `.btn` del sistema, que es de página. */
.btn--compacto { padding: 0.6rem 1.25rem; }
.btn--bloque { display: block; width: 100%; }
.btn--con-icono {
  display: inline-flex;
  align-items: center;
  gap: var(--hueco-s);
}
/* Botón con estado (por ejemplo, «Guardada»): el texto lo dice y el
   peso del icono lo confirma, nunca solo el color. */
.btn--marcado {
  --accent: var(--pastel-blue);
  --accent-dark: var(--dark-blue);
  color: var(--azul-texto);
}

/* ============================================================
   7. Tablas
   ============================================================ */

.tabla-envoltorio {
  overflow-x: auto;
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
}
.tabla {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
}
.tabla caption {
  padding: var(--hueco-s) var(--hueco-m);
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}
.tabla th {
  padding: var(--hueco-s) var(--hueco-m);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
  border-bottom: var(--border-thin);
  white-space: nowrap;
}
.tabla td {
  padding: var(--hueco-s) var(--hueco-m);
  border-bottom: var(--border-thin);
  vertical-align: top;
}
.tabla tbody tr:last-child td { border-bottom: none; }
.tabla tbody tr:hover { background: var(--off-white); }
.tabla-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tabla-vacia {
  padding: var(--hueco-xl) var(--hueco-l);
  text-align: center;
  color: var(--gris-texto);
}
.tabla-nota {
  display: block;
  font-size: var(--texto-app-s);
  color: var(--gris-texto);
}

/* ============================================================
   8. Etiquetas de estado

   Un estado nunca se lee solo por el color: la etiqueta lleva su
   texto y el tono solo lo subraya.
   ============================================================ */

.badge {
  --accent: var(--gray-mid);
  display: inline-flex;
  align-items: center;
  gap: var(--hueco-xs);
  padding: 0.15rem var(--hueco-s);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gray-dark);
  background: color-mix(in srgb, var(--accent) 18%, white);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  white-space: nowrap;
}
.badge--ok { --accent: var(--ok); }
.badge--aviso { --accent: var(--atencion); }
.badge--error { --accent: var(--error); }
.badge--neutro { --accent: var(--gray-mid); }

/* ============================================================
   9. Avisos

   La región `#avisos` de `base.html` es `role="status"`, así que lo
   que se escriba dentro se anuncia sin interrumpir.
   ============================================================ */

.avisos {
  display: flex;
  flex-direction: column;
  gap: var(--hueco-s);
  margin-bottom: var(--hueco-m);
}
.avisos:empty { display: none; }

.aviso {
  --accent: var(--gray-mid);
  --accent-fuerte: var(--gray-dark);
  display: flex;
  align-items: flex-start;
  gap: var(--hueco-s);
  padding: var(--hueco-s) var(--hueco-m);
  background: color-mix(in srgb, var(--accent) 10%, white);
  border: var(--border-thin);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  font-size: var(--texto-app-s);
  color: var(--gray-dark);
}
.aviso .icono {
  margin-top: 0.15em;
  color: var(--accent-fuerte);
}
.aviso--info { --accent: var(--info); --accent-fuerte: var(--info-fuerte); }
.aviso--exito { --accent: var(--ok); --accent-fuerte: var(--ok-fuerte); }
.aviso--atencion { --accent: var(--atencion); --accent-fuerte: var(--atencion-fuerte); }
.aviso--error { --accent: var(--error); --accent-fuerte: var(--error-fuerte); }

/* ============================================================
   10. Migas de pan
   ============================================================ */

.migas {
  margin-bottom: var(--hueco-m);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}
.migas ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hueco-s);
  list-style: none;
}
.migas li {
  display: flex;
  align-items: center;
  gap: var(--hueco-s);
}
.migas li + li::before {
  content: '·';
  color: var(--gray-mid);
}
.migas a {
  color: var(--gris-texto);
  text-decoration: none;
  border-bottom: 1px solid var(--gray-light);
}
.migas a:hover {
  color: var(--gray-dark);
  border-bottom-color: var(--pastel-blue);
}
.migas [aria-current='page'] { color: var(--gray-dark); }

/* ============================================================
   11. Listado y tarjetas de licitación
   ============================================================ */

/* Una columna de base: la tarjeta lleva mucha información y no se
   puede partir. A partir de 1100px caben dos. */
.listado {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--hueco-m);
  align-items: start;
}

.licitacion {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--hueco-s);
  padding: var(--hueco-l);
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
  transition: box-shadow var(--micro);
}
/* La barra lateral que crece, la misma firma que la `.card` del
   sistema. El acento lo pone `.triad` en el contenedor (invariante 1). */
.licitacion::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 4px;
  height: 0;
  background: var(--accent, var(--pastel-green));
  transition: height var(--micro);
}
.licitacion:hover { box-shadow: var(--shadow-card); }
.licitacion:hover::before { height: 100%; }

.licitacion-cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hueco-s);
}
.licitacion-tipo,
.licitacion-cpv {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}
.licitacion-cpv { margin-left: auto; }

.licitacion-objeto {
  font-size: var(--texto-tarjeta);
  font-weight: 400;
  line-height: 1.3;
  text-wrap: pretty;
}
.licitacion-objeto a {
  color: var(--gray-dark);
  text-decoration: none;
  border-bottom: 1px solid var(--gray-light);
}
.licitacion-objeto a:hover { border-bottom-color: var(--pastel-blue); }

.licitacion-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hueco-xs);
  font-size: var(--texto-app-s);
  color: var(--gris-texto);
}
.licitacion-meta .icono { color: var(--gray-mid); }

/* Datos al pie de la tarjeta: importe y fin de ofertas, siempre
   pegados abajo para que dos tarjetas se puedan comparar en vertical. */
.licitacion-datos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hueco-l);
  margin-top: auto;
  padding-top: var(--hueco-s);
  border-top: var(--border-thin);
}
.licitacion-dato {
  display: flex;
  flex-direction: column;
}
.licitacion-dato dt {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}
.licitacion-dato dd {
  margin-top: var(--hueco-xs);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--gray-dark);
}
.licitacion-dato--importe dd {
  font-size: var(--texto-seccion);
  color: var(--verde-texto);
}
/* Últimos días para presentarse: lo dice el texto, no el color. */
.licitacion-urgente { color: var(--coral-texto); }

.licitacion-acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hueco-s);
  padding-top: var(--hueco-s);
}
.licitacion-acciones .btn:last-child { margin-left: auto; }

.licitacion-fuentes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hueco-xs);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}

/* --- Pantalla vacía: una invitación a actuar, no un lamento --- */
.vacio {
  padding: var(--hueco-xl) var(--hueco-l);
  text-align: center;
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
  color: var(--gris-texto);
}
.vacio-titulo {
  margin-bottom: var(--hueco-s);
  font-size: var(--texto-tarjeta);
  font-weight: 400;
  color: var(--gray-dark);
}
.vacio-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hueco-s);
  justify-content: center;
  margin-top: var(--hueco-m);
}

/* ============================================================
   12. Panel de filtros plegable
   ============================================================ */

.filtros {
  margin-bottom: var(--hueco-l);
  padding: var(--hueco-m);
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
}
/* --- Los campos de una pieza (buscar, CPV, importes, orden) ---
   Alineados **arriba**: con `align-items: end` las celdas bajas se pegaban
   al fondo y el panel salía escalonado. */
.filtros-campos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
  gap: var(--hueco-m);
  align-items: start;
}

/* --- Las tres listas de casillas, en su propia fila ---
   Separadas de los campos por una línea: así las tres cajas cuadran entre
   ellas y la rejilla no mezcla alturas de controles distintos. */
.filtros-listas {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: var(--hueco-m);
  margin-top: var(--hueco-l);
  padding-top: var(--hueco-l);
  border-top: var(--border-thin);
}

.filtros-acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hueco-s);
  margin-top: var(--hueco-m);
}
/* El botón que pliega solo existe en pantalla pequeña; en el resto, el
   panel está siempre desplegado y `app.js` no lo toca. */
.filtros-boton {
  display: none;
  align-items: center;
  gap: var(--hueco-xs);
  padding: var(--hueco-s) var(--hueco-m);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gray-dark);
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
  cursor: pointer;
}
.filtros-boton:hover { border-color: var(--gray-mid); }

/* ============================================================
   13. Paginación
   ============================================================ */

.paginacion {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--hueco-m);
  margin-top: var(--hueco-l);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}
.paginacion-enlaces {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hueco-xs);
  list-style: none;
}
.paginacion-enlaces a,
.paginacion-enlaces span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--hueco-xs);
  min-width: 2.5rem;
  min-height: 2.25rem;
  padding: var(--hueco-xs) var(--hueco-s);
  border: var(--border-thin);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--gray-dark);
}
.paginacion-enlaces a:hover {
  background: var(--off-white);
  border-color: var(--gray-mid);
}
/* La página actual no es un enlace, y lo dice también el texto. */
.paginacion-enlaces [aria-current='page'] {
  border-color: var(--dark-green);
  color: var(--verde-texto);
  font-weight: 500;
}
.paginacion-enlaces .apagado {
  color: var(--gray-mid);
  border-color: var(--gray-light);
}

/* ============================================================
   14. Diálogo
   El centrado lo devuelve la corrección 3, al principio del archivo.
   ============================================================ */

.dialogo {
  width: min(34rem, calc(100% - 2rem));
  max-height: 85vh;
  overflow: auto;
  padding: var(--hueco-l);
  color: var(--gray-dark);
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  font-size: var(--texto-app);
}
.dialogo::backdrop {
  background: color-mix(in srgb, var(--gray-dark) 55%, transparent);
}
.dialogo-titulo {
  margin-bottom: var(--hueco-m);
  font-size: var(--texto-titulo);
  font-weight: 300;
  line-height: 1.15;
}
.dialogo-acciones {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--hueco-s);
  margin-top: var(--hueco-l);
}

/* ============================================================
   15. Ficha y pares dato/valor
   ============================================================ */

.ficha {
  padding: var(--hueco-l);
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
}
.ficha + .ficha { margin-top: var(--hueco-m); }
.ficha-titulo {
  margin-bottom: var(--hueco-m);
  font-size: var(--texto-seccion);
  font-weight: 300;
}
/* El `<div>` dentro del `<dl>` mantiene junto cada par cuando la
   rejilla los reparte en columnas. */
.datos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
  gap: var(--hueco-m);
}
.datos dt {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mono);
  color: var(--gris-texto);
}
.datos dd { margin-top: var(--hueco-xs); }

/* Lista sencilla con la acción a la derecha (guardadas, documentos). */
.lista-simple { list-style: none; }
.lista-simple li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hueco-s);
  padding: var(--hueco-s) 0;
  border-bottom: var(--border-thin);
}
.lista-simple li:last-child { border-bottom: none; }
.lista-simple li > .btn { margin-left: auto; }

/* ============================================================
   16. Campo trampa (honeypot)

   Lo llevan los formularios públicos de `/registro` y `/acceso`. No se
   oculta con `display: none` a propósito: los bots que saben leer CSS
   lo detectan y lo saltan, y el señuelo deja de servir. Se saca de la
   pantalla, se deja en el DOM con tamaño cero y sin desbordar.

   Quien navega con el teclado tampoco cae en él: el HTML le pone
   `tabindex="-1"` al campo y `aria-hidden="true"` al contenedor.
   ============================================================ */

.trampa {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ============================================================
   17. Adaptación a pantallas pequeñas
   Sin `font-size` en las media queries: la escala ya es fluida.
   ============================================================ */

@media (max-width: 900px) {
  .contenido { padding: var(--hueco-l) var(--hueco-m); }
  .cabecera-fila { padding: var(--hueco-s) var(--hueco-m); }
  .cabecera.scrolled { backdrop-filter: none; }
  .pagina-cabecera { align-items: flex-start; flex-direction: column; }
  .filtros-boton { display: inline-flex; }
  .filtros-campos { margin-top: var(--hueco-m); }
  /* En una columna, las cajas de casillas no tienen que cuadrar con nada:
     mandan las opciones que haya, con un tope para no comerse la pantalla. */
  .casillas { height: auto; max-height: 11rem; }
  .licitacion { padding: var(--hueco-m); }
}

@media (max-width: 640px) {
  .cabecera-nav { width: 100%; }
  .cabecera-nav ul { gap: 0; }
  .cabecera-nav a { padding: var(--hueco-s); }
  .licitacion-cpv { margin-left: 0; }
  .licitacion-datos { gap: var(--hueco-m); }
  .pagina-acciones .btn,
  .vacio-acciones .btn { width: 100%; }
}

/* Menos movimiento: el cargador no espera ni el tiempo de cortesía. */
@media (prefers-reduced-motion: reduce) {
  .loader { animation-delay: 0s; }
}

/* Sin JavaScript no hay quien retire el cargador ni quien añada
   `.visible`: la página se enseña entera y sin cortina. */
@media (scripting: none) {
  .loader { display: none; }
  .reveal { opacity: 1; transform: none; }
}

/* ============================================================
   18. Autocompletado de CPV, notas y pie de formulario

   Lo del Sprint 2 (listado, ficha, guardadas y perfiles). Todo es
   aditivo: ninguna regla de arriba cambia de comportamiento.
   ============================================================ */

/* --- Fila de botones al pie de un formulario ---
   La usan los formularios de esta web (`/perfil`, `/cuenta`, la nota
   de una guardada) desde el principio; hasta ahora no tenía reglas. */
.acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hueco-s);
}

/* --- La nota de una guardada ---
   Se respetan los saltos de línea que se hayan escrito, que es como se
   escriben las notas de verdad. */
.licitacion-nota {
  padding: var(--hueco-s) var(--hueco-m);
  background: var(--off-white);
  border-left: 2px solid var(--pastel-blue);
  border-radius: var(--radius);
  font-size: var(--texto-app-s);
  color: var(--gris-texto);
  white-space: pre-line;
}

/* --- Autocompletado de CPV ---

   El listado de sugerencias solo se coloca debajo del campo cuando
   cuelga de `.autocompletar`: el alta usa el mismo listado dentro de un
   `div` normal, donde tiene que seguir ocupando su sitio y no flotar.

   El aviso de carga va en la esquina para que no empuje ni al campo ni
   a la lista: sin eso, el hueco del aviso —que ocupa aunque esté
   oculto— dejaría un renglón en blanco bajo cada campo. */
.autocompletar { position: relative; }

.autocompletar > .cargando-aviso {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 21;
  padding-top: var(--hueco-xs);
}

.autocompletar .autocompletar-lista {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 20;
  margin: 0;
  padding: var(--hueco-xs) 0;
  list-style: none;
  max-height: 18rem;
  overflow-y: auto;
  background: var(--white);
  border: var(--border-thin);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
}

.autocompletar-opcion {
  display: flex;
  align-items: baseline;
  gap: var(--hueco-s);
  padding: var(--hueco-s) var(--hueco-m);
  font-size: var(--texto-app-s);
  cursor: pointer;
}
.autocompletar-opcion:hover { background: var(--off-white); }
/* La opción activa se marca con fondo y con una barra a la izquierda:
   quien no distinga el fondo por contraste tiene la barra. */
.autocompletar-opcion[aria-selected='true'] {
  background: var(--off-white);
  box-shadow: inset 2px 0 0 var(--dark-green);
}
.autocompletar-codigo {
  flex: none;
  font-family: var(--font-mono);
  color: var(--gray-dark);
}
.autocompletar-texto { color: var(--gris-texto); }

/* La opción de aviso («sin coincidencias») no se elige. */
.autocompletar-vacio {
  cursor: default;
  color: var(--gris-texto);
}
.autocompletar-vacio:hover { background: transparent; }

/* ============================================================
   El esqueleto de la espera del alta

   Mientras se leen los documentos, la pantalla enseña la **forma** de lo que va
   a venir —bloques grises con el perfil de la revisión— en lugar de un texto
   suelto. El brillo que los recorre dice «esto está en marcha» sin afirmar
   nada, y desaparece entero con `prefers-reduced-motion`.

   Todo con tokens del sistema: ni un hexadecimal nuevo.
   ============================================================ */

/* El texto de estado va con `.sr-solo`, la utilidad que ya tiene la casa. */

.espera-titulo {
  font-size: var(--text-subtitle);
  margin-bottom: var(--hueco-xs);
}

.espera-nota {
  max-width: 42rem;
  margin-bottom: var(--hueco-l);
  color: var(--gris-texto);
}

.esqueleto { max-width: 52rem; }

.esqueleto-bloque {
  position: relative;
  overflow: hidden;
  background: var(--gray-light);
  border-radius: var(--radius-small);
  margin-bottom: var(--hueco-s);
}

/* El brillo que recorre el bloque. Es lo único que se mueve. */
.esqueleto-bloque::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in srgb, var(--white) 65%, transparent),
    transparent
  );
  animation: esqueleto-brillo 1.6s ease-in-out infinite;
}

@keyframes esqueleto-brillo {
  100% { transform: translateX(100%); }
}

.esqueleto-bloque--titulo { width: 60%; height: 1.75rem; }
.esqueleto-bloque--linea { width: 100%; height: 0.85rem; }
.esqueleto-bloque--linea.esqueleto-bloque--corta { width: 45%; }
.esqueleto-bloque--etiqueta { width: 30%; height: 0.7rem; }
.esqueleto-bloque--campo { flex: 1; height: 2.75rem; margin-bottom: 0; }

.esqueleto-tarjeta {
  border: var(--border-thin);
  padding: var(--hueco-m);
  margin-bottom: var(--hueco-m);
}

.esqueleto-fila {
  display: flex;
  gap: var(--hueco-s);
  margin-bottom: var(--hueco-m);
}

/* Los documentos, en pequeño y en mono: es información, no adorno. */
.espera-documentos {
  list-style: none;
  padding: 0;
  margin: var(--hueco-m) 0 0;
  color: var(--gris-texto);
  font-size: var(--text-small);
}

.espera-documentos li {
  display: flex;
  gap: var(--hueco-s);
  align-items: baseline;
  padding: var(--hueco-xs) 0;
  border-top: var(--border-thin);
}

.espera-documentos li:first-child { border-top: 0; }

.espera-estado { color: var(--gray-mid); }

/* Sin movimiento: el esqueleto se queda quieto. Sigue entendiéndose (los
   bloques están), pero no parpadea a quien le molesta. */
@media (prefers-reduced-motion: reduce) {
  .esqueleto-bloque::after { animation: none; }
}

/* ============================================================
   19. Página de ventas (Fase 3)

   La portada pública: el cuadro de la lista de espera, las capturas
   de los tres pasos y las preguntas frecuentes. Usa los tokens del
   sistema; aquí no nace ningún color.
   ============================================================ */

/* --- El hero y el cuadro de la lista de espera ---
   La página de ventas no lleva la cabecera de sección de la app: el
   hero ocupa el ancho de lectura y el formulario va en un cuadro con
   el borde de la casa, para que se vea como lo que es: la puerta. */
.cuadro-espera {
  margin-top: var(--hueco-xl);
  padding: var(--hueco-l);
  border: var(--border-thin);
  border-top: 4px solid var(--pastel-green);
  background: var(--off-white);
  max-width: 44rem;
}

.cuadro-espera .campo-rejilla { align-items: start; }

.lista-espera .acciones { margin-bottom: var(--hueco-s); }

.cuadro-espera .nota {
  margin: var(--hueco-m) 0 0;
  font-size: var(--texto-app-s);
  color: var(--gris-texto);
}

.cuadro-espera .nota a { color: var(--azul-texto); }

/* --- Los tres pasos, con su captura ---
   Las capturas son de la app de verdad: mismo ancho que la tarjeta,
   con el borde fino del sistema y sin sombra. */
.pasos .card { display: flex; flex-direction: column; }

.pasos .card p { flex: 1; }

.captura {
  display: block;
  width: 100%;
  height: auto;
  margin-top: var(--hueco-m);
  border: var(--border-thin);
  background: var(--white);
}

/* --- Preguntas frecuentes ---
   `<details>` nativo: se abren y se cierran sin una línea de
   JavaScript, y el teclado funciona solo. */
.faq {
  border-top: var(--border-thin);
  padding: var(--hueco-m) 0;
}

.faq:last-of-type { border-bottom: var(--border-thin); }

.faq summary {
  cursor: pointer;
  font-size: var(--texto-seccion);
  font-weight: 300;
  padding: var(--hueco-xs) 0;
}

.faq summary::marker { color: var(--gray-mid); }

.faq p {
  margin: var(--hueco-s) 0 0;
  max-width: 46rem;
  color: var(--gris-texto);
}

/* --- En pantalla pequeña, el cuadro pierde el relleno ancho --- */
@media (max-width: 640px) {
  .cuadro-espera { padding: var(--hueco-m); }
}

/* El segundo camino de `/registro`: escribir el código a mano. Va
   separado del formulario de la lista de espera, con su propio aire,
   para que no se confundan los dos. */
.bloque-codigo {
  margin-top: var(--hueco-xl);
  padding-top: var(--hueco-l);
  border-top: var(--border-thin);
}

/* ============================================================
   20. Paso 1 del alta: los documentos por tipo (Fase 3)

   Cada tipo de documento es una caja con su borde de acento y su
   explicación de para qué sirve. El `input type="file"` va dentro de
   la etiqueta, escondido: quien pulsa es la etiqueta entera, que es
   lo que se puede usar con el teclado y con el dedo.
   ============================================================ */

.paso-titulo {
  font-size: var(--texto-seccion);
  font-weight: 300;
  margin: var(--hueco-xl) 0 var(--hueco-s);
}

.documentos-rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--hueco-m);
}

.documento-caja {
  border: var(--border-thin);
  border-left: 4px solid var(--gray-light);
  padding: var(--hueco-m);
  display: flex;
  flex-direction: column;
  gap: var(--hueco-xs);
}

/* El borde de la izquierda rota verde → azul → coral, como el resto. */
.documento-caja.accent-1 { border-left-color: var(--pastel-green); }
.documento-caja.accent-2 { border-left-color: var(--pastel-blue); }
.documento-caja.accent-3 { border-left-color: var(--pastel-coral); }

.documento-caja h3 {
  font-size: var(--texto-tarjeta);
  font-weight: 400;
  margin: 0;
}

.documento-caja .documento-para {
  flex: 1;
  margin: 0;
  font-size: var(--texto-app-s);
  color: var(--gris-texto);
}

.documento-elegir {
  display: inline-flex;
  align-items: center;
  gap: var(--hueco-s);
  align-self: start;
  cursor: pointer;
  padding: var(--hueco-s) var(--hueco-m);
  border: var(--border-btn) solid var(--gray-dark);
  font-family: var(--font-mono);
  font-size: var(--texto-app-s);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.documento-elegir:hover { background: var(--off-white); }

/* Sin `display: none`: un `input` escondido así no se puede enfocar con el
   teclado. Se deja fuera de la vista pero en el flujo de tabulación. */
.documento-elegir input[type='file'] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.documento-elegir:focus-within { outline: var(--foco); outline-offset: 2px; }

.documento-archivo {
  margin: 0;
  min-height: 1.2em;
  font-size: var(--texto-app-s);
  font-family: var(--font-mono);
  color: var(--verde-texto);
  word-break: break-all;
}

.documento-aviso {
  display: flex;
  gap: var(--hueco-s);
  align-items: flex-start;
  margin: var(--hueco-l) 0;
  padding: var(--hueco-m);
  background: var(--off-white);
  border-left: 4px solid var(--atencion);
  font-size: var(--texto-app-s);
}

.documento-aviso .icono { flex: none; margin-top: 2px; }

.enlace-mano {
  margin-left: var(--hueco-m);
  font-size: var(--texto-app-s);
  color: var(--azul-texto);
}

.bloque-reprocesar {
  margin-top: var(--hueco-xl);
  padding-top: var(--hueco-l);
  border-top: var(--border-thin);
}

.bloque-reprocesar .formulario { margin-top: var(--hueco-s); }

/* --- De dónde salió cada campo, en el paso 3 --- */
.fuente-campo {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--texto-app-s);
  color: var(--gris-texto);
}

/* ============================================================
   21. Modo revisión (Fase 3)

   Una tarjeta, tres botones y el gesto. La tarjeta se sustituye a sí
   misma con cada decisión, así que todo lo que se mueve vive aquí.
   ============================================================ */

.revision {
  max-width: 46rem;
  margin: 0 auto;
}

.revision-cabecera {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--hueco-m);
  flex-wrap: wrap;
  margin-bottom: var(--hueco-m);
  color: var(--gris-texto);
}

/* Los enlaces de la revisión —volver al listado, abrir la ficha, empezar la
   cola de nuevo— llevan el azul de la casa: sin regla propia salían con el
   azul y el subrayado del navegador, que no es el del sistema. */
.revision-volver,
.revision-ficha a,
.revision-fin a {
  color: var(--azul-texto);
}

.revision-volver {
  display: inline-flex;
  align-items: center;
  gap: var(--hueco-xs);
  font-size: var(--texto-app-s);
}

/* La tarjeta: el borde de acento a la izquierda, como las de documento
   del alta. Se puede arrastrar, así que nada de seleccionar el texto
   sin querer al hacerlo. */
.revision-tarjeta {
  position: relative;
  border: var(--border-thin);
  border-left: 4px solid var(--pastel-blue);
  background: var(--white);
  padding: var(--hueco-l);
  box-shadow: var(--shadow-card);
  touch-action: pan-y;
  transform: translateX(var(--arrastre, 0));
  transition: transform var(--micro) ease, opacity var(--micro) ease;
  user-select: none;
}

.revision-objeto {
  font-size: clamp(1.25rem, 2vw, 1.75rem);
  font-weight: 300;
  margin: var(--hueco-s) 0;
}

.revision-etiquetas,
.revision-organo,
.revision-cpv,
.revision-ficha {
  margin: 0 0 var(--hueco-s);
}

.revision-organo { color: var(--gris-texto); }

.revision-datos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--hueco-s) var(--hueco-m);
  margin: var(--hueco-m) 0;
  padding: var(--hueco-m) 0;
  border-top: var(--border-thin);
  border-bottom: var(--border-thin);
}

.revision-datos dt {
  font-family: var(--font-mono);
  font-size: var(--texto-app-s);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--gris-texto);
}

.revision-datos dd { margin: 0; }

.revision-cpv {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hueco-xs);
}

.revision-extracto {
  margin: var(--hueco-m) 0 0;
  color: var(--gris-texto);
}

.revision-ficha { margin-top: var(--hueco-m); }

.revision-acciones {
  display: flex;
  gap: var(--hueco-s);
  justify-content: space-between;
  margin-top: var(--hueco-l);
}

.revision-acciones .btn {
  display: inline-flex;
  align-items: center;
  gap: var(--hueco-xs);
}

.revision-no { border-color: var(--dark-coral); color: var(--coral-texto); }
.revision-interesa { border-color: var(--dark-green); color: var(--verde-texto); }

.revision-ayuda {
  margin-top: var(--hueco-m);
  text-align: center;
  color: var(--gris-texto);
}

.revision-fin { border-top: 0; }

/* --- El gesto y la animación de decisión ---
   Al arrastrar, la tarjeta sigue al dedo y se insinúa hacia dónde va.
   Al decidir, sale por su lado en 0,3 s; quien tenga desactivado el
   movimiento no ve la animación (dura cero) y no espera nada. */
.revision-arrastrando { transition: none; }

.revision-derecha { border-left-color: var(--pastel-green); }
.revision-izquierda { border-left-color: var(--pastel-coral); }

.revision-sale-no,
.revision-sale-interesa {
  opacity: 0;
}

.revision-sale-no { transform: translateX(-6rem); }
.revision-sale-interesa { transform: translateX(6rem); }
.revision-sale-saltar { opacity: 0.3; }

@media (prefers-reduced-motion: reduce) {
  .revision-tarjeta { transition: none; }
  .revision-sale-no,
  .revision-sale-interesa,
  .revision-sale-saltar { transform: none; opacity: 1; }
}

@media (max-width: 640px) {
  .revision-acciones { flex-direction: column; }
  .revision-acciones .btn { width: 100%; justify-content: center; }
}

/* --- El botón del feedback en la cabecera ---
   Va solo con el icono. El relleno y el borde de abajo son los de los
   enlaces de la nav, para que la fila quede alineada: un icono suelto, del
   alto y en el sitio de un enlace más. */
.cabecera-falta {
  display: inline-flex;
  align-items: center;
  padding: var(--hueco-s);
  border: 0;
  border-bottom: 3px solid transparent;
  background: none;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.cabecera-falta:hover { color: var(--azul-texto); }

.dialogo-nota {
  font-size: var(--texto-app-s);
  color: var(--gris-texto);
}

.comentario-gracias { margin-bottom: var(--hueco-m); }

/* --- Las dos puertas al modo revisión, encima del panel de filtros --- */
.revision-accesos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hueco-s);
  margin-bottom: var(--hueco-m);
}

/* ============================================================
   22. Semáforo de adecuación y chat con Iris (Fase 4, Sprint 3)
   ============================================================ */

/* --- Semáforo --- */
.semaforo-cabecera { margin-bottom: var(--hueco-m); }
.semaforo .lista-simple li { justify-content: space-between; }

/* --- Cabecera de una `.ficha` con acciones a la derecha (el chat, el
   contador de mensajes del mes) --- */
.ficha-cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--hueco-s);
  margin-bottom: var(--hueco-m);
}
.ficha-cabecera .ficha-titulo { margin-bottom: 0; }

/* --- Chat con Iris ---
   Sin streaming (DT-8): cada turno se pinta entero. La pregunta se alinea a
   la derecha como en cualquier chat; la respuesta, a la izquierda con un
   borde de acento, para distinguirlas sin depender solo del color. */
.chat-mensajes {
  display: flex;
  flex-direction: column;
  gap: var(--hueco-m);
  margin-bottom: var(--hueco-m);
}

.chat-turno { max-width: 42rem; }
.chat-turno p { margin-top: var(--hueco-xs); }

.chat-turno--usuario {
  align-self: flex-end;
  padding: var(--hueco-s) var(--hueco-m);
  background: var(--off-white);
  border-radius: var(--radius);
}

.chat-turno--iris {
  padding: var(--hueco-s) var(--hueco-m);
  border-left: 3px solid var(--pastel-green);
}

.chat-citas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hueco-xs);
  margin-top: var(--hueco-s);
}
.chat-citas .btn { padding: 0.1rem 0.5rem; }

.chat-formulario { margin-top: var(--hueco-m); }
.chat-formulario textarea { min-height: 4.5rem; }
