/* ===========================================================================
   public/css/components.css
   Badges, tarjetas, grilla de stats, toasts, estado vacio, spinner, tabs, paginacion y barra de filtros.

   Parte 7 de 12 de la hoja de estilos. El orden de carga IMPORTA:
   se enlazan en este mismo orden desde index.html y dashboard.html.
   =========================================================================== */
/* ===== 12. BADGES / STATUS PILLS ===== */
.badge {
  display: inline-flex;
  align-items: center;
  padding: .2rem .6rem;
  border-radius: 100px;
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  white-space: nowrap;
}

.badge-borrador         { background: var(--badge-bg-gray);  color: var(--clr-gray-soft); }
.badge-pendiente        { background: var(--badge-bg-amber);   color: var(--clr-amber-soft); }
.badge-en-revision      { background: var(--badge-bg-orange);   color: var(--clr-orange-soft); }
.badge-aprobada         { background: var(--badge-bg-green);   color: var(--clr-green-soft); }
.badge-enviada          { background: var(--badge-bg-blue);   color: var(--clr-blue-soft); }
/* CONFIRMADA PASA DE VIOLETA A VERDE, Y ES EL UNICO CAMBIO DE COLOR DE ESTADO
   La paleta usaba SEIS matices para los estados (ambar, naranja, azul, violeta,
   verde, rojo). Ese arcoiris es lo que mas queda de estetica generica, y el
   violeta era el mas arbitrario de todos: no significaba nada.

   'Confirmada' es la venta cerrada — exactamente lo que el verde ya significa
   en 'Aprobada'. Darle su propio matiz obligaba a aprender un color de mas para
   decir algo que la paleta ya sabia decir.

   Se eligio ESTE cambio y no reducir los seis a tres porque es el unico que no
   pisa un significado que la gente ya tenga incorporado: nadie asocia el
   violeta con nada en particular, mientras que ambar-pendiente y rojo-rechazada
   estan aprendidos despues de meses de uso.

   Se distinguen de 'Aprobada' por el peso: la confirmada usa el verde pleno,
   la aprobada el tono suave. Misma familia, distinta intensidad — que es lo
   que son: aprobar es un paso hacia cerrar. */
.badge-confirmada       { background: var(--badge-bg-green);    color: var(--clr-green); }
.badge-aceptada         { background: var(--badge-bg-green);    color: var(--clr-green); }
.badge-rechazada        { background: var(--badge-bg-red);    color: var(--clr-red-soft); }
.badge-archivada        { background: var(--badge-bg-slate);  color: var(--text-secondary); }

.badge-role-jefe        { background: var(--badge-bg-green);   color: var(--clr-green); }
.badge-role-ejecutivo   { background: var(--badge-bg-blue);   color: var(--clr-blue);  }
.badge-role-admin       { background: var(--badge-bg-amber);   color: var(--clr-amber); }
.badge-role-sysadmin    { background: var(--badge-bg-violet);   color: var(--clr-violet-soft); }
.badge-role-proyectos   { background: var(--badge-bg-teal);   color: var(--clr-teal-soft); }
.badge-active           { background: var(--badge-bg-green);   color: var(--clr-green); }
.badge-inactive         { background: var(--badge-bg-red);    color: var(--clr-red);   }

/* Licitación lifecycle state badges */
.badge-lic-preparacion  { background: var(--badge-bg-gray);  color: var(--clr-slate-soft); }
.badge-lic-cotizando    { background: var(--badge-bg-amber);   color: var(--clr-amber-soft); }
.badge-lic-evaluacion   { background: var(--badge-bg-indigo);   color: var(--clr-indigo-soft); }
.badge-lic-presentada   { background: var(--badge-bg-blue);   color: var(--clr-blue-soft); }
.badge-lic-adjudicada   { background: var(--badge-bg-green);   color: var(--clr-green-soft); }
.badge-lic-noadjudicada { background: var(--badge-bg-red);    color: var(--clr-red-soft); }
.badge-lic-archivada    { background: var(--badge-bg-slate);  color: var(--text-secondary); }

