/* El juego de iconos común (03/11/2026): lo cargan así todas las pantallas que cargan esta hoja. */
@import url("/compartido/css/iconos.css");
/* SISTEMA › APARIENCIA (05/11/2026, dirección: «por si se vende la app»): la letra, los colores de los botones y de la
   cabecera, las esquinas, la densidad y el tamaño de la letra de la empresa que elige dirección. La sirve el servidor
   (src/lib/apariencia) con `:root:root`, que gana a este `:root`; con todo de fábrica sale vacía. Un @import retiene el
   pintado hasta que llega: no hay parpadeo. Nadie escribe a mano esos colores: van por las variables de abajo. */
@import url("/api/apariencia.css");

:root {
  --amarillo: #EFC85F;
  --amarillo-suave: #FFF8DC;
  --negro: #1E1E1E;
  --gris-oscuro: #2D2D2D;
  --gris-medio: #555;
  --gris-suave: #f5f5f5;
  /* El gris de «no hay nada» (`.vacio`) y de lo apagado que aún se lee (03/11/2026, dirección: «Sí» a oscurecer el #aaa,
     que daba 2,1 de contraste): 4,9 sobre --gris-suave y 5,3 sobre blanco. */
  --gris-apagado: #6b6b6b;
  --blanco: #fff;
  --borde: #e8e8e8;
  --sombra: 0 2px 8px rgba(0, 0, 0, .07);
  --radio: 14px;
  --error: #c0392b;
  --exito: #16a34a;
  /* El ámbar de los avisos (03/11/2026, docs/normas-esteticas.md): lo que hay que mirar va en
     ámbar y arriba. Son los tres valores que ya usaban más de 200 reglas escritas a mano. */
  --ambar-fondo: #fdf6e3;
  --ambar-texto: #7a5310;
  --ambar-borde: #c9891a;

  /* LO QUE SE CAMBIA EN SISTEMA › APARIENCIA (05/11/2026, docs/normas-esteticas.md 4). De fábrica, los valores de
     siempre; la hoja de /api/apariencia.css los pisa solo si dirección los cambia. Nadie escribe a mano el color de un
     botón principal, uno secundario, la cabecera o la barra de abajo: se escribe con estas variables. Lo que NO se cambia
     (el ámbar de los avisos, el rojo de borrar, el verde de hecho) no tiene variable aquí a propósito. */
  --letra-app: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  /* El botón principal y lo ELEGIDO (la opción marcada de un selector, la fila de total), amarillo con letra negra
     (dirección, 05/11/2026: «botones negros, vamos a cambiarlos por amarillos, le damos un toque corporativo»). */
  --boton-fondo: var(--amarillo);
  --boton-letra: var(--negro);
  --boton2-fondo: var(--gris-suave);
  --boton2-letra: var(--negro);
  --cabecera-fondo: var(--negro);
  --cabecera-letra: var(--blanco);
  --cabecera-acento: var(--amarillo);
  --acento-letra: var(--negro);
  --radio-boton: 12px;
  --radio-campo: 10px;
  --densidad: 1;

  /* EL TAMAÑO DE LA LETRA (05/11/2026, dirección: «que cada uno pueda elegir varios tamaños de letra»). Es un `zoom` de la
     página entera: las medidas van en píxeles y agrandar solo la letra desbordaría los botones. La de la empresa la pone
     Apariencia (`--letra-escala`); la que elige cada persona, `data-letra` en <html> (public/compartido/apariencia.js).
     `--letra-tope` lo pone apariencia.js para que la página nunca quede más estrecha que 320 px (lo probado). Con zoom,
     los vh y vw también crecen: por eso se escriben `calc(90vh / var(--letra-zoom, 1))` (normas-esteticas.js, 4d). */
  --letra-escala: 1;
  --letra-zoom: min(var(--letra-escala), var(--letra-tope, 10));
}

/* Lo que elige cada persona gana a lo de la empresa (`:root:root` de Apariencia): por eso va con más peso. */
html:root[data-letra="normal"] { --letra-escala: 1; }
html:root[data-letra="grande"] { --letra-escala: 1.1; }
html:root[data-letra="muy_grande"] { --letra-escala: 1.2; }
@media screen {
  html { zoom: var(--letra-zoom); }
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: var(--letra-app);
  background: var(--gris-suave);
  color: var(--negro);
  min-height: calc(100vh / var(--letra-zoom, 1));
}

/* ARMAZÓN FIJO — la cabecera y la barra de abajo no se mueven al desplazar.
   La clase la pone header.js, así que solo la llevan las páginas internas; el login y
   las pantallas sueltas de acceso siguen siendo páginas normales que se desplazan
   enteras.

   Antes la cabecera era `sticky` y la barra de abajo `fixed`. Sobre el papel eso las
   deja quietas, pero en el móvil no: al desplazar, el navegador esconde y saca su
   propia barra de direcciones, la altura visible de la ventana cambia a mitad del gesto
   y los dos elementos dan un salto. Por eso solo pasaba en las pantallas con contenido
   largo — las cortas no llegan a desplazarse y nunca se notaba.

   La solución es que el documento NO se desplace en absoluto: la ventana se reparte en
   tres, y lo único que se mueve es el contenido de en medio. Así el navegador no tiene
   motivo para esconder su barra, y las nuestras no pueden moverse porque no están
   flotando sobre nada: están en su sitio.

   El precio es que la barra de direcciones del móvil ya no se recoge al bajar, así que
   se ve un poco menos de contenido. Merece la pena: unas barras que bailan hacen fallar
   los toques, y eso es peor que perder unos píxeles. */
html {
  height: 100%;
}

