/* ---------- NOTA SOBRE @media (hover: hover) ----------
   Las reglas :hover de este fichero van envueltas en
   "@media (hover: hover)". En una pantalla táctil no hay puntero, y sin
   ese guard el navegador aplica el :hover al tocar y lo DEJA PUESTO
   hasta que tocas otra cosa: botones y filas se quedan iluminados, que
   es una de las cosas que más delatan a una web dentro de una app.
   Envolver no cambia la especificidad ni el orden de la cascada, así que
   en escritorio no cambia nada. La respuesta al tacto la da :active, más
   abajo. */

/* =========================================================
   NextCliniq · Cuadro de mandos — Sistema de diseño
   =========================================================
   Paleta: negro-grisáceo (identidad del logo) + verde menta
   (color de marca de NextCliniq), sobre fondo neutro claro.

   Tipografía:
   - Manrope    -> títulos y navegación (personalidad, geométrica)
   - Poppins    -> el resto de toda la interfaz: tablas, cifras (KPIs,
     importes, indicadores, porcentajes), botones, campos, filtros,
     menús y etiquetas. Es la tipografía de cuerpo de toda la app
     (variable --font-body); Inter ya no se usa en ningún sitio.
   - IBM Plex Mono -> solo el log de importación (consola de texto)
   ========================================================= */

@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@500;700;800&family=IBM+Plex+Mono:wght@500;600&family=Poppins:wght@400;500;600;700;800&display=swap');

:root {
  --ink: #14181c;
  --ink-soft: #23292f;
  --slate: #5b6570;
  --mint: #1fae7e;
  --mint-dark: #178f67;
  --mint-soft: #e4f5ee;
  --bg: #f4f7f6;
  --card: #ffffff;
  --border: #e3e8e6;
  --text: #14181c;
  --text-muted: #6b7680;
  --danger: #e5484d;
  --danger-soft: #fdecec;
  --danger-text: #a12227; /* rojo oscuro, para texto sobre fondo claro */
  --warning: #f0a531;
  --warning-soft: #fdf3e2;
  --warning-text: #8a5a10; /* ámbar oscuro, para texto sobre fondo claro */
  --info: #2a5db0;
  --info-soft: #e3edff;
  --text-suave: #b8bfc5; /* gris claro, para datos con salvedades */
  --slate-soft: #eceef0; /* gris muy claro, fondo de fila de totales neutra */
  --success: #1fae7e;
  --success-soft: #e4f5ee;

  /* Barra superior (nombre de clínica, herramienta, cambiar de
     herramienta). Oct. 2026: "blanco con filo menta" (opción C del
     lienzo de opciones), en lugar del negro de antes. El verde va en el
     filo de arriba, en el punto junto al nombre y en el selector. */
  --barra-superior: var(--card);
  --alto-barra-superior: 44px; /* incluye el filo de 3px; de él dependen
    el alto mínimo del cuerpo y dónde se pega el menú lateral */
  --texto-barra-superior: var(--ink);
  --mint-profundo: #116b4d; /* verde oscuro para TEXTO verde sobre blanco o
    sobre --mint-soft: --mint-dark se queda en 4,07:1 sobre blanco, por
    debajo del mínimo de lectura a 12px; este da 6,49:1. */
  --mint-borde: #a6dcc4; /* borde menta suave (píldoras sobre --mint-soft) */
  --radius: 10px;
  --radius-lg: 16px;
  --radius-panel: 20px; /* esquinas del menú lateral y la barra de filtros
    flotantes (más redondeadas que las tarjetas normales, a juego con el
    aspecto "de cristal" de ambos) */
  --shadow: 0 1px 2px rgba(20, 24, 28, 0.04), 0 1px 8px rgba(20, 24, 28, 0.04);
  --sombra-panel: 0 10px 30px rgba(20, 24, 28, 0.08); /* sombra más
    pronunciada que --shadow, para que el menú y la barra de filtros
    floten visualmente sobre el fondo en vez de leerse como tarjetas
    planas */
  --font-display: 'Manrope', sans-serif;
  --font-body: 'Poppins', sans-serif;
  --font-mono: 'IBM Plex Mono', monospace;
  --font-numeros: 'Poppins', sans-serif; /* mismo valor que --font-body ahora; se deja aparte por claridad semántica en las reglas que la usan */

  /* Tipografías de la web corporativa (nextcliniq.com), usadas SOLO en
     la pantalla de acceso: Urbanist para el titular y Spectral cursiva
     para la palabra destacada, igual que su hero. No se cargan en el
     resto de la aplicación — el <link> va en login.html y
     nueva-contrasena.html, no en el @import de arriba. */
  --f-titular: 'Urbanist', 'Manrope', sans-serif;
  --f-destacado: 'Spectral', Georgia, serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  /* Un rastro muy leve de verde de marca detrás del menú y la barra de
     filtros, para que su cristal transparente tenga algo que dejar ver
     — sobre var(--bg) liso, la transparencia no se notaría. */
  background:
    radial-gradient(900px 600px at -5% 0%, rgba(31, 174, 126, 0.08) 0%, transparent 55%),
    radial-gradient(700px 500px at 62% -8%, rgba(90, 140, 255, 0.05) 0%, transparent 50%),
    var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--ink);
  margin: 0 0 4px 0;
}

a { color: inherit; }

button {
  font-family: var(--font-body);
  cursor: pointer;
}

/* ---------- Tacto ---------- */
/* El destello azul de Android/iOS al tocar cualquier cosa es lo que más
   delata que esto es una web dentro de una app. Quitarlo es seguro: el
   estado :active de cada botón ya da la respuesta visual. */
* {
  -webkit-tap-highlight-color: transparent;
}

/* Que no se seleccione el texto al mantener el dedo sobre un control.
   Se aplica SOLO a controles, nunca a celdas de tabla ni a inputs: en un
   CRM se copian nombres de pacientes, números de factura e importes
   constantemente, y bloquear eso sería un estorbo diario. */
button,
.btn,
.nav-item,
summary,
.pill-filtro,
.badge,
label,
.card-title,
th {
  -webkit-user-select: none;
  user-select: none;
}

/* Algunas etiquetas tienen una versión corta para el móvil. Se pintan
   las dos y decide el CSS, así un giro de pantalla no obliga a repintar
   nada por JavaScript. */
.solo-movil { display: none; }
@media (max-width: 880px) {
  .solo-escritorio { display: none; }
  .solo-movil { display: inline; }
}

/* La respuesta al tacto la da :active, no :hover: un hundido breve al
   pulsar, que es lo que hace una app nativa. Va dentro de
   prefers-reduced-motion para respetar a quien tiene el sistema
   configurado sin animaciones. */
@media (prefers-reduced-motion: no-preference) {
  button:active,
  .btn:active,
  .nav-item:active,
  .pill-filtro:active,
  summary:active {
    transform: scale(0.98);
  }
}

/* ---------- Pantalla de carga (evita el parpadeo del dashboard) ---------- */
.boot-loader {
  min-height: 100vh;  /* respaldo para navegadores sin dvh */
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 13.5px;
}

/* ---------- Barra superior de cambio de herramienta ---------- */
/* El alto era fijo (40px) y el nombre de la clínica no tenía tope de
   ancho, así que en un móvil "Clínica X — Cuadro de mandos" se partía en
   tres líneas y se derramaba fuera de la barra, encima del contenido
   (detectado en sept. 2026 con el selector de herramienta visible, que
   es el caso real de una clínica con Compras o Finanzas contratados).
   Ahora es un mínimo, no un alto fijo, y el nombre se recorta con
   puntos suspensivos en vez de partirse. */
.barra-herramientas {
  min-height: var(--alto-barra-superior);
  box-sizing: border-box;
  overflow-x: clip;
  background: var(--barra-superior);
  color: var(--texto-barra-superior);
  border-top: 3px solid var(--mint);
  box-shadow: 0 1px 0 var(--border);
  position: relative; /* la sombra de la línea inferior queda por encima del fondo de la página */
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 16px;
  font-size: 12.5px;
}
.barra-herramientas { font-family: var(--font-body); }
.barra-herramientas .nombre-clinica-top {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1px;
  /* min-width:0 es imprescindible: sin él un hijo flex no baja de su
     ancho de contenido y el recorte no llega a aplicarse nunca. */
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* Punto menta delante del nombre de la clínica, con un halo suave. */
.barra-herramientas .nombre-clinica-top::before {
  content: "";
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 0 3px var(--mint-soft);
  margin: 0 10px 1px 2px;
  vertical-align: middle;
}
.barra-herramientas .nombre-clinica-top:empty::before { display: none; }
.barra-herramientas .barra-separador {
  display: inline-block;
  width: 3px; height: 3px;
  border-radius: 50%;
  background: var(--slate);
  margin: 0 9px 2px;
  vertical-align: middle;
}
.barra-herramientas .nombre-herramienta-top { font-weight: 500; color: var(--slate); }

/* Píldora menta clarito con texto verde oscuro (opción C), con su
   panel de opciones con icono real en vez de emoji. */
.herramienta-selector > .btn-herramienta {
  background: var(--mint-soft);
  color: var(--mint-profundo);
  border: 1px solid var(--mint-borde);
  border-radius: 30px;
  padding: 4px 14px;
  font-size: 12px;
  font-family: var(--font-body);
  font-weight: 700;
  min-width: 230px;
  text-align: left;
  cursor: pointer;
  /* Quien se encoge cuando falta sitio es el nombre de la clínica (que
     se recorta con puntos suspensivos), nunca este botón: si se parte en
     dos líneas, arrastra el alto de toda la barra. */
  white-space: nowrap;
  flex: none;
}
@media (hover: hover) {
.herramienta-selector > .btn-herramienta:hover { border-color: var(--mint); }}
/* Icono de cuadrícula delante del texto, en el color del texto (misma
   técnica de máscara que la flecha de los desplegables). */
.herramienta-selector > .btn-herramienta::before {
  content: "";
  display: inline-block;
  width: 13px; height: 13px;
  margin: 0 7px 2px 0;
  vertical-align: middle;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='7' height='7'/%3E%3Crect x='14' y='3' width='7' height='7'/%3E%3Crect x='3' y='14' width='7' height='7'/%3E%3Crect x='14' y='14' width='7' height='7'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='7' height='7'/%3E%3Crect x='14' y='3' width='7' height='7'/%3E%3Crect x='3' y='14' width='7' height='7'/%3E%3Crect x='14' y='14' width='7' height='7'/%3E%3C/svg%3E") no-repeat center / contain;
}
.herramienta-panel a {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 6px;
  font-size: 13px; font-weight: 600; color: var(--text); text-decoration: none; cursor: pointer;
}
@media (hover: hover) {
.herramienta-panel a:hover { background: var(--bg); }}

.herramienta-panel a svg { width: 17px; height: 17px; flex-shrink: 0; }

/* ---------- Layout general ---------- */
.app-shell {
  display: none; /* oculto hasta que app.js confirme la sesión */
  grid-template-columns: 232px 1fr;
  min-height: calc(100vh - var(--alto-barra-superior));  /* respaldo */
  min-height: calc(100dvh - var(--alto-barra-superior));
}

.app-shell.is-visible {
  display: grid;
}

.sidebar {
  /* Panel flotante "de cristal": separado de los bordes, con su propio
     radio de esquina y sombra (igual que la barra de filtros), en vez
     de la franja oscura a sangre de antes. El 16px de margen arriba/
     abajo se descuenta de la altura del panel para que siga encajando
     exactamente entre la barra de herramientas (--alto-barra-superior) y el borde
     inferior de la pantalla. */
  background: rgba(255, 255, 255, 0.5);
  backdrop-filter: blur(22px) saturate(160%);
  -webkit-backdrop-filter: blur(22px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: var(--radius-panel);
  box-shadow: var(--sombra-panel);
  color: var(--text-muted);
  padding: 20px 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  position: sticky;
  top: var(--alto-barra-superior);
  height: calc(100vh - var(--alto-barra-superior) - 32px);  /* respaldo */
  height: calc(100dvh - var(--alto-barra-superior) - 32px);
  margin: 16px 12px 16px 16px;
  overflow: hidden;
}

.nav-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* El menú puede hacer scroll (con la rueda, el trackpad o el
     teclado) pero sin enseñar la barra: en las pocas pantallas donde
     hace falta, la barra partía el cristal del panel y hacía que el
     menú se viera distinto según la clínica (una opción más o menos).
     El auto-switch de js/sidebarFooterAuto.js aprieta el menú antes de
     llegar a esto, así que el caso es raro de por sí. */
  scrollbar-width: none;        /* Firefox y Chrome moderno */
  -ms-overflow-style: none;     /* Edge antiguo */
}
.nav-scroll::-webkit-scrollbar { width: 0; height: 0; }

.sidebar .logo {
  display: flex;
  align-items: center;
  gap: 8px;
  /* El hueco de debajo del botón de menú era de 22px, herencia de
     cuando en esta fila iba también el nombre de la aplicación al lado
     del botón. Hoy solo está el botón, y ese hueco dejaba "Dashboard"
     despegado del resto (26px hasta la primera opción, frente a 6px
     entre opciones). Se iguala al que ya usaba el menú contraído
     (8px, ver .app-shell.sidebar-colapsada .logo), para que el menú se
     vea igual abierto que cerrado. */
  padding: 6px 10px 8px 10px;
  /* Con el menú desplegado, la hamburguesa va a la DERECHA, junto al
     borde por el que se contrae. Contraído se centra (ver
     .app-shell.sidebar-colapsada .logo, más abajo). */
  justify-content: flex-end;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 16px;
  color: var(--ink);
  letter-spacing: 0.2px;
}

.sidebar .logo .dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 0 3px rgba(31, 174, 126, 0.25);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: 8px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: none;
  border: none;
  background: transparent;
  width: 100%;
  text-align: left;
}
@media (hover: hover) {

.nav-item:hover { background: rgba(31, 174, 126, 0.08); color: var(--ink); }}


.nav-group { margin: 2px 0; }
.nav-group summary {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 8px; font-size: 13.5px; font-weight: 500;
  color: var(--text-muted); cursor: pointer; list-style: none;
}
.nav-group summary::-webkit-details-marker { display: none; }
@media (hover: hover) {
.nav-group summary:hover { background: rgba(31, 174, 126, 0.08); color: var(--ink); }}

/* Flecha del acordeón: mismo icono de línea monocromo que el resto de
   la app (antes era el carácter de texto "›", que con Poppins en
   negrita se veía como una flecha maciza en forma de pirámide, no
   como el resto de los iconos). Gira 90° al abrirse el grupo, igual
   que hacía antes. */
.nav-group-flecha {
  margin-left: auto; flex: none; width: 13px; height: 13px;
  opacity: 0.7; transition: transform 0.15s;
}
.nav-group-flecha svg { width: 100%; height: 100%; display: block; }
.nav-group[open] .nav-group-flecha { transform: rotate(90deg); }
.nav-subitem { padding-left: 34px; font-size: 12.5px; }

/* Menú denso: último recurso antes de que al menú lateral le salga
   barra de scroll. Se activa solo cuando, ya con el pie del menú en
   modo compacto, las opciones siguen sin caber (ver
   js/sidebarFooterAuto.js). Aprieta el espacio entre opciones sin
   tocar el tamaño de letra, y gana unos 100px — suficiente para que
   en un portátil normal el menú entero quepa sin scroll, que es lo
   que hacía que dos clínicas con distinto número de opciones se
   vieran distintas en la misma pantalla. */
.sidebar.menu-denso .nav-scroll { gap: 2px; }
.sidebar.menu-denso .nav-item,
.sidebar.menu-denso .nav-group summary { padding-top: 6px; padding-bottom: 6px; }
.sidebar.menu-denso .logo { padding-bottom: 2px; }

.nav-item.active {
  background: rgba(31, 174, 126, 0.18);
  color: var(--mint-dark);
  box-shadow: inset 0 0 0 1px rgba(31, 174, 126, 0.3);
}

.nav-item.active .nav-dot { opacity: 1; }

/* Con el menú contraído no se ve ningún subapartado, así que estando
   en (por ejemplo) Configuración > Especialidades no habría nada
   marcado. Se marca el icono del grupo, con el mismo aspecto que un
   apartado activo, para que se sepa dónde estás. La clase la pone
   marcarGrupoDeLaSeccionActiva() y solo existe mientras está
   contraído. */
.app-shell.sidebar-colapsada .nav-group > summary.grupo-activo {
  background: rgba(31, 174, 126, 0.18);
  color: var(--mint-dark);
  box-shadow: inset 0 0 0 1px rgba(31, 174, 126, 0.3);
}

.nav-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--mint);
  opacity: 0;
  margin-left: auto;
}

.nav-alert-dot {
  display: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #e5484d;
  margin-left: auto;
  flex-shrink: 0; /* en un <summary> flexible se quedaba en 0 px */
}
.nav-alert-dot.visible { display: inline-block; }
/* En un grupo abierto la bolita ya se ve en su apartado: no se repite
   en el título del grupo. */
.nav-group[open] > summary .nav-alert-dot { display: none; }

.sidebar-footer {
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid rgba(20,24,28,0.08);
}

.sidebar-user {
  font-size: 12.5px;
  color: var(--text-muted);
  padding: 8px 10px;
}

.sidebar-user strong { display: block; color: var(--ink); font-size: 13px; }
/* Bug real (sept. 2026): "#userRol" es un <span>, y "overflow"/
   "text-overflow" no tienen ningún efecto sobre elementos inline (la
   propia especificación CSS los excluye) — por eso el recorte con "…"
   del pie compacto (ver más abajo, ".sidebar-footer.compacto
   .datos-pie-sidebar span") nunca llegaba a aplicarse de verdad: con
   un rol largo como "Superadministrador" (una sola palabra, no puede
   partirse), el texto se salía del hueco disponible y se montaba
   encima de los botones. display:block no cambia nada a simple vista
   en el pie normal (ya iba en su propia línea, debajo del nombre),
   pero es lo que permite que el recorte funcione quien lo necesite. */
#userRol { display: block; }

.btn-instalar-app {
  display: none; /* JS lo pasa a "flex" si el navegador confirma que se puede instalar */
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 30px;
  background: transparent;
  border: 1px solid rgba(20,24,28,0.12);
  color: var(--text-muted);
  font-size: 12.5px;
  font-family: var(--font-body);
  font-weight: 600;
  cursor: pointer;
}
.btn-instalar-app svg { width: 15px; height: 15px; flex-shrink: 0; }
@media (hover: hover) {
.btn-instalar-app:hover { border-color: var(--mint); color: var(--mint-dark); }}