/* Seguimiento comercial (estado_venta) — pipeline de venta con el cliente,
   independiente del estado de aprobación interno de arriba. */
.badge-seg-interesado   { background: var(--badge-bg-blue);   color: var(--clr-blue-soft); }
.badge-seg-negociacion  { background: var(--badge-bg-amber);  color: var(--clr-amber-soft); }
.badge-seg-confirmado   { background: var(--badge-bg-indigo); color: var(--clr-indigo-soft); }
.badge-seg-no-interesa  { background: var(--badge-bg-red);    color: var(--clr-red-soft); }
.badge-seg-concretada   { background: var(--badge-bg-green);  color: var(--clr-green); }
.badge-seg-otro         { background: var(--badge-bg-gray);   color: var(--clr-gray-soft); }

/* Audit log — acción category badges (semantic color by action family) */
.badge-audit-auth       { background: var(--badge-bg-violet);   color: var(--clr-violet-soft); }  /* LOGIN/LOGOUT — violet */
.badge-audit-auth-fail  { background: var(--badge-bg-red);    color: var(--clr-red-soft); }  /* LOGIN_FAILED — red */
.badge-audit-create     { background: var(--badge-bg-blue);   color: var(--clr-blue-soft); }  /* CREAR_* — blue */
.badge-audit-edit       { background: var(--badge-bg-amber);   color: var(--clr-amber-soft); }  /* EDITAR_*, CAMBIAR_ESTADO — amber */
.badge-audit-approve    { background: var(--badge-bg-green);   color: var(--clr-green-soft); }  /* APROBAR — green */
.badge-audit-reject     { background: var(--badge-bg-red);    color: var(--clr-red-soft); }  /* RECHAZAR, DESACTIVAR_USUARIO — red */
.badge-audit-file       { background: var(--badge-bg-teal);   color: var(--clr-teal-soft); }  /* SUBIR_PDF/DESCARGAR_PDF — teal */

/* Audit log — resultado badges */
.badge-resultado-exito  { background: var(--badge-bg-green);   color: var(--clr-green); }
.badge-resultado-fallo  { background: var(--badge-bg-red);    color: var(--clr-red);   }

/* ===== 26. EN ESPERA BADGE ===== */
.badge-en-espera {
  background: var(--badge-bg-indigo);
  color: var(--clr-indigo-soft);
}

/* ===== 13. CARDS ===== */
.card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: var(--transition-smooth);
}

.card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--bg-hover);
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border);
}

/* ── El titulo de una tarjeta ────────────────────────────────────────────────
   El filete naranja reemplaza al emoji que encabezaba cada seccion (habia 129
   en la interfaz, y un emoji en el titulo es el tic visual mas reconocible del
   software generado).

   No es un adorno elegido al azar: es LA MISMA linea que el PDF ya dibuja
   antes de cada seccion — ver sectionTitle() en reportePdfService.js, que hace
   doc.rect(MARGIN, y, 3.5, 12).fill(C.ORANGE). Traerla a la pantalla es lo que
   hace que la aplicacion y el documento que recibe el cliente se lean como la
   misma empresa.

   Va con ::before sobre el selector que ya existia, asi que ninguna pantalla
   tuvo que agregar una clase: alcanzo con borrar el emoji del texto. */
.card-header h3, .card-header h4 {
  font-size: .95rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: .55rem;
  min-width: 0;              /* deja que el texto se recorte antes de desbordar */
}

.card-header h3::before, .card-header h4::before {
  content: '';
  flex: none;
  width: 3px;
  height: 1em;
  border-radius: 1px;
  background: var(--clr-orange);
}

.card-body { padding: 1.25rem; }

.card-footer {
  padding: .75rem 1.25rem;
  border-top: 1px solid var(--border);
  background: rgba(255,255,255,.02);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .5rem;
}

