:root {
  --color-primary: #0f766e;
  --color-primary-dark: #0b5c56;
  --color-primary-light: #ccfbf1;
  --color-danger: #dc2626;
  --color-danger-light: #fee2e2;
  --color-success: #16a34a;
  --color-success-light: #dcfce7;
  --color-warning: #d97706;
  --color-warning-light: #fef3c7;
  /* Verde de la barra lateral. Las cabeceras de grupo lo invierten: fondo blanco y las letras de
     ESE mismo verde. Se probó antes con el verde oscurecido al 70% y quedaba casi negro; invertido
     los títulos destacan mucho más y separan los bloques de un vistazo.
     El texto se declara derivado de la variable, no repetido: cambiando el verde de la barra,
     cambian con él las letras de los títulos y no hay dos sitios que puedan quedar descuadrados. */
  --sidebar-verde: #0d3b37;
  --sidebar-titulo-fondo: #ffffff;

  --color-blue: #2563eb;
  --color-blue-light: #dbeafe;
  --color-purple: #7c3aed;
  --color-purple-light: #ede9fe;
  --color-rose: #db2777;
  --color-rose-light: #fce7f3;
  --color-slate: #475569;
  --color-slate-light: #f1f5f9;
  --color-bg: #f4f6f7;
  --color-surface: #ffffff;
  --color-border: #e2e8ec;
  --color-text: #1c2833;
  --color-text-muted: #64748b;
  --sidebar-w: 248px;
  --radius: 10px;
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.08);
  --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.1);
  font-size: 15px;
}

* { box-sizing: border-box; }

/* Los selectores de clase de abajo (.auth-shell, .app-shell) fijan `display` y ganan por
   origen de cascada a la regla [hidden] del navegador aunque tengan la misma especificidad
   — sin esto, ocultar con el atributo hidden no surte efecto y ambas vistas quedan visibles
   a la vez. */
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--color-bg);
  color: var(--color-text);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0 0 0.5em; font-weight: 650; }
p { margin: 0 0 0.75em; }
a { color: var(--color-primary); }

button { font-family: inherit; }

/* ---------- Layout de autenticación ---------- */

.auth-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(160deg, #0f766e 0%, #134e4a 100%);
  padding: 24px;
}

.auth-card {
  width: 100%;
  max-width: 420px;
  background: var(--color-surface);
  border-radius: 16px;
  box-shadow: var(--shadow-md);
  padding: 36px 32px;
}

.auth-card .brand-mark {
  display: block;
  width: 64px;
  height: 64px;
  object-fit: contain;
  margin-bottom: 16px;
}

.auth-card h2 { font-size: 1.35rem; }
.auth-card .subtitle { color: var(--color-text-muted); margin-bottom: 24px; font-size: 0.92rem; }

.auth-card .qr-box {
  display: flex;
  justify-content: center;
  padding: 16px;
  background: #f8fafc;
  border-radius: var(--radius);
  margin: 16px 0;
}
.auth-card .qr-box img { width: 180px; height: 180px; }

.auth-secret {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  background: #f1f5f9;
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 0.85rem;
  word-break: break-all;
  text-align: center;
}

/* ---------- App shell ---------- */

.app-shell { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sidebar-w);
  background: var(--sidebar-verde);
  color: #e6f4f2;
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  transition: margin-left 0.2s ease;
}

.sidebar-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px 18px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.sidebar-brand .brand-mark {
  width: 70px;
  height: 70px;
  object-fit: contain;
  flex-shrink: 0;
}

.sidebar-nav { flex: 1; overflow-y: auto; padding: 12px 10px; }
/* Las cabeceras de grupo ("Seguridad", "Configuraciones"...) van EN NEGATIVO: fondo blanco y las
   letras del verde de la barra. Antes eran texto suelto sobre el mismo fondo que sus enlaces, y con
   doce apartados dentro de Configuraciones costaba ver dónde acababa un grupo y empezaba el
   siguiente. Invertir el color separa los bloques de un vistazo, sin líneas ni más adornos.

   Los márgenes negativos hacen que la fila llegue de lado a lado: `.sidebar-nav` tiene 10px de
   padding lateral, así que sin esto el fondo quedaría flotando con un carril a los lados. */
.nav-group-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--sidebar-verde);
  font-weight: 700;
  padding: 10px 10px 10px 12px;
  margin: 10px -10px 4px;
  background: var(--sidebar-titulo-fondo);
}
.nav-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: 8px;
  color: #d8ece9;
  text-decoration: none;
  font-size: 0.92rem;
  cursor: pointer;
  margin-bottom: 2px;
}
.nav-link:hover { background: rgba(255, 255, 255, 0.08); }
.nav-link.active { background: var(--color-primary); color: white; font-weight: 600; }