body.app-fija {
  height: 100%;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Solo hay tres hijos en el reparto. Todo lo demás que cuelga del <body> —el botón
   flotante, los modales, la barra de selección, la hoja de impresión— va en `position:
   fixed` o escondido, así que no entra aquí. */
body.app-fija > .header,
body.app-fija > .bottom-nav {
  position: relative;
  flex: 0 0 auto;
}

body.app-fija > .contenido {
  flex: 1 1 auto;
  /* Imprescindible: `.contenido` lleva `margin: 0 auto` para centrarse, y dentro de una
     columna flexible un margen automático hace que el elemento se encoja hasta el tamaño
     de su contenido en vez de ocupar el ancho entero. Con el ancho fijado al 100%, el
     `max-width` sigue limitándolo y los márgenes siguen centrándolo, igual que antes. */
  width: 100%;
  overflow-y: auto;
  /* Al llegar al final, que no arrastre la página de detrás ni dispare el "recargar
     tirando hacia abajo" del navegador en mitad de una lista. */
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  /* El hueco de abajo se mantiene aunque la barra ya no flote sobre el contenido: el
     botón redondo de "+" sí sigue flotando, y sin este hueco la última tarjeta de la
     lista quedaría debajo de él justo al llegar al final. */
  padding-bottom: 90px;
}

button {
  cursor: pointer;
  font-family: inherit;
}

/* Botón desactivado mientras algo está en curso (ej. "Enviando…") — sin esto, el cambio de
   texto era la única pista de que ya se había pulsado, y era fácil pulsar otra vez de más. */
button:disabled {
  opacity: 0.6;
  cursor: default;
}

/* SELECT — estilo base para cualquier <select> suelto de la app (fuera de un .field, que
   ya tiene su propio estilo en calendario.css). Sin esto, un <select> hereda el aspecto
   plano del sistema operativo, desentonando con el resto del sistema de diseño. */
select {
  width: 100%;
  border: 1.5px solid var(--borde);
  border-radius: var(--radio-campo);
  padding: calc(12px * var(--densidad)) 34px calc(12px * var(--densidad)) 14px;
  font-size: 15px;
  font-family: inherit;
  background: var(--blanco) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23555" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>') no-repeat right 10px center;
  background-size: 18px;
  color: var(--negro);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  cursor: pointer;
  transition: border-color .15s;
}

select:focus {
  border-color: var(--amarillo);
}

/* HEADER — barra superior con el logo, compartida por todas las páginas internas
   (no se usa en la pantalla de login, que tiene su propio logo centrado a pantalla completa). */
.header {
  background: var(--cabecera-fondo);
  color: var(--cabecera-letra);
  padding: calc(16px * var(--densidad)) 20px;
  position: sticky;
  top: 0;
  /* ⚠️ 150 y no 100 (09/10/2026): la cabecera es un contexto de apilado, así que su panel de la
     campana compite con el contenido con el número de la CABECERA. Con 100 lo tapaban la fila
     pegada del buscador (`.pegada`, 120) y los menús ⋮ abiertos (130): el panel salía cortado
     por el buscador en Ofertas → Pendientes. Por debajo de avisos (200) y ventanas (1200+). */
  z-index: 150;
  box-shadow: 0 2px 12px rgba(0, 0, 0, .3);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.header .logo {
  display: flex;
  align-items: center;
  gap: 8px;
}

.header .logo-titulo {
  font-size: 16px;
  font-weight: 800;
  color: var(--cabecera-letra);
  /* ⚠️ (13/10/2026) Una palabra larga («Documentación») no se podía partir y se metía DEBAJO
     del «?» del manual. Ocupa el hueco que haya, se parte en dos renglones y lo que pase de dos se
     corta con «…». (03/11/2026, dirección con la captura de Sistema › Comerciales: salía
     «Comercia-les».) Se parte por las PALABRAS: una que cabe entera en su renglón baja entera, sin
     guion; solo la que no cabe ni sola se corta (`break-word`). Lo vigila normas-esteticas.js (9d). */
  flex: 1 1 auto;
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-height: 1.2;
  hyphens: manual;
  -webkit-hyphens: manual;
  overflow-wrap: break-word;
  /* Un título largo («Portal del proveedor») junto al rol más largo («CONTABILIDAD») no
     cabía a 390 px y empujaba el botón de salir fuera de la pantalla (08/10/2026). Ahora
     puede encoger y partirse en dos renglones; lo que ya cabía se ve igual. */
  min-width: 0;
  margin-right: 8px;
}

/* En el móvil (13/10/2026) el grupo de la derecha («?», campana, quién eres y salir) se aprieta
   para dejarle sitio al título: era lo que lo empujaba debajo del «?». */
@media (max-width: 420px) {
  .header { padding: 14px 12px; }
  .header .logo-titulo { font-size: 14px; margin-right: 4px; }
  .header .usuario { gap: 6px; }
  .header .btn-manual { margin-left: 2px; margin-right: 0; }
  .header .btn-campana { padding: 4px 2px; }
  .header .rol-badge {
    padding: 3px 8px;
    font-size: 10px;
    line-height: 1.15;
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    max-width: 76px;
  }
  .header .rol-badge .rol-sep { display: none; }
  .header .rol-badge .rol-nombre { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
  .header .btn-salir { width: 30px; height: 30px; }
}

.header .btn-volver {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--cabecera-acento);
  padding: 4px;
  margin-right: 4px;
}

.header .btn-volver svg {
  width: 20px;
  height: 20px;
}

.header .logo-text {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 2px;
  color: rgba(255, 255, 255, .5);
  color: color-mix(in srgb, var(--cabecera-letra) 50%, transparent);
  text-transform: uppercase;
}

.header .logo-tres {
  font-size: 22px;
  font-weight: 800;
  color: var(--cabecera-acento);
  letter-spacing: -0.5px;
}

.header .logo-dots {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-left: 2px;
}

.header .logo-dot1 {
  width: 8px;
  height: 8px;
  background: var(--cabecera-acento);
  border-radius: 50%;
}

.header .logo-dot2 {
  width: 6px;
  height: 6px;
  background: var(--cabecera-acento);
  border-radius: 50%;
  opacity: .7;
}

.header .usuario {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
}

.header .rol-badge {
  background: rgba(239, 200, 95, .15);
  background: color-mix(in srgb, var(--cabecera-acento) 15%, transparent);
  color: var(--cabecera-acento);
  padding: 4px 10px;
  border-radius: 20px;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: .3px;
  /* «Diego · DIR» (11/10/2026): en una sola línea, sin pasar a mayúsculas el nombre. */
  white-space: nowrap;
}

/* Icono de apagado en vez de la palabra "Salir": ocupa menos y se entiende igual. */
.header .btn-salir {
  background: rgba(255, 255, 255, .1);
  background: color-mix(in srgb, var(--cabecera-letra) 10%, transparent);
  color: var(--cabecera-letra);
  border: none;
  border-radius: 50%;
  width: 34px;
  height: 34px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.header .btn-salir svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.header .btn-salir:active {
  background: rgba(255, 255, 255, .2);
  background: color-mix(in srgb, var(--cabecera-letra) 20%, transparent);
}

/* BOTTOM NAV — barra de navegación inferior fija, compartida por todas las páginas
   internas. Cada página marca su propio botón como .active vía el atributo data-pagina
   en el <body> (ver base.js). */
.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--cabecera-fondo);
  border-top: 2px solid var(--cabecera-acento);
  display: flex;
  z-index: 100;
  padding: 6px 0 10px;
}

.nav-btn {
  flex: 1;
  padding: 6px 4px;
  border: none;
  background: none;
  font-size: 10px;
  color: rgba(255, 255, 255, .35);
  color: color-mix(in srgb, var(--cabecera-letra) 35%, transparent);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  font-weight: 600;
  letter-spacing: .3px;
  text-transform: uppercase;
  text-decoration: none;
  transition: color .15s;
}

.nav-btn svg {
  width: 22px;
  height: 22px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.nav-btn.active {
  color: var(--cabecera-acento);
}

/* Un apartado que solo ven algunos (Ruta, en la barra del trabajador) nace con [hidden] y se
   enciende al saber quién es. HACE FALTA ESTA REGLA: `[hidden]{display:none}` del navegador
   tiene la misma especificidad que `.nav-btn`, y como esta se declara después, GANA — el
   botón se vería igual con el atributo puesto. Es la sexta vez que pasa en este proyecto. */
.nav-btn[hidden] { display: none; }

/* EL CONTADOR DEL CHAT, EN LA MISMA LÍNEA DEL RÓTULO (22/09/2026)
 * ---------------------------------------------------------------------------
 * ⚠️ El botón de la barra es una COLUMNA (icono arriba, rótulo debajo), así que un número
 * colgado del botón caería en un tercer renglón y la barra crecería de alto en cuanto
 * alguien escribiera en el chat. Y flotando sobre el icono taparía el icono, que es
 * exactamente lo que pasó con la campana dos veces (05 y 16/09/2026). Va dentro del rótulo,
 * en flujo, y no puede hacer ninguna de las dos cosas. */
.nav-texto {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  /* Alto fijo: así la barra de abajo mide siempre lo mismo. Medido en un Chromium de verdad
   * cuando el rótulo llevaba un contador al lado — pasaba de 68 a 71 px en cuanto alguien
   * escribía en el chat. Ese contador ya no está, y el alto fijo se queda. */
  line-height: 12px;
}

/* `.nav-num` se retiró el 22/09/2026: dirección quiso la barra de abajo LIMPIA, sin ningún
 * número en «Más» (ver `compartido/nav.js`). Se deja dicho aquí por si vuelve a hacer falta
 * un contador en una pestaña: iba dentro de `.nav-texto`, en el flujo y nunca sobre el icono,
 * que es lo que tapó la campana dos veces. */

/* CONTENIDO — envoltorio central de cada página, con espacio reservado abajo para
   que el bottom-nav fijo no tape el final del contenido. */
.contenido {
  max-width: 960px;
  margin: 0 auto;
  padding: 20px 16px;
  padding-bottom: 90px;
}

/* EN EL ORDENADOR, LOS LISTADOS LARGOS EN VARIAS COLUMNAS (18/09/2026)
 * ---------------------------------------------------------------------------
 * Dirección: «en el ordenador la pantalla de clientes se ve otra vez en un listado largo».
 *
 * ⚠️ Nunca estuvo en varias columnas, y el motivo de fondo es la regla de aquí arriba:
 * `.contenido` mide 960 px SIEMPRE, así que en un monitor de 1600 la aplicación entera se
 * queda en una franja central y una lista de 1.599 clientes baja en una sola columna.
 *
 * No se ensancha para todo: un formulario o una ficha a 1500 px de ancho se lee peor, no
 * mejor. Se ensancha solo donde lo pide la pantalla, con `.ancha` en su `<main>`, y solo a
 * partir de 1100 px de ventana: por debajo manda el diseño de siempre.
 *
 * ⚠️ Y el ancho por sí solo no reparte nada: hay que decir además qué contenedor es una
 * rejilla (`.lista-rejilla`), o las tarjetas seguirían una debajo de otra, más anchas. */
@media (min-width: 1100px) {
  .contenido.ancha { max-width: 1500px; }
  /* ⚠️ Lo ancho es para los LISTADOS. Un formulario a 1.400 px pone la etiqueta en un
     extremo y lo que se teclea en el otro, y no se lee: toda sección que no lleve un listado
     dentro se queda en los 960 de siempre, centrada. Las que van DENTRO de un listado (el
     bloque «Sin usar» de la flota, la ficha que sustituye a la lista) no se tocan. */
  .contenido.ancha .form-section:not(:has(.lista-rejilla)):not(.lista-rejilla > *) {
    max-width: 960px; margin-left: auto; margin-right: auto;
  }
  /* `.ocupa-todo`: la pantalla entera del ordenador, sin tope. Para las parrillas de muchas
     columnas —los doce meses del PyG— que a 960 px obligaban a desplazar a lo ancho dentro
     de su caja para leer la mitad del año. Se pone y se quita desde el JS según la vista:
     el mismo PyG en su vista de un mes se lee mejor estrecho. */
  .contenido.ocupa-todo { max-width: none; }
}

/* `auto-fill` y no un número fijo de columnas: con cuatro clavadas, un portátil de 1280 las
 * deja estrechísimas y un monitor de 2560 las estira hasta no poder leer de un vistazo qué
 * línea sigue a cuál.
 *
 * ⚠️ El mínimo son 300 px porque es lo que da CUATRO por línea, que es lo que se pidió. Con
 * 240 salían CINCO a 1440 px — medido en un Chrome de verdad, no calculado. Con 300: una
 * columna en el móvil, dos en una tablet en vertical, tres en una tablet apaisada o un
 * portátil pequeño y cuatro de 1280 en adelante; el tope de 1500 del contenido impide que en
 * un monitor grande se estiren sin fin.
 *
 * ⚠️ Empieza en 640 px y no en 900 POR LA TABLET (24/09/2026, «ten en cuenta que también se
 * usa tablet»): con 900, un iPad en vertical (768-834) enseñaba UNA tarjeta de 730 px de
 * ancho con media pantalla en blanco. 640 es lo justo para dos de 300 con su hueco; un
 * teléfono en vertical (hasta 430) no llega nunca. */
@media (min-width: 640px) {
  .lista-rejilla {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 8px;
  }

  /* Sin `align-items: start` a propósito: así todas las de una fila miden lo mismo y la
     rejilla se lee como una cuadrícula. Con cada tarjeta a su altura, un nombre que ocupa dos
     renglones deja escalones y cuesta seguir la fila. */

  /* Las tarjetas traen su propio `margin-bottom` para apilarse; dentro de la rejilla el hueco
     lo pone `gap`, y con los dos se abriría el doble entre filas que entre columnas. */
  .lista-rejilla > * { margin-bottom: 0; }

  /* ⚠️ `minmax(300px, 1fr)` NO impide que una celda se ensanche por su contenido: un nombre
     largo sin espacios —un correo, un CIF pegado— haría más ancha su columna y, con ella,
     TODAS las demás. Se parte antes de llegar a eso. */
  .lista-rejilla > * { min-width: 0; overflow-wrap: anywhere; }

  /* ⚠️ Lo que NO es una tarjeta ocupa la fila entera. En ese mismo contenedor caen el
     «Cargando…» y el «Sin resultados», y en una rejilla saldrían metidos en una columna de
     300 px, descentrados y con la escoba recortada — pareciendo que la pantalla está rota. */
  .lista-rejilla > .vacio,
  .lista-rejilla > .empty,
  .lista-rejilla > .esc-track-wrap { grid-column: 1 / -1; }

  /* ⚠️ Y en general, TODO lo que no es una tarjeta: la nota de arriba de una lista, la barra
     de «marcar todos», el total del mes, el rótulo de un día, un bloque «Sin usar» al final,
     la ficha que sustituye a la lista al abrir un proveedor. Enumerarlos uno a uno garantiza
     que la pantalla siguiente meta otro y salga metido en una columna de 300 px. Así, lo que
     no se declara tarjeta ocupa la fila, y las tarjetas se reparten entre esos rótulos.
     ⚠️ En UNA sola línea: un salto dentro del selector es un espacio, o sea «descendiente»,
     y la regla deja de casar con nada sin avisar. */
  .lista-rejilla > :not(.trabajo-card):not(.lista-item):not(.rent-item):not(.pendiente-card):not(.sol-card):not(.nom-tarjeta):not(.pg-tarjeta):not(.asig-item) {
    grid-column: 1 / -1;
  }

  /* `.sueltas`: listas cuyas tarjetas se DESPLIEGAN (el panel de acciones, el hilo de
     observaciones, el desglose de una factura). Estiradas a la altura de la fila, abrir una
     haría crecer en blanco a sus tres vecinas; así cada una mide lo suyo. */
  .lista-rejilla.sueltas { align-items: start; }

  /* `.compactada` la pone compartido/rejillaCompacta.js (25/09/2026): la rejilla va por filas
     de 4 px y cada tarjeta ocupa las que mide, así la siguiente sube al hueco libre en vez de
     esperar a que acabe la fila. El hueco entre tarjetas pasa del `gap` a un margen, que es lo
     que la cuenta del JS suma (8 px). ⚠️ Los dos números (4 y 8) están también en el JS. */
  .lista-rejilla.sueltas.compactada { grid-auto-rows: 4px; row-gap: 0; }
  .lista-rejilla.sueltas.compactada > * { margin-bottom: 8px; }
}

/* QUÉ SE ESTÁ VIENDO, debajo del buscador (22/09/2026).
 *
 * Los listados que se pueden filtrar por una VISTA —hoy, activos/inactivos/todos de
 * Trabajadores— eligen esa vista desde el ⋮, y un control escondido en un menú deja de verse.
 * Esta línea es lo que impide mirar una lista sin saber que le faltan doce personas.
 *
 * ⚠️ `:empty` y no `hidden`: la pantalla la deja vacía mientras no hay nada cargado, y una
 * línea en blanco con su margen abriría un hueco bajo el buscador que parece un fallo. */
.lista-cuantos {
  margin: -4px 0 10px;
  color: #888;
  font-size: 12px;
}

.lista-cuantos:empty { display: none; }

/* ANIMACIÓN DE CARGA "ESCOBA" — sustituye al texto "Cargando…"/spinner circular.
   Inspirada en la referencia visual del usuario (organizador-trabajos): una escoba
   empuja una bolita creciente hasta un recogedor, en bucle, mientras se ondulan las
   cerdas. Ver public/escoba.js para la lógica de animación — se auto-inicializa sola
   en cualquier .esc-track que aparezca en el DOM, no hace falta llamarla a mano. */
/* La escoba mide 102 px y el carril solo 50, con overflow visible: el palo asoma unos 57 px
   por encima. Con 30 px de hueco arriba se salía del recuadro y se plantaba encima de la
   barra de búsqueda de la pantalla que estuviera cargando. El hueco tiene que dar para el
   palo entero. */
.esc-track-wrap {
  display: flex;
  justify-content: center;
  padding: 62px 10px 8px;
}

.esc-track {
  position: relative;
  height: 50px;
  width: 66%;
  max-width: 230px;
  overflow: visible;
}

.esc-linea-base {
  position: absolute;
  bottom: 6px;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: linear-gradient(90deg, transparent, #e2e2e2 12%, #e2e2e2 88%, transparent);
  z-index: 1;
}

.esc-bolita {
  position: absolute;
  bottom: 8px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  z-index: 3;
  background: radial-gradient(circle at 35% 30%, #f6dc93, var(--amarillo) 60%, #c79f2e 100%);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
  transition: left 0.7s cubic-bezier(.65, 0, .35, 1), width 0.7s cubic-bezier(.65, 0, .35, 1), height 0.7s cubic-bezier(.65, 0, .35, 1);
}

.esc-recogedor {
  position: absolute;
  bottom: 0;
  right: 2px;
  width: 36px;
  height: 34px;
  z-index: 6;
  transform-origin: 75% 95%;
  transition: transform .18s ease;
}

.esc-recogedor.recibiendo {
  animation: escRecWobble .3s ease;
}

@keyframes escRecWobble {
  0% { transform: rotate(0deg); }
  30% { transform: rotate(-4deg); }
  60% { transform: rotate(3deg); }
  100% { transform: rotate(0deg); }
}

.esc-escoba {
  position: absolute;
  bottom: 5px;
  width: 46px;
  height: 102px;
  z-index: 5;
  transform-origin: bottom center;
  filter: drop-shadow(0 3px 3px rgba(0, 0, 0, .13));
  transition: left 0.7s cubic-bezier(.65, 0, .35, 1);
}

.esc-cerda {
  transform-origin: 50% 0%;
}

.esc-texto {
  font-size: 12px;
  color: #aaa;
  margin-top: 10px;
  letter-spacing: .2px;
  text-align: center;
}

/* BOTONES */
.btn-primary {
  width: 100%;
  padding: calc(15px * var(--densidad)) 15px;
  background: var(--boton-fondo);
  color: var(--boton-letra);
  border: none;
  border-radius: var(--radio-boton);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: .3px;
  transition: opacity .15s, transform .1s;
}

.btn-primary:active {
  opacity: .85;
  transform: scale(.98);
}

.btn-primary:disabled {
  opacity: .5;
  cursor: not-allowed;
}

/* EL BOTÓN DE AL LADO EN GRIS («Cancelar», «Volver», lo que no es la acción principal):
   la misma forma que `.btn-primary`, en gris. Antes iba escrito a mano en 36 botones con un
   `style=` en línea y con clases locales de Planes modelo y Reorganizar planes (03/11/2026,
   docs/normas-esteticas.md). */
.btn-primary.btn-gris {
  background: var(--boton2-fondo);
  color: var(--boton2-letra);
}
/* Y el de borrar o quitar, en rojo suave con la letra roja: iba escrito en un `style=` en cinco
   sitios (Eliminar la ruta, la recurrencia, el registro de Altas y bajas, Desactivar un aviso y el
   «No volver a avisar» de los plazos de header.js). */
.btn-primary.btn-rojo {
  background: #fef2f2;
  color: var(--error);
}

/* BOTÓN SECUNDARIO. `class="modal-btn"` se usa en 41 sitios de 15 pantallas —incluido el
   modal que header.js pone en todas— y NO TENÍA NINGUNA REGLA en ninguna de las 66 hojas
   del proyecto: salía el botón gris del navegador. Es el fallo de la familia de siempre
   aquí —una clase usada sin su hoja, como .factura-cliente en Altas y bajas o las km- de
   Planes—, encontrado el 17/09/2026 al mirar por qué los cinco botones de los filtros de
   Facturas emitidas «quedaban faltos».
   Mismo aspecto que .modal-btn-cancelar, que es lo que se esperaba de él. Dentro de
   .modal-botones esa regla (0,1,1) sigue mandando en la forma; esto pone el color, que era
   justo lo que faltaba. */
.modal-btn {
  padding: calc(12px * var(--densidad)) 12px;
  border: none;
  border-radius: var(--radio-campo);
  font-size: 13px;
  font-weight: 700;
  background: var(--boton2-fondo);
  color: var(--boton2-letra);
}

/* EL TÍTULO DE UN BLOQUE DENTRO DE UNA TARJETA, y el par de campos en dos columnas con su
   nota pequeña. Estaban en ofertas.css y en buzon-facturas.css, y Facturas emitidas los usa
   SIN CARGAR ninguna de las dos: allí «Control» salía como texto corriente y los pares de
   campos del alta («Nº de factura» y «Serie», «Fecha» y…) caían uno debajo de otro. Aquí hay
   una sola copia y la carga cualquier pantalla. Mudados el 17/09/2026. */
.val-bloque {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--gris-medio);
  margin-bottom: 2px;
}

.bz-dos {
  display: grid;
  /* ⚠️ `minmax(0, 1fr)` y no `1fr`: un `1fr` a secas es `minmax(auto, 1fr)`, así que la
     columna no baja del ancho natural del campo que lleva dentro —unos 177 px— y en un móvil
     los dos campos se salen de la caja por la derecha. Es lo mismo que movía la pantalla de
     lado en el calendario de las cuotas. Medido: sin esto, el alta de Facturas emitidas se
     desplaza a lo ancho a 390 px. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 10px;
}

.bz-dos input,
.bz-dos select,
.bz-dos textarea { width: 100%; min-width: 0; }

/* DOS CAMPOS EN LA MISMA LÍNEA (21/09/2026, pedido por dirección para la ficha del
   trabajador: «hay muchos campos que se pueden poner en la misma línea dos, así la pantalla
   sería mucho más corta siendo más fácil ver todos los campos… pero se tiene que ver bien en
   el móvil. Siempre que sean del mismo apartado»).

   Se envuelven en esto los campos que van juntos, y la rejilla los reparte de dos en dos. Un
   número IMPAR de campos no es un problema: el último ocupa su columna y deja la otra vacía,
   que es lo que ya pasaba antes con todos.

   ⚠️ EN EL MÓVIL SIGUE HABIENDO UNO POR LÍNEA, y es la condición que puso dirección. El corte
   está en 560 px porque ahí cada columna mide 258 px —`.contenido` da 528 de ancho útil— y
   ese es el primer ancho en el que una columna pasa de los 229 px que pide por su cuenta un
   `<input type="date">`. Por debajo, dos columnas dejan el rótulo en tres renglones y una
   fecha recortada, que es justo lo contrario de «verse bien».

   ⚠️ `minmax(0, 1fr)` y no `1fr`: un `1fr` a secas es `minmax(auto, 1fr)`, así que la columna
   no baja del ancho natural del campo que lleva dentro y los dos se salen de la caja por la
   derecha. Es lo mismo que movía la pantalla de lado en el calendario de las cuotas, y por eso
   el `min-width: 0` va además sobre el HIJO: así vale también para un hueco que rellene el JS.

   El hueco entre filas NO lo pone la rejilla: lo sigue poniendo el `margin-bottom` de cada
   `.field`, que es el de siempre. Así el ritmo vertical de la ficha no cambia al agrupar. */
.campos-dos {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  column-gap: 12px;
}

@media (min-width: 560px) {
  .campos-dos {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

.campos-dos > * { min-width: 0; }
/* Con `display: grid` le gana al `[hidden]` del navegador (08/10/2026: la fecha a mano de
   Acuerdos de jornada se veía teniendo los cambios en la lista). */
.campos-dos[hidden] { display: none; }

.bz-nota {
  font-size: 11px;
  color: #6b7280;
  margin-top: 4px;
}

/* ALERTAS */
.alert {
  padding: 12px 16px;
  border-radius: 12px;
  font-size: 13px;
  font-weight: 700;
  margin-bottom: 12px;
  display: none;
}

/* LOS AVISOS FLOTAN POR ENCIMA DE TODO (03/09/2026).
 *
 * Antes eran un bloque más del principio de la página, sin posición ni z-index. Y eso tenía
 * una consecuencia que no se veía leyendo el CSS: **cuando algo falla dentro de un modal, el
 * aviso se pinta DETRÁS del modal**, arriba del todo, donde nadie lo ve. Desde fuera se
 * traduce en «le doy a Responder, no pasa nada y el cuadro no se va» — que es exactamente lo
 * que se reportó. El fallo estaba contado; simplemente no se podía leer.
 *
 * Así que se sacan a flotar, centrados arriba y por encima de los modales (que van a 3000).
 * Son mensajes que se van solos a los pocos segundos, así que un cartel flotante es además lo
 * que les corresponde: ocupando sitio en el flujo empujaban la página hacia abajo al aparecer
 * y la subían de golpe al irse.
 *
 * Solo afecta a `.alert.show`, que es la clase que se enciende y se apaga. Un aviso
 * permanente de una pantalla usa su propia clase (`.recl-aviso`, `.aviso-migracion`) y se
 * queda donde está. */
.alert.show {
  display: block;
  position: fixed;
  top: auto;
  bottom: calc(var(--aviso-abajo, 16px) + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translate3d(-50%, 0, 0);
  z-index: 4000;
  width: calc(100% - 24px);
  max-width: 520px;
  margin: 0;
  box-shadow: 0 4px 18px rgba(0, 0, 0, .18);
  pointer-events: none;
}

/* EL AVISO PASAJERO SALE ABAJO Y NO SE QUEDA CON LOS TOQUES (09/11/2026). Dirección, desde el correo en el
   móvil: «mejor que aparezca abajo para poder manejar el menú superior; ahora hasta que no desaparece no se
   puede hacer nada». Arriba tapaba los botones de la cabecera los seis segundos que dura. Ahora:
   - sale ABAJO, centrado, por encima de la barra de navegación (`--aviso-abajo`: 16 px sin barra, 78 px con
     ella, lo mismo que `.deshacer-banner` y `#avisoErrorJs`); una pantalla con su propia barra anclada abajo
     sube `--aviso-abajo` (el correo, por encima de Responder / A todos / Reenviar, en correo.css);
   - NUNCA se queda con los toques: la caja va con `pointer-events: none` y solo lo que lleva dentro para
     pulsar (un «Ver», un «Deshacer», la línea de un aviso plegable) los recibe. Un toque en la cabecera
     llega a su botón aunque el aviso se vea.
   Sigue por encima de los modales (z-index 4000), que era lo del 03/09/2026. Vale para los `.alert` de toda la
   aplicación (verde, rojo y el `.alert-aviso` pasajero); el aviso ámbar que es PARTE de la pantalla
   (`.alert-en-linea`, `.nota-ambar`, los `*-aviso`) no flota y sigue arriba (normas-esteticas.md, 5). */
body:has(.bottom-nav) { --aviso-abajo: 78px; }
body:has(.deshacer-banner.visible) { --aviso-abajo: 142px; }
.alert.show :is(a, button, summary, input, select, textarea, label, [role="button"], [tabindex]) { pointer-events: auto; }

/* UN AVISO QUE VIVE EN LA PÁGINA, NO FLOTANDO (05/09/2026).

   Hay avisos que no son una notificación de algo que acaba de pasar, sino parte de la
   pantalla: «faltan cuentas por clasificar» en PyG está ahí siempre, y flotando sobre el
   contenido tapaba lo que se venía a leer.

   Antes se apañaba con un `style="position:static"` a mano en cada sitio, y eso ESTABA MAL:
   quitaba el `position` pero NO el `transform: translateX(-50%)`, que se sigue aplicando a un
   elemento estático. El aviso salía desplazado media anchura hacia la izquierda y se veía
   cortado — que es exactamente como se reportó.

   Con una clase se apagan las cinco cosas a la vez, y no hay que acordarse de ninguna. */
.alert.show.alert-en-linea {
  position: static;
  transform: none;
  left: auto;
  bottom: auto;
  pointer-events: auto;
  width: auto;
  max-width: none;
  margin-bottom: 12px;
  box-shadow: none;
}

.alert-ok {
  background: #eafaf0;
  color: #116636;
}

.alert-err {
  background: #fdecec;
  color: #8a1f1f;
}

/* Aviso informativo (ni error ni confirmación): algo que conviene mirar, pero que no
   impide seguir. Ej. el consumo disparado de un vehículo en Repostajes. */
.alert-aviso {
  background: var(--ambar-fondo);
  color: var(--ambar-texto);
}

/* Aviso con varias cosas dentro (p. ej. un vehículo por línea en Repostajes): el titular
   arriba y debajo un renglón por cada una, en vez de todo seguido en un párrafo. */
.aviso-titulo {
  font-weight: 700;
  margin-bottom: 6px;
}

/* El .alert lo pone todo en negrita; aquí solo debe destacar el nombre del vehículo. */
.aviso-linea {
  padding: 4px 0;
  line-height: 1.4;
  font-weight: 500;
}

.aviso-linea + .aviso-linea {
  border-top: 1px solid rgba(138, 90, 0, .15);
}

/* EXPORTAR — barra de Excel/PDF encima de un listado (ver compartido/exportar.js). */
.exportar-barra {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-bottom: 10px;
}

.exportar-btn {
  border: 1.5px solid var(--borde);
  background: var(--blanco);
  border-radius: 10px;
  padding: 7px 12px;
  font-size: 12px;
  font-weight: 700;
  color: var(--negro);
}

.exportar-btn:active {
  background: var(--amarillo);
}

/* FILA DE ACCIONES — dos o tres acciones principales en una sola línea y, al final, un
   menú de tres puntos con las secundarias (PDF, Excel…). Mismo criterio que las tarjetas
   con menú: lo que se usa mucho se ve, lo demás se guarda para no llenar la pantalla. */
.acciones-fila {
  display: flex;
  align-items: stretch;
  gap: 8px;
  margin-bottom: 12px;
}

.acciones-fila .accion-btn {
  flex: 1;
  min-width: 0;
  border: 1.5px solid var(--borde);
  background: var(--boton2-fondo);
  border-radius: 10px;
  padding: 10px 8px;
  font-size: 12px;
  font-weight: 700;
  color: var(--boton2-letra);
  text-align: center;
  text-decoration: none;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
}

.acciones-fila .accion-btn:active {
  background: var(--amarillo);
}

/* ELEGIR A QUIÉN Y POR DÓNDE SE LE MANDA EL PARTE (23/09/2026)
 *
 * ⚠️ `.accion-btn` solo tenía regla DENTRO de `.acciones-fila`, así que los botones del modal
 * de destinatarios llevaban desde siempre el botón gris del navegador, sin formato. Es la
 * familia de fallos de siempre aquí —una clase usada fuera de donde está definida—, y se ve al
 * mirarlo, no al leer el código. La regla de `.acciones-fila` sigue mandando donde ya estaba
 * (0,2,0 le gana a esta), así que aquello no cambia.
 *
 * Los dos canales van en columnas iguales: WhatsApp abre la aplicación, el chat manda de
 * verdad, y ninguno de los dos puede parecer el principal. */
.envio-fila {
  border: 1px solid var(--borde);
  border-radius: 10px;
  padding: 10px;
  margin-bottom: 8px;
}

.envio-nombre {
  font-weight: 700;
  font-size: 13px;
  margin-bottom: 8px;
  overflow-wrap: anywhere;
}

/* `minmax(0, 1fr)` y no `1fr`: un `1fr` no baja del ancho natural de su contenido y la fila se
   saldría de la caja del modal. */
.envio-canales {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}

.envio-canales .accion-btn {
  border: 1.5px solid var(--borde);
  background: var(--boton2-fondo);
  border-radius: 10px;
  padding: 10px 6px;
  font-size: 13px;
  font-weight: 700;
  color: var(--boton2-letra);
  text-align: center;
  box-sizing: border-box;
  min-width: 0;
}

.envio-canales .accion-btn:active { background: var(--amarillo); }

/* Un canal que no puede funcionar se ve apagado, no escondido: así se sabe que existe y por
   qué no se puede usar (lo dice su `title`). */
.envio-canales .accion-btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

.acciones-menu {
  position: relative;
  flex-shrink: 0;
}

.acciones-menu > .menu-btn {
  width: 40px;
  height: 100%;
  border: 1.5px solid var(--borde);
  background: var(--blanco);
  border-radius: 10px;
  font-size: 18px;
  line-height: 1;
  color: var(--gris-medio);
  /* `height: 100%` solo vale si la fila estira la caja: junto a un título o en una fila con
     `align-items: center` el ⋮ salía de 20 px, aplastado y difícil de tocar (03/11/2026: Caja,
     Cobros, PyG, Repostajes, Reseñas, Encuestas, Portal del proveedor…). */
  min-height: 34px;
}

.acciones-menu > .menu-btn:active {
  background: var(--gris-suave);
}

.acciones-menu .menu-lista {
  display: none;
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 10;
  min-width: 170px;
  background: var(--blanco);
  border: 1.5px solid var(--borde);
  border-radius: 12px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .14);
  /* El alto lo limita exportar.js al abrirlo (hasta la barra de abajo); lo que no cabe se
     recorre dentro del menú, sin mover la página de detrás. */
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.acciones-menu .menu-lista.abierto {
  display: block;
}

.acciones-menu .menu-lista button {
  display: block;
  width: 100%;
  border: none;
  border-bottom: 1px solid var(--borde);
  background: none;
  padding: 11px 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--negro);
  text-align: left;
}

.acciones-menu .menu-lista button:last-child {
  border-bottom: none;
}

.acciones-menu .menu-lista button:active {
  background: var(--gris-suave);
}

/* Título de sección con una acción a la derecha, en la misma línea (el ⋮ de exportar
   sobre un listado, por ejemplo). El h3 pierde su margen inferior porque el subrayado lo
   pone la propia fila. */
.seccion-titulo-fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 14px;
  padding-bottom: 8px;
  border-bottom: 1.5px solid var(--borde);
}

/* Con el selector corto no basta: .form-section h3 (calendario.css) tiene la misma fuerza
   y se carga después, así que el título se quedaba con su propio subrayado y salían dos
   líneas grises, una bajo el texto y otra bajo la fila. */
.seccion-titulo-fila h3,
.form-section .seccion-titulo-fila h3 {
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: none;
}

/* Buscador (o desplegable) con el menú de tres puntos al lado, para no gastar una línea
   entera en dos botones que se usan de uvas a peras. */
.buscador-fila {
  display: flex;
  align-items: stretch;
  gap: 8px;
  margin-bottom: 12px;
}

/* ⚠️ NOVENA VEZ (16/09/2026): una regla por clase con `display` gana al `[hidden]` del
   navegador. El filtro del Resumen de variables de nómina es la única fila que se esconde, y
   se estaba viendo en las cuatro pestañas: un desplegable de filtro colgando en «El mes», en
   «Conceptos» y en «Anticipos», filtrando algo que no se estaba mirando. */
.buscador-fila[hidden] { display: none; }

/* ---------------------------------------------------------------------------
   EL BUSCADOR, PEGADO ARRIBA (17/09/2026)
   ---------------------------------------------------------------------------
   Dirección: «el buscador de clientes sube con la lista al deslizar; sería interesante que
   se quedara fijo arriba, para poder buscar sin tener que volver otra vez hacia arriba».

   Es `sticky` y no `fixed` a propósito: `fixed` se coloca respecto a la VENTANA, así que
   habría que saber cuánto mide la cabecera y volver a decírselo cada vez que cambie —y en el
   móvil, con la barra del navegador, ni eso—. `sticky` se pega al borde de arriba de lo que
   se desplaza, que en esta aplicación es `.contenido` (`body.app-fija > .contenido` lleva el
   `overflow-y: auto`): queda justo debajo de la cabecera sin que nadie tenga que medirla.

   ⚠️ SE PONE A MANO, PANTALLA A PANTALLA, y no a todas las `.buscador-fila` de golpe:
   esa clase se usa también para las filas de pestañas y de filtros, y varias pantallas
   llevan DOS seguidas. Dos pegadas con el mismo `top: 0` se montan una encima de la otra
   —gana la de abajo, que va después en el documento—, así que la de las pestañas quedaría
   tapada. Por eso se marca UNA por cada panel que se ve a la vez, y hay una comprobación en
   `.verificacion/buscador-pegado.js` que lleva la cuenta por fichero.

   ⚠️ Y hace falta FONDO: un elemento pegado es transparente, y las tarjetas se verían pasar
   por debajo del texto. Los márgenes negativos son exactamente el `padding` lateral de
   `.contenido` (16 px), para que ese fondo llegue a los dos bordes y las tarjetas no asomen
   por los lados; al compensarse con el padding no aparece barra horizontal.

   El `z-index` va por encima del contenido normal (100/500) y muy por debajo del selector de
   fecha (2500), la previsualización (3000), los avisos (4000) y la campana (9000). */
.pegada {
  position: sticky;
  /* ⚠️ `top: 0` NO lo deja arriba del todo, y esto solo se ve mirándolo: el navegador mide el
     pegado contra el hueco del que se desplaza YA DESCONTADO SU PADDING, y `.contenido` tiene
     20 px arriba. Con `top: 0` se quedaba 20 px más abajo y por esa rendija se veía pasar la
     lista por encima del buscador. Son los mismos 20 px del `padding-top` de `.contenido`;
     hay una comprobación que ata los dos números. */
  top: -20px;
  z-index: 120;
  background: var(--gris-suave);
  margin-left: -16px;
  margin-right: -16px;
  padding: 10px 16px;
  /* Una sombra corta hacia abajo: es lo que hace ver que la lista pasa por DEBAJO y no que
     el buscador se ha quedado colgado en mitad de la pantalla. */
  box-shadow: 0 4px 6px -4px rgba(0, 0, 0, .14);
}

/* Pegada y escondida siguen siendo compatibles: `[hidden]` tiene que ganar igual (el filtro
   del resumen de nómina es las dos cosas según la pestaña). */
.pegada[hidden] { display: none; }

.buscador-fila > .buscador-input,
.buscador-fila > select {
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
}

/* Un buscador dentro de una rejilla de filtros (Altas/Bajas, 24/09/2026): sin su margen, o la
   celda crece 12 px y el desplegable de al lado se estira a esa altura. */
.filtros-grid > .buscador-input {
  margin-bottom: 0;
}

/* Pestañas con un botón de acción al lado (p. ej. el PDF de Semana/Zonas/Mes): así el
   botón no se come una línea entera para él solo. El margen inferior lo lleva la fila,
   no las pestañas, para que ambos queden a la misma altura. */
/* LAS PESTAÑAS. Aquí, y no en la hoja de un módulo (31/08/2026).
 *
 * Vivían en rrhh.css, que solo cargan las pantallas de RRHH — pero las usan DIECISÉIS
 * pantallas: propuestas, calidad, contratos, alta de cliente, albaranes, compras, facturación,
 * la ficha de proveedor, Sistema, trabajos sueltos… En las quince que no cargaban esa hoja las
 * pestañas salían como los botones grises que pinta el navegador cuando no hay estilo, que es
 * lo que se veía en el ordenador («se ven un cuadrado con el texto»).
 *
 * En el móvil llamaba menos la atención porque ocupan el ancho entero de la pantalla y parecen
 * una barra; en un monitor ancho quedan sueltos y se ve que son botones sin vestir. */
/* ⚠️ SE PARTEN EN VARIAS FILAS, Y ESO ES LO QUE IMPIDE QUE LA PANTALLA SE MUEVA DE LADO
   (21/09/2026). Reportado por dirección desde Flota en un iPhone: «en el móvil está y alguna
   página más se mueven hacia los lados».

   `.tab-btn` llevaba `flex: 1`, o sea base 0 — pero un elemento flexible NO baja de su
   anchura mínima (`min-width: auto`, que en una palabra suelta es la palabra entera). Las
   ocho pestañas de Flota piden 628 px y la caja mide 358: el resto se salía.

   Y lo que se movía NO era el documento, sino `.contenido`: lleva `overflow-y: auto` y el
   CSS convierte entonces el otro eje en `auto` también, así que lo ancho se queda dentro de
   su propio desplazamiento. Por eso la cabecera y la barra de abajo no se movían —lo que se
   ve en la captura— y por eso medir `document.scrollWidth` decía que todo estaba bien.

   Se parten en vez de desplazarse a lo ancho DENTRO de la fila, que es el otro remedio
   habitual: ahí quedarían pestañas fuera de la vista y sin nada que indique que están —y
   «Costes» o «Conductores» no se encontrarían nunca. Partidas se ven las ocho.
   Con pocas pestañas no cambia nada: caben en una fila y siguen llenándola. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  background: var(--gris-suave);
  border-radius: 12px;
  padding: 3px;
  margin-bottom: 12px;
}

/* Sin esto, un <div class="tabs" hidden> SE SIGUE VIENDO: el `display:flex` de arriba es una
   regla de autor y gana al `display:none` que el navegador aplica a [hidden], que es de su
   hoja por defecto.
   Pasaba en Semana y Mes, donde los botones se mandaron al menú de tres puntos y se dejaron
   escondidos para que el menú los pulsara por dentro — y seguían ocupando su línea entera
   encima del selector de fecha, que era justo el espacio que se quería ganar. */
.tabs[hidden] {
  display: none;
}

.tab-btn {
  /* ⚠️ `1 1 auto` y no `flex: 1`: con la base en 0 el navegador cree que todas caben en una
     fila —da igual lo que midan de verdad— y no parte nunca. Con la base en su contenido,
     se parten donde toca y después crecen para llenar su fila. El precio es que dos
     pestañas de la misma fila ya no miden exactamente lo mismo, sino lo que pide su texto. */
  flex: 1 1 auto;
  padding: 9px;
  border: none;
  background: none;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  color: #888;
  transition: all .15s;
}

.tab-btn.active {
  background: var(--blanco);
  color: var(--negro);
  box-shadow: 0 1px 4px rgba(0, 0, 0, .1);
}

/* Pestañas en rejilla de tres columnas iguales (27/09/2026, Gestión de vacaciones: seis
   botones en dos filas de tres, «todos del mismo tamaño»). `minmax(0, 1fr)` y no `1fr`: un
   `1fr` no baja del ancho de su texto y movería la pantalla de lado. Una casilla que es un
   ENLACE a otra pantalla (`.tab-enlace`) va en negro con su número, para que no se lea como
   una pestaña más de esta. */
.tabs.tabs-rejilla {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 3px;
}
.tabs.tabs-rejilla[hidden] { display: none; }
/* En un móvil pequeño (320 px) con el ⋮ al lado, «Disfrutados» no cabe a 13 px. */
@media (max-width: 359px) {
  .tabs-rejilla .tab-btn { font-size: 12px; padding: 6px 2px; }
}
/* `.tabs-rejilla.dos`: dos por fila en el móvil (27/09/2026, Trabajos sueltos: cuatro
   botones, «dos arriba y dos abajo»). */
.tabs.tabs-rejilla.dos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* En el ordenador, TODAS en una fila y del mismo ancho, sean las que sean. */
@media (min-width: 900px) {
  .tabs.tabs-rejilla, .tabs.tabs-rejilla.dos {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }
}
.tabs-rejilla .tab-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: 36px;
  padding: 6px 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tab-btn.tab-enlace {
  position: relative;
  overflow: visible;
  background: var(--boton-fondo);
  color: var(--boton-letra);
  text-decoration: none;
}
.tab-btn.tab-enlace > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* El número va como globo en la esquina y no al lado del texto: al lado, con el ⋮ en la
   fila, «Solicitudes» no cabía en un tercio de móvil y salía «Solicitu…». */
.tab-btn.tab-enlace .sol-banner-num {
  position: absolute; top: -7px; right: -4px;
  min-width: 18px; height: 18px; line-height: 18px; font-size: 11px; padding: 0 5px;
  box-shadow: 0 0 0 2px var(--gris-suave);
}

.tabs-con-accion {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}

.tabs-con-accion .tabs {
  flex: 1;
  margin-bottom: 0;
}

.tabs-con-accion .exportar-btn {
  flex-shrink: 0;
  padding: 9px 14px;
}

/* CABECERA DE UNA PANTALLA DE LISTA (08/11/2026, dirección con la captura del móvil de Sistema › Formas de pago:
   «Demasiada información, ocupa toda la pantalla en el móvil… Buscador y botón crear en la misma línea»).
   docs/normas-esteticas.md, 8; lo vigila .verificacion/cabeceras-compactas.js.
   - `.tabs.tabs-una-fila`: en el móvil, UNA fila SIN desplazarse (08/11/2026, dirección: «Prefiero función tres puntos
     que desplazar a la derecha»): lo que no cabe va al menú del botón de tres puntos del final (`.tabs-mas`), y la
     pestaña activa se ve siempre (compartido/pestanasCompactas.js marca con `.tab-fuera` las que van al menú). Desde
     700 px, en filas como siempre y sin menú. El número de cada pestaña va en `.tab-num`: en ámbar si pide atención
     (`.tab-aviso`), y la pestaña a 0 más apagada (`.tab-cero`).
   - `.que-es-pantalla`: la explicación de la pantalla y la del apartado, plegadas en una línea pequeña. */
.tabs.tabs-una-fila .tab-num { font-weight: 700; }
.tabs.tabs-una-fila .tab-btn.tab-aviso .tab-num { color: var(--ambar-texto); }
.tabs.tabs-una-fila .tab-btn.tab-cero:not(.active) { opacity: .6; }
.tabs.tabs-una-fila .tabs-mas { display: none; }
@media (max-width: 699px) {
  .tabs.tabs-una-fila {
    flex-wrap: nowrap;
    overflow: hidden;
    gap: 2px;
  }
  .tabs.tabs-una-fila .tab-btn {
    flex: 0 0 auto;
    white-space: nowrap;
    padding: 8px 11px;
  }
  /* `!important`: una pantalla puede dar `display` a sus pestañas por id; la que va al menú no se ve en la fila. */
  .tabs.tabs-una-fila .tab-btn.tab-fuera { display: none !important; }
  .tabs.tabs-una-fila .tabs-mas {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    margin-left: auto;
    min-width: 36px;
    padding: 8px 6px;
    border: none;
    border-radius: 10px;
    background: none;
    color: var(--negro);
    font-size: 16px;
    cursor: pointer;
  }
  .tabs.tabs-una-fila .tabs-mas[hidden] { display: none; }
  .tabs.tabs-una-fila .tabs-mas[aria-expanded="true"] { background: var(--blanco); }
}
/* El menú de las que no caben: va en <body> (la barra recorta lo que sobresale) y se coloca debajo de los tres puntos. */
.tabs-mas-menu {
  position: fixed;
  z-index: 1200;
  min-width: 180px;
  max-width: calc(calc(100vw / var(--letra-zoom, 1)) - 16px);
  max-height: calc(60vh / var(--letra-zoom, 1));
  overflow-y: auto;
  padding: 4px;
  background: var(--blanco);
  border: 1px solid var(--borde);
  border-radius: 12px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
  box-sizing: border-box;
}
.tabs-mas-menu[hidden] { display: none; }
.tabs-mas-menu .tabs-mas-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 11px 12px;
  border: none;
  border-radius: 8px;
  background: none;
  color: var(--negro);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
.tabs-mas-menu .tabs-mas-item:hover,
.tabs-mas-menu .tabs-mas-item:focus-visible { background: var(--gris-suave); outline: none; }
.tabs-mas-menu .tabs-mas-item .tab-num { font-weight: 700; }
.tabs-mas-menu .tabs-mas-item.tab-aviso .tab-num { color: var(--ambar-texto); }
.tabs-mas-menu .tabs-mas-item.tab-cero { opacity: .6; }

.que-es-pantalla { margin: 0 0 10px; font-size: 13px; min-width: 0; }
.que-es-pantalla[hidden] { display: none; }
.que-es-pantalla > summary {
  display: inline-block;
  cursor: pointer;
  list-style: none;
  color: var(--gris-medio);
  font-weight: 600;
  padding: 2px 0;
}
.que-es-pantalla > summary::-webkit-details-marker { display: none; }
.que-es-pantalla > summary::before { content: "\25B8\00A0"; }
.que-es-pantalla[open] > summary::before { content: "\25BE\00A0"; }
.que-es-pantalla > p,
.que-es-pantalla > div {
  margin: 6px 0 0;
  color: var(--gris-medio);
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.que-es-pantalla a { color: var(--negro); font-weight: 600; }

/* HOJA DE IMPRESIÓN — se ve primero en pantalla (previsualización) y de ahí se manda a
   imprimir o a guardar como PDF. Se monta dentro de la propia página, no en una ventana
   nueva, que los navegadores móviles bloquean. Ver compartido/exportar.js. */
#zonaImpresion {
  display: none;
}

body.previsualizando {
  overflow: hidden;
}

body.previsualizando #zonaImpresion {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 3000;
  background: var(--gris-suave);
  overflow: auto;
}

.imp-barra {
  position: sticky;
  top: 0;
  display: flex;
  gap: 8px;
  justify-content: space-between;
  align-items: center;
  background: var(--negro);
  padding: 10px 12px;
}

.imp-barra button {
  border: none;
  border-radius: 10px;
  padding: 9px 14px;
  font-size: 13px;
  font-weight: 700;
}

.imp-barra .imp-cerrar {
  background: var(--gris-oscuro);
  color: var(--blanco);
}

.imp-barra .imp-imprimir {
  background: var(--amarillo);
  color: var(--negro);
}

/* La hoja en sí: en pantalla se ve como un folio blanco; al imprimir ocupa la página. */
.imp-hoja {
  background: var(--blanco);
  color: #000;
  max-width: 1000px;
  margin: 12px auto 40px;
  padding: 16px;
  box-shadow: var(--sombra);
  overflow-x: auto;
}

.imp-cabecera {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  border-bottom: 2px solid #000;
  padding-bottom: 6px;
}

/* El logo de verdad en la cabecera de cualquier listado impreso (09/09/2026). La altura
   manda y el ancho se calcula solo, para no deformarlo si algún día cambia el fichero. */
.imp-logo-img { height: 42px; width: auto; display: block; }

.imp-logo {
  font-size: 15px;
  font-weight: 300;
}

.imp-logo b {
  font-weight: 800;
}

.imp-fecha {
  font-size: 11px;
  color: #444;
}

.imp-titulo {
  font-size: 19px;
  margin: 14px 0 2px;
}

.imp-subtitulo {
  font-size: 12px;
  color: #444;
  margin-bottom: 10px;
}

.imp-tabla {
  width: 100%;
  border-collapse: collapse;
  margin-top: 8px;
  font-size: 12px;
}

.imp-tabla th,
.imp-tabla td {
  border: 1px solid #999;
  padding: 4px 6px;
  text-align: left;
  vertical-align: top;
}

.imp-tabla th {
  background: #eee;
}

.imp-tabla tbody tr:nth-child(even) {
  background: #f6f6f6;
}

.imp-tabla .num {
  text-align: right;
  white-space: nowrap;
}

.imp-totales {
  margin-top: 10px;
  font-weight: 800;
}

.imp-pie {
  margin-top: 4px;
  font-size: 11px;
  color: #444;
}

@media print {
  /* SOLO CUANDO HAY UNA PREVISUALIZACIÓN DELANTE (corregido el 02/09/2026).
     ------------------------------------------------------------------------
     Esta regla escondía TODO lo que no fuera la hoja de previsualización, en cualquier
     pantalla. Y hay pantallas que imprimen su propio contenido sin pasar por esa hoja: el
     informe de una reclamación, el albarán firmado y el recibo de kilometraje. En esas, la
     hoja no existe, así que se escondía la página entera y salía UN FOLIO EN BLANCO — que
     es como se reportó: «sale una página sin texto alguno, tanto en imprimir como en
     guardar».
     Con la clase delante, solo tapa lo de detrás cuando de verdad hay algo delante. */
  body.previsualizando > *:not(#zonaImpresion) {
    display: none !important;
  }

  /* Y lo que no debe salir en papel NUNCA, imprima la pantalla lo suyo o una hoja de
     previsualización: la cabecera, la barra de abajo, los avisos y los botones. Antes esto
     vivía solo en `albaranes.css`, así que una pantalla que no lo cargara imprimía el menú. */
  .app-header,
  .bottom-nav,
  .alert,
  .fab-anadir,
  .no-imprimir,
  #avisoErrorJs,
  #deshacerBanner {
    display: none !important;
  }

  /* Al imprimir deja de ser una capa flotante y vuelve al flujo normal: un elemento fijo
     se repetiría o se cortaría al pasar de página. */
  #zonaImpresion {
    display: block !important;
    position: static !important;
    overflow: visible !important;
    background: none !important;
    font-size: 10pt;
    /* Sin esto, el navegador quita los fondos al imprimir y se pierden tanto la fila de
       títulos como el rayado que hace legible una tabla larga. */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  .imp-barra {
    display: none !important;
  }

  .imp-hoja {
    max-width: none;
    margin: 0;
    padding: 0;
    box-shadow: none;
    overflow: visible;
  }

  /* EL ALBARÁN CABE EN UNA HOJA, y hay que ayudarle a caber.
     Lo que le sacaba a una segunda página era el apaisado —que la impresión de esta
     aplicación fuerza para los listados anchos— y ya se corrige inyectando `@page` vertical
     al abrirlo. Esto es lo otro: que no se parta por dentro. Un albarán partido entre dos
     hojas, con la firma en la segunda, no vale como justificante.
     Si las observaciones son largas, pasará a dos páginas: eso es correcto y es lo único que
     debería hacerlo. */
  .imp-hoja-albaran table,
  .imp-hoja-albaran tr {
    page-break-inside: avoid;
  }

  .imp-hoja-albaran .alb-firmas,
  .imp-hoja-albaran .alb-firma,
  .imp-hoja-albaran .alb-cliente,
  .imp-hoja-albaran .alb-nota-legal {
    page-break-inside: avoid;
  }

  /* EL APAISADO YA NO VIVE AQUÍ (02/09/2026).
     ------------------------------------------------------------------------
     Los listados anchos —Facturas son nueve columnas— sí lo necesitan. Pero `@page` no se
     puede escoger por clase: puesto aquí, valía para TODA la aplicación, y un informe de
     reclamación o un recibo salían tumbados. Se resolvía al revés —el albarán inyectaba
     `@page portrait` para deshacerlo—, lo que obliga a acordarse en cada documento nuevo.
     Ahora lo inyecta quien abre la hoja de previsualización (`compartido/exportar.js`),
     mientras dura. Lo demás sale vertical, que es lo que hace un navegador si nadie le
     dice otra cosa. */

  .imp-logo { font-size: 13pt; }
  .imp-fecha { font-size: 8pt; }
  .imp-titulo { font-size: 15pt; }
  .imp-subtitulo { font-size: 9pt; }
  .imp-tabla { font-size: 9pt; }
  .imp-totales { font-size: 10pt; }
  .imp-pie { font-size: 8pt; }

  /* Que la fila de títulos se repita en cada página del PDF. */
  .imp-tabla thead {
    display: table-header-group;
  }

  .imp-tabla tr {
    page-break-inside: avoid;
  }
}

/* Texto secundario, para líneas de detalle bajo un dato principal. Vive aquí y no en
   pendientes.css (de donde viene el nombre) porque lo usa media app. */
.pend-sub {
  font-size: 12px;
  color: var(--gris-medio);
}

/* UNA LÍNEA QUE HAY QUE MIRAR, EN ÁMBAR (03/11/2026, docs/normas-esteticas.md): lo que falta para
   que algo funcione («falta ejecutar docs/…-migracion.sql») no es una nota en gris. Va sola o con
   `.pend-sub` (que pone la medida). */
.nota-ambar,
.pend-sub.nota-ambar {
  display: block;
  color: var(--ambar-texto);
  background: var(--ambar-fondo);
  border-left: 3px solid var(--ambar-borde);
  border-radius: 8px;
  padding: 6px 10px;
}
.nota-ambar[hidden] { display: none; }

/* LOS INFORMES POR SERVICIO (08/11/2026): la fila de lo que no tiene servicio de la lista sale en ámbar con
   «Sin servicio de la lista (N)» y el enlace a Sistema › Términos (src/lib/agruparPorServicio.js). */
.fila-sin-servicio,
.lista-item.fila-sin-servicio,
.fac-portipo > div.fila-sin-servicio {
  background: var(--ambar-fondo);
  color: var(--ambar-texto);
  border-left: 3px solid var(--ambar-borde);
  padding-left: 6px;
}
.fila-sin-servicio .sin-servicio-aviso { display: block; font-size: 12px; font-weight: 400; color: var(--ambar-texto); }
/* Lo de antes sin elegir de una lista (Sistema › Orígenes y motivos, 08/11/2026): una frase en ámbar dentro de la línea. */
.ambar-texto { color: var(--ambar-texto); }
.fila-sin-servicio a { color: var(--ambar-texto); }
.fac-portipo > div.fila-sin-servicio b { white-space: nowrap; }

/* BOTÓN FLOTANTE + — compartido por todas las pantallas que dan de alta algo (Clientes,
   Facturas, Ofertas, Caja...). Vive aquí, no en clientes.css, porque base.css es la única
   hoja que cargan absolutamente todas las páginas. */
.fab-anadir {
  position: fixed;
  bottom: 90px;
  right: 20px;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--boton-fondo);
  color: var(--boton-letra);
  border: none;
  font-size: 28px;
  font-weight: 700;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .3);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 90;
}

/* Y la que hace que `hidden` funcione en el botón. `display: flex` va por clase y gana al
   `display: none` que el navegador aplica a [hidden]: sin esto, esconderlo desde el JS no lo
   esconde. Mismo tropiezo anotado ya en .tabs, .ind-detalle, .aviso-inline, .btn-campana,
   .campana-panel, #visorPdf y .fp-aprendido. */
.fab-anadir[hidden] { display: none; }

.fab-anadir:active {
  opacity: .85;
}

/* Campos que se guardan en mayúsculas (nombres, direcciones): se escriben ya en
   mayúsculas para que se vea desde el principio cómo va a quedar guardado. */
.mayus {
  text-transform: uppercase;
}

/* Aviso de error de la propia app (ver compartido/avisarError.js): va fijo abajo, encima
   de la barra de navegación, y se quita tocándolo. Solo aparece cuando algo falla. */
#avisoErrorJs {
  display: none;
  position: fixed;
  left: 10px;
  right: 10px;
  bottom: 78px;
  z-index: 200;
  background: #fef2f2;
  color: var(--error);
  border: 1.5px solid var(--error);
  border-radius: 12px;
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 600;
  max-height: calc(40vh / var(--letra-zoom, 1));
  overflow-y: auto;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .2);
}