/* ===== 14. STATS GRID ===== */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.stat-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 1.1rem 1.25rem;
  position: relative;
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: var(--transition-smooth);
}

.stat-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-hover);
}

.stat-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0;
  width: 4px; height: 100%;
  background: var(--stat-accent, var(--clr-blue));
}

.stat-card-value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
  color: var(--text-primary);
}

.stat-card-label {
  font-size: .75rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: .07em;
  margin-top: .35rem;
}

/* Acentos de tarjeta reutilizables: evitan un style="--stat-accent:...;" por
   cada tarjeta nueva. Las tarjetas ya existentes lo siguen fijando inline
   (no se tocaron), pero cualquier tarjeta nueva puede usar una clase. */
.stat-accent-blue   { --stat-accent: var(--clr-blue); }
.stat-accent-amber  { --stat-accent: var(--clr-amber); }
.stat-accent-green  { --stat-accent: var(--clr-green); }
.stat-accent-red    { --stat-accent: var(--clr-red); }
.stat-accent-violet { --stat-accent: var(--clr-violet); }
.stat-accent-gray   { --stat-accent: var(--clr-gray); }

/* ===== 17. TOAST NOTIFICATIONS ===== */
.toast-stack {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  z-index: 300;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  max-width: 340px;
}

.toast {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-left: 4px solid var(--clr-blue);
  border-radius: var(--radius-sm);
  padding: .75rem 1rem;
  font-size: .875rem;
  box-shadow: var(--shadow-md);
  transform: translateX(120%);
  opacity: 0;
  transition: transform .25s ease, opacity .25s ease;
}

.toast.toast-visible  { transform: translateX(0); opacity: 1; }
.toast.toast-success  { border-left-color: var(--clr-green); }
.toast.toast-error    { border-left-color: var(--clr-red);   }
.toast.toast-warning  { border-left-color: var(--clr-amber); }

/* ===== 21. EMPTY STATE ===== */
.empty-state {
  text-align: center;
  padding: 3rem 1.5rem;
  color: var(--text-muted);
}

/* Tipo de archivo, al lado del nombre en una lista de adjuntos. Se alinea con
   la línea de texto y hereda su color, como el resto de los íconos de trazo. */
.file-icon {
  width: 17px; height: 17px;
  vertical-align: -3px;
  color: var(--text-muted);
  flex-shrink: 0;
}

/* El ícono es de trazo y hereda el color del texto, así que se apaga con el
   resto del estado vacío. Antes era un emoji a font-size:2.5rem — a todo color
   sobre un panel gris, y dibujado distinto en cada sistema operativo. */
.empty-state-icon { margin-bottom: .85rem; color: var(--text-muted); opacity: .55; }
.empty-state-icon .empty-icon { width: 40px; height: 40px; }
.empty-state h4   { font-size: .95rem; color: var(--text-secondary); margin-bottom: .35rem; }
.empty-state p    { font-size: .82rem; }

/* ===== 22. LOADING / SPINNER ===== */
.spinner {
  display: inline-block;
  width: 1em; height: 1em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin .7s linear infinite;
  vertical-align: middle;
}

.spinner-lg {
  width: 2.5rem; height: 2.5rem;
  border-width: 3px;
}

.page-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 4rem;
  color: var(--text-muted);
  font-size: .875rem;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ===== 23. TABS ===== */
.tab-bar {
  display: flex;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1.5rem;
  gap: .1rem;
  overflow-x: auto;
}

.tab-btn {
  background: none;
  border: none;
  padding: .65rem 1.1rem;
  font-size: .875rem;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: color var(--transition), border-color var(--transition);
  white-space: nowrap;
  font-family: inherit;
}

.tab-btn:hover  { color: var(--text-secondary); }
.tab-btn.active { color: var(--clr-green); border-bottom-color: var(--clr-green); }

.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* ===== 24. PAGINATION ===== */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: 1rem 0 .5rem;
  font-size: .875rem;
}