/* Contador tipo notificación (estilo "mensajes sin leer" de WhatsApp) junto a un ítem del
   menú — a diferencia de .badge (pastilla de estado con forma de texto), esta es una burbuja
   pequeña y redonda, pensada para un número corto. */
.badge-nav-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--color-danger);
  color: white;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1;
}

/* Sidebar como árbol: "Seguridad"/"Configuraciones" son ramas plegables. */
.nav-group-arbol > summary.nav-group-label {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 4px;
}
.nav-group-arbol > summary::-webkit-details-marker { display: none; }

/* El texto empuja al tirador hasta el borde derecho. */
.nav-group-texto { flex: 1; }

/* Tirador para reordenar los títulos. Se ve tenue y se enciende al pasar por encima: tiene que
   descubrirse (si no, nadie sabría que se puede mover) sin competir con el nombre del grupo. */
.nav-group-agarre {
  cursor: grab;
  opacity: 0.4;
  font-size: 0.9rem;
  line-height: 1;
  padding: 0 2px;
  transition: opacity 0.15s ease;
}
.nav-group-label:hover .nav-group-agarre { opacity: 0.85; }
.nav-group-agarre:active { cursor: grabbing; }
.nav-group-arbol.arrastrando { opacity: 0.45; }

/* Mismo tirador, en cada apartado. Solo se ve al pasar el ratón por su fila: doce tiradores
   siempre visibles convertirían el menú en una lista de adornos. */
.nav-link-texto { flex: 1; display: flex; align-items: center; gap: 8px; min-width: 0; }
.nav-link-agarre {
  cursor: grab;
  opacity: 0;
  font-size: 0.85rem;
  line-height: 1;
  flex-shrink: 0;
  transition: opacity 0.15s ease;
}
.nav-link:hover .nav-link-agarre { opacity: 0.7; }
.nav-link-agarre:active { cursor: grabbing; }
.nav-link.arrastrando { opacity: 0.45; }
.nav-group-arbol > summary::before { content: '▸'; font-size: 0.65rem; }
.nav-group-arbol[open] > summary::before { content: '▾'; }

.sidebar-footer { padding: 14px; border-top: 1px solid rgba(255, 255, 255, 0.08); }

.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  height: 60px;
  background: #0d3b37;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  display: flex;
  align-items: center;
  padding: 0 20px;
  gap: 14px;
  flex-shrink: 0;
}
.sidebar-toggle { display: none; }
/* margin: 0 a propósito — es un <h1>, y el navegador le pone un margen por defecto (arriba y
   abajo) que descuadra el centrado vertical del `align-items: center` de .topbar. */