.btn-mi-cuenta {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 30px;
  background: transparent;
  border: 1px solid rgba(20,24,28,0.12);
  color: var(--text-muted);
  font-size: 12.5px;
  font-family: var(--font-body);
  font-weight: 600;
  cursor: pointer;
}
.btn-mi-cuenta svg { width: 15px; height: 15px; flex-shrink: 0; }
@media (hover: hover) {
.btn-mi-cuenta:hover { border-color: var(--mint); color: var(--mint-dark); }}


.logout-btn {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 30px;
  background: transparent;
  border: 1px solid rgba(20,24,28,0.12);
  color: var(--text-muted);
  font-size: 12.5px;
  font-family: var(--font-body);
  font-weight: 600;
  cursor: pointer;
}
.logout-btn svg { width: 15px; height: 15px; flex-shrink: 0; }
@media (hover: hover) {
.logout-btn:hover { border-color: var(--mint); color: var(--mint-dark); }}


/* ---------------------------------------------------------
   Bug #4: pie del menú lateral compacto (automático)

   Mismo pie de siempre (nombre de clínica + rol, Mi cuenta, Instalar
   como app, Cerrar sesión) mientras haya sitio de verdad para él sin
   que el menú de navegación necesite scroll. En cuanto no cabe (menos
   alto de pantalla, o más elementos de menú visibles, p.ej.
   "Configuración avanzada"), js/sidebarFooterAuto.js añade la clase
   "compacto" a este mismo #sidebarFooter — nunca se mueven ni se
   clonan los botones/textos, así que sus manejadores de evento
   siguen intactos — y este bloque los transforma en una fila
   compacta: avatar con iniciales + nombre/rol en una línea, y los
   tres botones como iconos redondos, con el nombre completo y el rol
   disponibles en el atributo "title" al pasar el ratón por encima.
   El avatar lleva la misma clase "dato-privado" que el nombre de la
   clínica, para que también se difumine con el modo privacidad.
   --------------------------------------------------------- */
.avatar-pie-sidebar { display: none; }

.sidebar-footer.compacto {
  display: flex;
  align-items: center;
  gap: 5px;
  padding-top: 10px;
}

.sidebar-footer.compacto .sidebar-user {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  padding: 0;
}

.sidebar-footer.compacto .avatar-pie-sidebar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--mint-soft);
  color: var(--mint-dark);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13px;
  flex: none;
  cursor: default;
}

.sidebar-footer.compacto .datos-pie-sidebar {
  min-width: 0;
  line-height: 1.25;
  cursor: default;
}

.sidebar-footer.compacto .datos-pie-sidebar strong,
.sidebar-footer.compacto .datos-pie-sidebar span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar-footer.compacto #userRol { font-size: 10.5px; }

/* Sin tocar "display" de btn-mi-cuenta/btn-instalar-app: ese último
   solo se muestra cuando app.js confirma que se puede instalar
   (display:none por defecto), y forzarlo aquí lo mostraría siempre. */
.sidebar-footer.compacto .btn-mi-cuenta,
.sidebar-footer.compacto .btn-instalar-app,
.sidebar-footer.compacto .logout-btn {
  width: 26px;
  height: 26px;
  padding: 0;
  margin-top: 0;
  border-radius: 50%;
  align-items: center;
  justify-content: center;
  flex: none;
}

.sidebar-footer.compacto .btn-mi-cuenta svg,
.sidebar-footer.compacto .btn-instalar-app svg,
.sidebar-footer.compacto .logout-btn svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
}

.sidebar-footer.compacto .texto-boton { display: none; }

.app-shell.sidebar-colapsada { grid-template-columns: 60px 1fr; }
.app-shell.sidebar-colapsada .sidebar { width: 60px; padding: 16px 6px; transition: width 0.2s ease, padding 0.2s ease; }
.app-shell.sidebar-colapsada .nav-text,
.app-shell.sidebar-colapsada #userInfo,
.app-shell.sidebar-colapsada .nav-group summary::marker,
.app-shell.sidebar-colapsada .nav-group-flecha,
.app-shell.sidebar-colapsada .nav-item span.nav-dot,
.app-shell.sidebar-colapsada .nav-item span.nav-alert-dot,
.app-shell.sidebar-colapsada summary span.nav-alert-dot {
  display: none;
}

/* Los subapartados, aparte y con !important. Es la ÚNICA regla de todo
   este fichero que lo lleva, y va explicado para que nadie lo quite
   pensando que sobra:

   Los subapartados que dependen de un permiso (Usuarios y permisos,
   Logs, Liquidación...) se enseñan desde JavaScript poniéndoles un
   "style.display = flex" EN LÍNEA. Un estilo en línea gana a cualquier
   regla de la hoja de estilos, así que con el menú contraído seguían
   colgando en texto debajo del icono de su grupo (lo vio Víctor en
   Compras, sept. 2026).

   Probé primero a cerrar el <details> del grupo al contraer —que se
   hace igualmente, porque es lo que se espera— y NO bastó: Chrome
   oculta el contenido de un <details> cerrado con una regla suya de
   navegador, y el estilo en línea también le gana a esa. Medido: el
   subapartado seguía con altura mayor que cero.

   La raíz de verdad es que la visibilidad por permisos se expresa con
   estilos en línea y el estado del menú con clases: dos cosas
   independientes peleándose por la misma propiedad. Arreglarlo bien es
   pasar los permisos a clases, y eso toca el filtrado de las tres
   herramientas — más riesgo del que vale este fallo. Queda anotado
   como deuda. */
.app-shell.sidebar-colapsada .nav-subitem {
  display: none !important;
}
.app-shell.sidebar-colapsada .logo { justify-content: center; padding: 0 0 8px; }
.app-shell.sidebar-colapsada .nav-item,
.app-shell.sidebar-colapsada summary {
  justify-content: center;
  padding: 10px 0;
  width: 100%;
}
.app-shell.sidebar-colapsada .nav-icon { width: 22px; height: 22px; margin: 0; }
.app-shell.sidebar-colapsada .sidebar-footer { display: none; }

/* Con el menú contraído queda mucho ancho libre a la izquierda del
   contenido — sin este ajuste, el título y las tarjetas de arriba
   quedan pegados al sidebar. Se recupera parte de ese espacio como
   aire extra (bastante menos que el ancho que ocupaba el menú
   desplegado, pero más que el margen fijo de siempre). Las tarjetas
   van con !important porque su margen llega por estilo en línea. */
.app-shell.sidebar-colapsada .page { padding-left: 64px; }
.app-shell.sidebar-colapsada .topbar,
.app-shell.sidebar-colapsada #barrasObjetivoDashboard,
.app-shell.sidebar-colapsada #barraPresupuestoTrabajador,
.app-shell.sidebar-colapsada #barraPresupuestoAdmin {
  margin-left: 64px !important;
}

.sidebar { transition: width 0.2s ease; }
.nav-icon { width: 17px; height: 17px; flex-shrink: 0; }
.nav-item, summary { display: flex; align-items: center; gap: 10px; }

/* ---------- Área principal ---------- */
.main, main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  /* Recorta lo que se salga de ancho, igual que hacía "hidden", pero
     sin convertir esto en un contenedor de scroll. Con "hidden", la
     especificación obliga al otro eje a comportarse como "auto", y eso
     anulaba cualquier "position: sticky" de dentro (la cabecera de las
     pantallas de edición y el panel de permisos se quedaban sin
     pegar). "clip" recorta igual y no tiene ese efecto. */
  overflow-x: hidden;  /* respaldo para navegadores sin "clip" */
  overflow-x: clip;
}

.topbar-titulo { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 14px; color: var(--ink); margin-right: 8px; }
.topbar-titulo-herramienta { color: var(--text-muted); font-weight: 600; }

.topbar {
  /* Cristal claro a juego con el menú lateral: separada de los bordes
     de la pantalla, translúcida y con desenfoque, en vez de la tarjeta
     blanca opaca de antes. */
  background: rgba(255, 255, 255, 0.55);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.65);
  border-radius: var(--radius-panel);
  box-shadow: var(--sombra-panel);
  margin: 16px 24px;
  padding: 14px 20px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

/* Selector de clínica: también pasa por selector-unico (el panel de
   opciones queda con bordes redondeados, igual que el resto), pero
   conserva su disparador en píldora menta sólida — es el selector
   principal de la topbar y debe distinguirse del resto de filtros. */
#selectorClinicaEnvoltorio { margin-right: 24px; }
#selectorClinicaEnvoltorio .selector-unico-boton {
  padding: 9px 16px;
  border: none;
  border-radius: 30px;
  background: var(--mint);
  color: #fff;
  font-weight: 700;
  min-width: 0;
  box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
@media (hover: hover) {
#selectorClinicaEnvoltorio .selector-unico-boton:hover { background: var(--mint-dark); }}

#selectorClinicaEnvoltorio .selector-unico-flecha { opacity: 1; }

.topbar select,
.topbar input[type="text"],
.filter-control {
  font-family: var(--font-body);
  font-size: 13px;
  padding: 7px 14px;
  border-radius: 30px;
  border: 2px solid var(--border);
  background: var(--bg);
  color: var(--text);
  min-width: 130px;
}

.topbar .spacer { flex: 1; }

/* "Borrar filtros" / "Aplicar filtros": van agrupados en este
   contenedor para que, si no cabe todo en una línea, sea el resto de
   filtros (fechas, Asesor...) lo que baje a una segunda línea — los
   botones se quedan siempre juntos y pegados a la derecha, nunca se
   separan el uno del otro ni caen sueltos en su propia línea. */
.topbar-acciones {
  display: flex;
  flex: none;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

.page {
  padding: 22px 24px 40px 24px;
  width: 100%;
}

.page-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 16px;
}

.page-header .crumb {
  font-size: 12.5px;
  color: var(--text-muted);
}

.section {
  display: none;
}
.section.active { display: block; }

/* ---------- Tarjetas KPI ---------- */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 10px;
  margin-bottom: 18px;
}

.kpi-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: var(--shadow);
  text-align: center;
  /* Sin esto, una tarjeta con un valor largo y sin espacios (un
     importe grande, por ejemplo) puede obligar a su columna del
     grid a ser más ancha que las demás: min-width del item de grid
     es "auto" por defecto, que respeta el ancho mínimo de su
     contenido aunque la columna sea "1fr". Con min-width:0 todas las
     columnas del kpi-grid quedan igual de anchas siempre, y el texto
     largo que no quepa simplemente hace salto de línea dentro de la
     tarjeta en vez de ensanchar su columna. */
  min-width: 0;
}

.kpi-card .kpi-label {
  font-family: 'Poppins', sans-serif;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  font-weight: 600;
  margin-bottom: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
}
.kpi-info {
  display: inline-flex;
  position: relative;
  cursor: help;
  color: var(--border);
}
.kpi-info:hover, .kpi-info:focus { color: var(--mint); outline: none; }
.kpi-info-icon { width: 13px; height: 13px; }

/* Tooltip propio (sustituye al title="" nativo del navegador/SO) */
.kpi-info[data-tooltip]::after,
.kpi-info[data-tooltip]::before {
  opacity: 0; pointer-events: none; position: absolute; bottom: calc(100% + 9px); left: 50%;
  transform: translateX(-50%) translateY(3px);
  transition: opacity 0.15s ease, transform 0.15s ease;
  z-index: 80;
}
.kpi-info[data-tooltip]::after {
  content: attr(data-tooltip);
  width: max-content; max-width: 220px;
  background: var(--ink); color: #fff;
  font-family: var(--font-body); font-weight: 500; font-size: 11.5px; line-height: 1.45;
  text-align: left; text-transform: none; letter-spacing: normal;
  padding: 8px 10px; border-radius: 8px;
  box-shadow: 0 8px 20px rgba(20, 24, 28, 0.18);
}
.kpi-info[data-tooltip]::before {
  content: ""; bottom: calc(100% + 5px);
  border: 5px solid transparent; border-top-color: var(--ink);
}
.kpi-info[data-tooltip]:hover::after, .kpi-info[data-tooltip]:hover::before,
.kpi-info[data-tooltip]:focus::after, .kpi-info[data-tooltip]:focus::before {
  opacity: 1; transform: translateX(-50%) translateY(0);
}

.kpi-card .kpi-value {
  font-family: var(--font-numeros);
  font-variant-numeric: tabular-nums;
  font-size: 22px;
  font-weight: 600;
  color: var(--ink);
  border-bottom: 2px solid var(--mint-soft);
  padding-bottom: 6px;
  margin-bottom: 6px;
}

.kpi-card .kpi-compare {
  font-size: 11.5px;
  color: var(--text-muted);
}

.kpi-card .kpi-delta {
  font-weight: 600;
}
.kpi-delta.up { color: var(--success); }
.kpi-delta.down { color: var(--danger); }

/* ---------- Tarjeta genérica ---------- */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 18px 20px;
  box-shadow: var(--shadow);
  margin-bottom: 18px;
}

.card-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  color: var(--ink);
  margin-bottom: 12px;
}

/* ---------- Barra de objetivo (pill de estado) ---------- */
.goal-bar-wrap {
  margin-bottom: 6px;
}

.goal-bar-label {
  display: flex;
  justify-content: center;
  font-size: 14.5px;
  font-weight: 700;
  font-family: var(--font-display);
  color: var(--ink);
  margin-bottom: 6px;
}

.goal-bar-track {
  height: 30px;
  border-radius: 999px;
  background: var(--bg);
  border: 1px solid var(--border);
  overflow: hidden;
  position: relative;
}

.goal-bar-fill {
  height: 100%;
  border-radius: 999px;
  transition: width 0.8s cubic-bezier(0.22, 1, 0.36, 1), background-color 0.6s ease;
}

.goal-bar-texto {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-numeros);
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  font-weight: 800;
  color: var(--ink);
  text-shadow: 0 0 4px #fff, 0 0 4px #fff, 0 0 8px #fff;
  white-space: nowrap;
  pointer-events: none;
}

.goal-bar-fill.state-rojo { background: var(--danger); }
.goal-bar-fill.state-amarillo { background: var(--warning); }
.goal-bar-fill.state-verde { background: var(--success); animation: pulso-verde 1.4s ease-out; }
/* El verde de objetivos celebra con un pulso que se ha alcanzado la
   meta. En Compras, verde significa "vas bien de gasto", no un logro:
   con esta clase se comparten los colores pero no la celebración. */
.goal-bar-fill.sin-pulso { animation: none; }

@keyframes pulso-verde {
  0% { filter: brightness(1.6); }
  100% { filter: brightness(1); }
}

/* ---------- Tablas ---------- */
table.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
}

.data-table th {
  text-align: left;
  font-size: 11px;
  /* Decidido: Poppins en toda la tabla (cabecera y contenido), no
     Outfit — con mayúsculas + negrita quedaba demasiado cargado. */
  font-family: 'Poppins', sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  font-weight: 700;
  padding: 12px 14px;
  border-bottom: 2px solid var(--border);
  white-space: nowrap;
}

.data-table td {
  padding: 13px 14px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  line-height: 1.4;
  font-family: 'Poppins', sans-serif;
}
@media (hover: hover) {

.data-table tbody tr:hover { background: var(--bg); }}

.data-table tbody tr.fila-clicable { cursor: pointer; }

/* ---------- Filtro de selección múltiple ---------- */
.data-table .num { font-family: var(--font-numeros); font-variant-numeric: tabular-nums; text-align: right; }
.multiselect { position: relative; display: inline-block; }
.multiselect > button {
  font-family: var(--font-body); font-size: 13px; padding: 7px 14px; border-radius: 30px;
  border: 2px solid var(--border); background: var(--bg); color: var(--text); min-width: 130px;
  text-align: left; cursor: pointer;
}
.multiselect > button::after {
  content: ""; float: right; width: 11px; height: 11px; opacity: 0.5; margin-left: 6px; margin-top: 2px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat center / contain;
}
.multiselect-panel {
  display: none; position: absolute; top: 100%; left: 0; margin-top: 4px;
  background: var(--card); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.12); min-width: 220px; max-height: 260px; overflow-y: auto;
  z-index: 60; padding: 6px;
}
.multiselect-panel.visible { display: block; }
.multiselect-panel label {
  display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px;
  font-size: 13px; cursor: pointer;
}
@media (hover: hover) {
.multiselect-panel label:hover { background: var(--bg); }}

.multiselect-panel .empty-state { padding: 10px; font-size: 12.5px; }

/* ---------- Selector avanzado (piloto: barra de filtros del Dashboard) ---------
   Mismo comportamiento que .multiselect (checkboxes en panel desplegable),
   con etiqueta inteligente (nombres o "+N") y botón de limpiar integrado
   en la propia píldora. Ver js/selectorAvanzado.js. */
.selector-avanzado { position: relative; display: inline-block; }

.selector-avanzado-caja {
  display: flex; align-items: stretch;
  border: 2px solid var(--border); border-radius: 30px; background: var(--bg);
  transition: border-color 0.15s ease, background-color 0.15s ease;
  overflow: hidden;
}
@media (hover: hover) {
.selector-avanzado-caja:hover { border-color: var(--slate); }}

.selector-avanzado.tiene-seleccion .selector-avanzado-caja {
  border-color: var(--mint); background: var(--mint-soft);
}

.selector-avanzado-boton {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-body); font-size: 13px; font-weight: 500;
  padding: 7px 12px 7px 14px; border: none; background: none; color: var(--text);
  /* Anchura fija (no min/max) para que Asesor/Especialidad/Doctor
     queden EXACTAMENTE igual de anchos entre sí, en vez de que cada
     uno se ajuste a su propio texto — 180px es lo que necesita la
     etiqueta más larga habitual ("Especialidad: todas") sin recortar;
     las demás se estiran hasta ahí. Si una selección concreta genera
     un texto más largo todavía, el ellipsis ya existente lo recorta,
     igual que hacía antes. */
  width: 180px; cursor: pointer; text-align: left;
}
.selector-avanzado.tiene-seleccion .selector-avanzado-boton { color: var(--mint-dark); font-weight: 600; }