.pagination-info { color: var(--text-muted); font-size: .8rem; }

/* Barra de herramientas ARRIBA de la tabla. Es card-footer dado vuelta: el
   borde va abajo en vez de arriba, porque separa del contenido que sigue.

   La paginacion vive aca y no al pie: con 50 filas en pantalla, cambiar de
   pagina u ordenar obligaba a recorrer toda la tabla hacia abajo, hacer el
   clic, y volver a subir para leer el resultado. Arriba queda a la vista junto
   con los filtros, que es donde uno ya esta mirando cuando decide moverse por
   la lista. Es la misma razon por la que Gmail lo tiene arriba. */
.card-toolbar {
  padding: .75rem 1.25rem;
  border-bottom: 1px solid var(--border);
  background: rgba(255,255,255,.02);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .5rem;
}

/* Vacia (sin resultados) la barra no debe dejar una franja fantasma. */
.card-toolbar:empty { display: none; }

/* ── Control estilo Gmail: «1–50 de 6.058» con salto al principio y al final ──
   El rango va al centro y las flechas a la derecha, como en cualquier bandeja
   de correo: es una disposición que la gente ya tiene aprendida, y copiarla
   ahorra tener que explicarla. */
.pagination {
  justify-content: flex-end;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  flex-wrap: wrap;
}

.pagination-size {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  margin-right: auto;   /* empuja el resto del control hacia la derecha */
}

.pagination-size-label {
  color: var(--text-muted);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.pagination-size-select {
  width: auto;
  min-width: 4.5rem;
  padding: .25rem 1.5rem .25rem .5rem;
  font-size: var(--text-sm);
}

.pagination-range { position: relative; }

.pagination-range-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: .35rem .6rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-family: inherit;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--transition), border-color var(--transition);
}

.pagination-range-btn:hover:not(:disabled) {
  background: var(--bg-hover);
  border-color: var(--border);
}

.pagination-range-btn:disabled {
  cursor: default;
  color: var(--text-muted);
}

/* El caret desaparece cuando no hay a dónde saltar: un menú desplegable que se
   abre vacío es peor que no ofrecerlo. */
.pagination-range-btn:disabled .pagination-caret { visibility: hidden; }

.pagination-caret { font-size: .7em; opacity: .7; }

.pagination-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 20;
  min-width: 11rem;
  padding: var(--space-1);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}

.pagination-menu[hidden] { display: none; }

.pagination-menu button {
  display: block;
  width: 100%;
  padding: .5rem .75rem;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
}

.pagination-menu button:hover:not(:disabled) { background: var(--bg-hover); }

/* Deshabilitado, no escondido: que la opción siga a la vista le dice al usuario
   que ya está en ese extremo de la lista. Si desapareciera, el menú cambiaría
   de tamaño entre páginas y costaría entender por qué. */
.pagination-menu button:disabled {
  color: var(--text-muted);
  cursor: default;
}

.pagination-arrows { display: flex; gap: var(--space-1); }

.pagination-arrows .btn {
  min-width: 2rem;
  font-size: 1.1rem;
  line-height: 1;
}

@media (max-width: 600px) {
  .pagination { justify-content: center; }
  .pagination-size { margin-right: 0; }
}

/* ===== 25. FILTER BAR ===== */
.filter-bar {
  display: flex;
  gap: .65rem;
  flex-wrap: wrap;
  align-items: flex-end;
  padding: .75rem 1.25rem;
  background: var(--bg-raised);
  border-bottom: 1px solid var(--border);
}

.filter-bar .form-group { margin-bottom: 0; }
.filter-bar .form-label { font-size: .68rem; }
.filter-bar .form-control { font-size: .82rem; padding: .4rem .6rem; }

/* ===== CAMPANA DE NOTIFICACIONES (barra superior) =====
   Estaba embebido con style="" en dashboard.html. La visibilidad se controla
   con la clase .hidden (base.css), NO tocando element.style desde el JS:
   mezclar ambos hace que el estado dependa de quién escribió último. */