/* INDICADORES PLEGABLES — ver compartido/indicadores.js. Plegados ocupan una línea en vez
   de media pantalla, y esa línea ya dice lo único que puede obligar a hacer algo hoy. */
.indicadores {
  margin-bottom: 12px;
}

.ind-linea {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  background: var(--blanco);
  border: none;
  border-radius: var(--radio);
  box-shadow: var(--sombra);
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  color: var(--gris-medio);
  text-align: left;
}

/* Cuando hay algo vencido o sin resolver, la línea deja de ser gris. Es lo que hace que
   valga la pena plegarlos: si no hay nada, no llama; si lo hay, se ve sin desplegar.
   Fondo en vez de borde lateral: con las esquinas redondeadas, un borde de 4px se curva
   y queda como un paréntesis rojo suelto. El fondo es además el mismo que ya usan los
   demás avisos de la aplicación. */
.ind-linea.ind-alerta {
  color: var(--error);
  background: #fdecea;
}

.ind-flecha {
  flex: 0 0 auto;
  font-weight: 400;
}

.ind-detalle {
  margin-top: 8px;
}

/* Hace falta decirlo explícitamente: una regla de clase que fije `display` gana al
   `display: none` que el navegador aplica por el atributo `hidden`. Lo usa el aviso plegable
   (`.aviso-plegable-detalle`); los indicadores de cifras van en `<details>` desde el 18/10/2026. */