.selector-avanzado-texto { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Dentro de un formulario (centros de un rol) no tiene que ir a la par
   de otros filtros: más ancho, para que se lean los nombres. */
.selector-avanzado.selector-avanzado-ancho .selector-avanzado-boton { width: 320px; max-width: calc(100vw - 120px); }

.selector-avanzado-flecha { flex: none; width: 13px; height: 13px; opacity: 0.5; }
.selector-avanzado-flecha svg { width: 100%; height: 100%; display: block; }

.selector-avanzado-limpiar {
  display: none; flex: none; align-items: center; justify-content: center;
  width: 30px; border: none; border-left: 1px solid var(--border);
  background: none; color: var(--mint-dark); opacity: 0.75; cursor: pointer;
}
.selector-avanzado-limpiar svg { width: 13px; height: 13px; }
@media (hover: hover) {
.selector-avanzado-limpiar:hover { opacity: 1; color: var(--danger); }}

.selector-avanzado.tiene-seleccion .selector-avanzado-limpiar { display: inline-flex; }

.selector-avanzado-panel {
  display: none; position: absolute; top: 100%; left: 0; margin-top: 6px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 10px 28px rgba(20, 24, 28, 0.14); min-width: 230px; max-height: 280px;
  overflow-y: auto; z-index: 60; padding: 6px;
  /* Scrollbar propia, fina, en vez de la gorda nativa del navegador
     (Firefox: scrollbar-width/color; Chrome/Safari/Edge: pseudo-elementos). */
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.selector-avanzado-panel.visible { display: block; }
.selector-avanzado-panel .empty-state { padding: 10px; font-size: 12.5px; color: var(--text-muted); }
.selector-avanzado-panel::-webkit-scrollbar { width: 7px; }
.selector-avanzado-panel::-webkit-scrollbar-track { background: transparent; }
.selector-avanzado-panel::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; }
@media (hover: hover) {
.selector-avanzado-panel::-webkit-scrollbar-thumb:hover { background: var(--slate); }}


.selector-avanzado-opcion {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 6px;
  font-size: 13px; cursor: pointer;
}
@media (hover: hover) {
.selector-avanzado-opcion:hover { background: var(--mint-soft); }}

.selector-avanzado-opcion input[type="checkbox"] { display: none; }
.selector-avanzado-opcion-texto { flex: 1; }
.selector-avanzado-opcion input:checked ~ .selector-avanzado-opcion-texto { font-weight: 600; color: var(--ink); }
.selector-avanzado-check { flex: none; width: 14px; height: 14px; color: var(--mint); visibility: hidden; }
.selector-avanzado-opcion input:checked ~ .selector-avanzado-check { visibility: visible; }

/* ---------- Botón "+ Filtros" (topbar del Dashboard) ----------
   Mientras Asesor/Especialidad/Doctor + Fecha + Borrar/Aplicar caben
   en una línea, la barra se queda igual que siempre. En cuanto no
   caben (y "Borrar/Aplicar filtros" saltaría a su propia línea),
   Asesor/Especialidad/Doctor se mueven aquí dentro. Ver
   js/topbarFiltrosAuto.js — el ancho se mide en vivo, no hay
   breakpoint fijo. */
.selector-mas-filtros { position: relative; display: inline-block; }
.boton-mas-filtros {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-body); font-size: 13px; font-weight: 500;
  padding: 7px 14px; border: 2px solid var(--border); border-radius: 30px;
  background: var(--bg); color: var(--text); cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
@media (hover: hover) {
.boton-mas-filtros:hover { border-color: var(--slate); }}

.boton-mas-filtros svg { flex: none; width: 14px; height: 14px; opacity: 0.6; }

.panel-mas-filtros {
  display: none; position: absolute; top: 100%; left: 0; margin-top: 6px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 10px 28px rgba(20, 24, 28, 0.14); min-width: 230px;
  z-index: 60; padding: 12px; flex-direction: column; gap: 10px;
}
.panel-mas-filtros.visible { display: flex; }
.panel-mas-filtros .selector-avanzado { display: block; }
.panel-mas-filtros .selector-avanzado-caja { width: 100%; }
.panel-mas-filtros .selector-avanzado-boton { width: auto; flex: 1; min-width: 0; }

/* ---------- Selector único (piloto: filtro de fecha rápida) ---------
   Capa visual en píldora sobre un <select> nativo de una sola opción
   (oculto, pero funcional). Ver js/selectorUnico.js. */
.selector-unico { position: relative; display: inline-block; }
.selector-unico-boton {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-body); font-size: 13px; font-weight: 500;
  padding: 7px 12px 7px 14px; border-radius: 30px; border: 2px solid var(--border);
  background: var(--bg); color: var(--text); min-width: 130px; cursor: pointer; text-align: left;
  transition: border-color 0.15s ease;
}
@media (hover: hover) {
.selector-unico-boton:hover { border-color: var(--slate); }}

.selector-unico-boton:focus-visible { outline: none; border-color: var(--mint); }
.selector-unico-texto { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.selector-unico-flecha { flex: none; width: 13px; height: 13px; opacity: 0.5; }
.selector-unico-flecha svg { width: 100%; height: 100%; display: block; }

.selector-unico-panel {
  /* Lo usan todos los <select> de una sola opción convertidos a
     píldora (filtro rápido de fechas, años, clínicas, filas por
     página, roles...). Con max-height + overflow-y ya vale tanto
     para una lista corta y fija como para una que venga de datos de
     la clínica y pueda ser larga. */
  display: none; position: absolute; top: 100%; left: 0; margin-top: 6px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 10px 28px rgba(20, 24, 28, 0.14); min-width: 205px; max-height: 400px;
  overflow-y: auto; z-index: 60; padding: 6px;
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.selector-unico-panel.visible { display: block; }
/* Abierto, flota sobre todo (colocarPanelFlotante, tablaUtils.js): así
   una tarjeta con desplazamiento no lo recorta. */
.selector-unico-panel.panel-flotante, .selector-avanzado-panel.panel-flotante { position: fixed; margin-top: 0; z-index: 200; max-width: min(360px, calc(100vw - 16px)); }
/* (Flotando ya no lo limita el ancho de su tarjeta: el tope evita que una
   opción muy larga lo estire de lado a lado.) */
.selector-unico-panel::-webkit-scrollbar { width: 7px; }
.selector-unico-panel::-webkit-scrollbar-track { background: transparent; }
.selector-unico-panel::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; }
@media (hover: hover) {
.selector-unico-panel::-webkit-scrollbar-thumb:hover { background: var(--slate); }}


.selector-unico-opcion {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 6px;
  font-size: 13px; cursor: pointer;
}
@media (hover: hover) {
.selector-unico-opcion:hover { background: var(--mint-soft); }}

.selector-unico-opcion.seleccionada { color: var(--mint-dark); font-weight: 600; }
.selector-unico-opcion.activa { background: var(--mint-soft); }

/* Cambios elegidos y todavía sin guardar (clasificar proveedores). */
tr.fila-pendiente > td { background: var(--warning-soft); }
.barra-cambios-pendientes {
  position: sticky; bottom: 16px; z-index: 30;
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  margin-top: 12px; padding: 10px 14px; border-radius: var(--radius);
  background: var(--card); border: 1px solid var(--warning); box-shadow: 0 10px 28px rgba(20, 24, 28, 0.14);
  font-weight: 600; color: var(--warning-text); font-size: 13px;
}
.barra-cambios-botones { display: inline-flex; gap: 8px; }

/* Con buscador (data-buscable): la caja se queda arriba al desplazar. */
.selector-unico-buscar { position: sticky; top: -6px; margin: -6px -6px 6px; padding: 6px; background: var(--card); z-index: 1; border-bottom: 1px solid var(--border); }
.selector-unico-buscar input {
  width: 100%; padding: 8px 12px; border-radius: 30px; border: 1px solid var(--border);
  font-family: var(--font-body); font-size: 13px; background: var(--card); color: var(--text);
}
.selector-unico-buscar input:focus { outline: none; border-color: var(--mint); }

/* Que ocupe todo su sitio (dentro de un formulario o con width:100%). */
.selector-unico--ancho { display: block; width: 100%; }
.selector-unico--ancho .selector-unico-boton { width: 100%; min-width: 0; }
/* Dentro de un formulario (.field) se ve como el resto de campos, no
   como una píldora de filtro. */
.selector-unico--campo .selector-unico-boton {
  padding: 11px 16px; border: 1px solid var(--border); background: var(--card);
  font-size: 14px; font-weight: 400; color: var(--text);
}
.selector-unico--campo .selector-unico-boton:focus { outline: none; border-color: var(--mint); box-shadow: 0 0 0 3px var(--mint-soft); }
.selector-unico-boton.campo-obligatorio-falta { border-color: var(--danger); background: var(--danger-soft); }
.selector-unico-boton:disabled { background: var(--bg); color: var(--text-muted); cursor: not-allowed; }
.selector-unico-check { flex: none; width: 14px; height: 14px; color: var(--mint); visibility: hidden; margin-left: auto; }
.selector-unico-opcion.seleccionada .selector-unico-check { visibility: visible; }

/* ---------- Filtro de fecha en píldora (disparador único: presets +
   calendario de rango en un solo panel). Ver js/filtroFecha.js. ---------- */
.selector-fecha { position: relative; display: inline-block; }
.selector-fecha-boton {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-body); font-size: 13px; font-weight: 500;
  padding: 7px 12px 7px 14px; border-radius: 30px; border: 2px solid var(--border);
  background: var(--bg); color: var(--text); cursor: pointer; text-align: left;
  transition: border-color 0.15s ease;
  /* A diferencia de .selector-avanzado-boton (ancho fijo), este se
     ajusta a su contenido: con un preset corto ("Este mes") no hace
     falta que ocupe más sitio del necesario, pero con un rango
     personalizado hay que poder enseñar las dos fechas completas
     ("dd/mm/aaaa — dd/mm/aaaa" son siempre 23 caracteres, quepa el
     mes que quepa) sin recortarlas — de ahí el max-width más generoso. */
  min-width: 132px; max-width: 250px;
}
@media (hover: hover) {
.selector-fecha-boton:hover { border-color: var(--slate); }}