.notif-btn {
  position: relative;
  display: inline-flex;
}

.notif-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  background: var(--clr-red);
  color: var(--clr-white);
  border-radius: 50%;
  font-size: .65rem;
  min-width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  line-height: 1;
  padding: 0 3px;
}

/* ===== SELECTOR DE TEMA (barra superior) =====
   El botón rota automático → claro → oscuro; el icono lo escribe theme.js. */

.theme-toggle {
  font-size: 1rem;
  line-height: 1;
  padding: .35rem .55rem;
}

/* Transición al cambiar de tema. Se aplica sólo a las propiedades de color:
   incluir `all` haría que cada layout shift de la app se anime también.
   No se toca el texto para que la lectura no "flashee" durante el cambio. */
@media (prefers-reduced-motion: no-preference) {
  body,
  .card, .sidebar, .topbar, .modal-dialog, .data-table,
  .form-control, .btn {
    transition: background-color .25s ease, border-color .25s ease, color .25s ease;
  }
}

/* ===== 25. ESQUELETOS DE CARGA =====
   Reemplazan al spinner en los paneles que listan datos. La idea no es
   entretener durante la espera sino RESERVAR EL ESPACIO: cuando llega la
   respuesta, la tabla ocupa el lugar que el esqueleto ya tenía y la página no
   pega el salto que daba al desaparecer un spinner centrado.

   El brillo se hace con una capa translúcida ENCIMA del color base
   (background-image sobre background-color) en vez de un degradado opaco: así
   el mismo gradiente sirve en tema claro y oscuro, y sólo cambian los dos
   tokens. La animación se apaga sola con prefers-reduced-motion — la regla
   global de base.css la reduce a .01ms, y un esqueleto quieto sigue
   comunicando perfectamente que algo está cargando. */

.skeleton {
  padding: var(--space-2) 0;
}

.skeleton-table {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.skeleton-row {
  display: flex;
  gap: var(--space-4);
  align-items: center;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}

/* La fila de cabecera imita el <thead>: barras más cortas y algo más marcadas. */
.skeleton-row-head {
  border-bottom-width: 2px;
}

.skeleton-row-head .skeleton-bar {
  width: 45%;
  opacity: .75;
}

.skeleton-cell {
  flex: 1;
  min-width: 0;
}

.skeleton-bar {
  display: block;
  height: .8rem;
  width: 70%;
  border-radius: var(--radius-sm);
  background-color: var(--skeleton-base);
  background-image: linear-gradient(
    90deg,
    transparent 0%,
    var(--skeleton-shine) 50%,
    transparent 100%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: skeleton-shine 1.4s ease-in-out infinite;
}

@keyframes skeleton-shine {
  from { background-position: 150% 0; }
  to   { background-position: -50% 0; }
}

/* Variante en tarjetas, para las grillas de indicadores del dashboard. */
.skeleton-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-4);
}

.skeleton-card {
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}


/* ── Pie de acciones de un modal ──────────────────────────────────────────────
   Cancelar a la izquierda, la accion principal a la derecha. Estaba escrito
   inline doce veces, con dos margenes distintos (1rem y 1.25rem) por puro
   descuido de copiar y pegar. Se unifica en el valor de la escala: la
   diferencia son 4 px que nadie va a extranar, y es exactamente la clase de
   inconsistencia que la escala de espaciado existe para eliminar. */
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

/* ═══════════════════════════════════════════════════════════════════════════
   PANEL DE PROFORMAS DEL DÍA (executiveStrategy.js)

   Una tarjeta con filete azul a la izquierda que el ejecutivo ve primero al
   entrar. Estaba escrita dos veces con el mismo style —una para el caso con
   datos y otra para el vacío— y las dos repetían el color, el borde y el
   margen. Un solo dueño: la clase.
   ═══════════════════════════════════════════════════════════════════════════ */