.ind-detalle[hidden] {
  display: none;
}

/* Lo que va debajo de las cifras cuando el indicador lo trae (`pie`, 01/10/2026): en Fichajes,
   QUÉ ha pasado en cada una. Grupos con su título y una línea por caso. */
.ind-pie { margin-top: 8px; }
.ind-grupo { margin-top: 8px; }
.ind-grupo-titulo { font-size: 12px; font-weight: 800; text-transform: uppercase; color: #666; margin-bottom: 2px; }
.ind-grupo-linea { font-size: 13px; padding: 4px 0; border-bottom: 1px solid #eee; overflow-wrap: anywhere; }
.ind-grupo-linea:last-child { border-bottom: 0; }

/* CIFRAS EN UNA LÍNEA Y «VER MÁS DATOS» (18/10/2026).
   Dirección no quiere bloques de tarjetas de cifras —la cifra grande con su rótulo debajo, en
   rejilla—: ocupan mucho y enseñan lo que no hace falta ver a simple vista. Lo que se mira al
   entrar (1-3 datos) va en UNA LÍNEA de texto, con los números en negrita normal; el resto, en
   un desplegable plegado. Lo usan `montarIndicadores` y las pantallas que pintan su resumen.
     <p class="linea-cifras"><span>Tesorería <b>163.313,02 €</b></span><span>…</span></p>
     <details class="mas-cifras"><summary>Ver más datos</summary>
       <ul class="mas-cifras-lista"><li><span>Rótulo</span><b>cifra</b></li>…</ul></details>
   En el móvil la línea se parte entre trozos, nunca dentro de uno. Una cifra en alerta lleva
   `.cifra-alerta` (rojo) o `.cifra-aviso` (ámbar) y no se esconde en el desplegable. */
.linea-cifras {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  row-gap: 2px;
  margin: 0 0 10px;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--gris-medio);
  min-width: 0;
}
.linea-cifras > * { min-width: 0; overflow-wrap: anywhere; }
/* El « · » va DETRÁS de cada trozo y no delante: cuando la línea se parte en el móvil, el
   renglón de abajo empieza por una cifra y no por un punto suelto. */
.linea-cifras > :not(:last-child)::after { content: "\00B7"; margin: 0 7px; color: #aaa; font-weight: 400; }
.linea-cifras b { font-weight: 700; color: var(--negro); }
.linea-cifras[hidden], .mas-cifras[hidden] { display: none; }
.cifras-alerta, .cifras-alerta b,
.cifra-alerta, .cifra-alerta b,
.linea-cifras .cifra-alerta b, .mas-cifras-lista .cifra-alerta b { color: var(--error); }
.cifra-aviso b, .linea-cifras .cifra-aviso b, .mas-cifras-lista .cifra-aviso b { color: #b7791f; }
.cifra-ok b, .linea-cifras .cifra-ok b, .mas-cifras-lista .cifra-ok b { color: #1e7a3c; }

/* Una cifra que filtra la lista de debajo (Portal del cliente): un botón que se lee como texto. */
.linea-cifras .cifra-filtro {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  border-bottom: 1px dashed #bbb;
  padding: 0;
  cursor: pointer;
}
.linea-cifras .cifra-filtro.cifra-elegida { background: #fff7d6; border-bottom: 2px solid var(--negro); }

.mas-cifras { margin: -4px 0 12px; font-size: 13px; min-width: 0; }
.mas-cifras > summary {
  display: inline-block;
  cursor: pointer;
  list-style: none;
  color: var(--gris-medio);
  font-weight: 600;
  padding: 2px 0;
}
.mas-cifras > summary::-webkit-details-marker { display: none; }
.mas-cifras > summary::before { content: "\25B8\00A0"; }
.mas-cifras[open] > summary::before { content: "\25BE\00A0"; }
.mas-cifras-lista {
  list-style: none;
  margin: 4px 0 0;
  padding: 2px 12px;
  max-width: 560px;
  background: var(--blanco);
  border-radius: 8px;
  box-shadow: var(--sombra);
}
.mas-cifras-lista li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 5px 0;
  border-bottom: 1px solid #eee;
}
.mas-cifras-lista li:last-child { border-bottom: 0; }
.mas-cifras-lista li > span { min-width: 0; overflow-wrap: anywhere; color: var(--gris-medio); }
.mas-cifras-lista li > b { flex: 0 0 auto; max-width: 60%; text-align: right; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.mas-cifras > .ind-pie { max-width: 560px; }

/* MENÚ DE TRES PUNTOS — grupos de opciones dentro del menú (ver compartido/exportar.js).
   Sirve para meter dentro un desplegable que no da para una línea entera, como el año. */
.menu-lista .menu-grupo + .menu-grupo {
  border-top: 1px solid var(--borde);
  margin-top: 4px;
  padding-top: 4px;
}

.menu-lista .menu-grupo-titulo {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--gris-medio);
  padding: 4px 12px 2px;
}

/* La opción elegida lleva una marca a la izquierda. El hueco se reserva en todas las
   opciones para que al cambiar de una a otra no se muevan de sitio.
   Solo en las opciones, no en PDF/Excel: esos botones ya existían en cinco pantallas y
   sangrarlos les cambiaría el aspecto sin motivo. */
.menu-lista button[data-valor]::before {
  content: '';
  display: inline-block;
  width: 14px;
}

.menu-lista button[data-valor].elegida {
  font-weight: 700;
}

.menu-lista button[data-valor].elegida::before {
  content: '✓';
  color: var(--amarillo);
}

/* Desplegable estrecho dentro de una fila de buscador: comparte línea con el campo de
   texto y con el menú de tres puntos. Se recorta el texto antes que empujar al resto —
   "Pendientes" cortado sigue leyéndose, y una fila que no cabe rompe la pantalla. */
.buscador-fila > .filtro-corto {
  flex: 0 1 130px;
  min-width: 0;
  padding-right: 28px;
  text-overflow: ellipsis;
  font-size: 14px;
}

/* Título de una sección que no va dentro de una tarjeta. Se usa cuando el enunciado tiene
   que llevar un dato variable —el año elegido, por ejemplo— que si no queda escondido en
   el menú de tres puntos y no se ve por ninguna parte. */
.titulo-seccion {
  font-size: 15px;
  font-weight: 800;
  margin-bottom: 10px;
}

/* Botón de lupa junto al título de una sección: despliega los filtros, que por defecto
   están guardados. Mismo tamaño y forma que el ⋮ de al lado para que se lean como un par
   de acciones, no como dos cosas distintas. */
.btn-lupa {
  position: relative; /* el punto rojo de «filtro puesto» se coloca sobre la lupa, no donde caiga */
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--borde);
  border-radius: 10px;
  background: var(--blanco);
  color: var(--negro);
}

.btn-lupa svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}

.btn-lupa:active {
  background: var(--gris-suave);
}

/* Con los filtros abiertos, la lupa se queda marcada: si no, con los filtros desplegados
   no se sabe si se cierran tocando otra vez ahí. */
.btn-lupa.abierta {
  background: var(--amarillo);
  border-color: var(--amarillo);
}

/* Y cuando hay un filtro puesto pero guardado, un punto avisa de que lo que se está
   viendo no es todo. Sin él, un filtro escondido es una lista incompleta sin explicación. */
.btn-lupa.con-filtro::after {
  /* Pegado a la esquina de la lupa. Con el margen negativo de antes, en el iPhone se iba por
     la pantalla (dirección, 01/11/2026). */
  content: '';
  position: absolute;
  top: -4px;
  right: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--error);
  border: 2px solid var(--blanco);
}

/* Botón principal metido en una fila junto a un campo, en vez de ocupar una línea entera
   para él solo. Se encoge a su texto y no se estira. */
.buscador-fila > .btn-en-fila {
  flex: 0 0 auto;
  width: auto;
  margin: 0;
  padding: 12px 20px;
  white-space: nowrap;
}

/* COMPORTAMIENTOS COMUNES §4 — aviso de deshacer.
   Abajo, encima de la barra de navegación: es donde está la mano y donde no tapa la lista
   que uno acaba de tocar. */
.deshacer-banner {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: 78px;
  z-index: 60;
  display: none;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-radius: 10px;
  background: var(--negro);
  color: #fff;
  font-size: 14px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .25);
}

