/* =====================================================================
   assets/css/app.css
   Sistema de diseño del panel: paleta de marca, tipografía, layout con
   sidebar colapsable, topbar, tarjetas, tablas/DataTables, formularios,
   botones, badges, login. Soporta tema claro/oscuro vía data-bs-theme
   de Bootstrap 5.3. Los overrides de --bs-* reskinean TODA la interfaz
   (botones, badges, alertas, focus rings) sin tocar cada módulo, porque
   las utilidades de Bootstrap ya están escritas contra estas variables.
   ===================================================================== */

:root {
    /* ---------- Layout ---------- */
    --sidebar-w: 264px;
    --topbar-h: 60px;
    --ticker-h: 34px;

    /* ---------- Marca ---------- */
    --brand: #4f46e5;      /* = --bs-primary, alias usado por el sidebar/login */
    --brand-2: #6366f1;

    /* ---------- Tipografía ---------- */
    --font-sans: 'Inter', system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

    /* ---------- Sombras / radios reutilizables ---------- */
    --shadow-sm: 0 1px 2px rgba(16, 24, 40, .06);
    --shadow-md: 0 4px 10px rgba(16, 24, 40, .08), 0 1px 3px rgba(16, 24, 40, .06);
    --shadow-lg: 0 12px 32px rgba(16, 24, 40, .14);
    --radius: .65rem;
    --radius-sm: .45rem;

    /* ---------- Reskin de la paleta semántica de Bootstrap ----------
       Bootstrap deriva botones outline, badges, alert-*, focus-ring y
       enlaces de estas variables en tiempo de uso (var(--bs-primary)…),
       así que sobreescribirlas aquí ya actualiza casi todo el sistema. */
    --bs-primary: var(--brand);
    --bs-primary-rgb: 79, 70, 229;
    --bs-primary-text-emphasis: #3730a3;
    --bs-primary-bg-subtle: #eef0fd;
    --bs-primary-border-subtle: #c7c8f5;

    --bs-secondary: #64748b;
    --bs-secondary-rgb: 100, 116, 139;
    --bs-secondary-text-emphasis: #384252;
    --bs-secondary-bg-subtle: #f1f4f7;
    --bs-secondary-border-subtle: #d3dae1;

    --bs-success: #16a34a;
    --bs-success-rgb: 22, 163, 74;
    --bs-success-text-emphasis: #0f6e34;
    --bs-success-bg-subtle: #eafbf0;
    --bs-success-border-subtle: #b8ecc9;

    --bs-danger: #dc2626;
    --bs-danger-rgb: 220, 38, 38;
    --bs-danger-text-emphasis: #9c1c1c;
    --bs-danger-bg-subtle: #fdecec;
    --bs-danger-border-subtle: #f6bcbc;

    --bs-warning: #f59e0b;
    --bs-warning-rgb: 245, 158, 11;
    --bs-warning-text-emphasis: #92610b;
    --bs-warning-bg-subtle: #fef6e7;
    --bs-warning-border-subtle: #fbdfa4;

    --bs-info: #0ea5e9;
    --bs-info-rgb: 14, 165, 233;
    --bs-info-text-emphasis: #08698f;
    --bs-info-bg-subtle: #e8f6fd;
    --bs-info-border-subtle: #a9dff6;

    --bs-body-bg: #e4e8f1;
    --bs-body-font-family: var(--font-sans);

    /* Patrón de contraste invertido respecto al primer intento: probar dos grises
       parecidos (página gris clarito + panel un poco más oscuro) no se notaba.
       Ahora es el patrón clásico de dashboard: el FONDO DE PÁGINA es el que lleva
       el color/gris notorio (--bs-body-bg), y los contenedores de formulario
       (.card/.modal/.dropdown — listados, tarjetas "Nuevo X" inline, el propio
       modal, menús) quedan blanco puro, "flotando" encima y separándose limpio
       de la página. Si se cambia --bs-body-bg de nuevo, estas tres siguen blanco
       puro sin necesidad de tocarlas. */
    --bs-card-bg: #ffffff;
    --bs-modal-bg: #ffffff;
    --bs-dropdown-bg: #ffffff;
}

body { min-height: 100vh; font-family: var(--font-sans); }

/* ---------- Botones sólidos: Bootstrap precompila el hover/active de
   .btn-primary/.btn-success como hex fijos (no var()), así que hay que
   redeclararlos para que sigan la marca nueva en todos los estados. ---------- */
.btn { border-radius: var(--radius-sm); font-weight: 500; }
.btn-primary {
    --bs-btn-bg: #4f46e5; --bs-btn-border-color: #4f46e5;
    --bs-btn-hover-bg: #4338ca; --bs-btn-hover-border-color: #4338ca;
    --bs-btn-active-bg: #3730a3; --bs-btn-active-border-color: #3730a3;
    --bs-btn-disabled-bg: #4f46e5; --bs-btn-disabled-border-color: #4f46e5;
    --bs-btn-focus-shadow-rgb: 79, 70, 229;
    box-shadow: 0 1px 2px rgba(79, 70, 229, .25);
}
.btn-success {
    --bs-btn-bg: #16a34a; --bs-btn-border-color: #16a34a;
    --bs-btn-hover-bg: #128a3e; --bs-btn-hover-border-color: #128a3e;
    --bs-btn-active-bg: #0f6e34; --bs-btn-active-border-color: #0f6e34;
    --bs-btn-disabled-bg: #16a34a; --bs-btn-disabled-border-color: #16a34a;
    --bs-btn-focus-shadow-rgb: 22, 163, 74;
}
/* Barras de progreso: mismo problema que los botones sólidos —
   --bs-progress-bar-bg viene precompilado como el azul por defecto de
   Bootstrap (hex fijo, no var()), así que toda .progress-bar del sistema
   quedaba azul aunque la marca sea indigo (detectado en ONB-SALUD-2.4
   sobre el wizard de configuración guiada; regla global a propósito,
   igual criterio que el hover de .btn-primary de arriba). */
.progress-bar { background-color: var(--bs-primary); }
/* ---------- Foco / formularios ---------- */
.form-control, .form-select { border-radius: var(--radius-sm); }
.form-control:focus, .form-select:focus {
    border-color: #a5a1f0;
    box-shadow: 0 0 0 .25rem rgba(79, 70, 229, .18);
}
.form-label { font-weight: 500; font-size: .875rem; color: var(--bs-secondary-color); }

/* Checkboxes/radios sin marcar: Bootstrap por defecto usa --bs-border-color para el
   borde, pensado para líneas divisorias sutiles (tarjetas, tablas) — como borde de
   checkbox casi se pierde contra el fondo blanco de una tarjeta, y lo mismo en modo
   oscuro contra el negro de sidebar/tarjetas. --bs-secondary-color (texto atenuado,
   ya theme-aware: ~75% negro en claro, ~75% gris claro en oscuro) da bastante más
   contraste sin tener que redeclarar un color aparte por tema. */
.form-check-input { border-color: var(--bs-secondary-color); }
.form-check-input:checked { border-color: var(--bs-primary); }

/* ---------- Badges: chips redondeados, siempre texto + color (nunca solo color) ---------- */
.badge { border-radius: 999px; font-weight: 600; letter-spacing: .01em; padding: .42em .75em; }