.panel-del-dia {
  border-left: 4px solid var(--clr-blue);
  margin-bottom: 1rem;
}

.panel-del-dia .card-header { padding-bottom: .5rem; }
.panel-del-dia .card-header h4 { margin: 0; color: var(--clr-blue); }

/* El texto de «no hay nada hoy». Lleva el padding de la tarjeta porque va
   suelto, sin la caja que envuelve a la tabla. */
.panel-del-dia-vacio { padding: .5rem 1rem 1rem; }

/* Insignia de conteo: cuántas van hoy. A diferencia de las de estado, no
   codifica una situación — es un número —, así que va en el azul de la
   sección y no en un color de la máquina de estados. */
.badge-conteo {
  background: var(--clr-blue);
  color: var(--clr-white);
}

/* Tabla secundaria: la que acompaña a un panel, no la del listado principal. */
.data-table-sm { font-size: .85rem; }

/* ═══════════════════════════════════════════════════════════════════════════
   LO QUE QUEDABA COMO ESTILO INLINE EN LOS PANELES

   Antes de escribir cada regla revisé si la clase ya existía. Varias sí:
   .filter-bar ya hacía flex/gap/wrap/align-end, .truncate ya recortaba con
   puntos suspensivos, .sub-modal ya traía max-width:560px y .item-input ya
   daba el ancho de los campos de la tabla de ítems. Estaban siendo
   reimplementadas inline encima de sí mismas.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Mensaje de error de un formulario. Reserva su alto SIEMPRE, aunque esté
   vacío: sin el min-height, al aparecer el error empuja todo lo de abajo y el
   botón que la persona estaba por apretar se corre de lugar. */
.form-error {
  color: var(--clr-red);
  min-height: 1.2em;
  font-size: .8rem;
}

/* Subtítulo dentro de un modal de detalle: separa bloques (Gastos, Documentos,
   Historial) sin la jerarquía de un encabezado de tarjeta. */
.sub-seccion-title { margin: 1rem 0 .35rem; }

/* Lista sin viñetas ni sangría — la base de toda lista de documentos, gastos
   o eventos. Se repetía como style="list-style:none;padding:0;margin:0". */
.lista-limpia { list-style: none; padding: 0; margin: 0; }

/* Un renglón de esas listas: ícono, nombre que se recorta, acciones. */
.lista-item {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .4rem 0;
  border-bottom: 1px solid var(--border);
}