.page-title { font-size: 1.1rem; flex: 1; color: #fff; font-weight: 700; margin: 0; line-height: 1; }
.topbar-user { font-size: 0.85rem; color: rgba(255, 255, 255, 0.75); display: flex; align-items: center; gap: 10px; }
.topbar-user-texto { text-align: right; cursor: default; }
.topbar-user strong { color: #fff; display: block; }
/* Línea gris claro alrededor de la foto/iniciales del topbar — a petición explícita, para que
   se distinga con el fondo oscuro de la propia barra. */
#avatar-topbar { border: 2px solid #d1d5db; }
.sidebar-toggle { color: #fff; }
.sidebar-toggle:hover { background: rgba(255, 255, 255, 0.12); }

/* Menú desplegable al pasar el ratón sobre el nombre/apellidos del topbar. */
.topbar-user-menu { position: relative; padding: 6px 0; }
.topbar-user-dropdown {
  display: none;
  position: absolute;
  top: 100%;
  right: 0;
  min-width: 190px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  padding: 6px;
  z-index: 20;
  text-align: left;
}
.topbar-user-menu:hover .topbar-user-dropdown,
.topbar-user-dropdown:hover { display: block; }
.topbar-user-dropdown-item {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border: none;
  background: none;
  color: var(--color-text);
  font-size: 0.85rem;
  text-align: left;
  text-decoration: none;
  border-radius: 6px;
  cursor: pointer;
}
.topbar-user-dropdown-item:hover:not(:disabled) { background: var(--color-bg); }
.topbar-user-dropdown-item:disabled { color: var(--color-text-muted); cursor: not-allowed; }
.avatar-circle {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--color-primary);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.8rem;
  flex-shrink: 0;
  overflow: hidden;
}
.avatar-circle img { width: 100%; height: 100%; object-fit: cover; }
.avatar-circle-md { width: 48px; height: 48px; font-size: 1rem; }

/* Envuelve el avatar grande + el <input type="file"> oculto: pulsar el círculo (el <label>
   entero) abre el selector de archivo directamente, sin botón aparte. */
.avatar-upload { position: relative; display: inline-block; cursor: pointer; }
.avatar-upload-overlay {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: rgba(15, 23, 42, 0.6);
  color: white;
  font-size: 0.68rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 6px;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.avatar-upload:hover .avatar-upload-overlay { opacity: 1; }
/* `hidden` (display:none) impide en algunos navegadores que el <label> reenvíe el clic al
   <input type="file"> anidado — se oculta así en su lugar, que sí conserva el reenvío. */
.avatar-upload-input { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }

/* Igual que .avatar-upload-input pero sin depender de un ancestro con position:relative —
   para los botones sueltos de "widgetSubidaInline" (no van dentro de un círculo de avatar). */
.input-archivo-oculto { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

.mini-subida { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 6px; }
.btn-link { background: none; border: none; padding: 0; color: var(--color-primary); text-decoration: underline; cursor: pointer; font-size: 0.82rem; }

/* Bloqueo visual de campos sin permiso de "Editar" (select/checkbox/file, que no tienen un
   "readonly" nativo) — a propósito NO usa "disabled": eso los excluiría del envío del
   formulario y, en un checkbox, datosFormulario() lo confundiría con "no marcado". */
.campo-bloqueado { pointer-events: none; opacity: 0.6; }
.input-invalido { border-color: var(--color-danger) !important; background: var(--color-danger-light); }

.etiquetas-widget { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 160px; }
.select-anadir-etiqueta { font-size: 0.8rem; padding: 2px 4px; max-width: 130px; }
.tabla-permisos td { vertical-align: top; padding-top: 10px; }
.tabla-permisos th { white-space: nowrap; }
/* Líneas verticales "estilo Excel" entre columnas — a petición explícita, para separar
   visualmente el mogollón de datos de cada columna sin tocar el fondo de zebra de las filas.
   Clase propia (no atada a .tabla-permisos) para poder reutilizarla en cualquier tabla, ej. la
   de "Excepciones por rol" de Política de contraseñas. */
.tabla-lineas-verticales td:not(:first-child), .tabla-lineas-verticales th:not(:first-child) { border-left: 2px solid #000; }
/* Cabecera fija al hacer scroll dentro de la tabla — sin esto, en pantallas con muchos campos
   se pierde de vista qué columna es cuál según se baja. background sólido obligatorio: si no,
   las filas de debajo se transparentan a través de la cabecera al pasar por debajo.
   `position: sticky` por sí solo NO basta aquí: `.table-wrap` ya tiene `overflow-x: auto`, y
   por la propia spec de CSS Overflow eso fuerza a `overflow-y` a comportarse también como
   `auto` (aunque no se declare) — convirtiendo a `.table-wrap` en el contenedor de scroll real,
   pero sin una altura acotada nunca llega a desbordar, así que la cabecera nunca "se pega" a
   nada: sigue subiendo con el resto de la página. `.table-wrap-cabecera-fija` le da una altura
   máxima con su propio scroll vertical, para que la cabecera sticky tenga de verdad respecto a
   qué quedarse fija. */
.table-wrap-cabecera-fija { max-height: 70vh; overflow-y: auto; }
/* `.tabla-cabecera-fija`: nombre genérico para reutilizar la cabecera fija en cualquier tabla
   de listado (Usuarios, Roles, Tipos de usuario, Nacionalidad...), no solo en las de matriz
   de permisos (`.tabla-permisos`, que la lleva incluida por su propio selector aquí). */
.tabla-permisos thead th, .tabla-cabecera-fija thead th { position: sticky; top: 0; background: var(--color-surface); z-index: 1; }
/* Cabeceras de las dos tablas de "Control de acceso" (Grupos de menú / Control de acceso en
   sí) — a petición explícita, fondo oscuro y letra blanca en negrita para que resalten más
   que el resto de tablas de la app (esas se quedan con su cabecera clara de siempre). */
.tabla-cabecera-oscura thead th { background: #0d3b37; color: #fff; font-weight: 700; }
/* Nombre/identificador principal de una fila, destacado (a petición explícita): negrita,
   fondo #0d3b37, letra blanca — reutilizable en cualquier listado (campo, usuario, rol,
   tipo de usuario, nacionalidad, pantalla...), no solo en Permisos de campo. */
.nombre-resaltado { display: inline-block; background: #0d3b37; color: #fff; font-weight: 700; padding: 3px 8px; border-radius: 4px; margin-bottom: 4px; }
.pantalla-acordeon { border: 1px solid var(--color-border, #ddd); border-radius: 8px; margin-bottom: 12px; overflow: hidden; }
.pantalla-acordeon > summary { cursor: pointer; padding: 10px 14px; font-weight: 600; background: var(--color-bg); list-style: none; }
.pantalla-acordeon > summary::-webkit-details-marker { display: none; }
.pantalla-acordeon > summary::before { content: '▸ '; }
.pantalla-acordeon[open] > summary::before { content: '▾ '; }
.pantalla-acordeon-contenido { padding: 10px 14px; }
.fila-cabecera-pantalla td { background: var(--color-bg); font-weight: 600; }
.grupo-archivo { margin-bottom: 16px; }
.grupo-archivo:last-child { margin-bottom: 0; }
.btn-ejecutar-prueba { border: 1px solid var(--color-border); background: #fff; border-radius: 6px; padding: 2px 8px; font-size: 0.78rem; cursor: pointer; color: var(--color-text-muted); white-space: nowrap; }
.btn-ejecutar-prueba:hover { background: #eef2f3; color: var(--color-text); }
.btn-ejecutar-prueba:disabled { opacity: 0.5; cursor: not-allowed; }

.content { flex: 1; padding: 24px; max-width: 1180px; width: 100%; margin: 0 auto; overflow-y: auto; }

/* Botón ☰ del topbar: siempre visible (antes solo aparecía en móvil) — a petición explícita,
   ahora también sirve para ocultar/mostrar la barra en escritorio (ver .sidebar-oculta). */
.sidebar-toggle { display: inline-flex; }

/* Modo "oculta" de la barra lateral — el botón ☰ lo alterna (app.js, se recuerda entre
   sesiones vía localStorage). La barra se recoge fuera de la pantalla dejando un borde de
   14px como "asa" visible, y reaparece entera como capa flotante (sin empujar el contenido,
   por eso `position: fixed`, no `display: none`) en cuanto el ratón pasa por encima de esa
   asa o de la propia barra ya abierta. */
.app-shell.sidebar-oculta .sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 45;
  transform: translateX(calc(-1 * var(--sidebar-w) + 14px));
  transition: transform 0.18s ease;
  box-shadow: var(--shadow-md);
}
.app-shell.sidebar-oculta .sidebar:hover,
.app-shell.sidebar-oculta .sidebar:focus-within {
  transform: translateX(0);
}

@media (max-width: 860px) {
  /* En móvil, sin ratón para "hover", la barra abierta ya ocupa toda la pantalla como capa —
     que no empuje ni dependa del ancho fijo evita que se corte en pantallas muy estrechas. */
  .app-shell.sidebar-oculta .sidebar:hover, .app-shell.sidebar-oculta .sidebar:focus-within { width: min(var(--sidebar-w), 86vw); }
}

/* ---------- Componentes ---------- */

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 20px;
  box-shadow: var(--shadow-sm);
}
.card h3 { font-size: 1rem; }

/* Fondo de color a petición explícita, para diferenciar de un vistazo un bloque del resto —
   "Grupos de menú" en Control de acceso, "Política global" en Política de contraseñas. Solo se
   tiñe el texto suelto que cuelga DIRECTAMENTE de la tarjeta (título, párrafo introductorio):
   cualquier panel interior con su propio fondo claro (la tabla, .politica-grupo...) conserva su
   texto normal — por eso `>` (hijo directo), no un descendiente cualquiera, que sí llegaría
   hasta dentro de esos paneles y los dejaría ilegibles. */
.card-fondo-teal { background: #1e8077; }
.card-fondo-teal > h3, .card-fondo-teal > .card-header h3, .card-fondo-teal > p { color: #fff; }
/* Formulario "+ Nuevo grupo": a diferencia de los campos de Política de contraseñas (que viven
   dentro de un .politica-grupo con fondo propio), este cuelga directo del fondo de color, así
   que sus hints (gris, pensados para fondo claro) necesitan quedar en blanco aquí. */
#form-nuevo-grupo .hint { color: rgba(255, 255, 255, 0.9); }
/* Marco reforzado a petición explícita, para que el recuadro que envuelve un bloque se note
   como su propio bloque — "Control de acceso" y "Excepciones por rol" (Política de
   contraseñas), ambas la tarjeta hermana de una con `.card-fondo-teal` justo encima. */
.card-marco-reforzado { border: 3px solid #000; }
/* La tabla en sí se queda en un panel blanco dentro de la tarjeta de color — si el teal se
   colara hasta detrás de las celdas, el texto/enlaces "Editar" (mismo tono que el fondo) y el
   texto muted dejarían de leerse bien. */
.card-fondo-teal .table-wrap { background: var(--color-surface); border-radius: 8px; padding: 2px 10px; }
/* Botón "Guardar" de Política global — a petición explícita, borde blanco para que se recorte
   del fondo de color. */
#form-politica-global .form-actions .btn { border-color: #fff; }
.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
  gap: 12px;
  flex-wrap: wrap;
}
.card-header h3 { margin: 0; }

.grid { display: grid; gap: 14px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.grid-direccion { grid-template-columns: 2fr 1fr; }
@media (max-width: 1000px) { .grid-5 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px) { .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) { .grid-2, .grid-3, .grid-4, .grid-5, .grid-direccion { grid-template-columns: 1fr; } }

/* Secciones coloreadas de la ficha de usuario: una franja de color a la izquierda + un
   fondo muy suave del mismo tono por sección, para diferenciar de un vistazo cada bloque. */
.ficha-seccion {
  border-left: 4px solid var(--color-acento, var(--color-primary));
  background: var(--color-acento-suave, var(--color-primary-light));
  border-radius: 8px;
  padding: 16px 18px;
  margin-bottom: 16px;
}
.ficha-seccion .field { margin-bottom: 0; }
.ficha-seccion .grid { margin-top: 0; }
.ficha-seccion-titulo {
  margin: 0 0 12px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-acento, var(--color-primary));
  display: flex;
  align-items: center;
  gap: 6px;
}
.ficha-seccion--identidad { --color-acento: var(--color-primary-dark); --color-acento-suave: var(--color-primary-light); }
.ficha-seccion--documento { --color-acento: var(--color-blue); --color-acento-suave: var(--color-blue-light); }
.ficha-seccion--contacto { --color-acento: var(--color-purple); --color-acento-suave: var(--color-purple-light); }
.ficha-seccion--telefonos { --color-acento: var(--color-warning); --color-acento-suave: var(--color-warning-light); }
.ficha-seccion--direccion { --color-acento: var(--color-success); --color-acento-suave: var(--color-success-light); }
.ficha-seccion--banco { --color-acento: var(--color-rose); --color-acento-suave: var(--color-rose-light); }
.ficha-seccion--notas { --color-acento: var(--color-slate); --color-acento-suave: var(--color-slate-light); }
/* Los inputs mantienen fondo blanco propio para que se lean bien sobre el color suave de fondo. */
.ficha-seccion input, .ficha-seccion select, .ficha-seccion textarea { background: var(--color-surface); }

.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.field label { font-size: 0.82rem; font-weight: 600; color: #334155; }
.field .hint { font-size: 0.76rem; color: var(--color-text-muted); }
.field-check { display: flex; align-items: center; gap: 8px; font-size: 0.9rem; }

/* Pantalla "Política de contraseñas": rediseño más visual a petición explícita — la política
   agrupada en bloques temáticos con una frase-resumen en lenguaje llano arriba, en vez de un
   único formulario denso de 9 campos sueltos. */
.politica-resumen {
  background: var(--color-primary-light);
  color: var(--color-primary-dark);
  border-radius: var(--radius);
  padding: 14px 16px;
  font-size: 0.98rem;
  line-height: 1.5;
  margin: 0 0 18px;
}
.politica-grupos { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 900px) { .politica-grupos { grid-template-columns: 1fr; } }
/* Dentro de un modal (excepción por rol) el ancho disponible es fijo y mucho más estrecho que
   la tarjeta de política global — 3 columnas ahí no caben (los inputs quedan tan estrechos que
   recortan el propio valor, ej. "10" se ve como "1"), así que se apilan siempre en una sola. */
.modal .politica-grupos { grid-template-columns: 1fr; }
.politica-grupo {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 16px;
}
.politica-grupo h4 { margin: 0 0 12px; font-size: 0.92rem; }
.politica-grupo .grid-2 { gap: 12px; }

.toggle-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.toggle-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  font-size: 0.86rem;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  user-select: none;
}
.toggle-pill input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-pill:has(input:checked) {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: white;
}
.toggle-pill:has(input:focus-visible) { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* Campo centrado HORIZONTALMENTE dentro de su celda de grid (label+input más estrechos que
   la columna). Deliberadamente SIN centrar verticalmente (sin justify-content): un campo con
   más contenido debajo (hint, widget de subida...) tiene una celda más alta, y centrar
   verticalmente desplazaba su label/input hacia el medio, descuadrándolo con los campos
   vecinos de la misma fila — dejarlo arriba (comportamiento por defecto) es lo que hace que
   todos los campos de una fila queden "a la misma altura", a petición explícita. */
.field-centrado { align-items: center; text-align: center; }
.field-centrado input, .field-centrado select { text-align: center; }

/* Grid más compacta (menos separación horizontal) — a petición explícita para "Teléfonos y
   colegiación", para que sus 4 campos queden más juntos entre sí. */
.grid-compacta { gap: 8px; }

/* Tramos "desde/hasta" de un mismo horario pegados entre sí dentro de la misma celda, y
   colores distintos por tipo de horario para diferenciarlos de un vistazo. */
.horario-tramo { display: flex; align-items: center; justify-content: center; gap: 4px; }
.horario-tramo input { max-width: 90px; }
.btn-copiar-horario { border: 1px solid var(--color-border); background: #fff; border-radius: 4px; width: 22px; height: 22px; line-height: 1; padding: 0; cursor: pointer; color: var(--color-text-muted); flex-shrink: 0; }
.btn-copiar-horario:hover { background: #eef2f3; color: var(--color-text); }
.btn-copiar-horario:disabled { visibility: hidden; }
.horario-col-laboral, .horario-col-mercantil, .horario-col-fuera { padding: 8px 12px !important; text-align: center; }
.horario-col-laboral { background: var(--color-blue-light); }
.horario-col-mercantil { background: var(--color-purple-light); border-left: 2px solid var(--color-text-muted); }
.horario-col-fuera { background: var(--color-slate-light); border-left: 2px solid var(--color-text-muted); }

input[type="text"], input[type="email"], input[type="password"], input[type="date"],
input[type="number"], input[type="tel"], select, textarea {
  border: 1px solid var(--color-border);
  border-radius: 8px;
  padding: 9px 11px;
  font-size: 0.92rem;
  background: white;
  color: var(--color-text);
  width: 100%;
  font-family: inherit;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-light);
}
textarea { resize: vertical; min-height: 70px; }
input[readonly], input:disabled { background: #f1f5f9; color: var(--color-text-muted); cursor: not-allowed; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 16px;
  border-radius: 8px;
  border: 1px solid transparent;
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
  background: var(--color-primary);
  color: white;
  transition: filter 0.12s ease;
}
.btn:hover { filter: brightness(1.08); }
.btn:disabled { opacity: 0.55; cursor: not-allowed; }
.btn-block { width: 100%; }
.btn-secondary { background: #eef2f3; color: var(--color-text); }
.btn-ghost { background: transparent; color: #d8ece9; border-color: rgba(255, 255, 255, 0.25); }
.btn-ghost:hover { background: rgba(255, 255, 255, 0.08); }
.btn-danger { background: var(--color-danger); }
.btn-sm { padding: 5px 10px; font-size: 0.8rem; }
.btn-icon {
  background: transparent; border: none; cursor: pointer; font-size: 1.2rem;
  color: var(--color-text-muted); padding: 4px 8px; border-radius: 6px;
}
.btn-icon:hover { background: #eef2f3; }

table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
thead th {
  text-align: left; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--color-text-muted); padding: 8px 10px; border-bottom: 1px solid var(--color-border);
}
tbody td { padding: 10px; border-bottom: 1px solid #eef1f3; vertical-align: middle; }
tbody tr:hover { background: #fafcfc; }
.table-wrap { overflow-x: auto; }

/* Filas/tarjetas pares en un tono distinto — estándar del proyecto para CUALQUIER listado,
   no solo tablas (ver aplicarZebra en ui.js, úsala en cualquier pantalla de listado nueva).
   Se marca con JS (clase .fila-par), no con :nth-child: con filtros activos, los elementos
   ocultos seguirían contando para la paridad y el rayado quedaría inconsistente. */
/* --zebra-color se fija en JS (app.js, al cargar la sesión) desde la preferencia guardada
   del usuario (Usuario.colorFilaPar, editable en "Mi perfil") — #e6e9eb es solo el valor por
   defecto para quien no ha elegido ninguno todavía. */
.fila-par { background: var(--zebra-color, #e6e9eb); }
tbody tr.fila-par:hover { filter: brightness(0.94); }

.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 9px; border-radius: 999px; font-size: 0.72rem; font-weight: 700;
  background: #eef2f3; color: #475569;
}
.badge-success { background: var(--color-success-light); color: #166534; }
.badge-danger { background: var(--color-danger-light); color: #991b1b; }
.badge-warning { background: var(--color-warning-light); color: #92400e; }
.badge-primary { background: var(--color-primary-light); color: var(--color-primary-dark); }
/* El gris del .badge base ya sirve para "sustituida"; se le da nombre propio para no depender de
   que quien lea el código sepa que un badge sin modificador queda gris. */
.badge-secondary { background: #eef2f3; color: #475569; }

/* Una contraseña que hay que LEER de la pantalla y teclear en otro sitio: monoespaciada para
   distinguir caracteres parecidos, grande, con espacio entre letras y seleccionable de un golpe.
   Es el único sitio de la aplicación donde se enseña un secreto, y solo una vez. */
.secreto-mostrado {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.35rem; font-weight: 700; letter-spacing: 0.06em;
  text-align: center; user-select: all;
  padding: 16px; margin: 8px 0 12px;
  background: #f8fafc; border: 2px dashed var(--color-primary, #2563eb); border-radius: 10px;
  overflow-wrap: anywhere;
}

.alert {
  padding: 10px 14px; border-radius: 8px; font-size: 0.86rem; margin-bottom: 14px;
  border: 1px solid transparent;
}
.alert-danger { background: var(--color-danger-light); color: #991b1b; border-color: #fecaca; }
.alert-warning { background: var(--color-warning-light); color: #92400e; border-color: #fde68a; }
.alert-info { background: #eff6ff; color: #1e40af; border-color: #bfdbfe; }

.empty-state { text-align: center; padding: 40px 20px; color: var(--color-text-muted); }

.toast-host {
  position: fixed; top: 16px; right: 16px; z-index: 100;
  display: flex; flex-direction: column; gap: 8px; max-width: 340px;
  /* Los toasts son solo informativos (no tienen ninguna acción propia) — sin esto, uno que
     quede flotando sobre contenido interactivo (ej. el lienzo de flujos) bloquea los clics
     de verdad, no solo en pruebas automatizadas. */
  pointer-events: none;
}
.toast {
  background: #1c2833; color: white; padding: 12px 14px; border-radius: 8px;
  font-size: 0.86rem; box-shadow: var(--shadow-md); animation: toast-in 0.15s ease;
}
.toast.success { background: #14532d; }
.toast.error { background: #7f1d1d; }
@keyframes toast-in { from { transform: translateY(-6px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }

.modal-backdrop {
  position: fixed; inset: 0; background: rgba(15, 23, 42, 0.45);
  display: flex; align-items: center; justify-content: center; z-index: 60; padding: 20px;
}
.modal {
  background: white; border-radius: var(--radius); padding: 22px; width: 100%; max-width: 480px;
  box-shadow: var(--shadow-md); max-height: 90vh; overflow-y: auto;
}
.modal h3 { margin-bottom: 14px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }

.form-actions { display: flex; gap: 10px; margin-top: 8px; flex-wrap: wrap; }

.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--color-border); margin-bottom: 18px; flex-wrap: wrap; }
.tab-btn {
  background: none; border: none; padding: 10px 14px; cursor: pointer;
  color: var(--color-text-muted); font-size: 0.88rem; font-weight: 600;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tab-btn.active { color: var(--color-primary); border-bottom-color: var(--color-primary); }

.spinner {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.4); border-top-color: white;
  animation: spin 0.6s linear infinite; display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }

.muted { color: var(--color-text-muted); }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.text-right { text-align: right; }
.flex { display: flex; align-items: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.gap-8 { gap: 8px; }
.mt-8 { margin-top: 8px; }
.mt-16 { margin-top: 16px; }

/* Grupos de opciones en línea (ej. elegir cuándo se ejecuta la sincronización del entorno de
   desarrollo). Se envuelven solos si no caben, para que no se salgan en pantallas estrechas. */
.radio-inline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-right: 18px;
  margin-bottom: 6px;
  cursor: pointer;
  font-weight: normal;
}
.radio-inline input { margin: 0; }

/* Caja donde se le escribe el encargo al asistente (pantalla Entorno de desarrollo). Resaltada a
   petición del usuario para que destaque sobre el resto de campos de la pantalla. */
#instruccion-ia-produccion {
  background: #c1f299;
  color: #000;
  font-weight: bold;
}
#instruccion-ia-produccion::placeholder {
  color: #4a6b2c; /* sobre este verde claro, el gris por defecto del placeholder casi no se lee */
  font-weight: normal;
}

/* ============================================================================
   Instalación de DESARROLLO — señal visual imposible de pasar por alto.
   La aplicación de desarrollo es una copia exacta de la de producción (misma pantalla, mismos
   datos con la misma pinta, solo que anonimizados), así que sin una marca clara es fácil creer
   que se está trabajando en una cuando se está en la otra. Lo único que cambia es esto: las dos
   barras en granate en vez de verde, y el aviso en la barra superior.
   La clase la pone `marcarEntorno()` (app.js) según TIPO_ENTORNO del servidor.
   ============================================================================ */
body.entorno-desarrollo .sidebar,
body.entorno-desarrollo .topbar {
  background: #470000;
}

.aviso-entorno {
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: 0.5px;
  color: #ffef5e; /* amarillo canario */
  white-space: nowrap;
  margin: 0 auto;
}

@media (max-width: 700px) {
  .aviso-entorno { font-size: 1rem; }
}

/* Listado largo con scroll propio (ej. el historial de pedidos al asistente): así puede crecer sin
   empujar hacia abajo el resto de la pantalla, y la cabecera se queda fija al desplazarse. */
.tabla-scroll-alta {
  max-height: 340px;
  overflow-y: auto;
}
.tabla-scroll-alta thead th {
  position: sticky;
  top: 0;
  z-index: 1;
}

/* Fila de filtros por fecha, encima de un listado. */
.filtro-fechas {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* Contorno negro para destacar un botón concreto sobre el resto (ej. "Elegir archivo JSON…", que
   es la acción principal de su tarjeta y se perdía entre los botones secundarios). */
.btn-contorno-negro {
  border: 1px solid #000;
}

/* Campo editable dentro de una celda (columna "Disparo" de los listados de programados): tiene que
   caber en la fila sin romper la tabla ni parecer un campo de formulario suelto. */
.input-disparo {
  width: 100%;
  min-width: 120px;
  max-width: 260px;
  padding: 3px 6px;
  font-size: 0.82rem;
}

/* Campo "Título de la solicitud" (pantalla Entorno de desarrollo). Resaltado a petición del
   usuario, en amarillo y con contorno negro, para que destaque sobre el resto de la tarjeta. */
#titulo-ia {
  background: #f8f9b9;
  border: 1px solid #000;
}
#titulo-ia::placeholder {
  color: #7a7b3a; /* sobre este amarillo claro, el gris por defecto del placeholder casi no se lee */
}

/* ============================================================================
   Fondos de los cinco apartados de "Entorno de desarrollo" (lado producción).
   Colores elegidos por el usuario para distinguir de un vistazo en qué paso del
   recorrido está: 1 y 3 comparten color porque ambos van de producción hacia
   desarrollo o al revés; el 2 (pedirle a la IA) se deja sin fondo a propósito,
   para que la caja de texto amarilla siga siendo lo que más destaca.
   ============================================================================ */
.card-paso-1,
.card-paso-3 {
  /* Un punto de color por cada 1,4 en blanco: el punto ocupa 1px y el paso de la trama es 2,4px.
     Opacidad 0,6 — un 50% más opaca que el 0,4 de partida. */
  background-color: #fff;
  background-image: radial-gradient(rgba(235, 188, 86, 0.6) 0.5px, transparent 0.5px);
  background-size: 2.4px 2.4px;
}

/* Misma trama que 1 y 3, en verde: este apartado es el que se envía al entorno de desarrollo, y
   el color lo separa de un vistazo de los dos que van y vienen de producción. */
.card-paso-2 {
  background-color: #fff;
  /* Opacidad 0,9 — un 50% menos transparente que el 0,6 de los otros apartados. */
  background-image: radial-gradient(rgba(78, 138, 51, 0.9) 0.5px, transparent 0.5px);
  background-size: 2.4px 2.4px;
}

.card-paso-4 {
  /* La misma trama de puntos que los apartados 1 y 3, pero en su color rosa (#e494a1) y con el
     punto más marcado: opacidad 0,84 (un 40% menos transparente que el 0,6 de los otros). */
  background-color: #fff;
  background-image: radial-gradient(rgba(228, 148, 161, 0.84) 0.5px, transparent 0.5px);
  background-size: 2.4px 2.4px;
}

/* Rayado diagonal (135deg = líneas inclinadas hacia la derecha, "\"). Finas a
   propósito: un tramado grueso detrás de una tabla hace ilegible el texto. */
.card-paso-5 {
  background-color: #fff;
  /* #c9a9a9 a opacidad 0,35 — un 30% menos opaco que el 0,5 de partida. */
  background-image: repeating-linear-gradient(135deg, rgba(201, 169, 169, 0.35) 0 2px, transparent 2px 7px);
}

/* Sobre estos fondos, las tablas y avisos necesitan su propio fondo opaco o el
   color/rayado se cuela por debajo y no se lee nada. */
.card-paso-1 .table-wrap,
.card-paso-3 .table-wrap,
.card-paso-4 .table-wrap,
.card-paso-5 .table-wrap {
  background: #fff;
  border-radius: 6px;
}

/* Etiquetas "Título de la solicitud" y "Qué quiere que haga": sobre la trama de puntos el texto
   normal se emborrona, así que van en negrita y en negro, y con una banda blanca detrás que las
   despega del fondo — que es lo que se pidió al decir "las letras por encima del fondo". */
.card-paso-2 .field > label {
  display: inline-block;
  font-weight: 700;
  color: #000;
  background: #fff;
  padding: 1px 6px;
  border-radius: 4px;
}