/* ---------- Tarjetas ---------- */
.card {
    background-color: #ffffff;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
.page-title { font-size: 1.3rem; font-weight: 700; letter-spacing: -.01em; }
.page-title i { font-size: 1em; }

/* ---------- Breadcrumb ---------- */
.breadcrumb { background: transparent; padding: 0; font-size: .85rem; }
.breadcrumb-item a { color: var(--bs-secondary-color); text-decoration: none; }
.breadcrumb-item a:hover { color: var(--bs-primary); }
.breadcrumb-item.active { color: var(--bs-body-color); font-weight: 600; }

/* ---------- Layout general ---------- */
.app-wrapper { min-height: 100vh; }

.app-topbar {
    position: fixed; top: 0; left: var(--sidebar-w); right: 0;
    height: var(--topbar-h);
    display: flex; align-items: center; gap: .75rem;
    padding: 0 1.25rem;
    background: #ffffff;
    border-bottom: 1px solid var(--bs-border-color);
    box-shadow: var(--shadow-sm);
    z-index: 1030;
    transition: left .25s ease;
}

/* ---------- Ticker (marquesina bajo el topbar): indicadores económicos + clima ----------
   Ver layouts/header.php (HTML/JS) + ajax/ticker.php + classes/TickerDatos.php. */
.app-ticker {
    position: fixed; top: var(--topbar-h); left: var(--sidebar-w); right: 0;
    height: var(--ticker-h);
    background: var(--bs-card-bg, #fff);
    border-bottom: 1px solid var(--bs-border-color);
    overflow: hidden;
    z-index: 1029;
    display: flex; align-items: center;
    transition: left .25s ease;
}
.app-ticker-track {
    display: inline-flex; align-items: center; white-space: nowrap;
    will-change: transform;
}
.app-ticker-item {
    display: inline-flex; align-items: center; gap: .35rem;
    font-size: .8rem; color: var(--bs-body-color);
}
.app-ticker-item i { color: var(--brand); }
.app-ticker-sep { color: var(--bs-border-color); }
@keyframes app-ticker-scroll {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}
.app-main {
    margin-left: var(--sidebar-w);
    padding: calc(var(--topbar-h) + var(--ticker-h) + 1.5rem) 1.5rem 2.5rem;
    transition: margin-left .25s ease;
    /* Red de seguridad: ningún contenido ancho (tabla, grilla táctil de POS,
       carta Gantt, etc.) puede empujar la página completa fuera del viewport
       en mobile. Los elementos que SÍ necesitan scroll horizontal propio
       (.dataTables_wrapper, .table-responsive, .pos-grid, .gantt-*) manejan
       su propio overflow-x internamente, así que siguen siendo desplazables. */
    overflow-x: hidden;
}

/* ---------- Sidebar ---------- */
.app-sidebar {
    position: fixed; top: 0; left: 0; bottom: 0;
    width: var(--sidebar-w);
    background: linear-gradient(180deg, #111827 0%, #0d1220 100%);
    color: #d1d5db;
    overflow-y: auto; overflow-x: hidden;
    z-index: 1040;
    transition: transform .25s ease;
    box-shadow: var(--shadow-lg);
}
.app-sidebar::-webkit-scrollbar { width: 6px; }
.app-sidebar::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .12); border-radius: 999px; }

.sidebar-brand {
    display: flex; align-items: center; gap: .65rem;
    height: var(--topbar-h);
    padding: 0 1.25rem;
    font-weight: 700; font-size: 1.05rem; color: #fff;
    border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.sidebar-brand i {
    display: inline-grid; place-items: center;
    width: 2rem; height: 2rem; border-radius: .55rem; font-size: 1.05rem;
    background: linear-gradient(135deg, var(--brand), var(--brand-2)); color: #fff;
}
.sidebar-brand img {
    width: 2rem; height: 2rem; object-fit: contain; flex-shrink: 0;
}

/* Reloj del sidebar (fecha/hora de Santiago, Chile) — contenedor con borde de
   marca y fondo más claro que el propio fondo oscuro del sidebar (overlay
   blanco translúcido en vez de un tono fijo, para que "más claro que el fondo"
   siga siendo cierto tanto en el gradiente de modo claro como en el negro
   puro de modo oscuro, sin declarar dos colores distintos). Ver layouts/sidebar.php. */
.sidebar-reloj {
    margin: .85rem .9rem .35rem;
    padding: .55rem .75rem;
    border: 1px solid var(--brand);
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, .08);
    display: flex; flex-direction: column; gap: .1rem;
}
.sidebar-reloj-fecha {
    font-size: .72rem; color: #a1a8b7; text-transform: capitalize;
}
.sidebar-reloj-hora {
    font-size: 1.05rem; font-weight: 600; color: #fff; letter-spacing: .02em;
}

.menu { list-style: none; margin: .6rem 0; padding: 0 .6rem; }
.menu-item { position: relative; margin-bottom: 2px; }
.menu-link {
    display: flex; align-items: center; gap: .7rem;
    padding: .55rem .7rem;
    border-radius: var(--radius-sm);
    color: #a1a8b7; text-decoration: none; font-size: .9rem; font-weight: 500;
    transition: background-color .15s ease, color .15s ease;
}
.menu-link:hover { color: #fff; background: rgba(255, 255, 255, .07); }
.menu-link.active {
    color: #fff;
    background: rgba(var(--bs-primary-rgb), .28);
}
.menu-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.65rem; height: 1.65rem; border-radius: .5rem; font-size: .85rem;
    background: rgba(255, 255, 255, .05); flex: none;
    transition: background-color .15s ease;
}
.menu-link:hover .menu-icon { background: rgba(255, 255, 255, .1); }
.menu-link.active .menu-icon { background: var(--brand); color: #fff; }
.menu-caret { margin-left: auto; font-size: .65rem; transition: transform .2s ease; opacity: .7; }
.menu-item.open > .menu-link .menu-caret { transform: rotate(90deg); }

.menu-sub {
    list-style: none; padding-left: 1.1rem; margin: 0;
    max-height: 0; overflow: hidden;
    transition: max-height .25s ease;
}
.menu-item.open > .menu-sub { max-height: 3000px; }

/* Sidebar colapsado (escritorio) / oculto (móvil) */
@media (min-width: 992px) {
    body.sidebar-collapsed .app-sidebar { transform: translateX(calc(-1 * var(--sidebar-w))); }
    body.sidebar-collapsed .app-topbar  { left: 0; }
    body.sidebar-collapsed .app-ticker  { left: 0; }
    body.sidebar-collapsed .app-main    { margin-left: 0; }
    .sidebar-backdrop { display: none; }
}
@media (max-width: 991.98px) {
    .app-sidebar { transform: translateX(calc(-1 * var(--sidebar-w))); }
    .app-topbar  { left: 0; }
    .app-ticker  { left: 0; }
    .app-main    { margin-left: 0; }
    body.sidebar-open .app-sidebar { transform: none; }
    .sidebar-backdrop {
        display: none; position: fixed; inset: 0;
        background: rgba(0, 0, 0, .45); z-index: 1035;
    }
    body.sidebar-open .sidebar-backdrop { display: block; }
}

/* ---------- Topbar ---------- */
.btn-icon {
    border: 0; background: transparent; color: var(--bs-body-color);
    padding: .45rem .65rem; border-radius: var(--radius-sm);
    transition: background-color .15s ease;
}
.btn-icon:hover { background: var(--bs-secondary-bg); }

.topbar-search { position: relative; width: 320px; }
.topbar-search i {
    position: absolute; left: .85rem; top: 50%; transform: translateY(-50%);
    color: var(--bs-secondary-color); font-size: .8rem;
}
.topbar-search input { padding-left: 2.1rem; border-radius: 999px; background: var(--bs-secondary-bg); border-color: transparent; }
.topbar-search input:focus { background: var(--bs-body-bg); }

/* ---------- Tablas / DataTables ----------
   Selector sobre .table (la clase Bootstrap que llevan TODAS las tablas del
   sistema, tablaExportable()/tabla() de DataTables o no — ej. las tablas de
   líneas dinámicas de Presupuestos/Pedidos/Órdenes de compra que nunca pasan
   por DataTables) en vez de table.dataTable a secas, para que ningún thead
   se quede afuera solo por no estar inicializado con el plugin. */
.table > thead { border-bottom-width: 2px; }
.table thead th {
    background: #000000;
    font-size: .74rem; text-transform: uppercase; letter-spacing: .04em;
    font-weight: 700; color: #ffffff;
    white-space: nowrap;
}
table.dataTable thead th.sorting:before,
table.dataTable thead th.sorting:after,
table.dataTable thead th.sorting_asc:before,
table.dataTable thead th.sorting_asc:after,
table.dataTable thead th.sorting_desc:before,
table.dataTable thead th.sorting_desc:after {
    color: #ffffff; opacity: .6;
}
table.dataTable tbody tr { transition: background-color .1s ease; }
table.dataTable tbody tr:hover { background-color: rgba(var(--bs-primary-rgb), .04); }
.table td.text-end, .table th.text-end { font-variant-numeric: tabular-nums; }

/* La celda de acciones de todo listado (tabla()/tablaExportable()) se marca
   siempre con .no-exportar para excluirla de PDF/Excel — se reutiliza acá
   como gancho para que sus botones nunca salten a una segunda línea dentro
   de la celda cuando hay varios (editar/eliminar/imprimir/cambiar estado,
   etc.): el ancho lo absorbe el scroll horizontal de .dataTables_wrapper de
   abajo, no un wrap vertical que descuadra el alto de la fila. Aplica
   automáticamente a todo módulo, sin tocarlo uno por uno. */
td.no-exportar { white-space: nowrap; }

/* Contiene el scroll horizontal de una tabla ancha DENTRO de su propio
   wrapper en pantallas chicas, en vez de desbordar toda la página (las
   columnas de un listado con muchas columnas/badges/botones empujaban
   el layout completo hacia el eje X en mobile). DataTables ya envuelve
   cada tabla en .dataTables_wrapper por su cuenta, así que esto aplica
   automáticamente a TODO listado inicializado con tabla()/tablaExportable()
   sin tener que tocar un solo módulo. */
.dataTables_wrapper { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }

.dataTables_wrapper .dataTables_filter input,
.dataTables_wrapper .dataTables_length select {
    border-radius: var(--radius-sm); border: 1px solid var(--bs-border-color);
}
.dataTables_wrapper .dataTables_paginate .page-link { border-radius: var(--radius-sm); margin: 0 .15rem; }
.dt-buttons .btn { border-radius: var(--radius-sm); font-size: .82rem; }

/* Espaciado de listados (regla global, corrección a pedido explícito del
   usuario — "las tablas quedan pegadas a los bordes"): el patrón estándar de
   TODO listado del sistema es `.card > .table-responsive > <table>` (ver
   docs/generacion-modulos.md), sin `.card-body` de por medio — Bootstrap solo
   agrega padding a `.card-body`, así que ese `.table-responsive` quedaba
   pegado directo al borde de la tarjeta en los ~96 módulos que siguen este
   patrón (filtros/buscador de DataTables, tabla y paginador sin aire
   alrededor). Selector de HIJO DIRECTO (`.card > .table-responsive`) a
   propósito, para no generar padding doble: nunca aplica si el listado ya
   vive dentro de un `.card-body` (que ya trae su propio padding de Bootstrap
   — ahí `.table-responsive` sería nieto de `.card`, no hijo) ni si la propia
   página ya puso un `p-*` a mano sobre el `.table-responsive` (los
   utilitarios de Bootstrap llevan `!important`, siguen ganando sobre esta
   regla). Envuelve TODO de una sola vez: la fila de buscador/botones de
   exportar (`Bf`/`lp` del `dom` de `tablaExportable()`, que ya trae su propio
   `mb-2`/`mt-2` interno) + la tabla + el paginador — automático para
   cualquier módulo nuevo o existente, sin tocarlo uno por uno. */
.card > .table-responsive { padding: 1rem; }

/* ---------- SYSADMIN → Usuarios / Organizaciones (UI-FIX-SYSADMIN-1) ----------
   admin/usuarios.php y admin/organizaciones.php son admin/ puro, fuera del
   patrón modules/{dir} generado por tabla()/tablaExportable() — por eso nunca
   habían adoptado table-responsive ni las convenciones de acciones/truncado
   del resto del sistema, y las filas se desconfiguraban al sumarles más
   acciones/columnas. Causas reales encontradas (no eran solo falta de CSS):
     - La celda de Nombre de Usuarios tenía display:flex EN el propio <td>
       (rompe el modelo de caja de la celda, pelea con el align-middle de la
       tabla) — corregido: el flex vive en un <span> interno, nunca en el <td>.
     - Nombre+RUT de Organizaciones usaba un <br> duro dentro de una sola
       celda, con el RUT emitiéndose siempre aunque estuviera vacío — dos
       <div> (RUT solo si existe) truncados por igual, altura consistente.
     - La columna Acciones de ambas tablas era una secuencia suelta de
       botones/formularios sin contenedor común: al angostarse la columna
       (DataTables, mobile) saltaban a una segunda línea de forma desprolija
       — ahora un único d-flex/gap/flex-nowrap por fila (mismo lenguaje visual
       en las dos pantallas), más `.no-exportar` (ya usada en el resto del
       sistema) como red de seguridad de white-space:nowrap en la celda.
   Selector acotado a .tabla-sysadmin (marcada en ambas <table>), nunca reglas
   globales sobre .table/td que afectarían los ~96 módulos restantes. */
.tabla-sysadmin .td-truncate {
    display: inline-block; max-width: 200px; vertical-align: middle;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tabla-sysadmin .td-email { display: inline-block; max-width: 240px; vertical-align: middle; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tabla-sysadmin .td-truncate-block { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Botones de exportar/imprimir (tablaExportable() en app.js): estilo "chip"
   tenue en vez de outline, para que se lean como acciones de la tabla y no
   compitan visualmente con los botones primarios de la página. Clases
   propias (no btn-outline-*) para no depender de cómo DataTables Buttons
   combine clases internamente. */
.btn-soft-danger, .btn-soft-success, .btn-soft-secondary {
    border-width: 1px; border-style: solid; font-weight: 600;
    transition: background-color .15s ease, border-color .15s ease;
}
.btn-soft-danger {
    background-color: var(--bs-danger-bg-subtle); border-color: var(--bs-danger-border-subtle); color: var(--bs-danger-text-emphasis);
}
.btn-soft-danger:hover { background-color: var(--bs-danger-border-subtle); color: var(--bs-danger-text-emphasis); }
.btn-soft-success {
    background-color: var(--bs-success-bg-subtle); border-color: var(--bs-success-border-subtle); color: var(--bs-success-text-emphasis);
}
.btn-soft-success:hover { background-color: var(--bs-success-border-subtle); color: var(--bs-success-text-emphasis); }
.btn-soft-secondary {
    background-color: var(--bs-secondary-bg-subtle); border-color: var(--bs-secondary-border-subtle); color: var(--bs-secondary-text-emphasis);
}
.btn-soft-secondary:hover { background-color: var(--bs-secondary-border-subtle); color: var(--bs-secondary-text-emphasis); }

/* ---------- Modales ---------- */
.modal-content { background-color: #ffffff; border: 0; border-radius: var(--radius); box-shadow: var(--shadow-lg); }
.modal-header { border-bottom-color: var(--bs-border-color); }
.modal-footer { border-top-color: var(--bs-border-color); }

/* Modal "workspace": para modales que agrupan muchas pestañas con contenido
   propio (listados, formularios inline, gráficos tipo Carta Gantt) — más
   ancho que el modal-xl de Bootstrap (tope 1140px), para que nada se vea
   apretado. Se agrega JUNTO a modal-xl en el HTML (no lo reemplaza): sirve
   de tope adicional en pantallas grandes, min() evita que se pase del
   viewport en pantallas chicas. Reutilizable por cualquier modal actual o
   futuro con esta misma necesidad de espacio (ver Eventos → Eventos). */
.modal-dialog.modal-workspace { max-width: min(1400px, 96vw); }

/* Modales anidados: varias pestañas embebidas del modal-workspace de Eventos
   → Eventos (Personal del Evento, Plano, Checklist, Rueda de Negocios,
   Patrocinios, ver CLAUDE.md) abren un SEGUNDO modal propio (ej. "Asignar
   personal") sin cerrar el modal grande de atrás — Bootstrap no soporta
   modales anidados oficialmente. De paso, cada modal/backdrop anidado sube
   su z-index por encima del anterior, para quedar siempre visualmente por
   delante y no a medias detrás del padre. */
.modal .modal.show { z-index: 1070; }
.modal .modal-backdrop.show { z-index: 1065; }

/* Alto fijo para CUALQUIER .modal-dialog-scrollable (no solo modales
   anidados — corregido 2026-08, reportado sobre Gestión Colegios → PIE →
   Ficha PIE, un modal de PRIMER nivel, no anidado, con muchas secciones
   apiladas: Equipo/Evaluaciones/NEE/FUDEI/Reevaluaciones/Plan/
   Intervenciones/Trabajo Colaborativo/Adecuaciones). Causa real, la MISMA
   en ambos casos (nested o no):
   1) Bootstrap calcula el alto de `.modal-dialog-scrollable` como
      `height: calc(100% - margen*2)`, un PORCENTAJE relativo a su `.modal`
      ancestro. Si ese ancestro es a su vez descendiente de OTRO `.modal` ya
      abierto (modal anidado), esa cadena de porcentajes puede no resolver a
      un alto definido y el navegador cae a un alto "auto" — el modal crece
      tanto como pida su contenido.
   2) INCLUSO cuando el modal NO está anidado (un solo `.modal`, alto 100%
      del viewport correctamente resuelto): casi todo modal de este sistema
      envuelve `.modal-header/.modal-body/.modal-footer` en un `<form>`
      (necesario para el submit nativo) — ese `<form>` es un simple bloque,
      no un flex item que participe en el `flex-shrink` de `.modal-content`,
      así que aunque `.modal-content` quede acotado, nada obliga a
      `.modal-body` a achicarse: renderiza a su alto NATURAL completo y el
      sobrante se recorta en silencio por el `overflow:hidden` de
      `.modal-content` — mismo síntoma exacto que el caso anidado (contenido
      inalcanzable, sin scrollbar), simplemente necesita un modal con
      contenido MUY largo para notarse (los ~90 módulos con modales cortos
      nunca lo disparan; Ficha PIE con 9 secciones sí).
   En ambos casos se corrige fijando un alto explícito en `vh` (no depende
   de ningún cálculo de porcentaje ni de que el navegador redistribuya
   flex-shrink correctamente): 88vh del diálogo, menos un margen generoso
   para header+footer+bordes en `.modal-body`. Con esto `.modal-body`
   SIEMPRE activa su propio scroll interno (`overflow-y:auto` ya viene de
   Bootstrap) mucho antes de que el conjunto se pase del alto del diálogo,
   dejando header y footer siempre visibles y clickeables — verificado con
   captura real de Chrome headless (modal de 10 secciones largas: sin este
   fix el modal crecía a lo alto de su contenido sin scrollbar ni footer
   visibles; con el fix, footer fijo y `.modal-body` con scroll interno
   real hasta el final del contenido). Un modal corto simplemente nunca
   activa el scroll (su contenido ya entra en los 88vh), sin cambio visual
   perceptible para esos casos. */
.modal-dialog-scrollable {
    height: 88vh;
    max-height: 88vh;
}
.modal-dialog-scrollable .modal-body {
    max-height: calc(88vh - 160px);
}

/* "Destello" de cambio aplicado: resalta en pastel un campo que se acaba de
   autocompletar (ej. al elegir un participante en Eventos → Itinerario, que
   asigna su nombre + rol al campo de texto de un clic), para que el cambio
   se note aunque el usuario no esté mirando el campo en ese instante.
   .input-flash-objetivo va SIEMPRE en el input (define la transición);
   .input-flash-activo la agrega el JS y la saca ~700ms después — el color
   entra al toque (transition: none en la combinación) y se desvanece solo
   al sacar la clase, porque la transición sigue viva en la base. Reutilizable
   por cualquier otro campo que se autocomplete a futuro. */
.input-flash-objetivo { transition: background-color 1.1s ease; }
.input-flash-objetivo.input-flash-activo { background-color: var(--bs-warning-bg-subtle); transition: none; }

/* Pestañas dentro de modales (Datos generales/Ítems/Tareas/Costos/...): el estilo
   "línea fina" de Bootstrap por defecto casi no se notaba. Ahora son una botonera
   segmentada — cada pestaña, activa o no, se ve como un botón propio (fondo +
   borde), no solo texto — para cualquier modal actual o futuro con <ul class="nav nav-tabs">.
   Inactivas: pastel de marca claramente visible (color-mix() sobre --bs-primary,
   no el --bs-primary-bg-subtle "de catálogo" que Bootstrap usa para badges/alerts
   tenues — ese quedaba casi imperceptible acá) para que se note a simple vista que
   son botones interactivos, y para que contrasten fuerte con el azul intenso sólido
   de la pestaña activa — nunca quedan en blanco liso ni parecen texto plano. */
.modal .nav-tabs {
    border-bottom: 0;
    display: flex; flex-wrap: wrap; gap: .4rem;
    padding: .4rem;
    background: var(--bs-tertiary-bg);
    border-radius: var(--radius-sm);
    margin-bottom: 1.1rem;
}
.modal .nav-tabs .nav-item { margin-bottom: 0; }
.modal .nav-tabs .nav-link {
    border: 1px solid color-mix(in srgb, var(--bs-primary) 45%, white);
    border-radius: calc(var(--radius-sm) - 2px);
    padding: .5rem 1.05rem;
    font-weight: 600;
    font-size: .85rem;
    color: var(--bs-primary-text-emphasis);
    background: color-mix(in srgb, var(--bs-primary) 24%, white);
    box-shadow: 0 1px 1px rgba(16, 24, 40, .04);
    transition: background-color .15s ease, color .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.modal .nav-tabs .nav-link:hover {
    color: #fff;
    border-color: var(--bs-primary);
    background: color-mix(in srgb, var(--bs-primary) 65%, white);
}
.modal .nav-tabs .nav-link.active {
    color: #fff;
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    box-shadow: var(--shadow-sm);
}

/* ---------- Avatar de usuario (foto de perfil o ícono genérico) ----------
   Generado por avatar_html() (PHP) / avatarHtml() (JS). El tamaño se fija
   inline por cada llamado; acá solo el recorte circular y el borde. */
.avatar-img {
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--bs-border-color);
    background: var(--bs-tertiary-bg);
    flex: none;
}
.avatar-icon { line-height: 1; flex: none; }

/* ---------- Foto de Producto (foto representativa o ícono genérico) ----------
   Generado por producto_foto_html() (PHP) / productoFotoHtml() (JS). Mismo
   criterio que .avatar-img pero con recorte cuadrado (no circular): una foto
   de producto se ve mejor como miniatura rectangular que como avatar redondo. */
.producto-foto-img {
    border-radius: .375rem;
    object-fit: cover;
    border: 1px solid var(--bs-border-color);
    background: var(--bs-tertiary-bg);
    flex: none;
}

/* Foto grande del modal "Mi perfil". */
.avatar-preview-lg {
    width: 96px; height: 96px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid var(--bs-border-color);
    background: var(--bs-tertiary-bg);
}
[data-bs-theme="dark"] .modal .nav-tabs { background: #1f2330; }
/* --bs-primary-bg-subtle (#eef0fd) es un pastel pensado para fondo claro: se vería
   como un cartel encendido sobre un modal oscuro. Acá va su equivalente "pastel
   oscuro" a mano (tinte de --brand mezclado con el panel oscuro, no un gris plano),
   para que la pestaña inactiva siga leyéndose como "azul tenue" en vez de gris neutro,
   y siga contrastando con el azul intenso sólido de la pestaña activa. */
[data-bs-theme="dark"] .modal .nav-tabs .nav-link { background: #3a3d75; border-color: #5f64b8; color: #d4d5ff; }
[data-bs-theme="dark"] .modal .nav-tabs .nav-link:hover { background: #4d51a0; border-color: var(--bs-primary); color: #f0f0ff; }

/* ---------- Login ---------- */
.login-body {
    display: flex; align-items: center; justify-content: center;
    min-height: 100vh; padding: 1rem;
    background:
        radial-gradient(circle at 15% 15%, rgba(99, 102, 241, .35), transparent 45%),
        radial-gradient(circle at 85% 85%, rgba(79, 70, 229, .25), transparent 45%),
        linear-gradient(135deg, #0d1220 0%, #1e1b4b 100%);
    font-family: var(--font-sans);
}
.login-card {
    width: 100%; max-width: 400px;
    background: var(--bs-body-bg);
    border-radius: 1.1rem; padding: 2.4rem;
    box-shadow: 0 24px 60px rgba(0, 0, 0, .4);
    animation: login-in .35s ease;
}
@keyframes login-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
.login-logo {
    width: 64px; height: 64px; margin: 0 auto;
    display: grid; place-items: center;
    border-radius: 1rem; font-size: 1.7rem; color: #fff;
    background: linear-gradient(135deg, var(--brand), var(--brand-2));
    box-shadow: 0 8px 20px rgba(79, 70, 229, .35);
}

/* ---------- Tema oscuro: negro real, no azul-marino tenue ---------- */
[data-bs-theme="dark"] {
    --bs-body-bg: #000000;
    --bs-tertiary-bg: #141414;
    --bs-card-bg: #111111;
    --bs-modal-bg: #111111;
    --bs-dropdown-bg: #161616;
    --bs-border-color: #2c2c2c;

    /* Los tokens "-bg-subtle/-border-subtle/-text-emphasis" (badges, alerts,
       btn-soft-*, y los fondos de las tarjetas de Mesas: mesa-libre/mesa-ocupada)
       solo estaban declarados una vez, en el :root de arriba, con valores claros
       (ej. --bs-success-bg-subtle: #eafbf0). Como :root sigue aplicando aunque
       el tema sea oscuro, esas tarjetas quedaban con fondo pastel CLARO +
       texto gris claro (--bs-secondary-color, ya sí theme-aware) encima — de ahí
       que en Mesas el texto se perdiera contra el color del cuadro. Acá van sus
       equivalentes oscuros, mismo criterio que --bs-card-bg/--bs-modal-bg arriba. */
    --bs-primary-text-emphasis: #b4b0f5;
    --bs-primary-bg-subtle: #221f45;
    --bs-primary-border-subtle: #3d3878;

    --bs-secondary-text-emphasis: #cbd5e1;
    --bs-secondary-bg-subtle: #232a35;
    --bs-secondary-border-subtle: #3a4452;

    --bs-success-text-emphasis: #7ee2a8;
    --bs-success-bg-subtle: #0f2c1c;
    --bs-success-border-subtle: #1c5934;

    --bs-danger-text-emphasis: #f5a3a3;
    --bs-danger-bg-subtle: #3a1414;
    --bs-danger-border-subtle: #6b2323;

    --bs-warning-text-emphasis: #f5cd7e;
    --bs-warning-bg-subtle: #3a2c0d;
    --bs-warning-border-subtle: #6b4f17;

    --bs-info-text-emphasis: #8fd7f7;
    --bs-info-bg-subtle: #0d2733;
    --bs-info-border-subtle: #17495e;
}
[data-bs-theme="dark"] .app-sidebar { background: #000000; }
[data-bs-theme="dark"] .app-topbar { background: #000000; }
[data-bs-theme="dark"] .app-ticker { background: #000000; }
[data-bs-theme="dark"] .card { background-color: var(--bs-card-bg); box-shadow: none; }
[data-bs-theme="dark"] .modal-content { background-color: var(--bs-modal-bg); }
[data-bs-theme="dark"] .login-card { background: #0a0a0a; }
[data-bs-theme="dark"] table.dataTable tbody tr:hover { background-color: rgba(var(--bs-primary-rgb), .12); }

/* ---------- Dashboard: mini-gráficos (barras horizontales + línea, sin librería) ----------
   Ver modules/dashboard/index.php. Especificaciones: barra ≤24px de grosor,
   extremo redondeado 4px (el opuesto a la base, que queda en escuadra), 2px
   de espacio de superficie entre marcas; línea 2px, marcador ≥8px con anillo
   de superficie; el texto (etiquetas/valores) SIEMPRE en tinta neutra, nunca
   con el color de la serie. */
.dash-bar-row { display: flex; align-items: center; gap: .6rem; }
.dash-bar-label {
    flex: 0 0 128px; font-size: .8rem; color: var(--bs-secondary-color);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dash-bar-track { flex: 1 1 auto; height: 18px; background: var(--bs-tertiary-bg); border-radius: var(--radius-sm); overflow: hidden; }
.dash-bar-fill {
    height: 100%; border-radius: 0 4px 4px 0; min-width: 3px;
    transition: width .3s ease;
}
.dash-bar-value { flex: 0 0 auto; min-width: 2.2rem; text-align: end; font-size: .82rem; font-weight: 600; color: var(--bs-body-color); }

.dash-line-svg { width: 100%; height: auto; overflow: visible; }
.dash-line-axis { stroke: var(--bs-border-color); stroke-width: 1; }
.dash-line-area { fill: var(--bs-primary); opacity: .1; }
.dash-line-path { fill: none; stroke: var(--bs-primary); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }

/* Calendario general del Dashboard (Reuniones/CxC/CxP/Despachos/Eventos/
   Tareas/Servicios/Mantenimientos) — mismo patrón CSS Grid puro que
   Despachos → Calendario y Reuniones → vista Calendario, acá global
   porque el Dashboard no es un módulo de área con su propio <style>. */
.dash-cg-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.dash-cg-dia-nombre { text-align: center; font-weight: 600; font-size: .8rem; color: var(--bs-secondary-color); padding: 4px 0; }
/* min-width:0 es lo que evita que una celda con un título largo (ej. "Pedido
   N° 000001 — {título de la tarea}") fuerce su columna a crecer por encima
   de 1fr — sin esto, un ítem de texto largo (aunque tenga overflow/ellipsis)
   sigue exigiendo su ancho mínimo de contenido para el track del grid, y las
   columnas a la derecha se achican para compensar. */
.dash-cg-celda { border: 1px solid var(--bs-border-color); border-radius: .4rem; min-height: 96px; padding: 4px; background: var(--bs-card-bg); min-width: 0; overflow: hidden; }
.dash-cg-celda.fuera-de-mes { background: var(--bs-body-bg); opacity: .55; }
.dash-cg-celda.hoy { border-color: var(--bs-primary); border-width: 2px; }
.dash-cg-numero { font-size: .78rem; font-weight: 600; margin-bottom: 2px; }
.dash-cg-item { font-size: .68rem; border-radius: .3rem; padding: 2px 5px; margin-bottom: 2px; cursor: pointer; border-left: 3px solid; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-cg-ver-todos:hover { text-decoration-thickness: 2px; }
.dash-cg-dia-item { cursor: pointer; transition: background-color .15s; }
.dash-cg-dia-item:hover { background: var(--bs-secondary-bg-subtle, var(--bs-tertiary-bg)); }

/* Vista agenda del Calendario general (DASH-1F § 12/§ 13) — predeterminada
   bajo el breakpoint `md` (767.98px) en vez de la grilla de 7 columnas
   (`.dash-cg-grid`, oculta ahí vía `d-none d-md-block` en el propio HTML),
   pintada por assets/js/dashboard.js::pintarAgendaCg() a partir del mismo
   `porFechaCg` que ya arma la grilla — mismo criterio de color/ícono/
   etiqueta (nunca solo color) y mismo click `.dash-cg-ver` delegado a
   nivel de documento, sin bind aparte ni datos nuevos. */
.dash-cg-agenda-fecha {
    font-weight: 600; font-size: .82rem; color: var(--bs-secondary-color); text-transform: capitalize;
    padding: .6rem 0 .3rem; border-bottom: 1px solid var(--bs-border-color); margin-bottom: .4rem;
}
.dash-cg-agenda-fecha:first-child { padding-top: 0; }
.dash-cg-agenda-fecha.hoy { color: var(--bs-primary); }
.dash-cg-agenda-item {
    display: flex; align-items: flex-start; gap: .6rem; padding: .5rem .6rem;
    border-left: 3px solid; border-radius: .4rem; background: var(--bs-tertiary-bg);
    margin-bottom: .4rem; cursor: pointer; min-width: 0;
}
.dash-cg-agenda-hora {
    flex: 0 0 auto; font-size: .74rem; font-weight: 600; color: var(--bs-secondary-color);
    min-width: 3.4rem; padding-top: .1rem;
}
.dash-line-dot {
    fill: var(--bs-primary); stroke: var(--bs-card-bg); stroke-width: 2;
    cursor: pointer; outline: none;
}
.dash-line-dot:focus-visible { stroke: var(--bs-body-color); }
.dash-line-tick { font-size: 9px; fill: var(--bs-secondary-color); text-anchor: middle; }
.dash-line-valor { font-size: 11px; font-weight: 700; fill: var(--bs-body-color); }
.dash-tooltip {
    position: absolute; transform: translate(-50%, -100%) translateY(-8px);
    background: var(--bs-body-color); color: var(--bs-body-bg);
    font-size: .75rem; font-weight: 600; padding: .25rem .55rem; border-radius: var(--radius-sm);
    white-space: nowrap; pointer-events: none; box-shadow: var(--shadow-sm); z-index: 5;
}

/* ---------- Dashboard personalizable (DASH-1C) ----------
   Modo normal: sin decoración extra (§ 15 — "Dashboard limpio", mínima
   decoración). Modo edición (`#dashGrid.dash-grid-editando`): cada widget
   suma un contorno punteado + su barra de controles — suficiente para
   entender que son modificables, sin sobrecargar visualmente. El color
   nunca es el único indicador de "modificable": el contorno punteado y la
   barra de controles ya son un cambio de forma, no solo de color. */
.dash-widget-editbar { display: none; align-items: center; gap: .4rem; margin-bottom: .6rem; padding-bottom: .5rem; border-bottom: 1px dashed var(--bs-border-color); }
.dash-grid-editando .dash-widget-editbar { display: flex; }
.dash-grid-editando .dash-widget { outline: 2px dashed var(--bs-border-color); outline-offset: 2px; }
.dash-widget-editbar .dash-widget-tamano { width: auto; flex: 0 0 auto; }
.dash-widget-handle { cursor: grab; color: var(--bs-secondary-color); padding: .25rem .4rem; }
.dash-widget-handle:active { cursor: grabbing; }
/* El widget en curso de arrastre se atenúa (feedback "widget seleccionado", § 6);
   el destino bajo el puntero resalta con el color de marca (feedback "posición
   de destino") — ambos además de forma (outline), nunca solo color. */
.dash-widget-col.dash-arrastrando > .dash-widget { opacity: .45; }
.dash-widget-col.dash-drop-target > .dash-widget { outline: 3px solid var(--bs-primary); outline-offset: 2px; }
.dash-catalogo-item { background: var(--bs-card-bg); }

/* ---------- Organigrama: árbol con conectores (CSS puro, sin librería) ----------
   Técnica clásica de "pure CSS org chart": cada <li> dibuja su propio tramo
   de línea horizontal hacia el hermano contiguo vía ::before/::after, y
   ul ul::before baja la línea vertical desde el padre hacia sus hijos.
   display:table/table-cell (no flex) para que el cálculo de 50%/50% por <li>
   sea estable con cualquier ancho de nodo. Ver modules/organigrama/index.php. */
.org-scroll { overflow-x: auto; padding: 1.25rem .5rem; }
.org-chart, .org-chart ul, .org-chart li { margin: 0; padding: 0; list-style: none; }
.org-chart { display: inline-block; min-width: 100%; text-align: center; }
.org-chart ul { display: table; margin: 0 auto; padding-top: 20px; position: relative; }
.org-chart li {
    display: table-cell; vertical-align: top;
    text-align: center; position: relative;
    padding: 20px 10px 0 10px;
}
.org-chart li::before, .org-chart li::after {
    content: ''; position: absolute; top: 0; right: 50%;
    width: 50%; height: 20px; border-top: 2px solid var(--bs-border-color);
}
.org-chart li::after { right: auto; left: 50%; border-left: 2px solid var(--bs-border-color); }
.org-chart li:only-child::after, .org-chart li:only-child::before { display: none; }
.org-chart li:only-child { padding-top: 0; }
.org-chart li:first-child::before, .org-chart li:last-child::after { border: 0 none; }
.org-chart li:last-child::before { border-right: 2px solid var(--bs-border-color); border-radius: 0 6px 0 0; }
.org-chart li:first-child::after { border-radius: 6px 0 0 0; }
.org-chart ul ul::before {
    content: ''; position: absolute; top: 0; left: 50%;
    border-left: 2px solid var(--bs-border-color); width: 0; height: 20px;
}
.org-chart > ul { padding-top: 0; }
.org-chart > ul > li { padding-top: 0; }
.org-chart > ul > li::before, .org-chart > ul > li::after { display: none; }

.org-node {
    display: inline-block; min-width: 170px; max-width: 220px;
    background: var(--bs-card-bg); border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-sm); padding: .6rem .75rem;
    box-shadow: var(--shadow-sm); text-align: left;
}
.org-node-inactivo { opacity: .55; border-style: dashed; }
.org-node-nombre { font-weight: 700; font-size: .85rem; color: var(--bs-body-color); }
.org-node-depto { font-size: .72rem; color: var(--bs-secondary-color); margin-top: 1px; }
.org-node-persona { margin-top: .4rem; font-size: .78rem; display: flex; align-items: center; gap: .35rem; }
.org-node-badge { display: block; margin-top: .3rem; font-size: .65rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em; color: var(--bs-secondary-color); }
.org-node-botones { margin-top: .5rem; display: flex; gap: .25rem; }

@media print {
    .app-sidebar, .app-topbar, .breadcrumb, .no-imprimir { display: none !important; }
    .app-main { margin-left: 0 !important; }
    .card { box-shadow: none !important; border: none !important; }
    .org-scroll { overflow: visible !important; }
}

/* ---------- Mis tareas y recordatorios (ícono del topbar, ver layouts/header.php) ---------- */
.tareas-badge {
    position: absolute; top: 2px; right: 2px;
    font-size: .6rem; min-width: 16px; height: 16px; line-height: 16px; padding: 0 4px;
}
.tarea-personal-item {
    display: flex; align-items: flex-start; gap: .5rem;
    padding: .5rem .6rem; border: 1px solid var(--bs-border-color); border-radius: var(--radius-sm);
    background: var(--bs-tertiary-bg);
}
.tarea-personal-item .form-check-label { cursor: pointer; }
.tarea-personal-titulo { font-size: .85rem; font-weight: 500; }
.tarea-completada .tarea-personal-titulo { text-decoration: line-through; color: var(--bs-secondary-color); }
.tarea-completada { opacity: .7; }
.drag-handle-tarea { cursor: grab; padding-top: .15rem; }
.tarea-personal-item.tarea-arrastrando { opacity: .4; }

/* Mini editor de texto enriquecido del detalle (negrita/cursiva/tachado/color/link) */
.detalle-tarea-editable { min-height: 3.5rem; height: 3.5rem; max-height: none; overflow: auto; resize: vertical; cursor: text; }
.detalle-tarea-editable:empty:before { content: attr(data-placeholder); color: var(--bs-secondary-color); }
.detalle-tarea-editable a { text-decoration: underline; }
.detalle-tarea-toolbar .btn { line-height: 1; }
.detalle-tarea-color-input {
    position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; border: 0; padding: 0;
}
.tarea-personal-detalle a { text-decoration: underline; }

/* Mini editor de texto enriquecido GENÉRICO (negrita/cursiva/tachado/viñetas/color/
   link) — mismo mecanismo exacto que el detalle de Tareas personales de arriba
   (.detalle-tarea-*), con nombres genéricos para reutilizar en cualquier campo de
   texto enriquecido nuevo (hoy: Inventario → Formatos de Presupuesto, textos de
   encabezado/condiciones/pie del PDF). Ver assets/js/app.js -> initEditorEnriquecido(). */
.editor-enriquecido { max-height: none; overflow: auto; resize: vertical; cursor: text; padding: .75rem 1rem; }
.editor-enriquecido:empty:before { content: attr(data-placeholder); color: var(--bs-secondary-color); }
.editor-enriquecido a { text-decoration: underline; }
.editor-enriquecido-toolbar .btn { line-height: 1; }
.editor-enriquecido-color-input {
    position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; border: 0; padding: 0;
}

/* Reordenar por drag & drop (HTML5 nativo, sin librerías) las líneas de un formulario
   maestro-detalle tipo Presupuestos: el handle es lo único con draggable="true", así
   que solo él debe verse "agarrable" — la fila entera nunca es draggable (rompería
   la selección de texto/edición de sus inputs). Clases genéricas por si otro módulo
   con líneas dinámicas (Pedidos/Despachos/Órdenes de Compra/etc.) suma lo mismo. */
.item-linea-drag-handle { cursor: grab; }
.item-linea-drag-handle:active { cursor: grabbing; }
.item-linea-arrastrando { opacity: .4; }

/* ---------- Eventos → Plano Interactivo (Event Layout) ---------- */
.ep-editor { display: flex; flex-direction: column; }
.ep-cuerpo { flex-wrap: wrap; }
.ep-panel-izquierda, .ep-panel-derecha {
    background: var(--bs-card-bg); border: 1px solid var(--bs-border-color); border-radius: var(--radius-sm);
    padding: .5rem; display: flex; flex-direction: column;
}
/* Sin max-height/overflow propio a propósito: un panel con su propio scroll interno DENTRO de un
   modal/página que también puede necesitar scroll produce doble scrollbar en el eje Y (reportado
   con datos reales). El lienzo (`.ep-lienzo-contenedor`) sigue con altura fija porque es un
   viewport de pan/zoom, no una lista — pero los paneles laterales fluyen con su contenido y dejan
   que la página/modal maneje un único scroll. */
.ep-panel-scroll { padding-top: .5rem; }
.ep-panel-centro { display: flex; flex-direction: column; }
.ep-lienzo-contenedor {
    position: relative; background: #f1f5f9; border: 1px solid var(--bs-border-color); border-radius: var(--radius-sm);
    height: 560px; overflow: hidden; touch-action: none;
}
[data-bs-theme="dark"] .ep-lienzo-contenedor { background: #0f172a; }
.ep-svg { width: 100%; height: 100%; display: block; cursor: default; }
.ep-svg.ep-modo-pan { cursor: grab; }
.ep-svg.ep-modo-pan:active { cursor: grabbing; }
.ep-svg.ep-modo-dibujar-ruta { cursor: crosshair; }
.ep-svg.ep-modo-colocar { cursor: crosshair; }
.ep-modo-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; padding: .35rem .6rem; border-radius: var(--radius-sm);
    background: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); margin-bottom: .5rem;
}
.ep-modo-bar.ep-modo-bar-activa { background: var(--bs-primary-bg-subtle); border-color: var(--bs-primary); color: var(--bs-primary-text-emphasis); }
.ep-regla-h { position: absolute; top: 0; left: 20px; right: 0; height: 20px; background: rgba(255,255,255,.85); border-bottom: 1px solid var(--bs-border-color); font-size: 9px; pointer-events: none; }
.ep-regla-v { position: absolute; top: 20px; left: 0; bottom: 0; width: 20px; background: rgba(255,255,255,.85); border-right: 1px solid var(--bs-border-color); font-size: 9px; pointer-events: none; }
[data-bs-theme="dark"] .ep-regla-h, [data-bs-theme="dark"] .ep-regla-v { background: rgba(17,17,17,.85); }
.ep-elemento { cursor: move; }
.ep-elemento.ep-bloqueado { cursor: not-allowed; }
.ep-elemento.ep-oculto { display: none; }
.ep-elemento-rect { stroke: rgba(0,0,0,.25); stroke-width: 1; }
.ep-elemento.ep-seleccionado .ep-elemento-rect { stroke: var(--bs-primary); stroke-width: 2; }
.ep-elemento-nombre { font-size: 10px; fill: #111; pointer-events: none; user-select: none; }
.ep-elemento-icono { pointer-events: none; }
.ep-overlay-handle { fill: #fff; stroke: var(--bs-primary); stroke-width: 1.5; cursor: pointer; }
.ep-overlay-marquee { fill: rgba(79,70,229,.12); stroke: var(--bs-primary); stroke-dasharray: 4 3; }
.ep-paleta { display: grid; grid-template-columns: repeat(2, 1fr); gap: .4rem; }
.ep-paleta-item {
    display: flex; flex-direction: column; align-items: center; gap: .2rem; padding: .5rem .3rem;
    border: 1px solid var(--bs-border-color); border-radius: var(--radius-sm); cursor: grab; background: var(--bs-tertiary-bg);
    font-size: .7rem; text-align: center; user-select: none;
}
.ep-paleta-item:hover { border-color: var(--bs-primary); }
.ep-paleta-item.ep-paleta-activo { border-color: var(--bs-primary); background: var(--bs-primary-bg-subtle); }
.ep-capa-item, .ep-sector-item, .ep-ruta-item {
    display: flex; align-items: center; gap: .4rem; padding: .35rem .4rem; border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-sm); margin-bottom: .3rem; font-size: .8rem; background: var(--bs-tertiary-bg);
}
.ep-capa-item .form-control-color, .ep-sector-item .ep-color-dot { width: 16px; height: 16px; border-radius: 50%; flex: 0 0 auto; border: 1px solid rgba(0,0,0,.2); }
.ep-mapa-asientos { display: grid; gap: 4px; grid-template-columns: repeat(auto-fill, minmax(26px, 1fr)); }
.ep-asiento-celda {
    width: 26px; height: 26px; border-radius: 4px; display: flex; align-items: center; justify-content: center;
    font-size: 9px; color: #fff; cursor: pointer; user-select: none;
}
.ep-leyenda-color { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 3px; vertical-align: middle; }
.ep-stats-bar .ep-stat-chip { background: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: 999px; padding: .1rem .6rem; }
@media (max-width: 991.98px) {
    .ep-lienzo-contenedor { height: 420px; }
}

/* Modal un poco más ancho que modal-xl (1140px) — a pedido explícito del usuario, para vistas
   previas con muchas columnas (ej. SII → Documentos recibidos). min() lo acota en pantallas
   angostas en vez de replicar el breakpoint interno de Bootstrap, así nunca desborda. Clase
   plana (nunca inline style) para poder intercambiarla limpio por modal-fullscreen vía JS al
   maximizar, sin que un estilo inline gane por especificidad y rompa el toggle. */
.modal-ancho {
    max-width: min(1320px, calc(100vw - 2rem));
}

/* ---------- Comunicación interna → Chat interno ----------
   Bloque único de estilos del área completa (modules/comunicacion_interna/chat/index.php)
   más el panel rápido del topbar (offcanvas, ver layouts/header.php). Todo theme-aware vía
   variables --bs-*, ningún color fijo — mismo criterio que el resto del sistema. */
.chat-app {
    display: flex; height: min(72vh, 720px); min-height: 420px;
    border: 1px solid var(--bs-border-color); border-radius: var(--radius-sm);
    overflow: hidden; background: var(--bs-card-bg);
}
.chat-col-lista {
    width: 320px; flex-shrink: 0; display: flex; flex-direction: column;
    border-right: 1px solid var(--bs-border-color); background: var(--bs-card-bg);
}
.chat-col-lista-header { padding: .75rem; border-bottom: 1px solid var(--bs-border-color); }
.chat-lista-scroll { flex: 1; overflow-y: auto; }
.chat-item-conversacion {
    cursor: pointer; border: 0; border-bottom: 1px solid var(--bs-border-color);
    background: transparent; width: 100%; text-align: left;
}
.chat-item-conversacion:hover, .chat-item-conversacion:focus-visible { background: var(--bs-tertiary-bg); }
.chat-item-conversacion.activo { background: var(--bs-primary-bg-subtle); }
/* Filas del directorio "Cuentas" (offcanvas y página completa): más aire a los costados que el
   padding por defecto de .list-group-item — el badge "Activo/No activo para chat" es ancho y sin
   esto quedaba pegado (a veces cortado) contra el borde derecho del panel angosto del offcanvas. */
.chat-cuenta-fila { padding-left: 1.1rem; padding-right: 1.1rem; }
.chat-cuenta-fila .badge { flex-shrink: 0; white-space: nowrap; }
.chat-cuenta-fila > .overflow-hidden { min-width: 0; }
.chat-col-thread { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.chat-thread-header {
    padding: .65rem 1rem; border-bottom: 1px solid var(--bs-border-color);
    display: flex; align-items: center; gap: .65rem;
}
.chat-thread-vacio { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--bs-secondary-color); }
.chat-mensajes {
    flex: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: .3rem;
    background: var(--bs-tertiary-bg);
}
.chat-fecha-separador { text-align: center; font-size: .72rem; color: var(--bs-secondary-color); margin: .6rem 0; }
.chat-bubble-fila { display: flex; align-items: flex-end; gap: .4rem; max-width: 78%; }
.chat-bubble-fila.propio { align-self: flex-end; flex-direction: row-reverse; }
.chat-bubble-fila.recibido { align-self: flex-start; }
.chat-bubble {
    padding: .5rem .75rem; border-radius: 14px; font-size: .875rem; word-break: break-word;
    white-space: pre-wrap; background: var(--bs-card-bg); border: 1px solid var(--bs-border-color);
}
.chat-bubble-fila.propio .chat-bubble {
    background: var(--bs-primary); color: #fff; border-color: var(--bs-primary); border-bottom-right-radius: 4px;
}
.chat-bubble-fila.recibido .chat-bubble { border-bottom-left-radius: 4px; }
.chat-bubble-meta { font-size: .68rem; opacity: .75; margin-top: .2rem; }
.chat-bubble-eliminado { font-style: italic; opacity: .7; }
.chat-compose { border-top: 1px solid var(--bs-border-color); padding: .6rem .75rem; background: var(--bs-card-bg); }
.chat-nuevo-mensaje-flotante {
    position: absolute; bottom: 4.5rem; left: 50%; transform: translateX(-50%);
    z-index: 5;
}
@media (max-width: 767.98px) {
    .chat-app { height: calc(100vh - 220px); min-height: 360px; border-radius: 0; border-left: 0; border-right: 0; }
    .chat-col-lista { width: 100%; border-right: 0; }
    .chat-app.chat-mostrando-thread .chat-col-lista { display: none; }
    .chat-app:not(.chat-mostrando-thread) .chat-col-thread { display: none; }
}

/* Línea de tiempo vertical genérica (introducida en Gestión Colegios → PIE →
   Bitácora, PROMPT 5.6.1) — reutilizable por cualquier vista futura que
   necesite el mismo patrón "línea + punto + tarjeta" (ej. Expediente PIE,
   6.1.1). Estructura esperada: <div class="timeline-vertical"><div
   class="timeline-item"><div class="timeline-punto text-bg-{color}"><i
   class="{icono}"></i></div><div class="timeline-contenido">...</div></div>...
   </div>. El conector es un ::before absoluto sobre .timeline-item, oculto
   en el último ítem vía :last-child. */
.timeline-vertical { position: relative; }
.timeline-item { position: relative; display: flex; gap: .75rem; padding-bottom: 1.25rem; }
.timeline-item::before {
    content: ''; position: absolute; left: 15px; top: 32px; bottom: 0; width: 2px;
    background: var(--bs-border-color);
}
.timeline-item:last-child::before { display: none; }
.timeline-punto {
    flex-shrink: 0; width: 32px; height: 32px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center; font-size: .8rem;
    z-index: 1;
}
.timeline-contenido { flex: 1; min-width: 0; padding-top: .1rem; }
.timeline-fecha { font-size: .72rem; color: var(--bs-secondary-color); }

/* Strip de pestañas Bootstrap horizontalmente desplazable (introducido en
   Gestión Colegios → PIE → Expediente, PROMPT 6.1.1, sección 13: "evitar
   scroll horizontal incómodo... tabs desplazables") — reutilizable por
   cualquier vista futura con muchas pestañas `.nav-tabs`. No reemplaza el
   wrap por defecto de Bootstrap, lo desactiva a propósito para que la fila
   completa de pestañas se deslice con scroll táctil/rueda en vez de partir
   en varias líneas.

   Flechas izquierda/derecha (a pedido del usuario, mismo Expediente PIE):
   la barra de scroll horizontal nativa se oculta (queda el scroll táctil/
   rueda intacto, `overflow-x` sigue en `auto`) y se reemplaza visualmente
   por dos botones `.nav-tabs-scroll-arrow` que desplazan el `<ul>` con
   `scrollBy()` — ver `initNavTabsScroll()` en `assets/js/app.js`. Genérico:
   cualquier `.nav-tabs-scroll` envuelto en `.nav-tabs-scroll-wrap` con esos
   dos botones como hermanos queda con flechas automáticamente. */
.nav-tabs-scroll {
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* Edge/IE viejo */
}
/* AUD-EVT-3 — bug real encontrado y corregido: dentro de un modal, `.modal .nav-tabs`
   (arriba, estilo "pastilla" de color) fuerza `flex-wrap:wrap` con 2 clases de
   especificidad — le ganaba SIEMPRE a `.nav-tabs-scroll` (1 clase) y dejaba las
   pestañas envueltas en 3-4 filas sin importar el orden en el archivo, inutilizando el
   scroll horizontal para cualquier `.nav-tabs-scroll` usado DENTRO de un modal (nadie
   lo había combinado así hasta Eventos → Eventos; Gestión Colegios → PIE → Expediente,
   el otro caso real, es una página normal, no un modal, así que nunca chocó). Este
   selector de 3 clases gana por especificidad, sin depender del orden — mantiene el
   estilo "pastilla" de cada `.nav-link` (no tocado acá), solo hace que el CONTENEDOR
   sea una fila con scroll en vez de envolver. */
.modal .nav-tabs.nav-tabs-scroll {
    flex-wrap: nowrap; overflow-x: auto;
    background: transparent; padding: 0;
}
.nav-tabs-scroll .nav-link { white-space: nowrap; }
.nav-tabs-scroll::-webkit-scrollbar { display: none; height: 0; } /* Chrome/Safari/Edge */

.nav-tabs-scroll-wrap { display: flex; align-items: stretch; }
.nav-tabs-scroll-wrap .nav-tabs-scroll { flex: 1 1 auto; min-width: 0; }
.nav-tabs-scroll-arrow {
    flex: 0 0 auto; width: 2rem; border: none; background: var(--bs-card-bg, #fff);
    color: var(--bs-secondary-color); cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.nav-tabs-scroll-arrow:hover { color: var(--bs-primary); background: var(--bs-tertiary-bg, rgba(0, 0, 0, .03)); }
.nav-tabs-scroll-arrow:disabled { opacity: 0; pointer-events: none; } /* sin sobrante que desplazar de ese lado */
.nav-tabs-scroll-arrow.nav-tabs-scroll-prev { border-right: 1px solid var(--bs-border-color); }
.nav-tabs-scroll-arrow.nav-tabs-scroll-next { border-left: 1px solid var(--bs-border-color); }

/* AUD-EVT-3 (Eventos → Eventos, navegación del workspace): etiquetas de grupo DENTRO
   de un .nav-tabs-scroll — NO son pestañas (sin data-bs-toggle, aria-hidden,
   pointer-events:none, sin tabindex), solo organizan visualmente pestañas
   relacionadas dentro de la MISMA fila con scroll horizontal (Resumen/Planificar/
   Comercial/Operación/Control) sin partir la navegación en varias filas ni crear un
   segundo sistema de agrupación. Genérico: cualquier <li class="nav-tabs-group-label">
   dentro de un .nav-tabs-scroll queda estilado igual, reutilizable por otro módulo. */
.nav-tabs-scroll .nav-tabs-group-label {
    display: flex; align-items: center; align-self: stretch; flex-shrink: 0;
    padding: 0 .6rem 0 .9rem;
    font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--bs-secondary-color);
    border-left: 1px solid var(--bs-border-color);
    pointer-events: none; user-select: none;
}
.nav-tabs-scroll .nav-tabs-group-label:first-child { border-left: 0; padding-left: .15rem; }

/* ---------- Asistente de configuración por módulo (wizard FlujoModulo) ----------
   ONB-SALUD-2.4 — lenguaje visual GENÉRICO del wizard #modalFlujoModulo
   (assets/js/flujo_modulo.js) y del Área "Asistente de configuración"
   (modules/gestion_salud/asistente). Todo theme-aware vía tokens --bs-*
   (los pares claro/oscuro de *-bg-subtle/-text-emphasis ya están
   redeclarados en [data-bs-theme="dark"] arriba — no repetir acá).
   El estado NUNCA se comunica solo por color: cada paso lleva además su
   etiqueta textual (.fm-paso-etiqueta) y su ícono/check propio. */
.fm-paso {
    display: flex; align-items: flex-start; gap: .65rem;
    padding: .5rem .65rem; border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-sm); background: var(--bs-card-bg);
}
.fm-paso + .fm-paso { margin-top: .4rem; }
/* Paso ACTUAL: tarjeta destacada — borde de marca + leve acento de fondo,
   más aire para su descripción/acciones reales. */
.fm-paso-activo {
    border-color: var(--bs-primary);
    box-shadow: 0 0 0 1px var(--bs-primary), var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
    padding: .75rem .8rem;
}
/* Pasos ya resueltos u omitidos: presentes pero sin competir con el actual. */
.fm-paso-resuelto, .fm-paso-omitido { background: var(--bs-tertiary-bg); }
.fm-paso-omitido { opacity: .8; }

/* Círculo de estado: número del paso (pendiente), check (completado),
   guion (omitido) o el ícono propio del paso (paso actual). */
.fm-paso-circulo {
    flex-shrink: 0; width: 30px; height: 30px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: .78rem; font-weight: 700;
    background: var(--bs-tertiary-bg); color: var(--bs-secondary-color);
    border: 1px solid var(--bs-border-color);
}
.fm-paso-activo .fm-paso-circulo { background: var(--bs-primary); border-color: var(--bs-primary); color: #fff; }
.fm-paso-resuelto .fm-paso-circulo { background: var(--bs-success-bg-subtle); border-color: var(--bs-success-border-subtle); color: var(--bs-success-text-emphasis); }
.fm-paso-omitido .fm-paso-circulo { background: var(--bs-secondary-bg-subtle); border-color: var(--bs-border-color); color: var(--bs-secondary-color); }

/* Etiqueta textual de estado (nunca solo color): "Paso actual" / "Completado" /
   "Omitido" / "No aplica" / "Pendiente". */
.fm-paso-etiqueta {
    font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
    padding: .1rem .5rem; border-radius: 999px; white-space: nowrap;
    background: var(--bs-tertiary-bg); color: var(--bs-secondary-color);
}
.fm-paso-activo .fm-paso-etiqueta { background: var(--bs-primary-bg-subtle); color: var(--bs-primary-text-emphasis); }
.fm-paso-resuelto .fm-paso-etiqueta { background: var(--bs-success-bg-subtle); color: var(--bs-success-text-emphasis); }

.fm-paso-cuerpo { flex: 1 1 auto; min-width: 0; }
.fm-paso-nombre { font-weight: 600; }
.fm-paso-activo .fm-paso-nombre { font-size: 1.02rem; }
/* Registro elegido en un paso completado ("Seleccionado: Juan Pérez"). */
.fm-paso-seleccion { font-size: .8rem; color: var(--bs-success-text-emphasis); margin-top: .1rem; }

/* Secuencia compacta del Área Asistente (✓ Paciente · ● Prestación · ○ Horario). */
.fm-secuencia { display: flex; flex-wrap: wrap; gap: .35rem .75rem; }
.fm-secuencia-item { display: inline-flex; align-items: center; gap: .35rem; font-size: .82rem; color: var(--bs-secondary-color); white-space: nowrap; }
.fm-secuencia-item.resuelto { color: var(--bs-success-text-emphasis); }
.fm-secuencia-item.actual { color: var(--bs-primary-text-emphasis); font-weight: 600; }

/* Ítem del catálogo "Continúa configurando Gestión Salud" (ONB-SALUD-3.1,
   pantalla final del Área Asistente): tarjeta-enlace compacta —
   ícono + nombre + descripción de una línea. Borde propio (no la utilidad
   .border de Bootstrap, que lleva !important y bloquearía el hover). */
.fm-continua-item {
    display: flex; align-items: flex-start; gap: .65rem;
    padding: .55rem .65rem; border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-sm); background: var(--bs-tertiary-bg);
    color: var(--bs-body-color); text-decoration: none;
}
.fm-continua-item:hover, .fm-continua-item:focus-visible {
    border-color: var(--bs-primary); color: var(--bs-body-color);
}
.fm-continua-item:hover .fw-semibold { color: var(--bs-primary); }

/* Banda discreta "Configuración guiada en curso" (layouts/footer.php) —
   visible SOLO en páginas de un módulo con flujo en progreso, cuando el
   usuario salió del wizard hacia un CRUD real. Debajo de los modales
   (backdrop de Bootstrap = 1050) para nunca taparlos. */
.fm-banda-guiada {
    position: fixed; right: 1rem; bottom: 1rem; z-index: 1020;
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    background: var(--bs-card-bg); border: 1px solid var(--bs-primary);
    border-radius: 999px; padding: .4rem .5rem .4rem .9rem;
    box-shadow: var(--shadow-sm, 0 2px 8px rgba(0,0,0,.15));
    font-size: .82rem; max-width: calc(100vw - 2rem);
}
.fm-banda-guiada .fm-banda-texto { color: var(--bs-body-color); font-weight: 600; white-space: nowrap; }

@media (max-width: 575.98px) {
    .fm-paso { padding: .45rem .5rem; gap: .5rem; }
    .fm-paso-activo { padding: .6rem .6rem; }
    .fm-paso-circulo { width: 26px; height: 26px; font-size: .72rem; }
    /* En móvil las etiquetas de pasos NO activos se ocultan (el círculo con
       número/check ya comunica el estado + queda la del paso actual) para
       que la lista no desborde — §4 del encargo: simplificar la secuencia. */
    .fm-paso:not(.fm-paso-activo) .fm-paso-etiqueta { display: none; }
    /* Área táctil suficiente para las acciones reales del paso activo. */
    #fmPasoContenido .btn-sm { padding: .45rem .75rem; }
    .fm-banda-guiada { left: 1rem; right: 1rem; justify-content: space-between; }
}