/* El nombre, que es lo único que puede crecer sin límite. */
.lista-item-nombre {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Fila de acciones al pie de un bloque. */
.acciones-fila {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: .5rem;
}

.acciones-centro { display: flex; justify-content: center; gap: .75rem; }

/* Detalle desplegable de la bitácora: pares dato/valor en dos columnas. */
.audit-dl {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: .4rem 1rem;
}
.audit-dl dt {
  color: var(--text-secondary);
  font-size: .78rem;
  font-weight: 600;
  white-space: nowrap;
}
.audit-dl dd { margin: 0; font-size: .82rem; word-break: break-word; }

.audit-detail-cell { background: var(--bg-raised); padding: .85rem 1rem; }

/* La dirección IP va en monoespaciada: se compara de un vistazo entre filas,
   y con tipografía proporcional los dígitos no quedan alineados. */
.mono { font-family: ui-monospace, 'Cascadia Code', Consolas, monospace; }

/* Encabezado de columna que ordena la tabla al hacer clic. */
.th-ordenable {
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

/* ── BARRA COMPARATIVA (misMetricas) ────────────────────────────────────────
   Emitidas contra cerradas, mes a mes. Dos barras superpuestas: la de atrás
   es el total emitido y la de adelante, en verde, lo que se cerró. La lectura
   es la proporción, no el número. */
.barra-mes {
  display: flex;
  align-items: center;
  gap: .75rem;
}
.barra-mes-etiqueta { min-width: 5rem; }
.barra-mes-cifra    { min-width: 8.5rem; text-align: right; }

.barra-pista {
  flex: 1;
  height: 1.15rem;
  background: var(--bg-hover);
  border-radius: var(--radius-sm);
  overflow: hidden;
  position: relative;
}
.barra-emitidas { height: 100%; background: var(--badge-bg-blue); }
.barra-cerradas { height: 100%; background: var(--clr-green); position: absolute; top: 0; left: 0; }

.panel-barras {
  padding: 1rem 1.25rem;
  display: flex;
  flex-direction: column;
  gap: .6rem;
}

/* ── LÍNEA DE TIEMPO (timelineView) ─────────────────────────────────────── */
.timeline { list-style: none; padding: 0; margin: 0; position: relative; }

.timeline-item {
  display: flex;
  gap: .75rem;
  margin-bottom: .75rem;
  align-items: flex-start;
}

.timeline-punto {
  flex-shrink: 0;
  width: 10px; height: 10px;
  border-radius: 50%;
  margin-top: 4px;
  background: var(--clr-indigo);
}
/* El evento más reciente, arriba de todo. */
.timeline-punto-actual { background: var(--clr-blue); }

.timeline-texto { font-size: .85rem; line-height: 1.4; }

.timeline-seccion {
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}
.timeline-seccion h4 {
  margin-bottom: .75rem;
  font-size: .95rem;
  color: var(--text-secondary);
}

/* Anchos de filtro que faltaban en la escala. */
.fc-xnarrow { min-width: 120px; }

/* Renglón compacto de una lista de archivos elegidos, antes de subirlos. */
.lista-item-compacto {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .15rem 0;
  font-size: .82rem;
}

/* Pares dato/valor de la cabecera de un detalle. */
.datos-dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .25rem .75rem;
  margin: .75rem 0;
}

/* Texto de ayuda al pie del encabezado de un panel. */
.panel-ayuda {
  margin: 0;
  padding: .6rem 1.25rem;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
}

/* La cifra de resultado (ganancia o pérdida) de una licitación. */
.resultado-cifra { font-size: 1.02rem; }

/* Detalle al pie de una tarjeta de indicador. */
.stat-detalle { margin-top: .35rem; line-height: 1.35; }

/* La grilla de indicadores cuando va DENTRO de una tarjeta y no suelta en la
   página: no necesita el margen inferior, que lo pone la tarjeta. */
.stats-grid-en-tarjeta { padding: 1rem 1rem 0; margin-bottom: 0; }

/* Esqueleto de una tarjeta de indicador: un renglón de rótulo y una cifra. */
.skeleton-bar-titulo { width: 55%; height: .65rem; }
.skeleton-bar-cifra  { width: 75%; height: 1.4rem; margin-top: .6rem; }

/* ===== CALENDARIO DE SEGUIMIENTOS (public/js/shared/calendarPicker.js) ===== */

.cal-picker { min-width: 260px; }

.cal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: .5rem;
}

.cal-titulo-mes {
  font-weight: 600;
  color: var(--text-primary);
  text-transform: capitalize;
}

.cal-dias-semana,
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: .25rem;
}

.cal-celda {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 2.1rem;
}

.cal-dia-nombre {
  font-size: .7rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
}

.cal-dia {
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-primary);
  cursor: pointer;
  font-size: .85rem;
  position: relative;
}

.cal-dia:hover { background: var(--bg-hover); }

.cal-dia.cal-hoy { border-color: var(--clr-blue); }

.cal-dia.cal-seleccionado {
  background: var(--clr-violet);
  color: var(--clr-white);
}

.cal-dia.cal-ocupado::after {
  content: '';
  position: absolute;
  bottom: 3px;
  left: 50%;
  transform: translateX(-50%);
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--clr-amber);
}

.cal-dia.cal-seleccionado.cal-ocupado::after { background: var(--clr-white); }

.cal-leyenda { display: flex; align-items: center; gap: .4rem; }

.cal-leyenda-punto {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--clr-amber);
}