.deshacer-banner.visible { display: flex; }

.deshacer-texto {
  flex: 1;
  min-width: 0;
  line-height: 1.35;
}

.deshacer-btn {
  flex: 0 0 auto;
  padding: 7px 14px;
  border: none;
  border-radius: 6px;
  background: var(--amarillo, #f5c518);
  color: var(--negro);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

/* La cuenta atrás dice cuánto queda para que deje de poder deshacerse. Sin ella, el aviso
   desaparece de golpe y uno no sabe si llegó a tiempo. */
.deshacer-cuenta {
  flex: 0 0 auto;
  font-size: 12px;
  opacity: .6;
  min-width: 22px;
  text-align: right;
}

/* FILA DE DATO: etiqueta a la izquierda, cifra a la derecha del todo.
   Es el ladrillo con el que están hechos los desgloses de toda la aplicación —el coste de un
   vehículo, el resumen de un pedido, las horas de una ficha—, y vive aquí porque lo usan doce
   pantallas.

   Estaba escrito solo en calidad.css, que carga UNA de esas doce. En las once restantes las
   filas salían como texto corrido: la etiqueta y el número pegados, sin separación ni
   alineación. Se veía sobre todo en el coste de Flota, que es una lista de siete cifras.

   `tabular-nums` hace que todos los dígitos ocupen lo mismo, así que las unidades de una
   columna de importes quedan una debajo de otra y se pueden comparar de un vistazo. */
.cal-linea {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--gris-suave, #eee);
  font-size: 14px;
}

.cal-linea:last-child {
  border-bottom: none;
}

/* La cifra: pegada a la derecha y sin partirse en dos líneas. */
.cal-linea > :last-child {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex-shrink: 0;
}

/* Cuando la fila es solo texto (dos etiquetas), no tiene sentido forzar que no se parta. */
.cal-linea.cal-texto > :last-child {
  white-space: normal;
}

/* Un aviso corto pegado al campo que lo provoca: el carnet que pide el vehículo, y cosas así.
   No es una alerta de las de arriba —esas son para lo que acaba de pasar—, sino una nota que
   se queda ahí mientras el dato siga siendo el que es.

   Vive en base.css y no en la hoja de una pantalla porque la trampa ya nos ha costado un
   arreglo: `.cal-linea` estaba en calidad.css y once pantallas la usaban sin cargarla. */
.aviso-inline {
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--ambar-texto);
  background: var(--ambar-fondo);
  border-left: 3px solid var(--ambar-borde);
  border-radius: 5px;
  padding: 7px 9px;
}

.aviso-inline[hidden] { display: none; }

/* QUIÉN VA ESCRITO A MANO (24/10/2026, Editar): cada nombre con sus botones para enlazarlo. */
.persona-escrita { margin-bottom: 8px; }
.personas-escritas-botones { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.btn-enlazar-persona {
  font: inherit;
  font-size: 12px;
  padding: 5px 9px;
  border-radius: 6px;
  border: 1px solid var(--ambar-borde);
  background: #fff;
  color: var(--ambar-texto);
  cursor: pointer;
  max-width: 100%;
  text-align: left;
}
.btn-enlazar-persona.quitar { border-color: #bbb; color: var(--gris-medio); }

/* `.field` trae `display: block` en calendario.css y le gana al `[hidden]` del navegador (30/09/2026,
   la fecha de fin de una baja en Altas y bajas). Vale para toda casilla que se esconda con `hidden`. */
.field[hidden] { display: none; }

/* UN MODAL MÁS ANCHO QUE EL DE UNA PREGUNTA (21/09/2026).

   `.modal-caja` mide 340 px, que es lo que necesita «¿seguro que quieres borrarlo?». Un
   formulario de treinta casillas en dos columnas dentro de 340 px sale ilegible — y es lo que
   reportó dirección del de editar un centro: «aparece demasiado estrecha».

   ⚠️ Y la causa era la de siempre: `.modal-ancha` se usa en SEIS pantallas que no cargan
   ninguna de las tres hojas que la definen (facturas-pendientes.css, pyg.css y
   solicitudes-vacaciones.css), así que esas seis salían a 340 px. Es el mismo fallo de
   `.modal-btn` del 17/09: una clase usada sin su hoja, que no se ve leyendo el código.

   Vive aquí, que la cargan todas. Las tres hojas que ya la definen van DESPUÉS en el
   ⚠️ Y VAN CON LAS DOS CLASES (`.modal-caja.modal-ancha`), NO CON UNA. `.modal-caja` vive en
   calendario.css, que TODAS estas pantallas cargan DESPUÉS de base.css: con la misma
   especificidad gana la última, así que un `.modal-ancha` suelto aquí no habría cambiado ni un
   píxel. Se midió en un Chrome, que es lo único que lo enseña. Las tres hojas que ya la definen
   (facturas-pendientes, pyg y solicitudes-vacaciones) llevan las dos clases por lo mismo, y al
   ir después siguen mandando donde ya funcionaba. */
.modal-caja.modal-ancha { max-width: 720px; }

/* Y para un formulario que tiene que leerse como una pantalla y no como un aviso —el centro de
   trabajo, con sus siete bloques— el ancho es el de la página (`.contenido`), que es lo que
   pidió dirección: «que sea por lo menos de grande como la página de Centro».
   `.modal-fondo` ya deja 24 px de margen alrededor, así que en un portátil no toca los bordes. */
.modal-caja.modal-pagina { max-width: 960px; }

/* ⚠️ `.campo` SE USABA EN TRES PANTALLAS SIN NINGUNA REGLA (21/09/2026), y por eso ensanchar su
   modal no cambiaba nada: sus casillas nunca se estiraron. Vivía solo en revision-furgoneta.css
   —la del móvil del trabajador— y Contratos, Cuotas y Beneficio por cliente no cargan esa hoja,
   así que un `<input type="text">` salía con su ancho NATURAL del navegador: 181 px de texto,
   229 px de fecha y 52 px de número. En un modal de 340 px eso pasaba por bueno porque 181 es
   justo la mitad; a 720 px se ve que nunca ocuparon su columna.

   Es el fallo de familia conocida del proyecto —una clase usada sin su hoja, como
   `.factura-cliente`, las `km-` de Planes, `.stat-card` o `.modal-btn`— y no se ve leyendo el
   código: el HTML es correcto y la hoja también; lo que falta es que se carguen juntos.

   Mismo aspecto que `.field` de calendario.css, que es lo que se esperaba de ella: las dos son
   «un rótulo encima y una casilla debajo». Y revision-furgoneta.css va DESPUÉS de base.css en la
   única pantalla que la carga, así que allí siguen mandando sus medidas de móvil (17 px). */
.campo {
  display: block;
  margin-bottom: 14px;
}

.campo > span {
  display: block;
  font-size: 11px;
  font-weight: 700;
  color: var(--gris-medio);
  text-transform: uppercase;
  letter-spacing: .5px;
  margin-bottom: 6px;
}

.campo input:not([type="radio"]):not([type="checkbox"]),
.campo select,
.campo textarea {
  width: 100%;
  box-sizing: border-box;
  border: 1.5px solid var(--borde);
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 15px;
  font-family: inherit;
  background: var(--blanco);
  color: var(--negro);
  outline: none;
  -webkit-appearance: none;
  transition: border-color .15s;
}

.campo input:focus,
.campo select:focus,
.campo textarea:focus {
  border-color: var(--amarillo);
}

/* La misma flecha que `.field select`: sin ella un desplegable se ve igual que una casilla de
   texto, que es lo que se arregló ese mismo día en la ficha del centro. */
.campo select {
  padding-right: 34px;
  background: var(--blanco) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23555" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>') no-repeat right 10px center;
  background-size: 18px;
  appearance: none;
  -moz-appearance: none;
  cursor: pointer;
}


/* UNA LISTA QUE NO HA PODIDO CARGARSE NO ES UNA LISTA VACÍA (21/09/2026).

   Reportado por dirección: «el pedido una vez aprobado no se va a preparar y debería de
   aparecer ahí». La cola de preparación había fallado, y el fallo se pintaba con la MISMA
   clase `.vacio` que «No hay nada aprobado esperando a prepararse»: gris, centrado y en
   pequeño. O sea que un error de carga y una lista de verdad vacía se leían igual, y lo que
   quedaba era la impresión de que el pedido se había perdido.

   Es la misma familia del arreglo del 18/09 en `botonOcupado.js`: un fallo de verdad y un
   «aquí no hay nada» no pueden verse igual desde fuera.

   Vive en base.css —que sí cargan todas las páginas— y no en la hoja donde está `.vacio`,
   que está repetida en calendario.css y clientes.css: son 187 pantallas y varias no cargan
   ninguna de las dos. Gana por especificidad (0,2,0) a las dos definiciones de `.vacio`. */
.vacio.fallo-carga {
  text-align: left;
  padding: 10px 12px;
  color: var(--ambar-texto);
  background: var(--ambar-fondo);
  border-left: 3px solid var(--ambar-borde);
  border-radius: 5px;
  font-size: 13px;
}

/* El prefijo va en el CSS y no en las 187 llamadas: lo que cada pantalla mete dentro es el
   mensaje del servidor, que por sí solo no dice si es un fallo o una respuesta. */
.vacio.fallo-carga::before {
  /* El icono del aviso del juego común (iconos.css), antes el signo de aviso escrito (03/11/2026). */
  content: "";
  display: inline-block;
  width: 1.1em;
  height: 1.1em;
  margin-right: .4em;
  vertical-align: -0.18em;
  background-color: currentColor;
  -webkit-mask: var(--ico) center / contain no-repeat;
  mask: var(--ico) center / contain no-repeat;
}

/* GUARDAR Y CANCELAR EN LA MISMA LÍNEA.
   Dos botones idénticos uno debajo del otro obligan a leerlos para saber cuál es cuál. Guardar
   es lo que se viene a hacer; cancelar es una salida, y con aspecto de enlace se distingue sin
   leer.

   En base.css porque lo usan la ficha del trabajador y las facturas de proveedor. Estuvo en la
   hoja de la ficha y en facturas salía sin estilo — la misma trampa de `.cal-linea`. */
.acciones-guardar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 4px;
}

.acciones-guardar .btn-primary {
  flex: 1;
  margin-top: 0;
}

.btn-texto {
  background: none;
  border: none;
  color: #777;
  font-size: 15px;
  padding: 12px 4px;
  text-decoration: underline;
  flex-shrink: 0;
}

.btn-texto:active { color: var(--negro); }

/* LA CAMPANA DE AVISOS (§5 del módulo Oferta). Vive en la cabecera de todas las páginas:
   un aviso tiene que encontrarte estés donde estés. Nace escondida y solo aparece cuando
   hay algo — una campana siempre a cero deja de mirarse. */
/* El «?» del Manual de uso (06/10/2026), al lado de la campana. */
.btn-manual {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  margin-right: 4px;
  border: 1.5px solid var(--cabecera-acento);
  border-radius: 50%;
  color: var(--cabecera-acento);
  font-weight: 700;
  font-size: 14px;
  line-height: 1;
  text-decoration: none;
  flex: 0 0 24px;
  margin-left: 8px;
}

.btn-campana {
  position: relative;
  background: none;
  border: none;
  padding: 6px;
  cursor: pointer;
  color: var(--cabecera-acento);
  display: flex;
  align-items: center;
}

.btn-campana[hidden] {
  display: none;
}

.btn-campana svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* EL NÚMERO, AL LADO Y NO ENCIMA (16/09/2026). TERCERA VUELTA A LO MISMO.

   Empezó pegado a `top:0; right:0`, o sea DENTRO del botón, y con dos caracteres («9+»)
   tapaba la campana: se veía el número flotando y no se entendía qué era. Se reportó el
   05/09/2026 como «la forma de la campana desaparece y solo se ve el número» y se sacó unos
   píxeles hacia la esquina de fuera.

   NO BASTÓ, y se volvió a reportar el 16/09/2026 con las mismas palabras: «pinchas la campana
   y desaparece, se ve sólo el número». Un óvalo de 19 px sobre un icono de 20 px sigue
   pisándole la esquina, se mire desde donde se mire — está a la vista en
   `.verificacion/campana-vista.html`, que es una página hecha para mirarlo.

   Así que deja de flotar: va EN EL FLUJO del botón, a la derecha del icono. Entonces no puede
   taparlo pase lo que pase, ni con «9+» ni con lo que venga. El botón crece unos píxeles y en
   la cabecera sobra sitio. */
.campana-num {
  min-width: 15px;
  height: 15px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--error);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 15px;
  text-align: center;
}

/* El hueco entre la campana y su número. `margin-right` se queda: el botón de salir va justo
   detrás y sin él quedan pegados. */
.btn-campana { margin-right: 4px; gap: 4px; }

.campana-panel {
  position: absolute;
  top: 100%;
  right: 8px;
  width: min(320px, calc(calc(100vw / var(--letra-zoom, 1)) - 24px));
  max-height: calc(60vh / var(--letra-zoom, 1));
  overflow-y: auto;
  background: var(--blanco);
  border-radius: 12px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .18);
  z-index: 60;
}