.selector-fecha-icono { flex: none; width: 14px; height: 14px; opacity: 0.6; }
.selector-fecha-icono svg { width: 100%; height: 100%; display: block; }
.selector-fecha-texto { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.selector-fecha-flecha { flex: none; width: 13px; height: 13px; opacity: 0.5; }
.selector-fecha-flecha svg { width: 100%; height: 100%; display: block; }

th[data-sort] { cursor: pointer; user-select: none; }
@media (hover: hover) {
th[data-sort]:hover { color: var(--ink); }}

th[data-sort]::after {
  content: ""; display: inline-block; width: 11px; height: 11px; margin-left: 5px; margin-bottom: -2px; opacity: 0.35;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 8 6 5 6-5'/%3E%3Cpath d='m6 13 6 5 6-5' opacity='.55'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 8 6 5 6-5'/%3E%3Cpath d='m6 13 6 5 6-5' opacity='.55'/%3E%3C/svg%3E") no-repeat center / contain;
}
th[data-sort].orden-asc::after {
  opacity: 1;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 15 6-6 6 6'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 15 6-6 6 6'/%3E%3C/svg%3E");
}
th[data-sort].orden-desc::after {
  opacity: 1;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

@media print {
  .sidebar, .topbar, #barrasObjetivoDashboard, .btn, .accion-menu, .modal-overlay,
  .page-header button, #presFiltroEstadoBotones, .card:has(#formCobro), .card:has(#formPresupuesto),
  .card:has(#prevFecha) { display: none !important; }
  .page { padding: 0 !important; }
  body { background: #fff !important; }
}

.badge {
  display: inline-block;
  padding: 4px 11px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
}
.badge.sin-clasificar { background: var(--warning-soft); color: #8a5a10; }
.badge.ok { background: var(--success-soft); color: var(--mint-dark); }
/* Compra en negativo: es un abono o devolución del proveedor, no un
   error de importación. Se marca para que un negativo colado por
   descuido tampoco pase desapercibido. */
.badge.abono { background: var(--danger-soft); color: var(--danger-text); }

/* ---------- Botones ---------- */
.btn {
  font-size: 13px;
  font-weight: 600;
  padding: 8px 16px;
  border-radius: 30px;
  border: 1px solid transparent;
  background: var(--ink);
  color: #fff;
}
@media (hover: hover) {
.btn:hover { background: var(--ink-soft); }}


/* Las pestañas de Configuración y Configuración avanzada son enlaces
   de verdad (<a href>), para que cada una tenga su URL y se puedan
   abrir en otra pestaña del navegador o guardar en favoritos. Con
   .btn ya heredan el aspecto; lo que hace falta es lo que el
   navegador solo le da a <button>: puntero, sin subrayado y caja. */
a.btn { display: inline-block; text-decoration: none; cursor: pointer; }

.btn-primary { background: var(--mint); }
@media (hover: hover) {
.btn-primary:hover { background: var(--mint-dark); }}

/* Botón de una acción que destruye algo (eliminar, perder cambios).
   Mismo peso que el primario, en rojo, para que no se confunda con
   "guardar" en una ventana de confirmación. */
.btn-peligro { background: var(--danger); color: #fff; }
@media (hover: hover) {
.btn-peligro:hover { background: var(--danger-text); }}


.btn-ghost {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
}
@media (hover: hover) {
.btn-ghost:hover { border-color: var(--mint); color: var(--mint-dark); }}


/* Botones "extender / contraer tabla" (Ver clasificados, Ver
   estadísticas extra, Extender ver sub-tipos...): icono de doble
   flecha alineado con el texto, gira 180° al abrir el bloque. */
.btn-toggle-tabla { display: inline-flex; align-items: center; gap: 6px; }
.icono-toggle-tabla { width: 13px; height: 13px; flex: none; transition: transform 0.2s ease; }
.icono-toggle-tabla.abierto { transform: rotate(180deg); }

/* Botón ghost con un icono de línea delante del texto (p. ej. "Reparar
   compras ya clasificadas"): mismo layout que .btn-toggle-tabla, pero
   sin la semántica de abrir/cerrar. */
.btn-con-icono { display: inline-flex; align-items: center; gap: 6px; }
.btn-con-icono svg { width: 14px; height: 14px; flex: none; }

/* Iconos de js/iconos.js sueltos, fuera de un botón: delante de un
   texto normal, alineados con la línea de base. */
.icono-linea { width: 14px; height: 14px; flex: none; vertical-align: -2px; }

/* Título de sección con icono delante (p. ej. "Estadísticas de
   cobros"): mismo patrón que .btn-con-icono pero para un <h3>. */
.titulo-con-icono { display: inline-flex; align-items: center; gap: 8px; }
.titulo-con-icono .icono-linea { width: 17px; height: 17px; }

/* Mensaje de estado con icono delante (p. ej. tras "Guardar": icono
   de check + "Guardado"), usado en los <p id="estado..."> que antes
   llevaban el emoticono metido en el propio texto. */
.estado-con-icono { display: inline-flex; align-items: center; gap: 6px; }
.estado-con-icono .icono-linea { width: 13px; height: 13px; }

/* Flechita de tendencia de los KPI del Dashboard ("12,3% vs año
   anterior"): mismo chevron que las cabeceras de tabla, rotado según
   suba o baje, coloreado con el mismo verde/rojo que ya usaba el texto. */
.icono-tendencia { width: 11px; height: 11px; flex: none; vertical-align: -1px; }
.icono-tendencia.subida { transform: rotate(180deg); }

/* Modo privacidad: botón del ojo en la barra superior (junto al nombre
   de clínica) y el blur que aplica sobre todo lo marcado con la clase
   "dato-privado" (nombre de clínica, KPI de importe del Dashboard,
   barras de cobrado/previsión). Un único interruptor para toda la
   sesión — arranca según "perfiles.blur_por_defecto", ver js/tablaUtils.js. */
.barra-herramientas .barra-izquierda { display: flex; align-items: center; gap: 8px; min-width: 0; }

/* ---------- Tablas plegables en móvil (js/tablaPlegable.js) ----------
   En escritorio no existe nada de esto: la columna del botón y las
   filas de detalle solo se crean cuando la pantalla es estrecha. */
.col-desplegar { width: 40px; text-align: right; }
.btn-desplegar-fila {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: 50%;
  width: 30px;
  height: 30px;
  min-height: 30px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
}
.btn-desplegar-fila svg { width: 15px; height: 15px; transition: transform 0.18s ease; }
.btn-desplegar-fila.abierto { border-color: var(--mint); color: var(--mint-dark); }
.btn-desplegar-fila.abierto svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  .btn-desplegar-fila svg { transition: none; }
}

/* Columnas que NO se ven nunca en la tabla: existen solo para que el
   desplegable de móvil tenga esos datos. En escritorio la tabla ya es
   bastante ancha y no hacen falta; en móvil salen dentro del "+". */
.solo-desplegable { display: none; }

/* La columna de texto libre se queda con el sitio que sobra y recorta.
   El truco del "max-width: 0" con "width: 100%" es lo que hace que una
   celda de tabla acepte recortarse en vez de estirar la tabla entera. */
.celda-elastica {
  max-width: 0;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fila-detalle > td { background: var(--bg); padding: 10px 12px 14px; }
.lista-detalle {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 12px;
  row-gap: 7px;
  align-items: baseline;
}
.lista-detalle dt {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-muted);
}
.lista-detalle dd { margin: 0; font-size: 12.5px; min-width: 0; overflow-wrap: anywhere; }

/* ---------- Barra de navegación inferior (solo móvil) ----------
   Existe en el HTML siempre, pero en escritorio no se enseña: allí está
   el menú lateral entero a la vista y no hace falta. La rellena
   js/barraInferior.js. */
.barra-inferior {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 240; /* por debajo del cajón del menú (300) y de su capa (250) */
  background: var(--card);
  border-top: 1px solid var(--border);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.item-barra-inferior {
  appearance: none;
  border: 0;
  background: transparent;
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 600;
  color: var(--text-muted);
  padding: 7px 2px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  min-width: 0;
}
.item-barra-inferior.active { color: var(--mint-dark); }
.marco-icono-barra-inferior {
  width: 42px;
  height: 24px;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.item-barra-inferior.active .marco-icono-barra-inferior { background: var(--mint-soft); }
.icono-barra-inferior { width: 18px; height: 18px; flex: none; }
.texto-barra-inferior {
  max-width: 100%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Hamburguesa de la barra negra. En escritorio no aparece: allí el menú
   está siempre a la vista y su propio botón lo contrae. Existe porque el
   botón de dentro del menú se va de la pantalla con el cajón. */
.menu-toggle-barra {
  display: none;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0;
  margin-left: -8px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 50%;
  color: var(--texto-barra-superior);
  opacity: 0.85;
}
.menu-toggle-barra svg { width: 20px; height: 20px; }
.btn-blur-privacidad {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0; flex: none;
  background: transparent; border: 1px solid transparent; border-radius: 50%;
  color: var(--texto-barra-superior); opacity: 0.75; cursor: pointer;
}
@media (hover: hover) {
.btn-blur-privacidad:hover { opacity: 1; background: color-mix(in srgb, var(--texto-barra-superior) 10%, transparent); }}

.btn-blur-privacidad.activo {
  opacity: 1;
  background: color-mix(in srgb, var(--texto-barra-superior) 16%, transparent);
  border-color: color-mix(in srgb, var(--texto-barra-superior) 30%, transparent);
}
.btn-blur-privacidad .icono-linea { width: 15px; height: 15px; }

/* El nombre de la clínica también aparece en el selector del topbar
   (solo lo ve el superadmin). Ahí no se puede poner la clase a mano sin
   tocar el componente genérico de los desplegables, que lo usan muchos
   más sitios, así que se difumina desde aquí. El panel desplegado no se
   difumina: si lo has abierto es porque quieres ver las clínicas. */
body.modo-privado #selectorClinicaEnvoltorio .selector-unico-texto {
  filter: blur(6px);
}

body.modo-privado .dato-privado {
  filter: blur(6px);
  user-select: none;
}

/* ---------- Acceso (login y contraseña nueva) ----------
   Dos caras de la misma página:
   - En escritorio, tarjeta centrada sobre un fondo claro con líneas en
     movimiento (rejilla a la deriva + haces que barren).
   - En móvil (<=640px), la tarjeta desaparece como tal: fondo oscuro,
     logo grande centrado y campos de vidrio oscuro, sin frase ninguna.
   Las fuentes Urbanist y Spectral son las de la web corporativa y se
   cargan SOLO en login.html y nueva-contrasena.html, no en toda la
   aplicación. */
.acceso {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh;
  overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  padding: 32px;
  background:
    radial-gradient(1200px 620px at 12% 0%, #e2f3ec 0%, transparent 56%),
    radial-gradient(900px 520px at 92% 100%, #dceff6 0%, transparent 56%),
    linear-gradient(160deg, #f7faf9 0%, #eef3f2 100%);
}

/* Fondo en movimiento. Las dos capas se desvanecen hacia el centro con
   una máscara radial para no competir con la tarjeta, y se paran solas
   si el sistema pide menos animación. */
.acceso .lineas,
.acceso .haces {
  position: absolute; inset: -80px; pointer-events: none;
  -webkit-mask-image: radial-gradient(circle at 50% 50%, transparent 0%, transparent 26%, #000 62%);
  mask-image: radial-gradient(circle at 50% 50%, transparent 0%, transparent 26%, #000 62%);
}
.acceso .lineas {
  background-image:
    linear-gradient(rgba(31,174,126,.10) 1px, transparent 1px),
    linear-gradient(90deg, rgba(31,174,126,.10) 1px, transparent 1px);
  background-size: 64px 64px;
  animation: acceso-deriva 24s linear infinite;
}
@keyframes acceso-deriva { from { transform: translate3d(0,0,0); } to { transform: translate3d(64px,64px,0); } }

.acceso .haz {
  position: absolute; top: -25%; height: 150%; width: 2px; border-radius: 2px;
  background: linear-gradient(to bottom, transparent, rgba(31,174,126,.42), transparent);
  transform: rotate(16deg);
}
.acceso .haz.uno  { left: 18%; animation: acceso-barrido-uno 17s ease-in-out infinite; }
.acceso .haz.dos  { left: 64%; animation: acceso-barrido-dos 23s ease-in-out infinite; }
.acceso .haz.tres { left: 40%; width: 1px; opacity: .6; animation: acceso-barrido-uno 29s ease-in-out infinite reverse; }
@keyframes acceso-barrido-uno {
  0%,100% { transform: rotate(16deg) translateX(-90px); opacity: .25; }
  50%     { transform: rotate(16deg) translateX(190px); opacity: .85; }
}
@keyframes acceso-barrido-dos {
  0%,100% { transform: rotate(16deg) translateX(150px); opacity: .8; }
  50%     { transform: rotate(16deg) translateX(-160px); opacity: .2; }
}
/* Halos del fondo oscuro de móvil: en escritorio no se pintan. */
.acceso .halo, .acceso .halo-alto { display: none; }

@media (prefers-reduced-motion: reduce) {
  .acceso .lineas, .acceso .haz { animation: none !important; }
}

/* ---------- Tarjeta ---------- */
.acceso-tarjeta {
  position: relative; z-index: 1; width: 100%; max-width: 438px;
  background: rgba(255,255,255,.86);
  backdrop-filter: blur(18px) saturate(150%); -webkit-backdrop-filter: blur(18px) saturate(150%);
  border: 1px solid rgba(255,255,255,.92); border-radius: 24px; padding: 40px 38px;
  box-shadow: 0 24px 70px rgba(20,24,28,.13);
}

.acceso-logo { display: flex; align-items: center; gap: 11px; margin-bottom: 26px; }
.acceso-logo .simbolo {
  width: 38px; height: 38px; border-radius: 12px; flex: none;
  background: var(--mint); color: #fff;
  display: flex; align-items: center; justify-content: center;
}
.acceso-logo .simbolo svg { width: 21px; height: 21px; stroke: currentColor; fill: none; }
.acceso-logo .nombre { font-family: var(--f-titular); font-weight: 800; font-size: 19px; letter-spacing: -.02em; }

/* Mismo tratamiento que el titular del hero de nextcliniq.com:
   Urbanist 800 con interletraje cerrado, y la palabra destacada en
   Spectral cursiva. */
.acceso-frase {
  font-family: var(--f-titular); font-weight: 800; font-size: 27px; line-height: 1.08;
  letter-spacing: -.035em; color: var(--ink); margin: 0 0 28px;
}
.acceso-frase em {
  font-family: var(--f-destacado); font-style: italic; font-weight: 400;
  font-size: .96em; letter-spacing: -.01em; color: var(--mint-dark);
}

/* El HTML es el mismo en las dos caras, así que el orden de las piezas
   se decide aquí: en escritorio el enlace de "¿Has olvidado…?" va
   encima del botón (alineado a la derecha) y en móvil debajo y
   centrado (ver la media query de más abajo). */
.acceso-tarjeta form { display: flex; flex-direction: column; }
.acceso-tarjeta form .acceso-explicacion { order: 1; }
.acceso-tarjeta form .field { order: 2; }
.acceso-tarjeta form .acceso-enlaces { order: 3; }
.acceso-tarjeta form .btn { order: 4; }
.acceso-tarjeta form .acceso-pie { order: 5; }

.acceso-explicacion { font-size: 13px; color: var(--text-muted); margin: 0 0 16px; line-height: 1.5; }
.acceso-enlaces { text-align: right; margin: 8px 0 20px; }
.acceso-enlaces a { font-size: 12.5px; color: var(--mint-dark); text-decoration: none; font-weight: 600; }
@media (hover: hover) {
.acceso-enlaces a:hover { text-decoration: underline; }}

.acceso-pie { margin-top: 24px; font-size: 11.5px; color: var(--text-muted); text-align: center; }

/* Botón de ver/ocultar la contraseña, dentro del propio campo. */
.boton-ojo {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 32px; height: 32px; border: none; background: none; cursor: pointer;
  color: var(--text-muted); display: flex; align-items: center; justify-content: center; border-radius: 50%;
}
@media (hover: hover) {
.boton-ojo:hover { background: #f4f7f5; }}

.boton-ojo svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; position: static; transform: none; }
.campo-con-ojo input { padding-right: 44px !important; }

.field { margin-bottom: 14px; text-align: left; }
.field label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: 5px;
}
.campo-input-wrap { position: relative; }
.campo-input-wrap svg {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  stroke: var(--text-muted);
  pointer-events: none;
}
.campo-input-wrap input { padding-left: 36px !important; }

.field input {
  width: 100%;
  padding: 11px 16px;
  border-radius: 30px;
  border: 1px solid var(--border);
  font-family: var(--font-body);
  font-size: 14px;
  background: var(--card);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.field select {
  width: 100%;
  padding: 11px 16px;
  border-radius: 30px;
  border: 1px solid var(--border);
  font-family: var(--font-body);
  font-size: 14px;
  background: var(--card);
  color: var(--text);
}
/* Un campo que no se puede cambiar (p. ej. la fecha de una compra de un
   mes cerrado) tiene que verse distinto: si no, parece que se puede. */
.field input:disabled, .field select:disabled {
  background: var(--bg);
  color: var(--text-muted);
  cursor: not-allowed;
}
.field input:focus, .field select:focus {
  outline: none;
  border-color: var(--mint);
  box-shadow: 0 0 0 3px rgba(31,174,126,0.15);
}

.login-error {
  background: var(--danger-soft);
  color: var(--danger-text);
  font-size: 12.5px;
  line-height: 1.5;
  padding: 11px 13px;
  border-radius: 12px;
  margin-bottom: 16px;
  display: none;
  text-align: left;
}

.acceso-tarjeta .btn {
  width: 100%;
  padding: 14px;
  font-size: 14.5px;
  font-weight: 700;
  border: none;
  border-radius: 30px;
  margin-top: 8px;
  background: linear-gradient(135deg, var(--mint) 0%, var(--mint-dark) 100%);
  box-shadow: 0 8px 20px rgba(31,174,126,0.3);
  transition: transform 0.1s, box-shadow 0.15s;
}
@media (hover: hover) {
.acceso-tarjeta .btn:hover { box-shadow: 0 10px 24px rgba(31,174,126,0.4); }}

.acceso-tarjeta .btn:active { transform: translateY(1px); }
.acceso-tarjeta .btn:disabled { opacity: 0.7; box-shadow: none; }

/* ---------- Acceso en móvil ----------
   Aquí la pantalla cambia de cara, no solo de tamaño: fondo oscuro,
   logo grande centrado, campos de vidrio oscuro y sin frase. La
   tarjeta deja de serlo (ni fondo, ni borde, ni sombra) y pasa a ser
   una columna que ocupa el alto entero. */
@media (max-width: 640px) {
  .acceso {
    padding: 0 24px 30px; align-items: stretch;
    background: linear-gradient(160deg, #0b1714 0%, #0f2620 48%, #0a1d19 100%);
  }

  /* La rejilla pasa a blanca sobre el fondo oscuro, y sin la máscara
     radial: aquí no hay tarjeta clara que proteger. */
  .acceso .lineas {
    inset: -60px; opacity: .07;
    background-image:
      linear-gradient(#fff 1px, transparent 1px),
      linear-gradient(90deg, #fff 1px, transparent 1px);
    background-size: 52px 52px;
    -webkit-mask-image: none; mask-image: none;
    animation-duration: 28s;
  }
  @keyframes acceso-deriva-movil { from { transform: translate3d(0,0,0); } to { transform: translate3d(52px,52px,0); } }
  .acceso .lineas { animation-name: acceso-deriva-movil; }
  .acceso .haces { display: none; }

  .acceso .halo, .acceso .halo-alto { display: block; position: absolute; border-radius: 50%; pointer-events: none; }
  .acceso .halo {
    width: 520px; height: 520px; left: 50%; bottom: -260px; transform: translateX(-50%);
    background: radial-gradient(circle, rgba(31,174,126,.30) 0%, rgba(31,174,126,0) 68%);
  }
  .acceso .halo-alto {
    width: 380px; height: 380px; left: -120px; top: -170px;
    background: radial-gradient(circle, rgba(31,174,126,.22) 0%, rgba(31,174,126,0) 70%);
  }

  /* Sin min-height aquí: el alto lo pone .acceso, que ya mide 100dvh.
     Si la tarjeta lo repitiera, se sumaría al padding inferior de
     .acceso y la pantalla acabaría con scroll por esos 30px. */
  .acceso-tarjeta {
    max-width: none; background: none; border: none; box-shadow: none;
    backdrop-filter: none; -webkit-backdrop-filter: none;
    padding: 0; border-radius: 0;
    display: flex; flex-direction: column; flex: 1;
  }

  .acceso-frase { display: none; }

  .acceso-logo {
    flex-direction: column; align-items: center; gap: 16px;
    padding: 16vh 0 10vh; margin-bottom: 0;
  }
  .acceso-logo .simbolo {
    width: 58px; height: 58px; border-radius: 18px;
    background: linear-gradient(140deg, var(--mint) 0%, #3fc99a 100%);
    box-shadow: 0 12px 30px rgba(31,174,126,.35);
  }
  .acceso-logo .simbolo svg { width: 30px; height: 30px; }
  .acceso-logo .nombre { font-size: 25px; letter-spacing: -.03em; color: #fff; }

  /* Los campos, en vidrio oscuro. */
  .acceso-tarjeta .field label { color: rgba(255,255,255,.58); padding-left: 4px; }
  .acceso-tarjeta .field input {
    /* 16px: por debajo de eso, iOS hace zoom al enfocar el campo. */
    font-size: 16px; padding-top: 15px; padding-bottom: 15px;
    color: #fff; background: rgba(255,255,255,.07);
    border-color: rgba(255,255,255,.16);
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  }
  .acceso-tarjeta .field input::placeholder { color: rgba(255,255,255,.34); }
  .acceso-tarjeta .field input:focus {
    border-color: var(--mint); background: rgba(255,255,255,.10);
    box-shadow: 0 0 0 4px rgba(31,174,126,.16);
  }
  /* Los iconos necesitan más contraste que el texto de apoyo: con el
     mismo tono se perdían dentro del campo. Y necesitan z-index: el
     "backdrop-filter" del input crea contexto de apilado, y como el
     input va después en el HTML, se pintaba ENCIMA del icono y lo
     dejaba borroso detrás del cristal. */
  .acceso-tarjeta .campo-input-wrap svg { stroke: rgba(255,255,255,.75); z-index: 1; }
  .acceso-tarjeta .boton-ojo { color: rgba(255,255,255,.7); }
@media (hover: hover) {
  .acceso-tarjeta .boton-ojo:hover { background: rgba(255,255,255,.08); }}


  .acceso-formulario-movil { display: flex; flex-direction: column; flex: 1; }
  .acceso-explicacion { color: rgba(255,255,255,.58); text-align: center; }
  .acceso-tarjeta form .btn { order: 3; }
  .acceso-tarjeta form .acceso-enlaces { order: 4; }
  .acceso-enlaces { text-align: center; margin: 18px 0 0; }
  .acceso-enlaces a { font-size: 13px; color: #5fdcb0; }
  .acceso-tarjeta .btn { font-size: 16px; padding: 17px; margin-top: 24px; }
  .acceso-pie { margin-top: auto; padding-top: 26px; color: rgba(255,255,255,.34); }

  .login-error {
    background: rgba(229,72,77,.16); color: #ffb4b6;
    border: 1px solid rgba(229,72,77,.32);
  }
}

/* ---------- Overlay de carga (al aplicar filtros) ---------- */
#overlayCargando {
  display: none;
  position: fixed; inset: 0;
  background: rgba(20,24,28,0.06);
  z-index: 900;
  pointer-events: none;
}
#overlayCargando.visible { display: block; }
.overlay-cargando-caja {
  position: fixed; top: 18px; left: 50%; transform: translateX(-50%);
  background: var(--mint);
  color: #fff;
  font-weight: 700;
  font-size: 13px;
  padding: 9px 18px;
  border-radius: 999px;
  box-shadow: 0 8px 24px rgba(31,174,126,0.35);
  display: flex; align-items: center; gap: 8px;
  z-index: 901;
}
.overlay-cargando-caja .spinner {
  width: 13px; height: 13px;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.5);
  border-top-color: #fff;
  animation: girar 0.7s linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }

/* ---------- Interruptor deslizante (rojo/verde) ---------- */
/* Etiqueta que envuelve un interruptor y su texto.
   Va en inline-flex y con el ancho ajustado al contenido a propósito:
   escrita como style="display:flex" en línea, la etiqueta ocupaba TODA
   la anchura de su contenedor, así que la zona que responde al clic
   llegaba hasta el borde de la ventana, mucho más allá del texto — se
   activaba el interruptor pulsando en un hueco vacío a su derecha.
   El "width: fit-content" hace falta además del inline-flex: sin él,
   dentro de un contenedor flex en columna la etiqueta se estiraba
   igualmente por el "align-items: stretch" que trae de serie. */
.fila-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  max-width: 100%;
}

/* Menú de descarga (Excel / CSV) que sale al pulsar el botón de una
   tabla. Se crea desde JavaScript y se cuelga del body, para que no lo
   recorte el "overflow" de la tarjeta que contiene la tabla. */
.menu-descarga {
  position: fixed; z-index: 1200;
  background: var(--card); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 10px 26px rgba(20,24,28,0.14); padding: 6px; min-width: 170px;
}
.menu-descarga button {
  display: block; width: 100%; text-align: left; padding: 8px 12px;
  border: 0; background: transparent; border-radius: 8px; cursor: pointer;
  font-family: var(--font-body); font-size: 13px; color: var(--text);
}
.menu-descarga button:hover { background: var(--bg); }

.toggle-switch { position: relative; display: inline-block; width: 38px; height: 21px; vertical-align: middle; }
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
  position: absolute; cursor: pointer; inset: 0;
  background: var(--danger); transition: background 0.15s; border-radius: 999px;
}
.toggle-slider::before {
  content: ""; position: absolute; height: 15px; width: 15px; left: 3px; bottom: 3px;
  background: #fff; transition: transform 0.15s; border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0,0,0,0.25);
}
.toggle-switch input:checked + .toggle-slider { background: var(--mint); }
.toggle-switch input:checked + .toggle-slider::before { transform: translateX(17px); }
.toggle-switch input:disabled + .toggle-slider { opacity: 0.5; cursor: not-allowed; }

/* Variante con texto SÍ / NO dentro, un poco más ancha */
.toggle-switch-texto { width: 48px; }
.toggle-switch-texto input:checked + .toggle-slider::before { transform: translateX(27px); }
.toggle-texto-si, .toggle-texto-no {
  position: absolute; top: 50%; transform: translateY(-50%);
  font-size: 8.5px; font-weight: 800; color: #fff; letter-spacing: 0.02em;
  pointer-events: none; transition: opacity 0.15s;
}
.toggle-texto-si { left: 6px; opacity: 0; }
.toggle-texto-no { right: 5px; opacity: 1; }
.toggle-switch-texto input:checked + .toggle-slider .toggle-texto-si { opacity: 1; }
.toggle-switch-texto input:checked + .toggle-slider .toggle-texto-no { opacity: 0; }

/* Variante "conciliado": gris cuando no está marcado, verde al marcar */
.toggle-switch.gris-verde .toggle-slider { background: #b9c0c6; }
.toggle-switch.gris-verde input:checked + .toggle-slider { background: var(--mint); }

/* Variante "albarán": gris cuando no está marcado, rojo al marcar */
.toggle-switch.gris-rojo .toggle-slider { background: #b9c0c6; }
.toggle-switch.gris-rojo input:checked + .toggle-slider { background: var(--danger); }

.toggle-switch.gris-azul .toggle-slider { background: #b9c0c6; }
.toggle-switch.gris-azul input:checked + .toggle-slider { background: #2a5db0; }

/* Quitar una fila de un formulario de varias. Como el resto de botones
   de borrar de la aplicación: de línea, en rojo, sin bloque relleno
   (13 oct. 2026: antes era un cuadrado rojo con el icono en blanco
   escrito a mano). */
.btn-quitar-fila {
  background: transparent;
  color: var(--danger-text);
  border: 1px solid var(--border);
  border-radius: 30px;
  width: 38px;
  height: 38px;
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 0;
}
@media (hover: hover) {
.btn-quitar-fila:hover { background: var(--danger-soft); border-color: var(--danger); }}
.btn-quitar-fila .icono-linea { width: 16px; height: 16px; }

/* Fila de «Añadir compras»: anchos fijos para lo que tiene un largo
   conocido, el proveedor se queda el resto y la observación ocupa la
   segunda línea entera. Con «Crear proveedor nuevo», su nombre sale
   justo debajo del proveedor. */
.fila-compra-campos {
  display: grid;
  grid-template-columns: 138px minmax(170px, 1fr) 130px 130px 138px 38px;
  gap: 8px 10px;
  align-items: end;
}
.fila-compra-campos .field { margin-bottom: 0; min-width: 0; }
.fila-compra-campos .fc-nuevo { grid-column: 2 / 3; grid-row: 2; }
.fila-compra-campos .fc-observacion { grid-column: 1 / 6; }
@media (max-width: 720px) {
  .fila-compra-campos { grid-template-columns: 1fr 1fr; }
  .fila-compra-campos .fc-proveedor,
  .fila-compra-campos .fc-observacion { grid-column: 1 / -1; }
  .fila-compra-campos .fc-nuevo { grid-column: 1 / -1; grid-row: auto; }
  .fila-compra-campos .fc-quitar { justify-self: end; } /* junto a la fecha de cobro */
}


/* ---------- Tabla de detalle anual por factura (Compras) ---------- */
.tabla-detalle-anual { table-layout: fixed; width: max-content; min-width: 100%; }
.tabla-detalle-anual th {
  background: var(--ink);
  color: #ffffff;
  font-size: 10.5px;
  text-transform: none;
  letter-spacing: 0.01em;
  font-weight: 700;
  padding: 9px 6px;
  white-space: nowrap;
  border-bottom: none;
}
.tabla-detalle-anual td { padding: 7px 6px; white-space: nowrap; font-size: 11px; overflow: hidden; text-overflow: ellipsis; }
.tabla-detalle-anual .celda-proveedor { white-space: normal; word-break: break-word; }
.tabla-detalle-anual tfoot td { font-weight: 700; background: var(--bg); border-top: 2px solid var(--border); }
.tabla-detalle-anual th:nth-child(1), .tabla-detalle-anual td:nth-child(1) { width: 78px; }
.tabla-detalle-anual th:nth-child(2), .tabla-detalle-anual td:nth-child(2) { width: 150px; }
.tabla-detalle-anual th:nth-child(3), .tabla-detalle-anual td:nth-child(3) { width: 110px; }
.tabla-detalle-anual th:nth-child(4), .tabla-detalle-anual td:nth-child(4) { width: 92px; }
.tabla-detalle-anual th:nth-child(n+5), .tabla-detalle-anual td:nth-child(n+5) { width: 80px; }

.filtro-rentabilidad-btn.active { background: var(--ink); border-color: var(--ink); color: #fff; font-weight: 600; }
input[type="range"] { accent-color: var(--mint); cursor: pointer; }

.th-ordenable { cursor: pointer; user-select: none; }
@media (hover: hover) {
.th-ordenable:hover { color: var(--ink); text-decoration: underline; }}


.chevron-prorrateo { display: inline-block; transition: transform 0.15s; color: var(--text-muted); font-weight: 700; }

.tabla-compacta td, .tabla-compacta th { padding: 6px 8px; font-size: 11.5px; }

.tabla-rejilla, .tabla-rejilla th, .tabla-rejilla td {
  border: 1px solid var(--border);
  border-collapse: collapse;
}

/* Selectores de clasificación (tipos de visita, formas de pago,
   financieras, especialidades...) — versión más moderna que el
   select por defecto del navegador. */
.esp-select, .tipo-select, .tipo-subtipo-select, .formapago-select, .financiera-select {
  appearance: none;
  -webkit-appearance: none;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  padding: 8px 32px 8px 16px;
  border-radius: 30px;
  border: 1.5px solid var(--border);
  background-color: var(--card);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235b6570' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  color: var(--text);
  min-width: 160px;
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s;
}
@media (hover: hover) {
.esp-select:hover, .tipo-select:hover, .tipo-subtipo-select:hover, .formapago-select:hover, .financiera-select:hover {
  border-color: var(--mint);
}}

.esp-select:focus, .tipo-select:focus, .tipo-subtipo-select:focus, .formapago-select:focus, .financiera-select:focus {
  outline: none;
  border-color: var(--mint);
  box-shadow: 0 0 0 3px rgba(31,174,126,0.15);
}

/* ---------- Utilidades ---------- */
.empty-state {
  text-align: center;
  padding: 40px 20px;
  color: var(--text-muted);
  font-size: 13.5px;
}

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

/* ---------- Importador ---------- */
.dropzone {
  border: 2px dashed var(--border);
  border-radius: var(--radius-lg);
  padding: 36px 20px;
  text-align: center;
  color: var(--text-muted);
  font-size: 13.5px;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.dropzone:hover, .dropzone.dragover {
  border-color: var(--mint);
  background: var(--mint-soft);
  color: var(--mint-dark);
}
.dropzone strong { color: var(--ink); }
.dropzone input { display: none; }

.file-list { margin-top: 14px; display: flex; flex-direction: column; gap: 8px; }

.file-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  font-size: 13px;
}
.file-row .file-name { font-weight: 600; color: var(--ink); flex: 1; }
.file-row .file-meta { color: var(--text-muted); font-size: 12px; }

.badge.tipo-cobros { background: var(--mint-soft); color: var(--mint-dark); }
.badge.tipo-presupuestos { background: #e8ecff; color: #3949ab; }
.badge.tipo-desconocido { background: var(--danger-soft); color: #a12227; }

/* ---------- Desglose de especialidades (Dashboard) ---------- */
.especialidad-row { margin-bottom: 12px; }
.especialidad-row:last-child { margin-bottom: 0; }
.especialidad-row .fila-top {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 12.5px; margin-bottom: 4px;
}
.especialidad-row .fila-top .nombre { font-weight: 600; color: var(--ink); }
.especialidad-row .fila-top .valores { font-family: var(--font-numeros); font-variant-numeric: tabular-nums; color: var(--text-muted); font-size: 11.5px; }
.especialidad-row .barra-track { height: 20px; border-radius: 999px; background: var(--bg); border: 1px solid var(--border); overflow: hidden; position: relative; }
.especialidad-row .barra-fill { height: 100%; background: var(--mint); border-radius: 999px; transition: width 0.8s cubic-bezier(0.22, 1, 0.36, 1); }
.especialidad-row .barra-texto {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-family: var(--font-numeros); font-variant-numeric: tabular-nums; font-size: 10.5px; font-weight: 700; color: var(--ink);
  text-shadow: 0 0 4px #fff, 0 0 4px #fff, 0 0 8px #fff; pointer-events: none;
}
.especialidad-row.sin-clasificar .barra-fill { background: var(--warning); }

/* ---------- Campos que se editan dentro de una tabla ----------
   Los mismos que se usan en Configuración (Especialidades, Asesores,
   Tipos de visita) y en la edición masiva de Objetivos, Producción y
   los presupuestos de Compras. Antes solo estaban vestidos los de
   texto, y los de importe se pintaban con un style="width:90px" a mano:
   se veían como un rectángulo del navegador al lado de los otros
   (lo cazó Víctor, sept. 2026). Ahora el aspecto lo pone esta regla y
   el HTML no lleva estilo propio. */
.data-table input[type="text"],
.data-table input[type="number"],
.data-table select {
  font-family: var(--font-body);
  font-size: 13px;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.data-table input[type="text"], .data-table select { min-width: 160px; }

/* Los de importe no pueden pedir 160 px: son doce en una fila. Van
   alineados a la derecha y con cifras de ancho fijo, como las celdas
   .num de al lado, para que la columna cuadre. */
.data-table input[type="number"] {
  width: 90px;
  min-width: 0;
  max-width: 100%;
  text-align: right;
  font-family: var(--font-numeros);
  font-variant-numeric: tabular-nums;
}
/* Las flechas de subir/bajar de los campos numéricos se comen el sitio
   justo por el lado donde está la cifra. Fuera: aquí se teclea. */
.data-table input[type="number"]::-webkit-outer-spin-button,
.data-table input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
}
.data-table input[type="number"] { -moz-appearance: textfield; }

.data-table input[type="text"]:focus,
.data-table input[type="number"]:focus,
.data-table select:focus {
  outline: none;
  border-color: var(--mint);
  box-shadow: 0 0 0 3px var(--mint-soft);
}
.chip-list { display: flex; flex-wrap: wrap; gap: 6px; max-width: 100%; }
.chip {
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: #ecefee;
  color: #5b6570;
  cursor: pointer;
  user-select: none;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.chip.selected { background: var(--success); border-color: var(--success); color: #fff; font-weight: 600; }

/* ---------- Pantallas de edición (rol, usuario) ----------
   Mientras se crea o edita un rol o un usuario no pintan nada el
   filtro de fechas del Dashboard, las barras de objetivo ni las
   pestañas de Configuración avanzada: la pantalla se queda solo con
   el menú lateral y una cabecera propia con "Volver" y "Guardar".
   Lo mismo se aplica a Configuración y Configuración avanzada
   enteras, donde esos controles tampoco tienen sentido (clase
   .seccion-sin-filtros, que pone js/app.js al navegar). */
/* Se apunta a la CLASE .topbar y no al id: en el Cuadro de mandos la
   barra de filtros es <div class="topbar" id="topbar">, pero en Compras
   y en Finanzas solo lleva la clase. Desde que el editor de usuario se
   construye desde JavaScript y funciona en las tres herramientas, la
   pantalla de edición tiene que quedarse igual de limpia en las tres. */
.app-shell.en-edicion .topbar,
.app-shell.en-edicion #barrasObjetivoDashboard,
.app-shell.en-edicion .avanzada-tabs,
.app-shell.en-edicion .config-tabs,
.app-shell.seccion-sin-filtros .topbar,
.app-shell.seccion-sin-filtros #barrasObjetivoDashboard { display: none !important; }

/* Filas de pestañas y de controles. Antes llevaban el "display: flex" en
   el atributo style, sin "flex-wrap", y en pantallas estrechas la fila
   seguía en una sola línea: las últimas pestañas se salían por la derecha
   y no había forma de llegar a ellas, porque .main recorta lo que sobra.
   Al repartirse en varias líneas se ven todas. */
.config-tabs,
.avanzada-tabs {
  display: flex;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.config-tabs { gap: 8px; }
.avanzada-tabs { gap: 6px; }

.fila-controles {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: flex-end;
}

.cabecera-edicion {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(246, 248, 247, 0.92);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
  padding: 14px 24px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.cabecera-edicion h2 { font-size: 17px; margin: 0; flex: 1; min-width: 180px; }
.cabecera-edicion .acciones-edicion { display: flex; gap: 8px; margin-left: auto; }
.btn-volver {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 600; color: var(--text-muted);
  padding: 6px 12px; border-radius: 30px; border: 1px solid var(--border);
  background: var(--card); cursor: pointer; font-family: var(--font-body);
}
@media (hover: hover) {
.btn-volver:hover { border-color: var(--slate); color: var(--ink); }}

.btn-volver svg { width: 15px; height: 15px; }
.btn.btn-mini { font-size: 12px; padding: 5px 12px; }

/* ---------- Selector de permisos por apartado ---------- */
.permisos-barra { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.permisos-buscador { flex: 1; min-width: 220px; position: relative; }
.permisos-buscador input {
  width: 100%; padding: 8px 14px 8px 36px; border-radius: 30px;
  border: 2px solid var(--border); font-family: var(--font-body); font-size: 13px; color: var(--text);
}
.permisos-buscador input:focus { outline: none; border-color: var(--mint); }
.permisos-buscador svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 15px; height: 15px; color: var(--slate); }
.permisos-contador { font-size: 12.5px; color: var(--text-muted); white-space: nowrap; }

.permisos-md { display: grid; grid-template-columns: 250px 1fr; gap: 14px; align-items: start; }
/* La lista de apartados se muestra entera, sin scroll propio: dos
   barras de scroll anidadas (la de la página y la de aquí dentro) son
   incómodas de usar y esconden apartados sin avisar. Crece lo que
   haga falta y se baja con el scroll normal de la página. */
.permisos-lista { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--card); }
.permisos-herramienta {
  font-size: 10.5px; font-weight: 700; color: var(--mint-dark);
  text-transform: uppercase; letter-spacing: 0.04em;
  padding: 8px 12px 4px; background: #f4f7f5; border-bottom: 1px solid var(--border);
}
.permisos-apartado { display: flex; align-items: center; gap: 8px; padding: 9px 12px; cursor: pointer; font-size: 13px; border-bottom: 1px solid var(--border); }
.permisos-apartado:last-child { border-bottom: none; }
@media (hover: hover) {
.permisos-apartado:hover { background: #f4f7f5; }}

.permisos-apartado.elegido { background: var(--mint-soft); color: var(--mint-dark); font-weight: 600; }
.permisos-apartado.sin-resultados { opacity: 0.35; }
.permisos-cuenta { font-size: 11.5px; font-weight: 500; color: var(--text-muted); background: #f0f3f1; border-radius: 999px; padding: 2px 8px; margin-left: auto; }
.permisos-cuenta.activa { background: var(--mint-soft); color: var(--mint-dark); }

/* Como la lista de la izquierda ya es larga, el panel de permisos
   acompaña al bajar en vez de quedarse arriba: si no, al elegir uno de
   los últimos apartados habría que subir otra vez para ver y marcar
   sus permisos. Se pega justo debajo de la cabecera de edición. */
.permisos-detalle {
  border: 1px solid var(--border); border-radius: 10px; background: var(--card);
  padding: 14px; min-height: 320px;
  position: sticky; top: 76px; align-self: start;
}
.permisos-detalle-cabecera { display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap; border-bottom: 1px solid var(--border); padding-bottom: 10px; margin-bottom: 8px; }
.permisos-detalle-cabecera h4 { margin: 0; font-size: 14.5px; }
.permisos-detalle-cabecera .ruta { color: var(--text-muted); font-size: 12px; }
.permisos-detalle-acciones { margin-left: auto; display: flex; gap: 6px; }
.permisos-vacio { color: var(--text-muted); font-size: 13px; padding: 20px 8px; text-align: center; }
.permisos-nota { color: var(--text-muted); font-size: 12.5px; padding: 12px 8px 4px; border-top: 1px solid var(--border); margin-top: 8px; }
.permisos-grupo-resultado { margin-bottom: 12px; }
.permisos-titulo-resultado { font-weight: 700; font-size: 12.5px; color: var(--text-muted); padding: 4px 8px; border-bottom: 1px solid var(--border); margin-bottom: 2px; }

.fila-permiso { display: flex; align-items: center; gap: 10px; padding: 7px 8px; border-radius: 8px; }
@media (hover: hover) {
.fila-permiso:hover { background: #f4f7f5; }}

.fila-permiso label { display: flex; align-items: center; gap: 10px; cursor: pointer; flex: 1; min-width: 0; }
.fila-permiso input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--mint); flex: none; cursor: pointer; }
.fila-permiso .desc { color: var(--text-muted); font-size: 12px; }
.fila-permiso.es-ver { background: #f7faf9; border: 1px solid var(--border); margin-bottom: 6px; }
.fila-permiso.sangrada { padding-left: 30px; }
.fila-permiso.apagada { opacity: 0.45; }
.marca-busqueda { background: #fff3c4; border-radius: 3px; padding: 0 1px; color: inherit; }

/* Grupo de opciones excluyentes en una píldora partida (orígenes de
   datos de la pantalla de clínica). Se ven las tres opciones a la vez
   con la elegida resaltada, en lugar de un desplegable que las esconde:
   aquí importa comparar de un vistazo cómo está cada tipo de dato.
   El <input type="radio"> real se esconde y manda el <label>, igual que
   hace el selector avanzado con sus checkboxes. */
.grupo-opciones { display: inline-flex; border: 1px solid var(--border); border-radius: 30px; overflow: hidden; background: var(--card); }
.grupo-opciones label {
  padding: 6px 15px; font-size: 12.5px; font-weight: 600; color: var(--text-muted);
  cursor: pointer; border-right: 1px solid var(--border); user-select: none;
}
.grupo-opciones label:last-child { border-right: none; }
@media (hover: hover) {
.grupo-opciones label:hover { background: #f4f7f5; }}

.grupo-opciones input[type="radio"] { display: none; }
.grupo-opciones label.elegida { background: var(--mint); color: #fff; }
@media (hover: hover) {
.grupo-opciones label.elegida:hover { background: var(--mint-dark); }}

/* "No se usa" no es un estado bueno ni malo: se marca en gris, para que
   no se lea como algo activo. */
.grupo-opciones label.elegida.apagada { background: var(--slate); }
@media (hover: hover) {
.grupo-opciones label.elegida.apagada:hover { background: var(--ink-soft); }}


@media (max-width: 860px) {
  .permisos-md { grid-template-columns: 1fr; }
  .permisos-lista { max-height: 220px; }
}

/* Aviso de "esto afecta a varios usuarios" al editar un rol: el
   vínculo entre rol y usuario es vivo, así que conviene verlo antes
   de tocar nada, no enterarse después. */
.aviso-rol-usuarios {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--warning-soft);
  color: var(--warning-text);
  border: 1px solid var(--warning);
  border-radius: var(--radius);
  padding: 10px 12px;
  font-size: 12.5px;
  margin: 10px 0 4px;
}
.aviso-rol-usuarios .icono-linea { flex: none; width: 16px; height: 16px; }
.guardado-ok { color: var(--mint-dark); font-size: 11.5px; margin-left: 6px; }

/* ---------- Estado visual (Presupuestos) ---------- */
.badge-estado { display: inline-block; padding: 4px 12px; border-radius: 999px; font-size: 11.5px; font-weight: 700; white-space: nowrap; }
.badge-estado.state-amarillo { background: var(--warning-soft); color: #8a5a10; }
.badge-estado.state-verde { background: var(--success-soft); color: var(--mint-dark); }
.badge-estado.state-gris { background: #ecefee; color: #5b6570; }
.badge-estado.state-rojo { background: var(--danger-soft); color: #a12227; }
.badge-estado.state-azul { background: #e3edff; color: #2a5db0; }

/* ---------- Filtros en botones (multi-selección) ---------- */
.pill-filtro {
  font-size: 12.5px; font-weight: 600; padding: 7px 14px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text-muted); cursor: pointer;
}
.pill-filtro.selected { background: var(--ink); border-color: var(--ink); color: #fff; }
.pill-filtro.pill-amarillo { border-color: var(--warning); color: #8a5a10; }
.pill-filtro.pill-amarillo.selected { background: var(--warning); border-color: var(--warning); color: #5a3d0a; }
.pill-filtro.pill-verde { border-color: var(--success); color: var(--mint-dark); }
.pill-filtro.pill-verde.selected { background: var(--success); border-color: var(--success); color: #fff; }
.pill-filtro.pill-rojo { border-color: var(--danger); color: var(--danger); }
.pill-filtro.pill-rojo.selected { background: var(--danger); border-color: var(--danger); color: #fff; }
.pill-filtro.pill-azul { border-color: #2a5db0; color: #2a5db0; }
.pill-filtro.pill-azul.selected { background: #2a5db0; border-color: #2a5db0; color: #fff; }
.pill-filtro.pill-gris { border-color: #b9bfc2; color: #5b6570; }
.pill-filtro.pill-gris.selected { background: #5b6570; border-color: #5b6570; color: #fff; }

.celda-clicable { cursor: pointer; }
@media (hover: hover) {
.celda-clicable:hover { text-decoration: underline; text-decoration-color: var(--mint); }}


/* ---------- Contenedor de gráficas (altura fija, evita el crecimiento infinito de Chart.js) ---------- */
.chart-wrap { position: relative; height: 260px; width: 100%; }

/* Donut + mini-tabla en paralelo (especialidad, captación/fidelización):
   sin tope de ancho, la leyenda inferior de Chart.js se estira en una
   sola línea muy separada cuando hay pocas categorías. Con pocas
   categorías el donut se queda a este ancho; con muchas, la propia
   leyenda de Chart.js va bajando de línea sola. */
.chart-wrap-donut { max-width: 340px; }

/* ---------- Menú de acciones (⋮) ---------- */
.accion-menu { position: relative; display: block; text-align: right; }
.accion-dropdown {
  display: none; position: absolute; right: 0; top: 100%; margin-top: 4px;
  background: var(--card); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.12); min-width: 130px; z-index: 50; overflow: hidden;
}
.accion-dropdown.visible { display: block; }
.accion-dropdown button {
  display: block; width: 100%; text-align: left; padding: 8px 12px; border: none; background: none;
  font-size: 13px; color: var(--text); cursor: pointer;
}
@media (hover: hover) {
.accion-dropdown button:hover { background: var(--bg); }}

.accion-dropdown button.peligro { color: var(--danger); }

/* ---------- Nota (vista previa en tabla) ---------- */
.celda-nota { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
.tabla-fija-presupuestos { table-layout: fixed; min-width: 980px; }
.tabla-fija-presupuestos td, .tabla-fija-presupuestos th {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tabla-fija-presupuestos td:last-child { overflow: visible; }

/* ---------- Estadísticas mini (Presupuestos) ---------- */
.mini-stat { flex: 1; min-width: 110px; text-align: center; position: relative; padding: 2px 0; }
.mini-stat:not(:last-child)::after {
  content: "";
  position: absolute;
  right: 0; top: 12%; bottom: 12%;
  width: 1px;
  background: var(--border);
}
.mini-stat-label { font-family: 'Poppins', sans-serif; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-muted); font-weight: 600; margin-bottom: 4px; }
.mini-stat-valor { font-family: var(--font-numeros); font-variant-numeric: tabular-nums; font-weight: 700; font-size: 16px; color: var(--ink); }
/* Segunda cifra pequeña bajo el valor (p. ej. «38.50% de lo presupuestado»). */
.mini-stat-nota { font-family: var(--font-numeros); font-variant-numeric: tabular-nums; font-size: 11.5px; font-weight: 600; color: var(--text-muted); margin-top: 2px; }

/* ---------- Resumen anual de Objetivos (tabla) ---------- */
.badge-pct {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 6px;
  font-family: var(--font-numeros);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 11.5px;
}
.badge-pct.state-rojo { background: var(--danger-soft); color: #a12227; }
.badge-pct.state-amarillo { background: var(--warning-soft); color: #8a5a10; }
.badge-pct.state-verde { background: var(--success-soft); color: var(--mint-dark); }
/* Meses futuros en la tabla "Acumulado del año" (Objetivos): el
   acumulado se corta en el último mes con cobros reales, y de ahí en
   adelante se enseña en gris con una raya, no vacío. */
.mes-futuro { color: var(--text-suave); text-decoration: line-through; text-decoration-color: var(--border); }
.tabla-resumen-anual.data-table th, .tabla-resumen-anual.data-table td,
.tabla-resumen-anual.data-table td.num { white-space: nowrap; text-align: center; }
.tabla-resumen-anual { table-layout: fixed; width: 100%; }
.tabla-resumen-anual td:first-child, .tabla-resumen-anual th:first-child { font-weight: 600; color: var(--ink); text-align: left; width: 90px; }
.tabla-forma-pago.data-table td:first-child, .tabla-forma-pago.data-table th:first-child { width: 160px; white-space: normal; }

/* ---------- Bug #2: "Cobros por forma de pago (mensual)" ----------
   Antes usaba .tabla-resumen-anual (table-layout:fixed + ancho 100%),
   que con muchas formas de pago fuerza columnas tan estrechas que el
   importe se sale de su celda. Ahora son dos vistas nuevas e
   independientes (no tocan .tabla-resumen-anual, que sigue usándola
   la tabla de Objetivos):
   - .tabla-ancha-normal: los mismos 12 meses en columna de
     siempre, pero SIN table-layout:fixed — cada columna usa el ancho
     que necesita de verdad.
   - .tabla-ancha-transpuesta: meses en fila y el resto en columna
     (Opción B) — se usa solo cuando la normal no cabe.
   El mecanismo es común a las tres tablas de doce meses (forma de
   pago, Resumen anual y Acumulado): ver js/tablaAdaptativa.js. */
.tabla-ancha-normal.data-table th, .tabla-ancha-normal.data-table td.num { white-space: nowrap; text-align: right; }
.tabla-ancha-normal td:first-child, .tabla-ancha-normal th:first-child { font-weight: 600; color: var(--ink); text-align: left; white-space: nowrap; }
.tabla-ancha-normal tr:last-child td { font-weight: 700; border-top: 2px solid var(--border); border-bottom: none; }

.tabla-ancha-transpuesta.data-table th, .tabla-ancha-transpuesta.data-table td.num { white-space: nowrap; text-align: right; }
.tabla-ancha-transpuesta td:first-child, .tabla-ancha-transpuesta th:first-child { font-weight: 600; color: var(--ink); text-align: left; white-space: nowrap; }
.tabla-ancha-transpuesta tr:last-child td { font-weight: 700; border-top: 2px solid var(--border); border-bottom: none; }

.nota-tabla-traspuesta {
  margin-top: 10px; font-size: 12px; color: var(--text-muted);
  display: flex; align-items: center; gap: 6px;
}
.nota-tabla-traspuesta svg { flex: none; opacity: 0.7; }

/* ---------- Modal ---------- */
.modal-overlay {
  display: none;
  position: fixed; inset: 0;
  background: rgba(20, 24, 28, 0.55);
  align-items: center; justify-content: center;
  z-index: 1000;
  padding: 20px;
}
.modal-overlay.visible { display: flex; }
.modal-box {
  background: var(--card);
  border-radius: var(--radius-lg);
  max-width: 1040px;
  width: 100%;
  max-height: 85vh;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.modal-box .modal-scroll {
  padding: 24px;
  max-height: 85vh;
  overflow-y: auto;
  box-sizing: border-box;
}
.modal-box .modal-header {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 16px;
}
.modal-box textarea {
  width: 100%; min-height: 80px; font-family: var(--font-body); font-size: 13px;
  padding: 10px; border-radius: 8px; border: 1px solid var(--border); resize: vertical;
}

/* ---------- Fila de botones con una selección en marcha ----------
   Cuando se marcan filas de una tabla aparecen las acciones sobre lo
   seleccionado, y todo junto no cabe en una línea: medido en sept.
   2026, la fila de Previsiones pide 1.182 px y en un portátil de 1.440
   solo tiene 1.118, así que se partía en dos y empujaba la tabla hacia
   abajo — justo lo que no se quería.

   En vez de mover la barra a su propia línea, los botones de siempre
   (Actualizar, Exportar, Imprimir) se quedan en su icono mientras dura
   la selección. Es exactamente lo que ya se hace en móvil, así que no
   es un invento nuevo: el nombre sigue en el "title" y para los
   lectores de pantalla. Con eso la fila baja a unos 890 px y cabe
   entera hasta en 1.280.

   La clase la pone y la quita _actualizarBotonesPrevisiones(). */
.fila-botones-con-seleccion .btn-con-icono .texto-boton-icono { display: none; }
.fila-botones-con-seleccion .btn-con-icono {
  width: 40px;
  padding-left: 0;
  padding-right: 0;
  justify-content: center;
  gap: 0;
}
/* El botón de la acción principal NO se encoge: es el que se va a
   pulsar y tiene que leerse. */
.fila-botones-con-seleccion #accionesPrevisionesSeleccionadas .btn-con-icono {
  width: auto;
  padding-left: 14px;
  padding-right: 14px;
  gap: 6px;
}
.fila-botones-con-seleccion #accionesPrevisionesSeleccionadas .texto-boton-icono { display: inline; }

/* ---------- Ventana de confirmación propia ----------
   Sustituye al confirm() del navegador. Va en su propia capa y por
   ENCIMA de la modal normal (z-index 1100 contra 1000), porque tiene
   que poder salir con una modal ya abierta: el aviso de "tienes datos
   sin guardar" es exactamente ese caso. */
.confirm-overlay {
  display: none;
  position: fixed; inset: 0;
  background: rgba(20, 24, 28, 0.55);
  align-items: center; justify-content: center;
  z-index: 1100;
  padding: 20px;
}
.confirm-overlay.visible { display: flex; }
.confirm-box {
  background: var(--card);
  border-radius: var(--radius-lg);
  max-width: 440px;
  width: 100%;
  padding: 24px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
  box-sizing: border-box;
}
.confirm-titulo {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 8px;
}
.confirm-texto {
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-muted);
  margin-bottom: 20px;
}
.confirm-botones {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  flex-wrap: wrap;
}

/* Modal de contenido corto: la medida por defecto (1040px) está
   pensada para tablas y formularios largos, y deja muy vacía una
   ventana de cuatro líneas. Se pide con abrirModal(html, "modal-estrecha"). */
.modal-box.modal-estrecha { max-width: 520px; }

/* ---------- Copias automáticas (js/copiasAutomaticas.js) ---------- */
.aviso-copias {
  background: var(--danger-soft);
  color: var(--danger-text);
  border-radius: var(--radius);
  padding: 12px 16px;
  margin: 0 0 14px;
  font-size: 13px;
  line-height: 1.5;
}
.aviso-copias-titulo { display: flex; align-items: center; gap: 8px; font-weight: 700; }
.aviso-copias-titulo .icono-linea { width: 16px; height: 16px; flex: 0 0 auto; }
.aviso-copias ul { margin: 6px 0 6px 24px; padding: 0; }
.aviso-copias p { margin: 6px 0 0; color: var(--text); font-size: 12.5px; }
.tabla-copias a, .tabla-historial-copias a { color: var(--mint-profundo); font-weight: 600; }
/* Marca de factura dividida en varios meses (punto 48). */
.marca-dividida { display: inline-flex; vertical-align: -2px; color: var(--text-muted); cursor: help; }
.marca-dividida svg { width: 14px; height: 14px; }

/* Configuración › Proveedores (puntos 52 y 53): barra con el buscador,
   la asignación en bloque y el botón de aplicar las reglas. */
.barra-acciones-proveedores { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 12px; }
.barra-acciones-proveedores > .btn { flex: none; }
/* Botón con icono cuyo texto NO se puede quitar en móvil (a diferencia
   de .btn-con-icono, que en móvil se queda en el icono): "Aplicar las
   reglas…" y "Reparar compras…" no se entienden solo con el dibujo. */
.btn-icono-con-texto { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; }
.btn-icono-con-texto svg { width: 14px; height: 14px; flex: none; }
.barra-acciones-proveedores > .text-muted { flex: 1 1 220px; min-width: 0; }
/* Con la columna de casillas, en móvil el desplegable se salía de la
   tarjeta: un nombre de proveedor largo fija el ancho mínimo del
   <select>. Con el reparto fijo, el desplegable se ajusta a su columna. */
.tabla-asignar-proveedores { table-layout: fixed; width: 100%; }
.tabla-asignar-proveedores .col-casilla { width: 40px; }
.tabla-asignar-proveedores .col-asignar { width: 45%; }
.tabla-asignar-proveedores td { overflow-wrap: anywhere; }
.tabla-asignar-proveedores select { min-width: 0; max-width: 100%; }
.data-table .col-casilla { width: 32px; text-align: center; }
.data-table .col-casilla input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--mint); cursor: pointer; }

/* Pantalla de "sin acceso" (catalogoModulos.js, opción B del boceto).
   Sin menú lateral ni barras; el superadmin conserva la barra de arriba
   porque ahí cambia de clínica. */
body.pantalla-sin-acceso .sidebar,
body.pantalla-sin-acceso .barra-inferior { display: none; }
body.pantalla-sin-acceso .app-shell.is-visible { grid-template-columns: 1fr; }

/* ---------------------------------------------------------
   Permisos detallados de Compras y Finanzas (migración 13)
   --------------------------------------------------------- */
/* Sin el permiso de descargar de la herramienta (auth.js). */
body.sin-descargas .boton-descarga { display: none; }
/* Candado de un registro de un mes cerrado, en lugar de «Eliminar». */
.candado-mes-cerrado { display: inline-flex; align-items: center; vertical-align: middle; color: var(--text-muted); cursor: help; }
.candado-mes-cerrado svg { width: 15px; height: 15px; }
/* Aviso dentro de la modal de editar un registro de un mes cerrado. */
.aviso-mes-cerrado { display: flex; gap: 8px; align-items: flex-start; font-size: 12.5px; background: var(--warning-soft); color: var(--warning-text); border: 1px solid var(--warning); border-radius: 8px; padding: 8px 10px; margin: 0 0 12px; }
.aviso-mes-cerrado svg { width: 16px; height: 16px; flex-shrink: 0; margin-top: 1px; }
/* «Solo lo suyo»: qué se está viendo. */
.aviso-solo-lo-suyo { font-size: 12.5px; color: var(--text-muted); background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 8px 12px; margin: 0 0 12px; }
.sin-acceso { min-height: calc(100dvh - var(--alto-barra-superior)); display: flex; align-items: center; justify-content: center; padding: 24px 16px; }
.sin-acceso-aviso { background: var(--card); border-radius: var(--radius-panel); box-shadow: var(--sombra-panel); padding: 40px 44px; text-align: center; max-width: 640px; width: 100%; }
.sin-acceso-icono { width: 72px; height: 72px; border-radius: 50%; background: var(--slate-soft); color: var(--slate); display: inline-flex; align-items: center; justify-content: center; margin-bottom: 18px; }
.sin-acceso-icono svg { width: 34px; height: 34px; }
.sin-acceso-aviso h2 { font-family: var(--font-display); font-size: 26px; font-weight: 800; margin: 0 0 10px; line-height: 1.2; }
.sin-acceso-aviso p { color: var(--text-muted); font-size: 14px; margin: 0 0 22px; line-height: 1.55; }
.sin-acceso-herramientas { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 22px; text-align: left; }
.sin-acceso-herramienta { border: 2px solid var(--border); border-radius: 14px; padding: 14px; display: flex; flex-direction: column; gap: 6px; text-decoration: none; color: inherit; }
.sin-acceso-herramienta svg { width: 22px; height: 22px; flex: none; }
.sin-acceso-herramienta .textos { display: flex; flex-direction: column; }
.sin-acceso-herramienta strong { font-family: var(--font-display); font-size: 14px; }
.sin-acceso-herramienta small { font-size: 12px; }
.sin-acceso-herramienta.activa { border-color: var(--mint); background: var(--mint-soft); color: var(--mint-profundo); }
.sin-acceso-herramienta.activa small { font-weight: 600; }
@media (hover: hover) { .sin-acceso-herramienta.activa:hover { background: var(--card); } }
.sin-acceso-herramienta.apagada { color: var(--text-suave); background: var(--bg); }
@media (max-width: 640px) {
  .sin-acceso-aviso { padding: 28px 20px; }
  .sin-acceso-aviso h2 { font-size: 22px; }
  .sin-acceso-herramientas { grid-template-columns: 1fr; gap: 8px; }
  .sin-acceso-herramienta { flex-direction: row; align-items: center; gap: 12px; padding: 10px 12px; }
}

/* Supervisión (js/supervision.js): borrados y cambios importantes. */
.filtros-supervision { align-items: center; margin-bottom: 14px; }
.boton-descargar-supervision { margin-left: auto; }
.kpi-value.valor-borrados { color: var(--danger-text); }
.kpi-value.valor-cambios { color: var(--warning-text); }
.aviso-supervision { margin: 0 0 10px; }
.aviso-supervision:empty { display: none; }
.tarjeta-supervision { padding: 0; overflow-x: auto; }
.tabla-supervision td { vertical-align: top; }
.tabla-supervision .celda-cuando { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--text-muted); }
.tabla-supervision .celda-cambios s.valor-antes { color: var(--text-muted); }
.tabla-supervision .celda-acciones-supervision { white-space: nowrap; text-align: right; }
.tabla-supervision .btn-recuperar { border-color: var(--mint); color: var(--mint-profundo); margin-left: 6px; }
.quien-sub-supervision { font-size: 11.5px; }
.detalle-supervision { margin-top: 10px; background: var(--bg); border-radius: 10px; padding: 8px 10px; }
.tabla-detalle-supervision { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.tabla-detalle-supervision th { text-align: left; font-weight: 600; color: var(--text-muted); padding: 4px 8px; }
.tabla-detalle-supervision td { padding: 4px 8px; }
.tabla-detalle-supervision tr.campo-cambiado td { background: var(--warning-soft); }

/* Importar producción (punto 45): los meses cuya cifra se sustituye. */
.data-table tr.fila-sustituye td { background: var(--warning-soft); }

/* Rótulo encima de un grupo de tarjetas, para decir de qué periodo son
   cuando no es el del filtro (p. ej. «Datos del año completo 2025»). */
.subtitulo-tarjetas { font-size: 12px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; margin: 0 0 8px; }

/* Explicación pequeña debajo de una barra de objetivo (p. ej. «sin
   presupuesto disponible»). Vacía no ocupa sitio. */
.goal-bar-nota { font-size: 11.5px; color: var(--text-muted); text-align: center; margin-top: 6px; }
.goal-bar-nota:empty { display: none; }

/* Campo obligatorio que se ha quedado sin rellenar (añadir compras). */
.field input.campo-obligatorio-falta, .field select.campo-obligatorio-falta { border-color: var(--danger); background: var(--danger-soft); }

/* Línea pequeña debajo de la cifra de una ficha KPI: dice qué se está
   contando (p. ej. si el presupuesto lleva arrastre o no). */
.kpi-nota { font-size: 11.5px; line-height: 1.35; color: var(--text-muted); margin-top: 4px; }

/* Clínica con la copia automática en pausa: se queda en la tabla, más apagada. */
.tabla-copias tr.fila-copia-pausada > td { color: var(--text-muted); }
.fila-historial-copias > td { background: var(--bg); padding: 10px 14px; }
.tabla-historial-copias { margin: 0; }
.tabla-historial-copias td { padding: 6px 10px; }

/* ---------- Ventana de progreso de la copia de seguridad ---------- */
/* Barra + cifra + una línea por tabla. Antes era una línea de texto
   bajo el botón y en las tablas grandes parecía que se había colgado. */
.progreso-copia {
  height: 8px;
  border-radius: 999px;
  background: var(--slate-soft);
  overflow: hidden;
  margin-top: 14px;
}
.progreso-copia-relleno {
  height: 100%;
  width: 0;
  background: var(--mint);
  border-radius: 999px;
  transition: width 0.25s ease;
}
.progreso-copia.con-fallos .progreso-copia-relleno { background: var(--danger); }
.progreso-copia-cifras {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 8px;
  font-size: 13px;
  color: var(--text-muted);
}
.progreso-copia-cifras #progresoCopiaPorcentaje {
  font-family: var(--font-numeros);
  font-variant-numeric: tabular-nums;
  color: var(--text);
  font-weight: 600;
}
.progreso-copia-registro {
  list-style: none;
  margin: 14px 0 0;
  padding: 10px 12px;
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  font-size: 12.5px;
}
.progreso-copia-registro li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 3px 0;
  color: var(--text-muted);
}
.progreso-copia-registro li .icono-linea { width: 14px; height: 14px; flex: 0 0 auto; margin-top: 2px; }
.progreso-copia-registro li.hecha .icono-linea { color: var(--mint); }
.progreso-copia-registro li.leyendo { color: var(--text); }
.progreso-copia-registro li.fallo { color: var(--danger-text); }
.progreso-copia-registro li.final {
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  font-weight: 600;
}
.progreso-copia-registro li.final.hecha { color: var(--text); }

/* ---------- Mi cuenta ---------- */
/* La ventana tenía un título por cada ajuste ("Apariencia",
   "Privacidad") y un párrafo de explicación debajo de cada uno. Ahora
   los ajustes personales van juntos bajo un solo título y sin
   explicación: el propio texto del interruptor ya dice lo que hace. */
.micuenta-titulo {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: 8px;
}
.micuenta-opcion {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.micuenta-opcion:last-child { margin-bottom: 0; }
/* .micuenta-separador se retiró en sept. 2026 junto con el título
   "Contraseña": la ventana va ahora de corrido y los tres botones
   abajo del todo. La regla se borra con él para no dejar CSS que no
   pinta nada. */

/* ---------- Resumen de tipos de visita (Dashboard) ---------- */
.resumen-clasificacion { margin-bottom: 16px; }
.resumen-clasificacion:last-child { margin-bottom: 0; }
.resumen-clasificacion .cabecera {
  display: flex; justify-content: space-between; align-items: baseline;
  font-family: var(--font-display); font-weight: 700; font-size: 13.5px; color: var(--ink);
  padding-bottom: 6px; border-bottom: 2px solid var(--mint-soft); margin-bottom: 6px;
}
.resumen-clasificacion .subfila {
  display: flex; justify-content: space-between;
  font-size: 12.5px; color: var(--text-muted);
  padding: 5px 0 5px 14px;
  border-bottom: 1px dashed var(--border);
}
.resumen-clasificacion .subfila:last-child { border-bottom: none; }
.resumen-clasificacion .subfila .num { font-family: var(--font-numeros); font-variant-numeric: tabular-nums; color: var(--text); }

.config-tab.active, .config-compras-tab.active, .admin-tab.active, .trab-tab.active, .liquidacion-tab.active, .config-finanzas-tab.active, .rentabilidad-tab.active, .pres-elim-tab.active { background: var(--mint-soft); border-color: var(--mint); color: var(--mint-dark); font-weight: 600; }
.avanzada-tab.active { background: var(--mint-soft); border-color: var(--mint); color: var(--mint-dark); font-weight: 600; }
.config-panel { display: none; }
.config-panel.active { display: block; }

.import-log {
  margin-top: 14px;
  font-family: var(--font-mono);
  font-size: 12px;
  background: var(--ink);
  color: #cfe8dd;
  border-radius: 10px;
  padding: 14px;
  max-height: 260px;
  overflow-y: auto;
  white-space: pre-wrap;
  display: none;
}
.import-log.visible { display: block; }
.import-log .linea-error { color: #ff9b9e; }
.import-log .linea-ok { color: #7de3b7; }

.card:has(table) { overflow-x: auto; }

@media (max-width: 880px) {
  /* LA CAUSA DEL FALLO DE RESPONSIVE EN MÓVIL (sept. 2026).
     Arriba hay ".app-shell.sidebar-colapsada { grid-template-columns:
     60px 1fr }" para el carril de iconos del menú contraído. Esa regla
     tiene DOS clases (especificidad 0,2,0) y esta una sola (0,1,0), y la
     especificidad gana a la media query: en móvil el shell seguía con dos
     columnas. Como aquí el menú pasa a position:fixed, sale de la
     rejilla, la primera columna de 60px se queda vacía y .main caía
     dentro de ELLA — 60px de ancho para todo el contenido. El topbar
     quedaba en 32px y las tarjetas cortadas.
     Hay que nombrar también el estado colapsado para igualar
     especificidad; con la media query sola no basta. */
  .app-shell,
  .app-shell.sidebar-colapsada { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; width: 240px; z-index: 300;
    /* En móvil el menú es un cajón a pantalla completa (no un panel
       flotante): se anula el margen/radio/alto del cristal de
       escritorio para que ocupe todo el lateral, de borde a borde. */
    margin: 0;
    border-radius: 0;
    height: 100%;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
  }
  .sidebar.abierto { transform: translateX(0); }
  .menu-toggle { display: inline-flex !important; }

  /* El menú hereda del escritorio un fondo de cristal translúcido con
     desenfoque. Como panel flotante sobre el contenido queda bien; como
     cajón a pantalla completa en un móvil, deja ver el dashboard por
     detrás y se lee fatal. Aquí va opaco, y la capa de debajo más
     oscura para que el cajón destaque. */
  .sidebar {
    background: var(--card);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-right: 1px solid var(--border);
  }
  #sidebarOverlay.visible { background: rgba(16, 20, 24, 0.6); }
  #sidebarOverlay.visible {
    display: block; position: fixed; inset: 0; background: rgba(20,24,28,0.5); z-index: 250;
  }

  /* .kpi-grid a secas se usa para dos cosas distintas con la misma
     clase: filas de tarjetas numéricas (KPIs) Y también parejas
     gráfica+tabla con un ancho de columnas puesto a mano (p.ej.
     "gráfica al 50% + tabla al 50%", ver más abajo en el dashboard).
     Una gráfica o una tabla no caben partidas en dos columnas
     estrechas en un móvil, así que ESTAS se siguen forzando siempre
     a una sola columna. */
  .kpi-grid { grid-template-columns: 1fr !important; }

  /* Las tarjetas numéricas sí llevan la clase extra .kpi-grid-numeros
     (además de .kpi-grid) precisamente para distinguirlas de las
     parejas gráfica+tabla de arriba. Con auto-fit + minmax cada una
     decide en vivo cuántas columnas caben según su ancho real y el
     nº de tarjetas que tenga, en vez de forzar siempre una sola
     columna como antes: 1 columna en los móviles más estrechos, 2 en
     la mayoría, y alguna más en pantallas ya un poco más anchas
     (phablets, tablets pequeñas en el rango de esta media query). */
  .kpi-grid-numeros { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)) !important; }

  .page { padding: 16px 14px 32px; }
  .page-header { flex-wrap: wrap; gap: 10px; }
  .topbar { margin: 12px 14px; padding: 10px 14px; }
  /* Tarjeta de barras de objetivo (Cobrado/Previsión) del Dashboard:
     lleva un margen de 24px por lado puesto a mano en el HTML (no
     pensado para móvil), igual al que ya se recorta aquí para
     .topbar. Solo aparece cuando la clínica tiene un objetivo activo. */
  #barrasObjetivoDashboard { margin: 12px 14px !important; }
  .topbar select, .topbar input[type="text"], .topbar input[type="date"], .filter-control {
    min-width: 0;
    flex: 1 1 140px;
  }
  #selectorClinicaEnvoltorio { margin-right: 0; flex: 1 1 100%; }
  .topbar-acciones { width: 100%; margin-left: 0; justify-content: flex-end; }
  /* Mismo comportamiento responsive que .filter-control, pero para
     el selector avanzado (piloto Dashboard) y el de fecha, que no
     usan esa clase. */
  .selector-avanzado { flex: 1 1 140px; }
  /* Excepto cuando el filtro ha entrado dentro del panel "+ Filtros"
     (ver js/topbarFiltrosAuto.js, que ahora también agrupa ahí los
     filtros en móvil): el panel es una columna flex, y ese
     "flex-basis: 140px" heredado de la regla de arriba se aplicaría
     al alto (eje principal de una columna), no al ancho, dejando
     cada filtro con ~140px de alto vacío dentro del desplegable. Ahí
     dentro se quiere el alto natural del filtro; el ancho ya lo fija
     ".panel-mas-filtros .selector-avanzado-caja" más abajo. */
  .panel-mas-filtros .selector-avanzado { flex: none; }
  .selector-avanzado-caja { width: 100%; }
  .selector-avanzado-boton { min-width: 0; flex: 1; }
  .selector-avanzado-panel, .panel-mas-filtros { max-width: calc(100vw - 40px); }
  /* El botón "+ Filtros" (mismo componente que en portátil, ver
     .boton-mas-filtros más arriba) se coloca junto a la píldora de
     fecha con el mismo criterio que ella. */
  .selector-mas-filtros { flex: 1 1 140px; }
  .boton-mas-filtros { width: 100%; justify-content: center; }
  .selector-fecha { flex: 1 1 140px; }
  .selector-fecha-boton { min-width: 0; max-width: none; width: 100%; }

  /* El calendario de rango en escritorio enseña los dos meses en
     columnas, uno al lado del otro; en móvil no caben, así que se
     apilan uno encima del otro y el panel pasa a ocupar casi todo el
     ancho de la pantalla. Los presets, si los hay, pasan de columna a
     una fila horizontal con scroll por encima del calendario. */
  .cal-rango-flotante, .cal-rango-flotante.con-presets { width: calc(100vw - 24px); padding: 14px; }
  .cal-rango-cuerpo { flex-direction: column; gap: 12px; }
  /* Las declaraciones que también fija la regla base de más abajo
     (.cal-rango-presets, pensada para escritorio) necesitan
     !important aquí — mismo motivo que .kpi-grid/.menu-toggle un
     poco más arriba en este mismo bloque: por orden de aparición en
     el fichero, si no, ganaría siempre la regla base. */
  .cal-rango-presets {
    flex-direction: row !important; flex: 0 0 auto !important;
    border-right: none !important; padding-right: 0 !important;
    border-bottom: 1px solid var(--border); padding-bottom: 10px; overflow-x: auto;
  }
  .cal-rango-preset { white-space: nowrap; }
  .cal-rango-meses { flex-direction: column; gap: 16px; }

  .modal-overlay { padding: 10px; align-items: flex-start; }
  .modal-box { max-width: 100%; max-height: 92vh; margin-top: 16px; }
  .modal-box .modal-scroll { padding: 16px; }

  table.data-table { font-size: 12px; }
  .multiselect-panel { max-width: calc(100vw - 40px); }

  /* Las tablas "resumen anual" (Cobros por forma de pago, Objetivos,
     Financiaciones...) fuerzan table-layout:fixed + width:100% para
     que en escritorio los 12 meses + total queden bien repartidos.
     En pantalla estrecha eso es justo el problema: la obliga a
     encogerse dentro del ancho del móvil en vez de desbordar y dejar
     que el contenedor haga scroll horizontal (que es lo que de verdad
     se necesita aquí). En móvil se anula ese ancho fijo y se le pone
     un mínimo, así sí desborda y el contenedor scrollea en condiciones. */
  .tabla-resumen-anual { table-layout: auto; width: auto; min-width: 640px; }

  /* ---------- El fallo del responsive en móvil (sept. 2026) ----------
     "sidebar-colapsada" es la clase del menú CONTRAÍDO DE ESCRITORIO:
     desplaza el contenido 64px para dejar sitio al carril de iconos.
     En móvil no hay carril —el menú es un cajón que tapa la pantalla—
     pero la clase se aplicaba igual, porque perfiles.menu_colapsado
     viene a true por defecto y el botón de hamburguesa la conmuta.
     Sobre 390px de pantalla, esos 64px dejaban el topbar en 30px de
     ancho (con el botón "Aplicar filtros" saliéndose por su lado) y
     las tarjetas cortadas por la derecha. Aquí se devuelven los
     márgenes normales de móvil, en los dos estados del menú. */
  .app-shell.sidebar-colapsada .page { padding-left: 14px; }
  .app-shell.sidebar-colapsada .topbar,
  .app-shell.sidebar-colapsada #barrasObjetivoDashboard,
  .app-shell.sidebar-colapsada #barraPresupuestoTrabajador,
  .app-shell.sidebar-colapsada #barraPresupuestoAdmin {
    margin-left: 14px !important;
  }

  /* El botón "Cambiar de herramienta" reserva 230px en escritorio para
     tener el mismo ancho que el <select> nativo al que sustituyó. En un
     móvil de 390px eso deja 128px para el nombre de la clínica, que es
     lo que lo obligaba a recortarse casi entero. Aquí usa solo el ancho
     de su texto.
     La flecha de estos botones es un ::after con float:right, que con
     230px de sobra se coloca a la derecha del texto; sin ese ancho de
     más se caía a una segunda línea y estiraba la barra. Solo para este
     botón se cambia el float por una fila flex, que es lo mismo pero sin
     depender de que sobre sitio. No se toca .multiselect en general,
     que lo usan muchos otros botones. */
  .herramienta-selector > .btn-herramienta {
    min-width: 0;
    display: inline-flex;
    align-items: center;
  }
  .herramienta-selector > .btn-herramienta::after { float: none; margin-top: 0; }
  /* En pantalla estrecha el icono de cuadrícula le quitaba sitio al
     nombre de la clínica, que es lo que importa leer: fuera. */
  .herramienta-selector > .btn-herramienta::before { display: none; }

  /* La hamburguesa de la barra: aquí sí. */
  .menu-toggle-barra { display: inline-flex; }

  /* ---------- Tablas traspuestas: apurar el ancho ----------
     Medido con cifras reales (objetivos de 40.000 € y un total anual de
     483.000,00 €): la tabla pedía 367px y en la tarjeta solo hay 320, así
     que quedaba scroll lateral justo a la altura del símbolo de %. El
     sobrante era casi todo relleno de celda: 14px por lado y celda, o
     sea 112px solo de aire en una tabla de cuatro columnas. Aquí se
     aprieta el relleno y la pastilla del porcentaje; el tamaño de letra
     no se toca, que son cifras y hay que leerlas. */
  .tabla-ancha-transpuesta.data-table th,
  .tabla-ancha-transpuesta.data-table td {
    padding-left: 7px;
    padding-right: 7px;
  }
  .tabla-ancha-transpuesta td:first-child,
  .tabla-ancha-transpuesta th:first-child {
    padding-left: 2px;
  }
  .tabla-ancha-transpuesta td:last-child,
  .tabla-ancha-transpuesta th:last-child {
    padding-right: 2px;
  }
  .tabla-ancha-transpuesta .badge-pct { padding-left: 6px; padding-right: 6px; }

  /* ---------- Tarjetas de resumen (Cobros, Devoluciones, Presupuestos)
     Van en un contenedor flex con wrap, y la divisoria se dibuja con un
     ::after en todas menos en la última de la lista. Al partirse en dos
     columnas, la que cierra cada fila NO es la última de la lista, así
     que pintaba una raya pegada al borde derecho sin nada detrás.
     Con una rejilla de dos columnas sí se sabe cuál cierra fila: la
     par. Y si el total es impar, la última ocupa el ancho entero en vez
     de dejar un hueco al lado. */
  #cobrosEstadisticas,
  #devolucionesEstadisticas,
  #presEstadisticas {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
  }
  .mini-stat { min-width: 0; }
  .mini-stat:nth-child(2n)::after { display: none; }
  .mini-stat:last-child:nth-child(odd) { grid-column: 1 / -1; }

  /* En Presupuestos la cifra de cabecera es "Presupuestado": va sola en
     la primera fila y el resto en parejas. Como la primera ocupa las dos
     columnas, la que cierra fila pasa a ser la IMPAR, no la par, así que
     hay que invertir de dónde se quita la divisoria. */
  #presEstadisticas .mini-stat:first-child { grid-column: 1 / -1; }
  #presEstadisticas .mini-stat:last-child:nth-child(odd) { grid-column: auto; }
  #presEstadisticas .mini-stat:nth-child(2n)::after { display: block; }
  #presEstadisticas .mini-stat:nth-child(odd)::after { display: none; }

  /* Las pastillas de estado, centradas: su contenedor iba alineado a la
     derecha junto a la etiqueta "Filtrar:", que en móvil ya no está. */
  #presFiltroEstadoBotones { flex: 1 1 100%; justify-content: center; }

  /* ---------- Botones de acción de las tablas ----------
     "Actualizar", "Exportar CSV" e "Imprimir / PDF" con su texto no
     caben en una pantalla de móvil: se salían de la tarjeta y el último
     quedaba cortado por la mitad. Aquí se quedan en su icono, que en los
     tres casos es universal (recargar, descargar, impresora). El nombre
     sigue estando en el atributo title y para los lectores de pantalla.
     Preferí esto a un menú "Exportar" desplegable: resuelve lo mismo sin
     añadir un componente nuevo. Si prefieres el menú, se cambia. */
  .btn-con-icono .texto-boton-icono { display: none; }
  .btn-con-icono {
    width: 48px;
    padding-left: 0;
    padding-right: 0;
    justify-content: center;
    gap: 0;
  }
  .acciones-tabla { flex-wrap: wrap; justify-content: flex-end; }
  /* La fila de filtros de cada tabla (buscador, selectores y los botones
     de Actualizar / Exportar) y, dentro de ella, el grupo de la izquierda.

     Un contenedor flex metido dentro de otro trae de fábrica
     "min-width: auto", que significa "no encojas por debajo de lo que
     ocupa tu contenido". En Presupuestos ese mínimo salía de 348 px para
     un hueco de 320: la tarjeta se quedaba con 8 px de scroll lateral
     (medido en sept. 2026 en un móvil de 390 px, y era de antes de las
     tablas plegables). Con min-width:0 el grupo puede encogerse y sus
     campos hacen lo que ya sabían hacer: pasar a la línea de abajo.
     En las otras tres filas no cambia nada porque ya cabían. */
  .fila-filtros-tabla, .grupo-filtros { min-width: 0; max-width: 100%; }
  /* Las celdas que se van al desplegable. Fuera de esta media query no
     existe la clase, así que en escritorio la tabla está entera. */
  .celda-oculta-movil { display: none; }
  /* Con tres columnas ya no hace falta apretar la letra. */
  .tabla-plegada th, .tabla-plegada td { padding-left: 8px; padding-right: 8px; }

  /* La palabra "Mostrar" quedaba pegada al borde de la tarjeta, sin
     respiro. Sobra: el desplegable de al lado ya dice "35 filas". */
  .etiqueta-mostrar { display: none; }

  /* La barra inferior, y el hueco para que no tape el final del
     contenido. La clase la pone barraInferior.js solo cuando de verdad
     ha pintado la barra: si el usuario ve un único apartado no hay barra
     y tampoco debe sobrar ese hueco. */
  .barra-inferior { display: grid; }
  body.con-barra-inferior .page {
    padding-bottom: calc(76px + env(safe-area-inset-bottom, 0px));
  }

  /* El botón de herramienta con los 48px de zona táctil quedaba enorme
     para lo poco que se usa, y apretado contra los bordes de la barra.
     Se le baja el alto y se le da aire a la barra. */
  .barra-herramientas { min-height: 56px; }
  .herramienta-selector > .btn-herramienta {
    min-height: 38px;
    padding: 6px 12px;
    font-size: 11.5px;
  }

  /* ---------- Filtros del topbar, en tres filas simétricas ----------
        Centro
        + Filtros   |   Fecha
        Borrar      |   Aplicar
     Antes iban en fila con "flex: 1 1 140px", así que se repartían de
     forma desigual según el texto de cada uno y los dos botones se
     quedaban pegados a la derecha. */
  .topbar { gap: 10px; }
  #selectorClinicaEnvoltorio { flex: 1 1 100%; margin-right: 0; }
  #selectorMasFiltrosTopbar,
  #filtroFechaRapidoEnvoltorio {
    flex: 1 1 calc(50% - 5px);
    min-width: 0;
  }
  .topbar-acciones {
    width: 100%;
    margin-left: 0;
    gap: 10px;
  }
  .topbar-acciones > * { flex: 1 1 0; min-width: 0; }

  /* ---------- Tablas anchas dentro de tarjetas ----------
     51 tarjetas con tabla no llevan overflow-x propio, así que una tabla
     ancha se cortaba por la derecha (p. ej. "Primeras visitas, revisiones
     y urgencias", 1156px). Añadirlo a mano en las 51 es olvidarse de la
     52: la tabla se desplaza dentro de sí misma y así vale para todas.
     Se excluyen las tres que gestiona js/tablaAdaptativa.js: esas deciden
     si trasponerse midiendo si su tarjeta desborda, y si se desplazaran
     solas esa medida nunca daría positivo. */
  .card table.data-table:not(.tabla-ancha-normal):not(.tabla-ancha-transpuesta):not(.tabla-resumen-anual):not(.tabla-plegada) {
    display: block;
    max-width: 100%;
    overflow-x: auto;
  }
  /* Atada al ancho de su tarjeta, y con reparto FIJO de columnas: con el
     reparto automático una celda nunca baja de lo que mide su contenido,
     así que un nombre de paciente largo se comía el importe y el botón
     por mucho "text-overflow" que se le pusiera. Con el reparto fijo, las
     columnas de fecha e importe piden lo suyo, el botón lo suyo, y la
     columna de texto libre se queda con lo que sobra y recorta. */
  /* Dos clases porque hay que ganarle a ".tabla-fija-presupuestos", que
     le pone a esa tabla un min-width de 980px pensado para escritorio.
     Con ese mínimo puesto, ningún reparto de columnas sirve de nada. */
  .tabla-plegada.data-table { width: 100%; max-width: 100%; min-width: 0; table-layout: fixed; }
  .tabla-plegada th, .tabla-plegada td { width: 86px; }
  .tabla-plegada .celda-elastica { width: auto; }
  .tabla-plegada .col-desplegar { width: 44px; }

  /* ---------- Uso con el dedo ----------
     16px en los campos NO es estética: por debajo de eso, Safari de iOS
     hace zoom automático al enfocar un input y deja la página
     descuadrada. Es la causa más habitual de que una web se note "web"
     en un iPhone. Solo aquí; en escritorio el topbar sigue compacto. */
  input, select, textarea, .filter-control {
    font-size: 16px;
  }

  /* 48px de alto mínimo en todo lo que se toca (la recomendación de
     Android es 48dp; la de Apple, 44pt). Los checkbox se quedan como
     están: agrandarlos descuadraría las tablas de permisos. */
  button, .btn, select, .filter-control,
  input[type="text"], input[type="number"], input[type="email"],
  input[type="password"], input[type="date"], textarea {
    min-height: 48px;
  }
  .nav-item, summary {
    min-height: 48px;
  }
  /* ---------- Botones con tamaño propio ----------
     El "min-height: 48px" de arriba es para los botones de formulario,
     pero alcanza a TODO <button>. Los que tienen un ancho fijo se
     convertían en óvalos: los redondos del pie del menú pasaban de
     26x26 a 26x48 (lo cazó Víctor), y con ellos las flechas del
     calendario y algún otro. Aquí se les devuelve su forma, y de paso
     se agrandan un poco, que para el dedo 26px es poco. */
  .btn-blur-privacidad {
    width: 44px; height: 44px; min-height: 44px;
  }
  .sidebar-footer.compacto .btn-mi-cuenta,
  .sidebar-footer.compacto .btn-instalar-app,
  .sidebar-footer.compacto .logout-btn {
    width: 40px; height: 40px; min-height: 40px;
  }
  .sidebar-footer.compacto .btn-mi-cuenta svg,
  .sidebar-footer.compacto .btn-instalar-app svg,
  .sidebar-footer.compacto .logout-btn svg {
    width: 16px; height: 16px;
  }
  .cal-flotante-header button { width: 36px; height: 36px; min-height: 36px; }
  .menu-toggle { width: 40px; height: 40px; min-height: 40px; }
  .btn-quitar-fila { min-height: 38px; }

  /* ---------- Filtros de estado de Presupuestos ----------
     "Filtrar:" quedaba solo, alineado a la derecha y encima de las
     pastillas. En móvil sobra: se ve de sobra que son filtros. Y las
     pastillas se aprietan para que las tres quepan en una línea. */
  .etiqueta-filtrar { display: none; }
  .pill-filtro { font-size: 11.5px; padding: 7px 11px; }

  /* ---------- Notch y barra de gestos ----------
     Sin esto, la barra negra se mete debajo del reloj del móvil y el pie
     del menú debajo de la barra de gestos. Los valores son 0 en un
     navegador de escritorio, así que no hacen daño en ningún sitio. */
  .barra-herramientas {
    padding-top: env(safe-area-inset-top);
    padding-left: max(16px, env(safe-area-inset-left));
    padding-right: max(16px, env(safe-area-inset-right));
  }
  .sidebar {
    padding-top: max(16px, env(safe-area-inset-top));
    padding-bottom: max(16px, env(safe-area-inset-bottom));
  }
  .page {
    padding-bottom: max(32px, calc(32px + env(safe-area-inset-bottom)));
  }
}

/* Solo el icono, sin caja alrededor (oct. 2026, a petición de Víctor).
   Se conserva el tamaño de 34px como zona de clic: el dibujo es de
   18px y pulsar justo encima sería incómodo. */
.menu-toggle {
  display: inline-flex;
  align-items: center; justify-content: center;
  width: 34px; height: 34px;
  border-radius: 8px; border: none;
  background: transparent; color: var(--ink);
  font-size: 16px; cursor: pointer; padding: 0;
}
@media (hover: hover) {
.menu-toggle:hover { color: var(--mint-dark); }}
.menu-toggle:focus-visible { outline: 2px solid var(--mint); outline-offset: 2px; }
#sidebarOverlay { display: none; }

/* ---------- Calendario propio (sustituye a <input type="date">) ---------- */
input.campo-fecha-custom { cursor: pointer; }
.cal-flotante {
  display: none; position: fixed; z-index: 1100; width: 280px;
  background: #fff; border: 1px solid var(--border); border-radius: 16px;
  box-shadow: 0 12px 30px rgba(20,24,28,0.16); padding: 14px;
  font-family: var(--font-body);
}
.cal-flotante.visible { display: block; }
.cal-flotante-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.cal-flotante-header button {
  border: none; background: var(--bg); width: 28px; height: 28px; border-radius: 50%;
  cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--ink); font-size: 15px;
}
@media (hover: hover) {
.cal-flotante-header button:hover { background: var(--mint-soft); color: var(--mint-dark); }}

.cal-flotante-mes { font-family: 'Poppins', sans-serif; font-weight: 600; font-size: 13.5px; text-transform: capitalize; }
.cal-flotante-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; text-align: center; }
.cal-flotante-diasemana { font-size: 10.5px; font-weight: 700; color: var(--text-muted); padding: 4px 0; }
.cal-flotante-dia { font-size: 12.5px; padding: 7px 0; border-radius: 10px; cursor: pointer; color: var(--text); }
@media (hover: hover) {
.cal-flotante-dia:hover { background: var(--mint-soft); }}

.cal-flotante-dia.fuera-mes { color: var(--border); cursor: default; }
@media (hover: hover) {
.cal-flotante-dia.fuera-mes:hover { background: transparent; }}

.cal-flotante-dia.hoy { font-weight: 700; color: var(--mint-dark); }
.cal-flotante-dia.seleccionado { background: var(--mint); color: #fff; font-weight: 600; }
.cal-flotante-footer { margin-top: 8px; text-align: center; border-top: 1px solid var(--border); padding-top: 8px; }
.cal-flotante-footer button {
  border: none; background: transparent; color: var(--mint-dark); font-weight: 600; font-size: 12px;
  cursor: pointer; font-family: var(--font-body);
}
@media (hover: hover) {
.cal-flotante-footer button:hover { text-decoration: underline; }}


/* ---------- Calendario de rango (doble mes) + presets: panel único
   del filtro de fechas. Ver js/filtroFecha.js y js/calendarioRango.js. ---------- */
.cal-rango-flotante {
  display: none; position: fixed; z-index: 1100; width: 580px; max-width: calc(100vw - 24px);
  background: #fff; border: 1px solid var(--border); border-radius: 16px;
  box-shadow: 0 12px 30px rgba(20,24,28,0.16); padding: 16px;
  font-family: var(--font-body);
}
.cal-rango-flotante.visible { display: block; }
/* Con presets el panel se ensancha para que quepa la columna de la
   izquierda sin apretar el calendario de doble mes. */
.cal-rango-flotante.con-presets { width: 740px; }
.cal-rango-cuerpo { display: flex; gap: 18px; }
.cal-rango-calendario { flex: 1; min-width: 0; }
.cal-rango-presets {
  flex: 0 0 138px; display: flex; flex-direction: column; gap: 2px;
  border-right: 1px solid var(--border); padding-right: 16px; overflow-y: auto;
}
.cal-rango-preset {
  text-align: left; border: none; background: none; border-radius: 8px;
  padding: 8px 10px; font-family: var(--font-body); font-size: 13px;
  color: var(--text); cursor: pointer;
}
@media (hover: hover) {
.cal-rango-preset:hover { background: var(--mint-soft); }}

.cal-rango-preset.activo { background: var(--mint-soft); color: var(--mint-dark); font-weight: 600; }
.cal-rango-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 12px; }
.cal-rango-nav {
  flex: 0 0 auto; border: none; background: var(--bg); width: 28px; height: 28px; border-radius: 50%;
  cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--ink); font-size: 15px;
}
@media (hover: hover) {
.cal-rango-nav:hover { background: var(--mint-soft); color: var(--mint-dark); }}

.cal-rango-resumen {
  flex: 1; text-align: center; font-family: 'Poppins', sans-serif; font-weight: 600;
  font-size: 12.5px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-rango-meses { display: flex; gap: 22px; }
.cal-rango-mes { flex: 1 1 0; min-width: 0; }
.cal-rango-mes-titulo {
  font-family: 'Poppins', sans-serif; font-weight: 600; font-size: 12.5px;
  text-transform: capitalize; text-align: center; color: var(--ink); margin-bottom: 8px;
}
.cal-rango-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; text-align: center; }
.cal-rango-diasemana { font-size: 10.5px; font-weight: 700; color: var(--text-muted); padding: 4px 0; }
.cal-rango-dia { font-size: 12.5px; padding: 7px 0; border-radius: 10px; cursor: pointer; color: var(--text); }
@media (hover: hover) {
.cal-rango-dia:hover { background: var(--mint-soft); }}

.cal-rango-dia.fuera-mes { color: var(--border); cursor: default; }
@media (hover: hover) {
.cal-rango-dia.fuera-mes:hover { background: transparent; }}

.cal-rango-dia.hoy { font-weight: 700; color: var(--mint-dark); }
.cal-rango-dia.en-rango { background: var(--mint-soft); color: var(--mint-dark); }
.cal-rango-dia.en-rango-preview { background: var(--mint-soft); color: var(--mint-dark); opacity: 0.65; }
.cal-rango-dia.inicio, .cal-rango-dia.fin { background: var(--mint); color: #fff; font-weight: 600; }
.cal-rango-footer {
  margin-top: 10px; border-top: 1px solid var(--border); padding-top: 8px;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.cal-rango-footer .cal-rango-limpiar {
  border: none; background: transparent; color: var(--text-muted); font-weight: 600; font-size: 12px;
  cursor: pointer; font-family: var(--font-body); padding: 0;
}
@media (hover: hover) {
.cal-rango-footer .cal-rango-limpiar:hover { color: var(--danger-text); text-decoration: underline; }}
/* "Aplicar" solo se puede pulsar con el rango completo (dos clics). */
.cal-rango-footer .cal-rango-aplicar { padding: 6px 16px; font-size: 12.5px; }
.cal-rango-footer .cal-rango-aplicar:disabled { opacity: 0.45; cursor: default; }
@media (hover: hover) {
.cal-rango-footer .cal-rango-aplicar:disabled:hover { background: var(--mint); }}


/* Aviso dentro de la ficha de un presupuesto que se borró en Nubimed.
   Ámbar cuando está fuera de la estadística, azul cuando alguien lo
   devolvió a mano (ver js/presupuestosEliminados.js). */
.aviso-eliminado {
  background: var(--warning-soft); color: var(--warning-text);
  border-radius: var(--radius); padding: 10px 12px; font-size: 12.5px;
  line-height: 1.45; margin: 0 0 14px;
}
.aviso-eliminado-devuelto { background: var(--info-soft); color: var(--info); }