.campana-panel[hidden] {
  display: none;
}

.campana-aviso {
  display: block;
  padding: 10px 12px;
  border-bottom: 1px solid var(--gris-suave);
  color: var(--negro);
  text-decoration: none;
}

.campana-aviso:last-child {
  border-bottom: none;
}

.campana-titulo {
  font-size: 13px;
  font-weight: 700;
}

.campana-texto {
  font-size: 11px;
  color: var(--gris-medio);
  margin-top: 2px;
}

/* La aceptación por actuación (§12) y los recursos de la mesa de valoración: una fila con
   su nombre a la izquierda, su importe a la derecha y sus botones debajo. */
.ace-fila {
  padding: 8px 0;
  border-bottom: 1px solid var(--gris-suave);
}

.ace-fila:last-child {
  border-bottom: none;
}

.ace-nombre {
  font-size: 13px;
  font-weight: 600;
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: baseline;
}

.ace-importe {
  font-weight: 700;
  white-space: nowrap;
}

.ace-botones {
  display: flex;
  gap: 6px;
  margin-top: 6px;
}

.ace-btn {
  flex: 1;
  padding: 6px;
  border: 1px solid var(--gris-suave);
  border-radius: 8px;
  background: var(--blanco);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}

.ace-btn.ace-si { background: #1e7e34; color: #fff; border-color: #1e7e34; }
.ace-btn.ace-no { background: var(--error); color: #fff; border-color: var(--error); }
.ace-btn.ace-pend { background: var(--ambar-borde); color: #fff; border-color: var(--ambar-borde); }

.ace-resumen {
  font-size: 12px;
  padding: 8px 0;
  border-bottom: 2px solid var(--negro);
  margin-bottom: 4px;
}

/* LA BANDEJA en la campana: «qué hay que mirar hoy».

   Lo urgente lleva una barra roja a la izquierda. No color de fondo: en una lista
   de diez avisos, tres fondos rojos no dejan leer ninguno. */
.campana-aviso.urgente { border-left: 3px solid var(--error); }

.campana-importe {
  font-weight: 700;
  font-size: 14px;
  margin-top: 2px;
}

/* «Sigue pendiente» en vez de una fecha: en los avisos automáticos lo que importa
   no es cuándo se publicó, es que sigue pasando. Una fecha ahí haría creer que es
   una novedad de ese día. */
.campana-sigue { font-style: italic; opacity: .75; }

/* BOTONES EN UNA FILA, no apilados.
 *
 * `.btn-primary` ocupa el ancho entero, así que tres seguidos salen uno debajo de otro. Para
 * las acciones de un registro —activar, saltar, eliminar— eso ocupa media pantalla y hace
 * pensar que son tres pasos en orden, cuando son tres cosas sueltas. Pedido el 02/09/2026.
 *
 * En un móvil estrecho vuelven a apilarse solas (`flex-wrap`): tres botones de texto largo en
 * 320 px no caben sin cortar palabras. */
.botonera {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.botonera .btn-primary {
  width: auto;
  flex: 1 1 auto;
  min-width: 140px;
  margin: 0;
  padding: 13px 16px;
  font-size: 15px;
}

/* Un botón trabajando. Se ve apagado y no acepta el cursor: refuerza lo que ya dice el texto
   («Guardando…») y deja claro que no sirve de nada volver a pulsarlo. */
.boton-ocupado {
  opacity: .6;
  cursor: progress !important;
}

/* El mismo banner de avisos, en verde cuando es un acierto. Lo usa la campana al posponer:
   «vuelve el día 12» y «no vuelve» son cosas distintas y hay que decir cuál se ha elegido. */
#avisoErrorJs.aviso-ok {
  background: #1e6b3f;
}

/* La fila de un aviso de la campana: el aviso ocupa lo que puede y el reloj de posponer se
   queda a la derecha, sin empujarlo. */
.campana-fila {
  display: flex;
  align-items: stretch;
  gap: 0;
}
.campana-fila .campana-aviso { flex: 1; min-width: 0; }
.campana-posponer {
  flex: 0 0 auto;
  width: 42px;
  border: none;
  border-left: 1px solid var(--gris-suave, #eee);
  background: none;
  font-size: 16px;
  color: var(--gris, #888);
  cursor: pointer;
}
.campana-posponer:hover { background: var(--gris-suave, #f4f4f4); }

/* El modal de posponer lo crea la cabecera al vuelo, así que va por encima del panel de la
   campana, que ya está flotando. */
.modal-fondo.campana-modal { z-index: 9000; }

/* LO RESUELTO POR OTRA PERSONA (09/11/2026, laboral: «si alguien lo da por resuelto, que no le desaparezca a los
   demás»). Atenuado, sin contar en el número rojo, con quién y cuándo; la cruz lo quita de la vista de quien la pulsa. */
.campana-resueltos-titulo {
  padding: 8px 12px 4px;
  font-size: 12px;
  font-weight: 600;
  color: var(--gris-apagado);
  border-top: 1px solid var(--gris-suave);
}
.campana-fila.resuelto .campana-aviso { opacity: .6; }
.campana-fila.resuelto .campana-titulo { text-decoration: line-through; text-decoration-thickness: 1px; }
.campana-resuelto-por { font-size: 12px; color: var(--gris-oscuro); margin-top: 2px; }
.campana-quitar-resuelto {
  flex: 0 0 auto;
  width: 56px;
  font-size: 12px;
  border: none;
  border-left: 1px solid var(--gris-suave);
  background: none;
  color: var(--gris-medio);
  cursor: pointer;
}
.campana-quitar-resuelto:hover { background: var(--gris-suave); }
.campana-quitar-todos {
  display: block;
  width: 100%;
  padding: 8px 12px;
  border: none;
  background: none;
  text-align: left;
  font: inherit;
  font-size: 12px;
  color: var(--gris-medio);
  text-decoration: underline;
  cursor: pointer;
}

/* Un nombre que lleva a su ficha (02/09/2026, compartido/enlaceFicha.js). Se subraya con
   puntos en vez de a lo bruto: van dentro de tarjetas que ya tienen su propia acción, y un
   enlace azul de toda la vida ahí dentro compite con ella. */
.ficha-enlace {
  cursor: pointer;
  text-decoration: underline dotted;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.ficha-enlace:hover { color: #1a3a5c; text-decoration-style: solid; }

/* Una nota debajo de un campo: de dónde sale ese número, o qué va a pasar con lo que se
   escriba. Pegada al campo y en gris: es una aclaración, no un error. */
.campo-nota {
  display: block;
  margin-top: 4px;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--gris-medio);
}
/* Con su propio display, el [hidden] del navegador no la escondía (03/11/2026, normas-esteticas.js). */
.campo-nota[hidden] { display: none; }

/* UN AVISO PLEGABLE (04/09/2026).
 *
 * Hereda de `.ind-linea` —la línea de los indicadores— para que se comporte igual: mismo
 * gesto, misma flecha, misma memoria. Lo único propio es que va dentro de un `.alert`, que
 * ya trae su color de aviso, así que la línea no tiene que pintarse otra vez. */
.alert .aviso-plegable-linea {
  width: 100%;
  background: none;
  border: none;
  color: inherit;
  font: inherit;
  text-align: left;
  padding: 0;
  cursor: pointer;
}

.alert .aviso-plegable-detalle {
  margin-top: 6px;
}

.alert .aviso-plegable-detalle .aviso-linea {
  line-height: 1.45;
  padding: 2px 0;
}

/* UNA CASILLA CON SU TEXTO AL LADO. Vivía en calidad.css y la usan pantallas que NO cargan esa
   hoja —el catálogo del Almacén y Flota—, así que se veían sin alineación ni separación: el
   fallo de familia conocida de este proyecto (una clase usada sin su hoja). Una sola copia.

   ⚠️ Y su `[hidden]` explícito: `display: flex` le gana al `[hidden]` del navegador, así que
   sin esta línea una casilla escondida se sigue viendo. Duodécima vez en este proyecto. */
.cal-check {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--gris-medio);
  margin: 6px 0;
}

.cal-check[hidden] { display: none; }

/* UN PDF ENSEÑADO DENTRO DE LA PÁGINA: sus estilos (.vpp-*) los pone compartido/vistaPdfPantalla.js
   al abrirse (04/10/2026), porque el portal del cliente no carga esta hoja. */

/* Una .form-section escondida con `hidden` se tiene que esconder (01/10/2026). `.form-section`
   (calendario.css) trae su propio `display: block`, que le gana al [hidden] del navegador: en
   Acuerdos de jornada, tras mandar uno, el texto y «Cómo se firma» se quedaban a la vista con
   el acuerdo ya olvidado, y «Mandar» decía «Prepara antes el texto». Vale para todas las
   pantallas (dieciséis secciones nacen con `hidden`). */
.form-section[hidden] { display: none; }

/* «‹ Volver» DENTRO DE UNA PANTALLA (17/10/2026): vuelve a la vista anterior de la misma página,
   no a otra. Vivía solo en rentabilidad.css y albaranes.css, y en las pantallas que no cargan
   ninguna de las dos (Historial por cliente) salía como el botón gris del navegador. */
.btn-volver-seccion {
  background: none;
  border: none;
  color: #2d6a9f;
  font-size: 14px;
  font-weight: bold;
  cursor: pointer;
  margin-bottom: 12px;
  padding: 0;
}

/* El aviso de las pantallas que sustituye Contabilidad › Analítica (tanda 8, 18/10/2026): PyG,
   Rentabilidad y Beneficio por cliente. En ámbar y arriba, que es como se dice lo que hay que saber. */
.aviso-nueva-analitica { background: var(--ambar-fondo); color: var(--ambar-texto); border: 1px solid var(--ambar-borde); border-radius: 12px; padding: 10px 12px; margin: 0 0 12px; font-size: 13px; line-height: 1.4; }
.aviso-nueva-analitica a { color: var(--ambar-texto); font-weight: 700; text-decoration: underline; }
/* DATOS SIN ENLAZAR A SU FICHA (18/10/2026, regla «todo dato que se pueda enlazar, se enlaza»).
   Lo que se tecleó antes y el script de enlace no supo casar con seguridad: en ámbar, con su
   selector al lado para enlazarlo a mano. */
.enlace-sin { background: var(--ambar-fondo); color: var(--ambar-texto); border: 1px solid var(--ambar-borde); border-radius: 8px;
  padding: 6px 10px; margin: 6px 0; font-size: 13px; line-height: 1.4; }
.enlace-sin[hidden] { display: none; }
.enlace-sin select { max-width: 100%; margin: 4px 6px 0 0; }
.enlace-sin button { margin-top: 4px; }
/* Enlazar un texto viejo con su ficha (compartido/elegirFicha.js, 19/10/2026). */
.ef-sin { display: inline-block; margin: 4px 0; }
.ef-sin[hidden] { display: none; }
.ef-fondo { position: fixed; inset: 0; background: rgba(0, 0, 0, .45); display: flex; align-items: center;
  justify-content: center; z-index: 9500; padding: 16px; }
.ef-caja { background: #fff; border-radius: 12px; padding: 18px; width: 100%; max-width: 420px;
  max-height: 100%; overflow-y: auto; box-sizing: border-box; }
.ef-titulo { font-weight: 800; font-size: 16px; margin-bottom: 8px; }
.ef-texto, .ef-explica { font-size: 13px; color: #5b6170; margin-bottom: 8px; overflow-wrap: anywhere; }
.ef-campo { display: block; font-size: 13px; }
.ef-campo span { display: block; margin-bottom: 4px; font-weight: 600; }
.ef-campo input { width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #d0d4dc; border-radius: 8px; font-size: 15px; }
.ef-error { color: #a12a1a; font-size: 13px; margin-top: 6px; }
.ef-error[hidden] { display: none; }
.ef-botones { display: flex; gap: 8px; margin-top: 14px; }
.ef-botones button { flex: 1; padding: 11px; border: none; border-radius: 10px; font-weight: 700; font-size: 13px; }
.ef-no { background: #eef0f4; color: #1d1f24; }
.ef-si { background: #ffd200; color: #1d1f24; }
/* Lo leído de una ficha que no se puede tocar aquí (solicitudes con cliente). */
.campo-de-ficha { background: #f3f4f6 !important; color: #3b3f48; }
/* El buscador del plan de cuentas (financiero/selectorCuenta.js): en el flujo, debajo del campo. */
.sc-sug { background: #fff; border: 1px solid #d0d4dc; border-radius: 10px; max-height: 240px; overflow-y: auto; margin-top: 4px; }
.sc-sug[hidden] { display: none; }
.sc-sug button { display: block; width: 100%; text-align: left; border: none; background: none; padding: 8px 10px; font-size: 13px; cursor: pointer; overflow-wrap: anywhere; }
.sc-sug button:hover { background: #fff3c4; }
/* Un nombre suelto, sin ficha, en una línea de tarjeta: en ámbar. */
.sol-sin-ficha { color: var(--ambar-texto); font-weight: 600; }

/* EL MENÚ DE TRES PUNTOS DE UNA PANTALLA (20/10/2026). Nació en Almacén (19/10) y lo usa también
   Contabilidad: las secciones de la pantalla dentro de un menú, a la derecha de lo que queda
   arriba (el buscador, la sociedad). El comportamiento, en compartido/menuTresPuntos.js. El padre
   del panel lleva `position: relative`. */
.menu-tres-btn {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1.5px solid var(--borde);
  border-radius: 10px;
  background: var(--blanco);
  color: #666;
  cursor: pointer;
}
.menu-tres-btn svg { fill: currentColor; display: block; }
.menu-tres-btn:hover,
.menu-tres-btn[aria-expanded="true"] { background: #f1f1f1; }
/* Un punto ámbar cuando hay algún filtro puesto: la lista no es la entera. */
.menu-tres-btn.con-filtro::after {
  content: '';
  position: absolute;
  top: 5px;
  right: 5px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--amarillo);
  box-shadow: 0 0 0 1.5px var(--blanco);
}
.menu-tres {
  position: absolute;
  z-index: 60;
  top: calc(100% + 6px);
  right: 0;
  width: 300px;
  max-width: calc(calc(100vw / var(--letra-zoom, 1)) - 32px);
  max-height: calc(calc(100vh / var(--letra-zoom, 1)) - 140px);
  overflow-y: auto;
  padding: 6px 0 10px;
  background: var(--blanco);
  border: 1px solid var(--borde);
  border-radius: 10px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .18);
}
/* ⚠️ La regla de arriba no trae `display`, pero las de dentro sí: el `[hidden]`, explícito. */
.menu-tres[hidden] { display: none; }
.menu-tres-tit {
  display: block;
  padding: 8px 14px 4px;
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: #888;
}
/* Las secciones, una por renglón; la actual, marcada. Son los `.tab-btn` de siempre. */
.menu-tres-secciones { display: flex; flex-direction: column; }
.menu-tres-secciones .tab-btn {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  width: 100%;
  padding: 10px 14px 10px 30px;
  border-radius: 0;
  text-align: left;
  font-size: 14px;
  font-weight: 600;
  color: inherit;
  box-shadow: none;
}
/* ⚠️ Las secciones que la pantalla esconde por permisos llevan `hidden`, y la regla de arriba
   trae su `display`. */
.menu-tres-secciones .tab-btn[hidden] { display: none; }
.menu-tres-secciones .tab-btn:hover { background: var(--gris-suave); }
.menu-tres-secciones .tab-btn.active {
  background: var(--amarillo-suave);
  font-weight: 800;
  box-shadow: none;
}
.menu-tres-secciones .tab-btn.active::before {
  content: '';
  position: absolute;
  left: 13px;
  top: 50%;
  width: 7px;
  height: 7px;
  margin-top: -4px;
  border-radius: 50%;
  background: var(--negro);
}
/* FICHAR DESDE OFICINA (20/10/2026, compartido/ficharDesdeOficina.js). Lo usan Fichajes y
   Firmar albarán, que no cargan las mismas hojas: por eso vive aquí. */
.fichar-oficina-aviso {
  background: var(--ambar-fondo);
  border-left: 3px solid var(--ambar-borde);
  color: var(--ambar-texto);
  font-size: 12px;
  line-height: 1.45;
  padding: 8px 10px;
  border-radius: 4px;
  margin: 4px 0 10px;
}
.fichar-oficina-error {
  color: #a1261b;
  font-size: 13px;
  font-weight: 600;
  margin: 0 0 10px;
}
.fichar-oficina-error[hidden] { display: none; }
/* Quién llevaba el vehículo (02/11/2026): la nota de quién no puede y lo que no se pudo apuntar, en ámbar. */
#ficharOficinaVehiculo[hidden] { display: none; }
.fichar-oficina-nota { font-size: 12px; color: var(--ambar-texto); margin-top: 4px; line-height: 1.4; }
.fichar-oficina-ambar {
  background: var(--ambar-fondo);
  border-left: 3px solid var(--ambar-borde);
  color: var(--ambar-texto);
  font-size: 13px;
  line-height: 1.45;
  padding: 8px 10px;
  border-radius: 4px;
  margin: 4px 0 12px;
}
.fichar-oficina-ambar > div + div { margin-top: 4px; }

/* EL CENTRO SIN DIRECCIÓN (20/10/2026). Dirección: «muchos centros no tienen dirección, porque
   Contabilidad crea el centro solo con el nombre». Una marca ámbar pequeña detrás del nombre del
   centro: en la lista de recurrencias, al elegirlo, en la ficha del cliente y en el móvil. Aquí y
   no en la hoja de una pantalla, porque la pintan cinco (public/compartido/lineaCentro.js). */
.centro-sin-dir {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ambar-texto);
  background: var(--ambar-fondo);
  border: 1px solid var(--ambar-borde);
  border-radius: 4px;
  padding: 0 5px;
  white-space: nowrap;
}

/* La línea del centro de una recurrencia (lineaCentro.js), en las pantallas que no son la lista
   de Trabajos recurrentes: más oscura que el resto de la tarjeta, que es lo que se busca. */
.linea-centro-rec { font-size: 12px; color: var(--negro); margin-top: 3px; overflow-wrap: anywhere; }
.linea-centro-rec .rec-centro-nota { color: var(--gris-medio); }
/* Cada recurrencia de la ficha del cliente es un enlace a su edición: sin subrayado ni azul. */
.fc-rec-item { display: block; text-decoration: none; color: inherit; }
/* Turno de noche: la casilla de la salida al día siguiente, debajo de las horas. */
.fichar-oficina-casilla {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 14px;
  line-height: 1.35;
  margin: 0 0 12px;
  cursor: pointer;
}
.fichar-oficina-casilla input { flex: 0 0 auto; width: 18px; height: 18px; margin: 1px 0 0; }
.fichar-oficina-casilla span { min-width: 0; }

/* LO FIRMADO FRENTE A LA CUOTA (21/10/2026, compartido/cuotaFirmada.js y la pestaña Contrato de la
   ficha). En base.css porque lo pintan dos pantallas: la ficha del cliente y Facturación. Ámbar y
   arriba: es algo que hay que decidir, no una nota. */
.cfq-aviso {
  margin: 8px 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ambar-texto);
  background: var(--ambar-fondo);
  border-left: 4px solid var(--ambar-borde);
  border-radius: 8px;
  padding: 10px 12px;
  overflow-wrap: anywhere;
}
.cfq-aviso[hidden] { display: none; }
.cfq-lista, .cfq-acciones { margin: 6px 0 0; padding-left: 18px; }
.cfq-lista li, .cfq-acciones li { margin: 3px 0; }
.cfq-acciones { font-size: 13px; line-height: 1.45; }
.cfq-nota { display: block; font-size: 12px; color: var(--gris-apagado); margin-top: 3px; }
.cfq-error { color: #b42318; font-size: 13px; min-height: 1em; margin: 6px 0 0; }
.cfq-h { margin: 12px 0 4px; font-size: 14px; }
.cfq-botones { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.cfq-campos { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0; }
.cfq-campos label { display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: var(--gris-medio); min-width: 0; flex: 1 1 180px; }
.cfq-campos select { max-width: 100%; font: inherit; padding: 6px; }
.cfq-confirmar[hidden] { display: none; }
.cfq-elegido { font-size: 12px; color: var(--ambar-texto); margin-top: 6px; }
/* «Crear la cuota» y «No necesita cuota» (25/10/2026, compartido/sinCuota.js): en el cierre van en la fila de acciones. */
.fac-linea-acciones .sc-salidas { display: contents; }
/* La cuota de cada mes (25/10/2026): una línea y el detalle plegado. Sin tarjetas de cifras. */
.cfq-meses { font-size: 13px; line-height: 1.45; margin-top: 6px; overflow-wrap: anywhere; }
.cfq-meses details { margin-top: 2px; }
.cfq-meses summary { cursor: pointer; font-size: 12px; color: var(--gris-medio); }
.cfq-meses ul { margin: 4px 0 0; padding-left: 18px; font-size: 12px; }

/* LA DOCUMENTACIÓN DE ALTA DE UN CLIENTE (22/10/2026, compartido/documentacionAlta.js): la línea
   de su estado en la ficha, en la lista de clientes, en Pendientes y en la gestión del portal.
   Ámbar lo pendiente (sin enviar, enviada sin devolver), verde lo devuelto. Aquí y no en una hoja
   de pantalla: la usan cuatro. */
.docalta-linea { font-size: 13px; line-height: 1.45; border-radius: 5px; padding: 7px 9px; margin: 6px 0; overflow-wrap: anywhere; }
.docalta-ambar { color: var(--ambar-texto); background: var(--ambar-fondo); border-left: 3px solid var(--ambar-borde); }
.docalta-ok { color: #15803d; background: #e7f6ec; border-left: 3px solid #15803d; }
.docalta-botones { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0; }
.docalta-motivo { margin: 6px 0; }
.docalta-motivo[hidden] { display: none; }
.docalta-historial { font-size: 12px; margin: 6px 0; }
.docalta-historial summary { cursor: pointer; color: var(--gris-medio); }
.docalta-historial ul { margin: 6px 0 0; padding-left: 18px; }
.docalta-historial li { margin: 3px 0; overflow-wrap: anywhere; }
.docalta-marca { display: inline-block; font-size: 11px; border-radius: 4px; padding: 1px 6px; margin-left: 4px; }
.docalta-marca.docalta-ok, .docalta-marca.docalta-ambar { border-left-width: 2px; }
/* Cada documento (23/10/2026): «nombre · rellenado el … · Ver» o «falta», y el formulario en línea de
   «Dar por buena» / «Quitar la validación». */
.docalta-docs { list-style: none; margin: 4px 0; padding: 0; font-size: 13px; }
.docalta-docs li { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 10px; padding: 4px 2px; border-bottom: 1px solid #eee; overflow-wrap: anywhere; }
.docalta-docs li:last-child { border-bottom: 0; }
.docalta-doc-nombre { min-width: 0; }
.docalta-doc-que { white-space: nowrap; }
/* El motivo del SEPA (25/10/2026) es una frase: se parte en el móvil en vez de salirse. */
.docalta-doc-motivo { white-space: normal; }
.docalta-doc-ok { color: #15803d; }
.docalta-doc-falta { color: var(--ambar-texto); font-weight: 600; }
.docalta-doc-nada { color: #777; }
.docalta-ver { font-weight: 700; }
.docalta-panel { border: 1px solid #e4e4e4; border-radius: 8px; padding: 8px 10px; }
.docalta-panel-titulo { font-weight: 700; margin-bottom: 4px; }
[data-docalta="panel"][hidden] { display: none; }

/* LOS DOCUMENTOS DE ALTA TRAÍDOS POR OTRO MEDIO, LOS TRES DE UNA VEZ (29/10/2026,
   compartido/documentosAltaTraidos.js): una fila por documento con su casilla, su archivo y su
   nombre. Cada parte que se esconde con `hidden` lleva su `[hidden]` (tienen su propio display). */
.dat-docs { margin: 8px 0; }
.dat-doc { border: 1px solid #e4e4e4; border-radius: 8px; padding: 8px 10px; margin: 6px 0; }
.dat-doc.dat-marcado { border-color: #9cc3a6; background: #fafdfb; }
.dat-doc.dat-error { border-color: var(--ambar-borde); background: var(--ambar-fondo); }
.dat-cabeza { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 2px 10px; font-size: 13px; }
.dat-trae { display: flex; align-items: center; gap: 6px; font-weight: 700; cursor: pointer; min-width: 0; overflow-wrap: anywhere; }
.dat-tiene { font-size: 12px; overflow-wrap: anywhere; }
.dat-cuerpo { margin-top: 6px; display: block; }
.dat-cuerpo[hidden], .dat-archivo[hidden], .dat-comun[hidden], .dat-bueno[hidden] { display: none; }
.dat-opciones { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 13px; margin-bottom: 4px; }
.dat-opciones label, .dat-ya label { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.dat-archivo { display: block; }
.dat-archivo input[type="text"], .dat-archivo input[type="file"] { width: 100%; box-sizing: border-box; }
.dat-ya { font-size: 13px; border-left: 3px solid var(--ambar-borde); background: var(--ambar-fondo); color: var(--ambar-texto); padding: 6px 8px; border-radius: 4px; margin-top: 6px; }
.dat-gris { color: #777; font-size: 12px; font-weight: 400; }
.dat-nota { color: var(--gris-medio); font-size: 12px; line-height: 1.4; margin-bottom: 4px; }
.dat-comun, .dat-bueno { display: block; border-top: 1px solid #eee; padding-top: 6px; margin-top: 6px; }
.dat-fila { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
@media (max-width: 520px) { .dat-fila { grid-template-columns: 1fr; } }
.dat-fila .field select, .dat-fila .field input, .dat-comun .field input { width: 100%; box-sizing: border-box; }
.dat-botones { justify-content: flex-end; }
.dat-mas { margin-top: 10px; font-size: 13px; }
.dat-mas > summary { cursor: pointer; color: var(--gris-medio); }
/* El nombre del cliente que lleva a su ficha (Ofertas › Pendientes: la tarjeta y la ventana). */
.enlace-ficha-cliente { color: inherit; text-decoration: underline; text-decoration-color: #9bb; text-underline-offset: 2px; }
.enlace-ficha-cliente:hover { color: #1f6f8b; }

/* TODO TRABAJO LLEVA CLIENTE Y CENTRO (22/10/2026, src/lib/clienteYCentro.js): el aviso en ámbar
   del cliente sin centros (selectorCentro.js) y el de un trabajo antiguo al que le falta alguno. */
.centro-aviso, .aviso-cliente-centro { color: var(--ambar-texto); background: var(--ambar-fondo); border-left: 3px solid var(--ambar-borde); padding: 6px 10px; margin-top: 6px; font-size: 0.9em; border-radius: 4px; }
.centro-aviso[hidden], .aviso-cliente-centro[hidden] { display: none; }
/* El lugar del centro elegido, debajo del desplegable (03/11/2026, selectorCentro.js con `conLugar`). */
.centro-lugar { color: var(--gris-medio); font-size: 0.9em; margin-top: 4px; overflow-wrap: anywhere; }
.centro-lugar[hidden] { display: none; }

/* LOS TRABAJOS DE UN CLIENTE DADO DE BAJA (25/10/2026, compartido/bajaCliente.js): el aviso al darlo
   de baja y la pregunta al reactivarlo, en ámbar y arriba. */
.bc-aviso { font-size: 13px; margin-bottom: 10px; }
.bc-aviso ul { margin: 4px 0 4px 18px; padding: 0; }
.bc-aviso .bc-casillas { list-style: none; margin-left: 0; }
.bc-aviso .bc-casillas label { display: flex; gap: 6px; align-items: flex-start; }
.bc-aviso .bc-mano { color: var(--ambar-texto); font-style: italic; }
.bc-botones { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.bc-resultado { margin-top: 6px; font-weight: 600; }
.bc-resultado[hidden] { display: none; }
#avisoBajaTrabajos[hidden] { display: none; }
/* La baja de un centro con lo pendiente (28/10/2026, compartido/bajaCentro.js): la previa en el modal
   y la pregunta al reactivarlo (la misma caja ámbar de arriba). */
#avisoBajaCentro[hidden] { display: none; }
.bcen-caja { font-size: 13px; max-height: calc(calc(100vh / var(--letra-zoom, 1)) - 48px); overflow-y: auto; }
.bcen-intro { margin: 0 0 8px; }
.bcen-sub { margin: 8px 0 4px; font-weight: 600; }
.bcen-bloque { border-top: 1px solid #e5e7eb; padding: 6px 0; }
.bcen-casilla { display: flex; gap: 6px; align-items: flex-start; }
.bcen-nota { color: var(--ambar-texto); font-style: italic; margin: 2px 0 0 22px; }
.bcen-bloque .bc-lista { margin-left: 22px; }
.bcen-bloque ul { margin: 4px 0 4px 18px; padding: 0; }
#bcenError { color: #b42318; }

/* La casilla del importe de Editar y Añadir trabajo (26/10/2026): desactivada se tiene que ver que no se
   puede escribir (el motivo va debajo, en ámbar). */
#importeTrabajo:disabled { background: var(--gris-suave); color: var(--gris-medio); cursor: not-allowed; }

/* ELEGIR LAS COLUMNAS ANTES DE EXPORTAR (28/10/2026, compartido/exportar.js con `elegirColumnas`).
   Clases propias y aquí, en base.css, que la cargan todas: `.modal-fondo`/`.modal-caja` viven en
   calendario.css y no todas las pantallas que exportan la cargan. */
.elegir-col-fondo {
  position: fixed; inset: 0; z-index: 9200;
  background: rgba(0, 0, 0, .45);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.elegir-col-caja {
  background: var(--blanco); border-radius: var(--radio);
  width: 100%; max-width: 560px; max-height: 100%;
  display: flex; flex-direction: column; padding: 16px; box-sizing: border-box;
}
.elegir-col-titulo { font-size: 16px; font-weight: 800; margin-bottom: 8px; }
.elegir-col-aviso-ambar {
  background: var(--amarillo-suave); border: 1px solid var(--ambar-borde); color: var(--ambar-texto);
  border-radius: 8px; padding: 8px 10px; font-size: 13px; margin-bottom: 8px;
}
.elegir-col-acciones { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 8px; }
.elegir-col-acciones .modal-btn { padding: 8px 12px; }
.elegir-col-cuenta { font-size: 12px; color: var(--gris-medio); margin-left: auto; }
.elegir-col-lista {
  overflow-y: auto; -webkit-overflow-scrolling: touch; min-height: 0; flex: 1 1 auto;
  border-top: 1px solid var(--borde); border-bottom: 1px solid var(--borde);
  display: grid; grid-template-columns: 1fr 1fr; column-gap: 12px; padding: 4px 0;
}
.elegir-col-grupo {
  grid-column: 1 / -1; font-size: 12px; font-weight: 800; text-transform: uppercase;
  color: var(--gris-medio); margin: 10px 0 2px;
}
.elegir-col-linea { display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 14px; cursor: pointer; }
.elegir-col-linea input { width: 18px; height: 18px; flex: 0 0 auto; margin: 0; }
.elegir-col-linea.apagada { color: #999; cursor: default; }
.elegir-col-sin { font-size: 11px; color: var(--ambar-texto); white-space: nowrap; }
.elegir-col-falta { font-size: 13px; color: var(--error); margin-top: 8px; }
.elegir-col-falta[hidden] { display: none; }
.elegir-col-botones { display: flex; gap: 8px; margin-top: 12px; }
.elegir-col-botones button { flex: 1; }
.elegir-col-exportar {
  padding: 12px; border: none; border-radius: 10px; font-size: 13px; font-weight: 700;
  background: var(--amarillo); color: var(--negro); cursor: pointer;
}
.elegir-col-exportar:disabled { opacity: .5; cursor: default; }
@media (max-width: 520px) {
  .elegir-col-lista { grid-template-columns: 1fr; }
}

/* «Una fila por…» arriba de la ventanita de las columnas (28/10/2026, `filasPor` en compartido/exportar.js).
   Las líneas, grupos y casillas que no tocan en el modo elegido se esconden con `hidden`: su regla de `display`
   ganaría al `[hidden]` del navegador, así que lleva el suyo. */
.elegir-col-filaspor {
  border: 1px solid var(--borde); border-radius: 8px; padding: 8px 10px; margin-bottom: 8px;
}
.elegir-col-filaspor-titulo { font-size: 13px; font-weight: 800; margin-bottom: 4px; }
.elegir-col-filaspor-opciones { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.elegir-col-filaspor-op, .elegir-col-filaspor-casilla {
  display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 14px; cursor: pointer;
}
.elegir-col-filaspor-op input, .elegir-col-filaspor-casilla input { width: 18px; height: 18px; margin: 0; flex: 0 0 auto; }
.elegir-col-filaspor-total { font-size: 12px; color: var(--gris-medio); margin-top: 2px; }
.elegir-col-filaspor-casilla[hidden], .elegir-col-linea[hidden], .elegir-col-grupo[hidden] { display: none; }

/* LAS FECHAS DEL IPHONE EN DOS COLUMNAS (01/11/2026, captura de dirección en Mis gastos: «Desde» se salía por la
   derecha y «Hasta» se montaba sobre el campo de al lado). El `input type=date` de iOS trae su propio ancho mínimo y
   su aspecto nativo: dentro de una rejilla, a todo el ancho de su columna y sin aspecto nativo, con la altura de los
   demás campos. Fuera de una rejilla solo se le quita el mínimo. Las rejillas, con `minmax(0, 1fr)` (ver .filtros-grid
   en calendario.css y .campos-dos aquí). */
input[type="date"], input[type="datetime-local"], input[type="month"], input[type="time"] {
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
}
.filtros-grid input[type="date"], .campos-dos input[type="date"], .bz-dos input[type="date"],
.gt-filtros-cuerpo input[type="date"], .filtros-grid input[type="month"], .campos-dos input[type="month"] {
  width: 100%;
  -webkit-appearance: none;
  appearance: none;
  min-height: 42px;
  line-height: normal;
  text-align: left;
}
.filtros-grid input[type="date"]::-webkit-date-and-time-value,
.campos-dos input[type="date"]::-webkit-date-and-time-value,
.bz-dos input[type="date"]::-webkit-date-and-time-value,
.gt-filtros-cuerpo input[type="date"]::-webkit-date-and-time-value { text-align: left; }

/* EL CAMPO DE FECHA Y HORA COMÚN (03/11/2026, compartido/fechaHora.js). Dirección, sobre «Programar»
   del Chat («AAAA-MM-DD HH:MM» en una ventanita): «Hazlo igual que el resto». El día con el selector
   común (dd/mm/aaaa) y la hora con su campo, uno al lado del otro; y la misma pareja en una ventana
   para corregir una hora de pasada (`preguntarFechaHora`). */
.fh-campo { display: flex; gap: 8px; margin-top: 6px; max-width: 320px; }
.fh-campo[hidden] { display: none; }
.fh-campo > input {
  /* Un ancho propio: dentro de un menú que se ajusta a lo que lleva (los ⋮ del Chat), el ancho
     de fábrica de un campo lo ensancharía hasta el borde de la pantalla. */
  width: 9em;
  min-width: 0;
  flex: 1 1 9em;
  padding: 8px 10px;
  border: 1px solid var(--borde);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  background: var(--blanco);
  box-sizing: border-box;
}
.fh-campo > .fh-hora { width: 8em; flex: 0 0 8em; }
.fh-fondo {
  position: fixed; inset: 0; z-index: 9300;
  display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(0, 0, 0, .45);
}
.fh-caja {
  width: 100%; max-width: 380px; box-sizing: border-box;
  padding: 18px; border-radius: var(--radio); background: var(--blanco); box-shadow: var(--sombra);
}
.fh-titulo { font-weight: 700; margin-bottom: 10px; }
.fh-caja .fh-campo { max-width: none; margin-top: 0; }
.fh-aviso { margin-top: 8px; font-size: 13px; color: var(--error); }
.fh-aviso[hidden] { display: none; }
.fh-botones { display: flex; gap: 8px; margin-top: 16px; }
.fh-botones > button { flex: 1 1 0; margin: 0; }

/* EL TAMAÑO DE LA LETRA Y EL NOMBRE DE LA APLICACIÓN (05/11/2026, Sistema › Apariencia; public/compartido/apariencia.js).
   «Quién eres» de la cabecera es un botón: abre el menú de la sesión con los tres tamaños. */
.header button.rol-badge {
  border: none;
  font-family: inherit;
  cursor: pointer;
}
.header .menu-sesion { padding: 12px; }
.header .logo-app { min-width: 0; flex: 0 1 auto; }
.header .logo-app-img { display: block; max-height: 32px; max-width: 160px; object-fit: contain; }
.header .logo-app-nombre {
  font-size: 18px;
  font-weight: 800;
  color: var(--cabecera-acento);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: calc(50vw / var(--letra-zoom, 1));
}
.ap-letra-titulo { font-size: 13px; font-weight: 800; color: var(--negro); margin-bottom: 8px; }
.ap-letra-fila { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.ap-letra-op {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-height: 56px;
  padding: 6px 4px;
  border: 1.5px solid var(--borde);
  border-radius: var(--radio-campo);
  background: var(--blanco);
  color: var(--negro);
  font-size: 12px;
  font-weight: 700;
  font-family: inherit;
}
.ap-letra-op.elegido { background: var(--boton-fondo); color: var(--boton-letra); border-color: var(--boton-fondo); }
.ap-letra-a { font-weight: 800; line-height: 1; }
.ap-letra-nota { font-size: 12px; color: var(--gris-medio); margin-top: 8px; }
.ap-letra-caja { background: var(--blanco); border-radius: var(--radio); padding: 14px; margin: 14px 0; box-shadow: var(--sombra); }
/* El logo de la pantalla de entrada cuando dirección ha subido otro o ha cambiado el nombre (apariencia.js). */
.logo-app-entrada { display: block; max-width: 220px; max-height: 90px; margin: 0 auto; object-fit: contain; }
.logo-app-entrada-nombre { display: block; text-align: center; font-size: 30px; font-weight: 800; color: var(--negro); overflow-wrap: anywhere; }
/* CON LA LETRA AMPLIADA (05/11/2026), la cabecera mide menos píxeles de los suyos: «Muy grande» en un móvil de 390 la deja
   en 325. Se mide la CABECERA (no la ventana, que sigue diciendo 390) y con menos de 335 de contenido se quita «LIMPIEZAS» del logo
   y se aprieta lo de la derecha; si no, el botón de salir se salía por el lado. */
.header { container: cabecera / inline-size; }
@container cabecera (max-width: 335px) {
  .header .logo-text { display: none; }
  .header .logo { gap: 4px; }
  .header .usuario { gap: 4px; }
  .header .btn-manual { margin-left: 0; }
  .header .rol-badge { max-width: 64px; }
}

/* LAS VENTANAS, CON SUS BOTONES SIEMPRE A LA VISTA (07/11/2026). Dirección, desde el iPhone, en Editar
   centro: «El menú cancelar o aceptar no se ve». En el iPhone la ventana se quedaba metida en la zona del
   contenido, por debajo de la cabecera y de la barra de abajo, y la barra tapaba Cancelar/Guardar al
   final de un formulario largo. Dos cosas: la ventana deja libre el alto de la cabecera y de la barra
   (en las pantallas con barra abajo), y su fila de botones se queda pegada abajo mientras se baja por el
   formulario. No se saca la ventana al <body>: hay pantallas que escuchan sus botones desde la pestaña. */
body.app-fija:has(> .bottom-nav) .modal-fondo {
  padding-top: calc(24px + 56px);
  padding-bottom: calc(24px + 66px + env(safe-area-inset-bottom, 0px));
}
.modal-caja > .modal-botones {
  position: sticky;
  bottom: 0;
  z-index: 1;
  background: var(--blanco, #fff);
  padding-top: 12px;
  /* La segunda sombra, blanca, tapa el margen de abajo de la caja: sin ella el formulario asoma debajo. */
  box-shadow: 0 -12px 12px -12px rgba(0, 0, 0, .18), 0 24px 0 0 var(--blanco, #fff);
}

/* EL CONTACTO DE UN CAMPO, ELEGIDO EN LUGAR DE TECLEADO (08/11/2026, compartido/contactoElegir.js). */
.kce { margin: 0 0 8px; }
.kce[hidden] { display: none; }
.kce-linea { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; font-size: 14px; }
.kce-texto { flex: 1 1 200px; min-width: 0; overflow-wrap: anywhere; }
.kce-panel { margin-top: 6px; padding: 8px; border: 1px solid var(--borde); border-radius: 8px; background: var(--gris-suave); }
.kce-q { width: 100%; box-sizing: border-box; margin-bottom: 6px; }
.kce-lista { display: flex; flex-direction: column; gap: 4px; max-height: 260px; overflow-y: auto; }
.kce-op { display: flex; flex-direction: column; align-items: flex-start; text-align: left; width: 100%; padding: 6px 8px; border: 1px solid var(--borde); border-radius: 6px; background: var(--blanco, #fff); cursor: pointer; font: inherit; }
.kce-op[hidden] { display: none; }
.kce-op-nombre { font-weight: 600; }
.kce-nuevo { margin: 8px 0; }
.kce-nuevo > summary { cursor: pointer; font-size: 14px; }
.kce-error { margin: 6px 0; }
input.kce-del-contacto, textarea.kce-del-contacto { background: var(--gris-suave); }
/* UNA PERSONA, UNA ELECCIÓN (10/11/2026): las casillas de siempre, escondidas para quien elige el contacto (siguen en el
   formulario). Clase y no [hidden]: la fila es `display: grid`. */
.fp-fila.kce-oculta, .field.kce-oculta { display: none; }
.kce-rotulo { font-weight: 600; }
/* El texto de una elección es largo (lo escrito de antes tal cual): los botones van debajo. */
.kce-larga .kce-texto { flex-basis: 100%; }
.kce-elegido { padding: 6px 8px; border: 1px solid var(--borde); border-radius: 8px; background: var(--gris-suave); }
.kce-dudosos { margin-top: 6px; }
.kce-otras { margin: 8px 0 0; }
.kce-otras-q { width: 100%; box-sizing: border-box; margin-bottom: 6px; }
.kce-mismo { display: flex; align-items: flex-start; gap: 8px; margin: 4px 0 8px; font-size: 14px; cursor: pointer; }
.kce-mismo input { margin-top: 2px; flex: none; }
.kce-mismo-datos { margin: 0 0 8px; }
#mismaPersona { margin: 0 0 8px; }
#mismaPersona[hidden] { display: none; }
.kce-mismo-datos[hidden] { display: none; }

/* EL MUNICIPIO, DE LA LISTA DEL INE CON BUSCADOR (08/11/2026, compartido/municipioElegir.js). En base.css porque lo
   usan las fichas de clientes, centros, proveedores, trabajadores, ofertas, comerciales, candidatos y recurrencias. */
/* Flota sobre lo de abajo (en una fila de dos casillas no empuja la de al lado), al menos 280 de ancho; el lado al que
   se abre lo pone el componente (.muni-derecha) si no cabe hacia la derecha. */
.muni-ancla { position: relative; }
.muni-lista {
  position: absolute;
  left: 0;
  z-index: 40;
  width: max(100%, 280px);
  margin-top: 4px;
  border: 1.5px solid var(--borde);
  border-radius: 8px;
  max-height: 240px;
  overflow-y: auto;
  background: var(--blanco);
  box-shadow: var(--sombra);
}
.muni-lista[hidden] { display: none; }
.muni-lista.muni-derecha { left: auto; right: 0; }
.muni-item {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 10px 12px;
  font: inherit;
  font-size: 14px;
  text-align: left;
  color: inherit;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--borde);
  cursor: pointer;
}
.muni-item:last-child { border-bottom: none; }
.muni-item:hover, .muni-item:focus { background: var(--amarillo-suave); }
.muni-prov { color: var(--gris-apagado); font-size: 12px; white-space: nowrap; }
.muni-vacio { padding: 10px 12px; font-size: 13px; color: var(--gris-apagado); }
