
  /* ---------- Paleta y base — Estilo "Carbón", ver assets/manual-de-marca.md en la
     raíz del repo (contrato del sistema de diseño). Copiado acá porque este
     proyecto despliega SOLO lo que está bajo script/ en Cloudflare Pages: un
     <link>/<script> apuntando fuera de esta carpeta no se serviría en producción
     (ver architecture/arquitectura.md). ---------- */
  /* ACC-02 (2026-09-14): --sem-rojo-texto y --accent-texto son variantes MAS
     CLARAS de --sem-rojo/--accent pensadas solo para texto sobre --card/--bg
     (contraste WCAG AA) -- --sem-rojo y --accent originales NO se tocan, siguen
     usandose tal cual en fondos/bordes, donde el contraste lo da el color de
     TEXTO sobre ese fondo, no el propio fondo. */
  /* DIS-04 (2026-09-14): semantica canonica del semaforo de color en toda la
     app -- un color quiere decir SIEMPRE lo mismo, sin excepcion por modulo:
     verde (--sem-verde) = ok/cumplido; amarillo (--sem-amarillo) = atencion;
     naranja (--sem-naranja) = riesgo/por vencer; rojo (--sem-rojo/-texto) =
     problema/vencido; celeste (--sem-celeste) = informativo; --accent/-texto
     = seleccion/interaccion, NUNCA estado. Los 5 iconos de estado viven en
     ICONOS_ESTADO (ok/atencion/riesgo/problema/info, ver mas abajo en el JS)
     -- reemplaza a los antiguos CRO_ICONOS_ESTADO y SEM_ICONOS_ASIG. */
  /* ACC-06: texto visualmente oculto pero legible por lectores de
     pantalla -- usado por semaforoIconoAsig() para no depender solo
     del color/forma del icono. */
  .sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
  :root { --bg:#242528; --card:#2f3134; --border:#3a3c40; --accent:#66b6ff; --accent-text:#07153a; --text:#ffffff; --muted:#a8a8a8; --selected:#ffffff; --sem-verde:#3ecf6e; --sem-amarillo:#e8c547; --sem-rojo:#e05d5d; --sem-rojo-texto:#ef8585; --sem-naranja:#e8954a; --sem-celeste:#a8d4ff; --border-ctl:#7a7d85; --accent-texto:#7cc2ff; --r-pill:999px; --r-ctrl:9px; --r-ico:8px; --h-ctrl:38px; --h-ico:32px; --h-pill:32px; --r-card:12px; --r-card-sm:10px; --accent-05: color-mix(in srgb, var(--accent) 5%, transparent); --accent-08: color-mix(in srgb, var(--accent) 8%, transparent); --accent-14: color-mix(in srgb, var(--accent) 14%, transparent); --accent-16: color-mix(in srgb, var(--accent) 16%, transparent); --accent-32: color-mix(in srgb, var(--accent) 32%, transparent); --accent-55: color-mix(in srgb, var(--accent) 55%, transparent); --sem-verde-12: color-mix(in srgb, var(--sem-verde) 12%, transparent); --sem-rojo-10: color-mix(in srgb, var(--sem-rojo) 10%, transparent); --sem-rojo-35: color-mix(in srgb, var(--sem-rojo) 35%, transparent); --sem-amarillo-12: color-mix(in srgb, var(--sem-amarillo) 12%, transparent); --sem-naranja-10: color-mix(in srgb, var(--sem-naranja) 10%, transparent); --sem-celeste-12: color-mix(in srgb, var(--sem-celeste) 12%, transparent); --texto-error: color-mix(in srgb, var(--sem-rojo) 55%, #fff); --sem-verde-text: #07301a; --superficie: color-mix(in srgb, var(--card) 55%, transparent); --superficie-borde: var(--accent-16); --superficie-solida:#2a2b2f; --superficie-solida-pill:#2a2d31; }
  :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: inherit;
  }
  * { box-sizing: border-box; }
  body { font-family: -apple-system, Segoe UI, Roboto, Arial, sans-serif; background: radial-gradient(900px circle at 12% -10%, var(--accent-08), transparent 55%), radial-gradient(700px circle at 100% 10%, rgba(58,143,217,.06), transparent 50%), var(--bg); color: var(--text); margin:0; }
  /* PERF-09 (2026-09-14): background-attachment:fixed obliga a repintar el
     degradado del body entero en cada frame de scroll -- iOS Safari y Chrome
     Android no lo promueven a una capa fija barata. Se deja solo en pantallas
     grandes con mouse/trackpad (min-width:769px, pointer:fine), donde no hay
     ese costo de scroll tactil. El degradado se ve igual en el primer pintado
     (arranca arriba del todo) tanto con fixed como sin el -- la diferencia es
     solo el costo de repintado durante el scroll, no el resultado visual. */
  @media (min-width: 769px) and (pointer: fine) {
    body { background-attachment: fixed; }
  }
  h1 { font-size: 22px; margin: 0 0 4px 0; }

  /* ---------- Shell: topbar + tabs + contenido ---------- */
  .topbar { display:flex; align-items:center; justify-content:space-between; padding-block: 10px; padding-inline: 28px; height:auto; min-height:58px; flex-wrap:wrap; column-gap:8px; row-gap:6px; background: var(--superficie); -webkit-backdrop-filter: blur(14px) saturate(140%); backdrop-filter: blur(14px) saturate(140%); border-bottom: 1px solid var(--superficie-borde); will-change: backdrop-filter; contain: paint; } /* PERF-09: unico elemento que conserva el vidrio -- es el unico con contenido real scrolleando por debajo. No es ancestro de los paneles position:fixed de los combos-filtro (viven dentro de .table-card/.detalle-card), asi que contain:paint aca es seguro -- NO agregar contain a esos dos. */
  /* UI-011 (2026-10-05): con un proyecto de nombre largo, el selector (sin tope, Chromium lo
     dimensiona a la opcion mas larga: hasta 520px) empujaba los 4 botones de modulo a un 2do
     renglon que la barra (height:58px + contain:paint) recortaba -- «Administracion» quedaba
     invisible y sin puntero. Ahora el selector tiene tope de 340px y corta con «...» (el nombre
     completo va en su title), .topbar-left no se achica y .modules no baja de renglon; la barra
     mide 58px mientras todo entre (10px + 37px de contenido + 10px + borde) y, SOLO si ni con el
     selector comprimido a su minimo de 200px entra, crece (flex-wrap + height:auto) en vez de
     recortar. Ahi .topbar-right (margin-left:auto) baja al 2do renglon. Los bloques de celular
     de abajo resetean cada propiedad de este grupo -- no son excluyentes, ver .header-row. */
  .topbar-right { display:flex; align-items:center; gap:14px; flex:1 1 0; min-width:min-content; justify-content:flex-end; margin-left:auto; }
  .env-badge-dev { display:none; align-items:center; font-size:12px; font-weight:700; letter-spacing:.04em; color:var(--bg); background:var(--sem-amarillo); border-radius: 999px; padding: 4px 12px; }
  .topbar-project-wrap { flex:0 1 auto; min-width: 200px; }
  /* sin overflow:hidden + white-space:nowrap Chromium no pinta los puntos suspensivos en un <select> */
  .topbar-project-wrap .selector { min-width: 200px; width:100%; max-width:340px; text-overflow:ellipsis; overflow:hidden; white-space:nowrap; }
  .logout-link { color: var(--muted); background: none; border: none; cursor: pointer; font-family: inherit; padding: 6px; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; }
  .logout-link:hover { color: var(--text); background: var(--card); }
  /* DIS-03 (2026-09-14): .icon-only-btn (parche de padding con !important)
     se elimina -- #sync-redmine-btn pasa a usar directamente el tamano de la
     familia (b) de botones de icono, escopado por id para no perder el fondo
     con acento de .primary-btn. */
  #sync-redmine-btn { width: var(--h-ico); height: var(--h-ico); min-height: var(--h-ico); padding: 0; border-radius: var(--r-ico); justify-content: center; }
  /* RES-06 (2026-09-14): el gutter lateral pasa de fijo a fluido para que una
     tablet en vertical no pierda 168px de ancho, SIN agregar un breakpoint de
     tablet -- el sistema sigue teniendo 3 layouts (desktop, mobile portrait
     <=768px, landscape de celular). 6vw da 84px a partir de 1400px de ancho,
     asi que de notebook para arriba el valor no cambia; por debajo de 768px
     los bloques de mobile y de landscape (mas abajo) siguen pisando el
     padding con sus propios valores (16px y 20px), o sea que el clamp solo
     actua en el rango 768-1400px. */
  .tabs-bar { display:flex; gap:8px; padding: 14px clamp(16px, 6vw, 84px); }
  .nav-btn { display: inline-flex; align-items: center; text-align: left; min-height: var(--h-pill); padding: 0 16px; border: 1px solid rgba(102,182,255,.6); background: var(--superficie-solida-pill); border-radius: var(--r-pill); cursor: pointer; font-size: 13px; color: var(--muted); font-family: inherit; } /* ACC-02: borde 3.35:1 sobre el topbar; .nav-btn.active no se ve afectado (usa background/border-color:var(--accent) solido, no este rgba) */
  .nav-btn.active { background: var(--accent); color: var(--accent-text); border-color: var(--accent); font-weight: 600; box-shadow: 0 0 28px var(--accent-55); }
  /* RES-06: mismo criterio que .tabs-bar de arriba. */
  .content { padding: 19px clamp(16px, 6vw, 84px) 28px; }

  /* ---------- Selector de módulos (arriba del todo) — Fase 2: la app pasa a
     ser multi-módulo (Incidentes ya existía; Administración se suma acá,
     después vendrán Asignaciones y Plan de Proyecto). Mismo patrón .nav-btn
     (píldora, borde siempre visible) reutilizado en vez de inventar un
     componente nuevo -- ver skill report-design-system, principio rector. ---------- */
  .topbar-left { display:flex; align-items:center; gap:22px; min-width:0; flex:none; }
  .app-name { font-size:15px; font-weight:700; letter-spacing:.02em; color:var(--muted); white-space:nowrap; cursor:pointer; }
  .modules { display:flex; gap:6px; flex-wrap:nowrap; }
  .mod-btn { display:inline-flex; align-items:center; gap:6px; min-height:var(--h-pill); padding:0 16px; border:1px solid var(--accent-14); background:var(--superficie-solida-pill); border-radius:var(--r-pill); cursor:pointer; font-size:13px; font-weight:600; color:var(--muted); font-family:inherit; }
  .mod-btn:hover { border-color:var(--muted); color:var(--text); }
  .mod-btn.active { background:var(--accent); color:var(--accent-text); border-color:var(--accent); box-shadow:0 0 28px var(--accent-55); }
  /* UI-029: «Reportar» dentro del menu de modulos. Solo se ve en Celular y Celular horizontal: ahi
     la barra no tiene lugar para el boton #reportar-btn (con un 5to icono el selector de proyecto
     baja de los 200 px de UI-017); en PC el boton va en la barra y esta entrada no se muestra. */
  .mod-extra { display:none; }
  .mod-extra svg { width:16px; height:16px; flex:none; }
  .mod-extra:hover { border-color:var(--muted); color:var(--text); }
  /* Selector de modulos como botonera desplegable en mobile vertical (mejora 2026-09-09,
     pedida por Lucho). Mismo mecanismo <details>/<summary> nativo que .filtros-compactos
     (ver initFiltrosCompactos()/initModulosCompactos()): oculto por CSS en desktop
     (donde el panel de 4 modulos se ve siempre, igual que hoy), visible dentro del
     breakpoint de mobile portrait y, desde UI-018, tambien en Celular horizontal (alli el panel
     abierto flota debajo de la barra) -- ver esos @media mas abajo. Las pestanas de CADA
     modulo (.tabs-bar) no cambian, siguen como fila de pildoras con scroll horizontal. */
  .modules-summary { display:none; align-items:center; justify-content:space-between; gap:8px; list-style:none; cursor:pointer; min-height:var(--h-pill); padding:0 16px; border-radius:var(--r-pill); background:var(--accent); color:var(--accent-text); font-size:13px; font-weight:700; font-family:inherit; }
  .modules-summary::-webkit-details-marker { display:none; }
  /* Fix reporte-pendientes 2026-09-22: en mobile portrait angosto (375px),
     "Administracion" (el nombre de modulo mas largo) empujaba este pill
     ~10px fuera del viewport -- .topbar-left/.modules-details ya tenian
     min-width:0 para poder achicarse, pero el texto en si no tenia forma de
     ceder porque un <span> no trunca solo. Con min-width:0 + ellipsis ahora
     el texto trunca en vez de desbordar si algun dia hay menos espacio
     todavia (fuente mas grande, telefono mas angosto, etc.). */
  #modules-summary-current { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .modules-summary-chevron { flex:none; transition:transform .18s ease; }
  .modules-details[open] > .modules-summary .modules-summary-chevron { transform:rotate(180deg); }
  /* Fix reporte-pendientes 2026-09-22: .modules es un selector de clase, con mas
     especificidad que la regla nativa del navegador que oculta el contenido no-summary
     de un <details> cerrado -- sin esto, .modules quedaba en display:flex (reservando su
     alto real) aunque activateModule()/initModulosCompactos() le sacaran el atributo
     open al <details> padre, dejando un hueco vacio arriba del contenido. */
  .modules-details:not([open]) > .modules { display:none; }

  /* Cada módulo (Incidentes, Administración, ...) es un bloque tabs-bar +
     content propio; solo el módulo activo se muestra. activateView() queda
     scopeado por módulo (ver <script> al pie) para que cambiar de tab en un
     módulo no pise el estado de tabs/vistas del otro. */
  .module-panel { display:none; }
  .module-panel.active { display:block; }

  /* ---------- Módulo Asignaciones -- WIP (2026-09-08, fase E en curso) ---------- */
  /* Placeholder mientras se porta la lógica de cruce.py/datos.py del sistema
     standalone "Control de Asignaciones". Ver /areas/asignaciones-migration-plan.md
     (memoria de proyecto de Claude) para el plan completo por fases. */
  .wip-card { border:1px dashed var(--border); border-radius:var(--r-card); padding:28px 24px; color:var(--muted); font-size:13.5px; line-height:1.6; max-width:640px; }
  .wip-card strong { color:var(--text); }

  /* ---------- Asignaciones -- Proyectos (plan de capacidad) ----------
     Fase E (parcial, 2026-09-08): decision de Lucho "Solo plan por ahora"
     -- ver /areas/asignaciones-migration-plan.md (memoria de proyecto). */
  .resumen-compacto { font-size: 11px; color: var(--muted); margin-top: 0; line-height: 13px; }
  .pill.neutral { background: var(--bg); border-color: var(--border); color: var(--muted); }

  .heatmap-scroll { overflow-x: auto; }
  table.heatmap { border-collapse: collapse; font-size: 12px; white-space: nowrap; }
  table.heatmap th, table.heatmap td { border: 1px solid var(--border); padding: 4px 8px; text-align: center; }
  table.heatmap th.fase-padre, table.heatmap th.fase-hijo { background: var(--bg); color: var(--muted); font-weight: 600; font-size: 11px; }
  table.heatmap th.col-semana { font-weight: 600; color: var(--text); }
  table.heatmap th.col-semana .fecha-semana { display: block; font-weight: 400; color: var(--muted); font-size: 11px; }
  table.heatmap td.perfil-nombre, table.heatmap th.perfil-nombre { position: sticky; left: 0; background: var(--card); text-align: left; font-weight: 600; z-index: 1; }
  table.heatmap tr.fila-total td { background: var(--bg); font-weight: 700; }
  table.heatmap td.total-fila, table.heatmap th.total-fila { font-weight: 700; background: var(--bg); }
  .heatmap-legend { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 11px; color: var(--muted); }
  .heatmap-legend .swatch { display: inline-block; width: 14px; height: 14px; border-radius: 3px; border: 1px solid var(--border); }

  /* ---------- Asignaciones -- Carga de horas (Fase D, 2026-09-08) ----------
     Wizard de 4 pasos, decision de Lucho ("opcion A", ver /areas/
     asignaciones-migration-plan.md): parseo del Excel client-side (SheetJS),
     revision de todo antes de escribir nada en la base. */
  /* DIS-05 (2026-09-14): .carga-step-badge dejaba de ser una pildora de
     navegacion real (nunca se hace click, es puro indicador de progreso) pero
     tenia el mismo border+radius:999px que .mod-btn/.nav-btn -- se convierte en
     stepper: circulo .num + texto, sin caja propia, unidos por un conector fino
     (::after) en vez de bordes. gap:0 en .carga-steps porque el conector de 12px
     de cada badge YA es el espacio entre uno y el siguiente (con gap>0 quedaria
     doble espacio). cursor:default porque no es clickeable. */
  .carga-steps { display:flex; gap:0; margin-bottom:22px; flex-wrap:wrap; }
  .carga-step-badge { display:flex; align-items:center; gap:8px; min-height:var(--h-pill); padding:0 16px; font-size:13px; font-weight:600; color:var(--muted); position:relative; cursor:default; }
  .carga-step-badge .num { width:18px; height:18px; border-radius:50%; background:var(--border); color:var(--text); display:flex; align-items:center; justify-content:center; font-size:11px; flex-shrink:0; }
  .carga-step-badge:not(:last-child)::after { content:''; position:absolute; right:0; top:50%; width:12px; height:1px; background:var(--border); display:block; }
  .carga-step-badge.activo { color:var(--text); }
  .carga-step-badge.activo .num { background:var(--accent); color:var(--accent-text); }
  .carga-step-badge.hecho { color:var(--text); }
  .carga-step-badge.hecho .num { background:var(--sem-verde); color:var(--sem-verde-text); }

  .dropzone { border:1.5px dashed var(--border); border-radius:var(--r-card); padding:46px 24px; text-align:center; color:var(--muted); font-size:13.5px; margin-bottom:20px; cursor:pointer; }
  .dropzone.arrastrando { border-color:var(--accent); background:var(--accent-05); }
  .dropzone .icon { font-size:30px; margin-bottom:10px; opacity:.6; }
  .dropzone b { color:var(--text); display:block; font-size:14.5px; margin-bottom:6px; }
  .dropzone .cols { margin-top:14px; font-size:11.5px; color:var(--muted); }
  .dropzone .cols code { background:var(--bg); border:1px solid var(--border); border-radius:5px; padding:1px 6px; font-family:ui-monospace,Menlo,monospace; color:var(--text); }
  .carga-error { background:var(--sem-rojo-10); border:1px solid var(--sem-rojo-35); color:var(--texto-error); border-radius:10px; padding:12px 14px; font-size:13px; margin-bottom:20px; }
  /* Mejora 5 (reporte_pendientes, 2026-09-09): dropzone + Cargas anteriores
     en la misma fila (60/40) en vez de apiladas, cada mitad en su propia
     .detalle-card; el historial queda con alto maximo + scroll interno para
     no volver a estirar la fila a medida que se acumulen mas archivos.
     Ajuste (reporte_pendientes, 2026-09-09, Opcion B del mockup "vidrio en
     Carga de horas"): align-items:stretch pareja la altura de las dos
     tarjetas de la fila (antes "start", quedaban de alto distinto); el
     historial deja el scroll interno de 260px y pasa a mostrar fijo los
     ultimos 8 (ver .limit(8) en cargaRenderRecientes) sin scrollbar propia.
     .detalle-card--halo agrega el halo de acento elegido por Lucho sin
     tocar el .detalle-card base que usa el resto de la app. */
  .carga-fila { display:grid; grid-template-columns: 3fr 2fr; gap:18px; align-items:stretch; }
  .carga-card-stretch { display:flex; flex-direction:column; height:100%; min-width:0; }
  .carga-hist-scroll { flex:1; overflow-y:auto; min-width:0; }
  .carga-hist-foot { margin-top:10px; padding-top:8px; border-top:1px solid var(--border); font-size:11px; color:var(--muted); min-height:1em; }
  .detalle-card--halo { border:1px solid rgba(102,182,255,.45); box-shadow:0 0 0 1px rgba(102,182,255,.20), 0 0 46px -6px rgba(102,182,255,.5), inset 0 1px 0 rgba(255,255,255,.05); }

  h3.section-h { font-size:13px; color:var(--muted); font-weight:600; margin:28px 0 10px; }

  table.recientes { width:100%; border-collapse:collapse; font-size:12.5px; }
  table.recientes th, table.recientes td { text-align:left; padding:6px 4px; border-bottom:1px solid var(--border); }
  table.recientes th { color:var(--muted); font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.03em; }

  .resumen-lectura { display:flex; gap:10px; flex-wrap:wrap; margin-bottom:20px; }

  .grupo-card { border-left:3px solid var(--border); }
  .grupo-card.g-verde { border-left-color:var(--sem-verde); }
  .grupo-card.g-amarillo { border-left-color:var(--sem-amarillo); }
  .grupo-card.g-rojo { border-left-color:var(--sem-rojo); }
  .grupo-card.g-celeste { border-left-color:var(--sem-celeste); }
  .grupo-top { display:flex; align-items:flex-start; justify-content:space-between; gap:14px; flex-wrap:wrap; }
  .grupo-titulo { font-size:14px; font-weight:600; margin:0 0 3px; }
  .grupo-sub { font-size:12px; color:var(--muted); }
  .grupo-nota { margin-top:10px; font-size:12.5px; color:var(--text); background:var(--bg); border:1px solid var(--border); border-radius:8px; padding:10px 12px; display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; }
  .radio-pair { display:flex; gap:6px; }
  .radio-pill { font-size:11.5px; font-weight:600; padding:5px 12px; border-radius:999px; border:1px solid var(--border-ctl); background:none; color:var(--muted); cursor:pointer; font-family:inherit; }
  .radio-pill.sel-rojo { background:rgba(224,93,93,0.14); border-color:var(--sem-rojo); color:var(--texto-error); }
  .radio-pill.sel-neutral { background:rgba(255,255,255,0.06); border-color:var(--muted); color:var(--text); }

  table.consultores { width:100%; border-collapse:collapse; font-size:13px; }
  table.consultores th { text-align:left; padding:6px 8px; font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:.03em; border-bottom:1px solid var(--border); }
  table.consultores td { padding:6px 8px; border-bottom:1px solid var(--border); vertical-align:middle; }
  table.consultores td.crudo { font-family:ui-monospace,Menlo,monospace; font-size:12px; color:var(--muted); }
  .estado-icon { display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; border-radius:50%; font-size:11px; font-weight:700; }
  .estado-icon.ok { background:rgba(62,207,110,0.15); color:var(--sem-verde); }
  .estado-icon.warn { background:rgba(232,197,71,0.15); color:var(--sem-amarillo); }
  .estado-icon.bad { background:rgba(224,93,93,0.15); color:var(--sem-rojo); }
  .sim { font-size:11px; color:var(--muted); }

  .step-nav { display:flex; justify-content:space-between; margin-top:24px; }

  .kv-list { display:grid; grid-template-columns:1fr auto; gap:10px 18px; font-size:13.5px; }
  .kv-list dt { color:var(--muted); }
  .kv-list dd { margin:0; font-weight:600; text-align:right; }
  .kv-list dd.warn { color:var(--sem-amarillo); }
  .kv-list dd.bad { color:var(--sem-rojo); }
  .aviso { background:rgba(224,93,93,0.08); border:1px solid rgba(224,93,93,0.3); border-radius:10px; padding:12px 14px; font-size:12.5px; color:var(--texto-error); margin-top:18px; }

  .resultado-head { display:flex; align-items:center; gap:12px; margin-bottom:18px; }
  /* Mejora reporte-pendientes 2026-09-15 (pedido de Lucho): .check pasa de
     un <div>&#10003;</div> de texto plano a reusar el SVG real de
     ICONOS_ESTADO.ok (misma clase .cro-ico.ok, mismo vidriado que el resto
     de la familia) -- unificacion acotada, no se toca .carga-step-badge (es
     un stepper con numero, no un check) ni ICONOS_ACCION.check (es un icono
     de ACCION, no de estado). Solo queda el tamano/layout puntual de este
     lugar. */
  .resultado-head .resultado-check { flex-shrink:0; }
  pre.consola { background:var(--bg); border:1px solid var(--border); border-radius:10px; padding:14px 16px; font-family:ui-monospace,Menlo,monospace; font-size:12px; line-height:1.7; color:var(--muted); overflow-x:auto; margin:0; white-space:pre-wrap; }
  pre.consola .l1 { color:var(--sem-verde); }
  pre.consola .l2 { color:var(--sem-amarillo); }
  pre.consola .l3 { color:var(--sem-celeste); }

  /* ---------- Panel de Administración (Proyectos / Usuarios) ---------- */
  .header-row .sub { font-size:13px; color:var(--muted); margin-top:4px; }
  .primary-btn { display:inline-flex; align-items:center; gap:8px; font-family:inherit; font-size:13px; font-weight:600; background:var(--accent); color:var(--accent-text); border:1px solid var(--accent); border-radius:var(--r-ctrl); min-height:var(--h-ctrl); padding:0 16px; cursor:pointer; white-space:nowrap; }
  .primary-btn:hover { filter:brightness(1.08); }
  .primary-btn .plus { font-size:15px; line-height:1; }
  .primary-btn:disabled { opacity:.5; cursor:default; filter:none; }

  .form-card { background:var(--superficie-solida); border:1px solid var(--superficie-borde); border-radius:var(--r-card); padding:22px 24px; margin-bottom:22px; }
  .form-card-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:18px; }
  .form-card-head h2 { font-size:15px; font-weight:700; margin:0; }
  .form-card-head .close-x { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; line-height:1; padding:4px; }
  .form-card-head .sub { font-size:13px; color:var(--muted); margin-top:2px; }
  .form-grid { display:grid; grid-template-columns:1fr 1fr; gap:16px 20px; }
  .field { display:flex; flex-direction:column; gap:6px; }
  .field.span-2 { grid-column:1/-1; }
  .field label { font-size:11px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); font-weight:600; }
  .field .hint { font-size:11px; color:var(--muted); font-weight:400; text-transform:none; letter-spacing:0; }
  /* Ajuste 2026-09-08: mismo gap que ya se habia encontrado en el <select>
     de Rol -- .field nunca estilaba input[type=date], asi que quedaba con
     el look nativo del navegador (fondo claro, sin tamano fijo) dentro de
     un formulario oscuro; color-scheme:dark hace que el navegador dibuje
     el icono/calendario del date picker nativo en variante oscura. */
  .field input[type=text], .field input[type=number], .field input[type=date] { width:100%; min-height:44px; background:var(--bg); border:1px solid var(--border-ctl); border-radius:9px; color:var(--text); font-family:inherit; font-size:14px; padding:0 14px; color-scheme: dark; }
  .field input[type=date] { cursor:pointer; }
  .field input:focus { border-color:var(--accent); }
  .field input:focus:not(:focus-visible) { outline:none; }
  .field textarea { width:100%; min-height:120px; background:var(--bg); border:1px solid var(--border-ctl); border-radius:9px; color:var(--text); font-family:ui-monospace,Menlo,monospace; font-size:12.5px; padding:10px 14px; color-scheme:dark; resize:vertical; }
  .field textarea:focus { border-color:var(--accent); }
  .field textarea:focus:not(:focus-visible) { outline:none; }
  /* select dentro de un .field -- mismo tratamiento que .role-select (Usuarios): sin
     apariencia nativa del navegador, flecha propia en SVG. Ajuste 2026-09-08 -- los
     select agregados en Roster/Estructura/Consultores habian quedado con el estilo
     nativo, no coincidia con el resto del sistema de diseno (bug real, no una eleccion). */
  .field select { width:100%; min-height:44px; background:var(--bg); border:1px solid var(--border-ctl); border-radius:9px; color:var(--text); font-family:inherit; font-size:14px; padding:0 34px 0 14px; cursor:pointer; appearance:none; -webkit-appearance:none; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23a8a8a8'/%3E%3C/svg%3E"); background-repeat:no-repeat; background-position:right 14px center; }
  .field select:focus { border-color:var(--accent); }
  .field select:focus:not(:focus-visible) { outline:none; }
  .field .field-error { font-size:11.5px; color:var(--sem-rojo-texto); min-height:14px; }
  .field.combo { position:relative; }
  .combo-input-row { position:relative; }
  .combo-input-row input[type=text] { padding-right:38px; }
  .combo-icon { position:absolute; right:13px; top:50%; transform:translateY(-50%); width:15px; height:15px; color:var(--muted); pointer-events:none; }
  .combo-panel { position:absolute; left:0; right:0; top:calc(100% + 6px); background:var(--bg); border:1px solid var(--border-ctl); border-radius:10px; box-shadow:0 12px 28px rgba(0,0,0,0.35); overflow-y:auto; max-height:230px; z-index:5; }
  .combo-option { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:10px 13px; cursor:pointer; border-bottom:1px solid var(--border); font-size:13px; }
  .combo-option:last-child { border-bottom:none; }
  .combo-option:hover { background:rgba(102,182,255,0.12); }
  .combo-option .op-id { font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:11.5px; color:var(--muted); background:var(--card); border:1px solid var(--border-ctl); border-radius:5px; padding:2px 7px; white-space:nowrap; }
  .combo-empty { padding:12px 13px; font-size:12.5px; color:var(--muted); }
  .combo-option.nueva { color: var(--accent); font-weight: 600; }

  /* Vista previa de "Cargar plan (pegado)" -- Plan de capacidad, Fase F
     (reporte-pendientes mejoras 2026-09-09). Reutiliza el semaforo de
     --sem-verde/--sem-amarillo/--sem-rojo ya usado en el resto del
     sistema, sin agregar un color nuevo. */
  .plan-preview-table { width:100%; border-collapse:collapse; margin-top:4px; }
  .plan-preview-table th, .plan-preview-table td { border:1px solid var(--border); padding:6px 8px; font-size:12px; text-align:center; }
  .plan-preview-table th { background:var(--bg); color:var(--muted); font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.03em; }
  .plan-preview-table td.rol-cell { text-align:left; font-weight:600; }
  .plan-preview-table tr.fila-error td.rol-cell { color:var(--sem-rojo); }
  /* DIS-04: el estado de cada celda del preview de Plan de Capacidad ahora se
     lee como .pill (igual que el resto de la app) en vez de texto coloreado
     suelto -- ver reprevisualizarPlanPegado(). .estado-cambia/.estado-sin-match
     no se migran: 0 usos reales en el JS actual (confirmado con grep), eran
     selectores huerfanos de una version anterior de este componente. */
  .plan-preview-resumen { font-size:12px; color:var(--muted); margin:10px 0; }
  .plan-preview-banda-row th, .plan-preview-banda-row td { background:var(--bg); }
  .plan-preview-table td.plan-preview-banda { font-weight:700; font-size:11px; text-transform:none; letter-spacing:normal; }
  .plan-preview-table td.plan-preview-banda.sin-resolver { color:var(--sem-rojo); font-weight:600; }
  .plan-preview-select { font-family:inherit; font-size:11.5px; padding:2px 6px; border-radius:6px; background:var(--card); border:1px solid var(--border-ctl); color:var(--text); margin-left:6px; }

  .alias-pills { display:flex; flex-wrap:wrap; gap:6px; align-items:center; }
  .alias-pill { display:inline-flex; align-items:center; gap:6px; font-size:11.5px; padding:4px 6px 4px 10px; border-radius:999px; background: var(--bg); border:1px solid var(--border-ctl); color: var(--muted); font-family: ui-monospace, Menlo, monospace; }
  .alias-pill button { background:none; border:none; color:var(--muted); cursor:pointer; font-size:12px; width:16px; height:16px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; padding:0; }
  .alias-pill button:hover { color: var(--sem-rojo-texto); }
  .alias-add-inline { display:inline-flex; align-items:center; gap:6px; font-size:11.5px; padding:4px 10px; border-radius:999px; background:none; border:1px dashed var(--border); color:var(--muted); cursor:pointer; font-family:inherit; }
  .alias-add-inline:hover { color: var(--accent); border-color: var(--accent); }
  .alias-add-form { display:inline-flex; align-items:center; gap:6px; }
  .alias-add-form input[type=text] { min-height:26px; font-size:11.5px; padding:0 8px; background:var(--card); border:1px solid var(--border-ctl); border-radius:999px; color:var(--text); font-family:ui-monospace,Menlo,monospace; width:130px; }
  .alias-add-form button { background:none; border:1px solid var(--border-ctl); color:var(--muted); border-radius:50%; width:20px; height:20px; cursor:pointer; font-size:11px; }
  .alias-add-form button:hover { color:var(--text); border-color:var(--muted); }

  /* ---------- Filtro de columna multi-seleccion (combo-filtro), reutilizable ----------
     Ajuste 2026-09-08 (Administracion > Proyectos y > Consultores): boton por
     columna que abre un panel con buscador + checkboxes -- mismo criterio que
     .combo-panel/.combo-option ya existentes, pero para ACOTAR una lista a
     varios valores a la vez en vez de elegir uno solo (ver skill
     report-design-system, seccion .combo-filtro). */
  .filtro-th { display:flex; align-items:center; gap:5px; }
  /* ACC-05 (2026-09-15): reset visual para el <button> de orden de columna
     del Explorador (antes <span> con cursor:pointer) -- mismo aspecto que antes. */
  .th-orden { background: none; border: none; font: inherit; color: inherit; cursor: pointer; padding: 0; }
  .filtro-panel-wrap { position:relative; display:inline-flex; }
  .filtro-btn { background:none; border:none; color:var(--muted); cursor:pointer; padding:2px; display:inline-flex; align-items:center; justify-content:center; border-radius:4px; }
  .filtro-btn:hover { color:var(--text); }
  .filtro-btn.activo { color:var(--accent); }
  .filtro-btn svg { width:12px; height:12px; display:block; }
  /* white-space:normal (2026-09-22, reportado por Lucho con captura): reset
     defensivo -- .filtro-panel cuelga (en initFiltroColumna) de un <th> con
     white-space:nowrap (.table-card thead th) y, aunque se dibuja con
     position:fixed fuera del flujo, position:fixed no corta la cadena de
     herencia de CSS -- heredaba nowrap y ningun word-break/min-width alcanzaba
     para que el texto de .filtro-opcion pasara a una segunda linea (nowrap
     suprime cualquier oportunidad de salto de linea). Mismo criterio que el
     reset de .panel-acciones en Finanzas Personales para el mismo tipo de
     herencia no deseada desde un <th>. */
  .filtro-panel { position:absolute; top:calc(100% + 8px); left:0; background:var(--bg); border:1px solid var(--border-ctl); border-radius:10px; box-shadow:0 12px 28px rgba(0,0,0,0.35); width:220px; z-index:10; padding:10px; text-transform:none; letter-spacing:normal; font-weight:400; white-space:normal; }
  .filtro-panel input[type=text] { width:100%; min-height:32px; background:var(--card); border:1px solid var(--border-ctl); border-radius:7px; color:var(--text); font-family:inherit; font-size:12.5px; padding:0 10px; margin-bottom:8px; }
  .filtro-panel input[type=text]:focus { border-color:var(--accent); }
  .filtro-panel input[type=text]:focus:not(:focus-visible) { outline:none; }
  .filtro-panel-lista { max-height:180px; overflow-y:auto; display:flex; flex-direction:column; gap:1px; }
  .filtro-opcion { display:flex; align-items:flex-start; gap:8px; padding:5px 6px; border-radius:6px; font-size:12.5px; color:var(--text); cursor:pointer; }
  .filtro-opcion:hover { background:var(--accent-08); }
  .filtro-opcion input[type=checkbox] { accent-color:var(--accent); width:14px; height:14px; cursor:pointer; flex-shrink:0; margin-top:2px; }
  /* FIX (2026-09-22, reportado por Lucho con captura): un <span> dentro de un
     flex container sin min-width:0 usa el ancho de su texto sin cortar como
     minimo (min-width:auto por defecto de flexbox) -- con un valor largo
     ("BYMA - Implementación S4 H...") eso empujaba .filtro-opcion mas ancho
     que el panel (220px fijo) y, como .filtro-panel-lista ya fija
     overflow-y:auto, el overflow-x en 'visible' pasa a comportarse como
     'auto' (spec de CSS) -- aparecia una barra de scroll horizontal en vez de
     que el texto se ajustara. min-width:0 deja que el span se achique y
     word-break lo hace pasar a una segunda linea en vez de desbordar; mismo
     patron ya usado en .topbar-left/.kpi-ico/.carga-card-stretch. */
  .filtro-opcion span { min-width:0; word-break:break-word; }
  .filtro-panel-footer { display:flex; justify-content:space-between; align-items:center; margin-top:8px; padding-top:8px; border-top:1px solid var(--border); }
  .filtro-limpiar { background:none; border:none; color:var(--muted); font-size:11px; cursor:pointer; font-family:inherit; text-decoration:underline; padding:0; }
  .filtro-limpiar:hover { color:var(--text); }
  .filtro-vacio { font-size:11.5px; color:var(--muted); padding:6px 4px; }
  .filtro-todos { background:none; border:none; color:var(--accent); font-size:11px; cursor:pointer; font-family:inherit; text-decoration:underline; padding:0; }
  .filtro-todos:hover { color:var(--text); }
  .filtro-panel.der { left:auto; right:0; }
  .selmes-btn { display:inline-flex; align-items:center; gap:8px; background:var(--card); border:1px solid var(--border-ctl); border-radius:var(--r-ctrl); min-height:var(--h-ctrl); color:var(--text); font-family:inherit; font-size:13px; font-weight:600; padding:0 16px; cursor:pointer; }
  .selmes-btn:hover { border-color: var(--accent); }
  .selmes-btn.activo { border-color:var(--accent); color:var(--accent); }
  .selmes-btn svg { width:13px; height:13px; flex-shrink:0; }

  /* ---------- Control por modulo / detalle de perfil (Fase E) ---------- */
  .control-grid { display:grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap:12px; margin-bottom: 16px; }
  .control-card { position:relative; overflow:hidden; background:var(--superficie-solida); border:1px solid var(--superficie-borde); border-left:4px solid transparent; border-radius:var(--r-card); padding:14px 16px; }
  .control-card::before { content:""; position:absolute; left:0; top:0; bottom:0; width:4px; background:transparent; }
  .control-card.sem-verde::before { background:linear-gradient(180deg, var(--sem-verde), rgba(62,207,110,0) 85%); }
  .control-card.sem-amarillo::before { background:linear-gradient(180deg, var(--sem-amarillo), rgba(232,197,71,0) 85%); }
  .control-card.sem-rojo::before { background:linear-gradient(180deg, var(--sem-rojo), rgba(224,93,93,0) 85%); }
  /* FIX (2026-09-15, reportado por Lucho): .control-nombre/.control-pct no
     tenian color propio -- heredaban var(--text) del body, que funciona en
     un <div> (Control por modulo/Detalle por perfil) pero NO en un <button>
     (checklist de Planificacion, ppItemHtml()): el navegador le pone su
     color de texto por defecto (negro/buttontext) salvo que se fije
     explicitamente. Se fija ahora en la regla base -- sin efecto visual en
     los usos con <div> (ya mostraban blanco por herencia), corrige los
     usos con <button>. */
  .control-card .control-nombre { font-size:13px; font-weight:600; margin-bottom:10px; color:var(--text); }
  .control-card .control-fila { display:flex; align-items:center; justify-content:space-between; }
  .control-card .control-pct { font-size:26px; font-weight:700; color:var(--text); }
  .control-card .control-pct.sub { font-size:15px; color:var(--muted); }
  .control-card .control-lbl { font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:.04em; margin-top:2px; }
  .control-card .control-horas { font-size:11px; color:var(--muted); margin-top:8px; }
  /* Ajuste posterior a FUN-04 (2026-09-15, a pedido de Lucho): modificador
     dedicado -- NO se toca la regla base
     .control-grid de arriba (report-design-system, Principio rector) para no
     afectar a "Control por modulo" ni a "Detalle por perfil", que tambien la
     usan. auto-fill reserva columnas vacias del mismo ancho minimo aunque no
     haya tarjetas para llenarlas y no les cede ese espacio a las tarjetas
     reales; auto-fit las colapsa a 0 y reparte el espacio entre las
     tarjetas existentes (ya son 1fr). Se nota recien con ventanas anchas
     (~1490px+): con 5 tarjetas de min 210px, auto-fill arma una 6a columna
     fantasma a partir de ahi. Aplicado solo a #pp-checklist-grid. */
  .control-grid--full { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
  /* Titulo suelto del checklist de carga (ajuste posterior a FUN-04,
     2026-09-15): mismo tamano/color que
     .detalle-header h3, pero sin el layout de boton/chevron -- el checklist
     dejo de ser una tarjeta colapsable. */
  .pp-checklist-titulo { font-size:13px; line-height:16px; color:var(--muted); font-weight:600; margin-bottom:10px; }

  .barras-perfil { display: flex; flex-direction: column; gap: 12px; }
  .bp-row { display: grid; grid-template-columns: 110px 1fr 50px; align-items: center; gap: 10px; }
  .bp-label { font-size: 12px; color: var(--text); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .bp-track { position: relative; height: 16px; background: rgba(255,255,255,0.08); border-radius: 4px; }
  .bp-fill { position: absolute; left: 0; top: 0; height: 100%; border-radius: 4px 0 0 4px; background: rgba(102,182,255,0.8); border: 1px solid var(--accent); box-sizing: border-box; } /* ACC-02: 3.93:1, cambio cosmetico opcional */
  .bp-fill.bp-over { background: linear-gradient(to right, var(--accent-55) 0%, var(--accent-55) var(--bp-split), rgba(224,93,93,0.55) var(--bp-split), rgba(224,93,93,0.55) 100%); border: 1px solid var(--sem-rojo); }
  .bp-refline { position: absolute; top: -3px; bottom: -3px; width: 2px; background: rgba(255,255,255,0.45); left: var(--bp-refpos); }
  .bp-value { font-size: 12px; font-variant-numeric: tabular-nums; color: var(--text); }
  .bp-value.bp-over { color: var(--sem-rojo); font-weight: 600; }
  .bp-scale { display: grid; grid-template-columns: 110px 1fr 50px; gap: 10px; margin-top: 4px; }
  .bp-ticks { position: relative; height: 14px; font-size: 11px; color: var(--muted); }
  .bp-ticks span { position: absolute; transform: translateX(-50%); }

  table.tabla-cruce { width:100%; border-collapse:collapse; font-size:13px; }
  table.tabla-cruce th { text-align:left; padding:6px 8px; font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:.03em; border-bottom:1px solid var(--border); }
  table.tabla-cruce td { padding:6px 8px; border-bottom:1px solid var(--border); vertical-align:middle; }
  tr.fila-cruce-clic { cursor:pointer; }
  tr.fila-cruce-clic:hover td { background: var(--accent-05); }
  .chevron-cruce { display:inline-block; transition: transform .15s ease; color:var(--muted); }
  .chevron-cruce.open { transform: rotate(90deg); }
  /* UI-015: celdas y filas del desglose del Detalle por perfil (antes style.cssText en el JS). Las celdas llevan table.tabla-cruce td para ganarle al padding de table.tabla-cruce td sin !important. */
  table.tabla-cruce td.cruce-sub-perfil { padding:4px 8px 14px 36px; background:rgba(255,255,255,0.02); }
  table.tabla-cruce td.cruce-sub-mes { padding:4px 8px 10px 26px; background:rgba(255,255,255,0.03); }
  .cruce-desglose-fila { display:flex; justify-content:space-between; padding:3px 0; max-width:300px; font-size:12.5px; color:var(--muted); }
  .sem-icon { display:inline-flex; vertical-align:middle; }
  /* DIS-10 (2026-09-15): ICONOS_ESTADO (ver DIS-04, JS mas abajo) pinta su
     forma de fondo con fill="currentColor" en vez de un hex fijo por icono --
     mismo patron que ya usa KPI_ICO_SVG (stroke="currentColor" + color: en el
     wrapper). El color final lo pone esta regla, por la clase de estado que ya
     trae cada SVG (ok/atencion/riesgo/problema/info) combinada con la clase
     que YA tenia cada uno (.cro-ico o .sem-icon, sin unificarlas para no darle
     display:inline-flex a los 4 que hoy son <svg aria-hidden="true"> inline sueltos -- eso
     cambiaria su alineacion vertical real, ver ChangeLogUI.md). Los detalles
     internos (check/exclamacion/X/punto) NO son parte de esta escala -- son
     colores de contraste fijos sobre el fondo, se dejan como estaban. */
  .cro-ico.ok { color: var(--sem-verde); }
  .cro-ico.atencion { color: var(--sem-amarillo); }
  .cro-ico.riesgo { color: var(--sem-naranja); }
  .sem-icon.problema { color: var(--sem-rojo); }
  .cro-ico.info { color: var(--sem-celeste); }
  /* Mejora reporte-pendientes 2026-09-15 (pedido de Lucho): "vidriado" de
     ICONOS_ESTADO -- mismo lenguaje visual que ya tienen las barras de
     progreso (Avance por fase/Cronograma/Plan de capacidad), pero SOLO
     bajando la opacidad del color de fondo (75%, elegido entre 3 niveles
     verificados con Playwright -- 55% quedaba por debajo del contraste
     grafico minimo de WCAG 1.4.11 en 'riesgo'/'problema', ver
     assets/manual-de-marca.md secc. Vidriado). Sin reborde nuevo y sin tocar
     el signo interior (check/exclamacion/X/punto) -- pedido explicito de que
     la traslucidez vaya solo al color. El primer hijo del <svg> es siempre
     la forma exterior (circulo o triangulo) en los 5 iconos de
     ICONOS_ESTADO -- ver JS mas abajo. */
  .cro-ico > :first-child, .sem-icon > :first-child { fill-opacity: .75; }

  /* ---------- Explorador de datos (Fase E, 2026-09-08) ---------- */
  .explorador-selector { display:flex; flex-wrap:wrap; gap:8px; margin-bottom:16px; }
  /* COS-03: aviso de "mostrando 1.000 de N" cuando PostgREST corta una tabla del
     Explorador en su tope de max_rows -- neutral/informativo, no un error. */
  .expl-aviso-truncado { display:flex; align-items:center; gap:10px; flex-wrap:wrap; background:rgba(168,212,255,0.10); border:1px solid rgba(168,212,255,0.35); border-radius:10px; padding:10px 14px; font-size:12.5px; color:var(--text); margin-bottom:14px; }
  /* DIS-05 (2026-09-14): el halo con box-shadow de acento queda reservado a
     .mod-btn/.nav-btn (navegacion real, primer orden) -- .expl-tab-btn conmuta
     entre 2 vistas DENTRO de la pildora Explorador, no navega, asi que baja a
     chip rectangular de segundo orden: mas chico, sin fondo acento solido ni
     halo. min-height:var(--h-pill) (32px) se saca a proposito -- el alto ahora
     lo define el padding (6px 11px), el reset a 44px de touch target en mobile
     (.expl-tab-btn en el grupo min-height:44px de @media max-width:768px) sigue
     aplicando igual. */
  .expl-tab-btn { display:inline-flex; align-items:center; font-family:inherit; font-weight:600; cursor:pointer; border-radius: 7px; font-size: 12px; padding: 6px 11px; background: var(--bg); border: 1px solid var(--border); color: var(--muted); }
  .expl-tab-btn:hover { color:var(--text); }
  .expl-tab-btn.active { background: var(--accent-14); border-color: var(--accent); color: var(--accent); }
  th.th-acciones, td.td-acciones { width:44px; text-align:center; }
  .agg-lbl { font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:.03em; margin-left:4px; }
  td.agg-ninguno { color:var(--muted); } /* ACC-02: con ACC-01 (--muted mas claro) ya da 5.91:1 */
  tfoot td { padding:8px 16px; border-top:2px solid var(--accent); background:rgba(255,255,255,0.03); font-weight:700; white-space:nowrap; }
  .explorador-footer { display:flex; align-items:center; justify-content:space-between; margin-top:14px; font-size:12px; color:var(--muted); flex-wrap:wrap; gap:10px; }
  .footer-left { display:flex; align-items:center; gap:8px; }
  .pagina-nav { display:flex; align-items:center; gap:10px; }
  .pagina-btn { background:none; border:1px solid var(--border-ctl); color:var(--text); border-radius:var(--r-ico); width:var(--h-ico); height:var(--h-ico); display:inline-flex; align-items:center; justify-content:center; cursor:pointer; font-size:13px; }
  .pagina-btn:hover:not(:disabled) { border-color:var(--accent); color:var(--accent); }
  .pagina-btn:disabled { opacity:.4; cursor:default; }

  /* ---------- Vistas tipo consultor (Detalle por consultor / Preventa /
     Sin asignacion), Fase E ---------- */
  .resumen-filtros-vista { font-size:11.5px; color:var(--muted); margin-bottom:18px; }
  .combo-bloqueado { opacity:.55; pointer-events:none; }
  .toggle-row { display:flex; align-items:center; gap:10px; min-height:44px; }
  .toggle { position:relative; width:40px; height:22px; padding:0; border-radius:999px; background:var(--border-ctl); border:1px solid var(--border-ctl); flex:none; cursor:pointer; }
  .toggle.on { background:var(--accent); border-color:var(--accent); }
  .toggle::after { content:''; position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%; background:var(--text); transition:.15s; }
  .toggle.on::after { left:20px; background:var(--accent-text); }
  .toggle-row .toggle-label { font-size:13px; color:var(--text); }
  .form-actions { display:flex; justify-content:flex-end; gap:10px; margin-top:20px; }
  .btn-ghost { font-family:inherit; font-size:13px; font-weight:600; background:none; color:var(--muted); border:1px solid var(--border-ctl); border-radius:var(--r-ctrl); min-height:var(--h-ctrl); padding:0 16px; cursor:pointer; display:inline-flex; align-items:center; gap:8px; }
  .btn-ghost:hover { color:var(--text); border-color:var(--muted); }

  .table-card { background:var(--superficie-solida); border:1px solid var(--superficie-borde); border-radius:var(--r-card); overflow:hidden; }
  .table-card table { width:100%; border-collapse:collapse; font-size:13.5px; }
  .table-card thead th { text-align:left; font-size:11px; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); font-weight:700; padding:9px 16px; border-bottom:1px solid var(--border); white-space:nowrap; }
  .table-card tbody td { padding:8px 16px; border-bottom:1px solid var(--border); vertical-align:middle; }
  .table-card tbody tr:last-child td { border-bottom:none; }
  .table-card tbody tr:hover { background: var(--accent-05); }
  /* Variante compacta (Explorador de datos, 2026-09-08): mismo padding/tamaño
     de fuente que .tabla-detalle (tabla de Incidentes), sobre el mismo
     esqueleto visual de .table-card (tarjeta con borde). */
  .table-card-compact table { font-size: 12.5px; }
  .table-card-compact thead th { padding: 4px 14px; font-size: 11px; }
  .table-card-compact tbody td { padding: 2px 14px; }

  /* ---------- Edicion inline en tabla (patron nuevo, 2026-09-08) ----------
     Antes "editar" abria un form-card arriba de la tabla (Camino que sigue
     usando "Nuevo X"); ahora la fila se convierte en editable en el lugar
     -- misma idea que ya existia para "+ alias" en Consultores, generalizada
     con clases reutilizables para cualquier tabla con boton de lapiz. */
  .tabla-input, .tabla-select { min-height:32px; background:var(--bg); border:1px solid var(--border-ctl); border-radius:7px; color:var(--text); font-family:inherit; font-size:13px; padding:0 10px; }
  .tabla-input:focus, .tabla-select:focus { border-color:var(--accent); }
  .tabla-input:focus:not(:focus-visible), .tabla-select:focus:not(:focus-visible) { outline:none; }
  .tabla-input[type=date] { color-scheme:dark; cursor:pointer; }
  .tabla-select { width:100%; cursor:pointer; appearance:none; -webkit-appearance:none; padding-right:26px; color-scheme:dark; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23919191'/%3E%3C/svg%3E"); background-repeat:no-repeat; background-position:right 10px center; }
  .tabla-inline-error { font-size:11px; color:var(--sem-rojo-texto); margin-top:5px; }
  .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
  .muted-cell { color: var(--muted); }
  .pill { display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:600; padding:5px 12px; border-radius:999px; border:1px solid transparent; }
  .pill-dot { width:6px; height:6px; border-radius:50%; }
  .pill.activo { background: var(--sem-verde-12); border-color: rgba(62,207,110,0.4); color: var(--sem-verde); }
  .pill.activo .pill-dot { background: var(--sem-verde); }
  .pill.inactivo { background: var(--sem-rojo-10); border-color: var(--sem-rojo-35); color: var(--sem-rojo-texto); }
  .pill.inactivo .pill-dot { background: var(--sem-rojo); }
  /* Ajuste 2026-09-08: variantes semaforo del pill, usadas por los badges de
     grupos en Carga de horas (listo/sin vincular/ya existe/variante) y por
     los resumenes colapsados de "Proyectos y meses" / "Consultores" -- ya
     se usaban estas clases pero faltaba definirlas. */
  .pill.verde { background: var(--sem-verde-12); border-color: rgba(62,207,110,0.4); color: var(--sem-verde); }
  .pill.amarillo { background: var(--sem-amarillo-12); border-color: rgba(232,197,71,0.4); color: var(--sem-amarillo); }
  .pill.rojo { background: var(--sem-rojo-10); border-color: var(--sem-rojo-35); color: var(--sem-rojo-texto); }
  .pill.celeste { background: var(--sem-celeste-12); border-color: rgba(168,212,255,0.4); color: var(--sem-celeste); }
  .pill.pendiente { background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.22); color: var(--text); }
  .pill.pendiente .pill-dot { background: #fff; }
  .pill.naranja { background: var(--sem-naranja-10); border-color: rgba(232,149,74,0.35); color: var(--sem-naranja); }
  .pill.azul { background: var(--accent-16); border-color: var(--accent); color: var(--accent); } /* DIS-04: le faltaba a .pill, la usa .incidente-pill.sem-azul */
  .row-actions { display:flex; gap:8px; justify-content:flex-end; }
  .icon-btn { background:none; border:1px solid var(--border-ctl); color:var(--muted); border-radius:var(--r-ico); width:var(--h-ico); height:var(--h-ico); display:inline-flex; align-items:center; justify-content:center; cursor:pointer; font-size:14px; }
  /* DIS-12: los 7 iconos de accion que eran glifos de texto pasan a SVG del mismo trazo
     que KPI_ICO_SVG (ver ICONOS_ACCION en el JS) -- este selector fija su tamano visual,
     el boton que los contiene sigue midiendo lo mismo (var(--h-ico)/18px/16px). */
  .icon-btn svg, .close-x svg, .chip-rm svg, .primary-btn > svg, .primary-btn .ico-accion, .alias-pill svg { width:16px; height:16px; display:block; }
  .icon-btn:hover { color:var(--text); border-color:var(--muted); }
  .empty-cell { color:var(--muted); font-style:italic; }
  /* DIS-08: estados de carga/vacio, toasts y dialogo de confirmacion */
  .estado-bloque { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px; padding:4px 0; text-align:center; }
  .estado-bloque .estado-titulo { font-weight:600; font-style:normal; color:var(--text); }
  .estado-bloque .estado-detalle { font-weight:400; font-style:normal; }
  .estado-bloque.cargando { gap:10px; padding:10px 0; }
  .estado-bloque.cargando::before {
    content:''; width:18px; height:18px;
    border:2px solid var(--border); border-top-color:var(--accent);
    border-radius:50%; animation: dis08-spin .7s linear infinite;
  }
  @keyframes dis08-spin { to { transform: rotate(360deg); } }
  .estado-bloque-card { padding:28px 24px; max-width:640px; margin:0 auto; }

  .toast-wrap { position:fixed; top:16px; right:16px; z-index:10000; display:flex; flex-direction:column; gap:8px; max-width:360px; }
  .toast { background:var(--card); border:1px solid var(--border); border-left:4px solid var(--accent); border-radius:8px; padding:10px 14px; box-shadow:0 4px 16px rgba(0,0,0,.35); font-size:13px; line-height:1.4; color:var(--text); cursor:pointer; }
  .toast.toast-error { border-left-color:var(--sem-rojo); }
  .toast.toast-ok { border-left-color:var(--sem-verde); }

  .dlg-overlay { position:fixed; inset:0; background:rgba(0,0,0,.55); display:flex; align-items:center; justify-content:center; z-index:10001; padding:16px; }
  .dlg-card { max-width:420px; width:100%; margin:0; }
  .dlg-mensaje { color:var(--text); font-size:14px; line-height:1.5; margin:0 0 18px; }

  /* ---------- Reportar un problema o pedir una mejora (UI-029) ----------
     Dialogo nativo dialog.dialogo, el estandar de project:design (formularios.md). Convive con
     .dlg-overlay: los dialogos que ya existen (confirmarDialogo y demas) siguen en esa mecanica
     hasta que se migren en un item aparte. Cabecera y acciones fijas; solo el cuerpo scrollea.
     El contenido lo pinta js/24-reporte-problemas.js. Las medidas de Celular y de Celular
     horizontal viven en responsive.css. */
  dialog.dialogo { background:var(--superficie-solida); color:var(--text); border:1px solid var(--border-ctl); border-radius:var(--r-card); padding:0; width:min(520px, calc(100vw - 32px)); max-height:calc(100dvh - 32px); box-shadow:0 12px 40px rgba(0,0,0,.45); margin:auto; overflow:hidden; }
  dialog.dialogo[open] { display:flex; flex-direction:column; }
  dialog.dialogo::backdrop { background:rgba(0,0,0,.55); backdrop-filter:blur(2px); }
  .dialogo-cabecera { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; padding:22px 24px 0; flex:none; }
  .dialogo-titulo { font-size:16px; font-weight:700; margin:0; line-height:1.3; }
  .dialogo-titulo:focus { outline:none; }
  .dialogo-sub { font-size:13px; color:var(--muted); margin:4px 0 0; line-height:1.4; }
  .dialogo-cerrar { background:none; border:none; color:var(--muted); cursor:pointer; width:var(--h-ico); height:var(--h-ico); border-radius:var(--r-ico); display:inline-flex; align-items:center; justify-content:center; flex:none; margin:-4px -6px 0 0; }
  .dialogo-cerrar:hover:not(:disabled) { color:var(--text); background:var(--card); }
  .dialogo-cerrar:disabled { opacity:.4; cursor:not-allowed; }
  .dialogo-cerrar svg { width:18px; height:18px; }
  .dialogo-cuerpo { display:flex; flex-direction:column; gap:14px; padding:16px 24px; overflow:auto; min-height:0; flex:1 1 auto; }
  .dialogo-acciones { display:flex; justify-content:flex-end; gap:10px; flex-wrap:wrap; padding:14px 24px 20px; border-top:1px solid var(--border); flex:none; }
  .dialogo-acciones .izq { margin-right:auto; }
  .dialogo-acciones .btn-ghost, .dialogo-acciones .primary-btn, .reporte-respaldo-acciones .btn-ghost { min-height:44px; justify-content:center; }
  .dialogo-acciones button:disabled { opacity:.5; cursor:not-allowed; }
  .reportar-btn { flex:none; }

  /* Campos de prosa: el .field textarea de la app es monoespaciado, pensado para pegar planillas. */
  .field--prosa textarea { font-family:inherit; font-size:14px; line-height:1.45; min-height:88px; }
  .field .opcional { text-transform:none; letter-spacing:0; font-weight:400; }
  .field-ayuda { font-size:12px; color:var(--muted); line-height:1.4; margin:0; }

  /* Contexto que viaja solo (plegado) y adjunto de la consola. */
  .reporte-contexto { border:1px solid var(--border); border-radius:var(--r-ctrl); background:var(--bg); }
  .reporte-contexto > summary { list-style:none; cursor:pointer; display:flex; align-items:center; justify-content:space-between; gap:10px; padding:10px 12px; min-height:44px; font-size:13px; color:var(--muted); line-height:1.35; }
  .reporte-contexto > summary::-webkit-details-marker { display:none; }
  .reporte-contexto > summary b { color:var(--text); font-weight:600; }
  .reporte-contexto > summary .chev { flex:none; display:inline-flex; color:var(--muted); }
  .reporte-contexto > summary .chev svg { width:16px; height:16px; transition:transform .15s; }
  .reporte-contexto[open] > summary .chev svg { transform:rotate(180deg); }
  .reporte-datos { display:flex; flex-wrap:wrap; gap:6px; padding:0 12px 12px; }
  .reporte-revision .reporte-datos { padding:0; }
  .reporte-dato { font-size:12px; color:var(--muted); border:1px solid var(--border); border-radius:var(--r-pill); padding:3px 10px; line-height:1.4; }
  .reporte-dato b { color:var(--text); font-weight:600; }
  .reporte-adjunto { display:flex; align-items:center; gap:10px; font-size:13px; color:var(--muted); line-height:1.4; min-height:44px; }
  .reporte-adjunto input { accent-color:var(--accent); width:18px; height:18px; flex:none; }

  /* Pantalla de revision: exactamente lo que se envia. */
  .reporte-aviso { font-size:12.5px; color:var(--muted); line-height:1.45; border-left:3px solid var(--accent); padding:2px 0 2px 10px; margin:0; }
  .reporte-revision { display:grid; grid-template-columns:minmax(0, 118px) minmax(0, 1fr); gap:10px 14px; margin:0; font-size:13px; }
  .reporte-revision dt { font-size:11px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); font-weight:600; padding-top:2px; }
  .reporte-revision dd { margin:0; line-height:1.45; overflow-wrap:anywhere; }
  .reporte-revision pre { margin:0; font:12px/1.4 ui-monospace, Menlo, monospace; color:var(--muted); background:var(--bg); border:1px solid var(--border); border-radius:var(--r-ctrl); padding:8px 10px; white-space:pre-wrap; overflow-wrap:anywhere; max-height:200px; overflow:auto; }

  /* Enviado y falla (con el respaldo por correo). */
  .reporte-ok { display:flex; flex-direction:column; align-items:flex-start; gap:6px; }
  .reporte-ok-num { font-size:24px; font-weight:700; color:var(--sem-verde); }
  .reporte-falla { background:var(--sem-rojo-10); border:1px solid var(--sem-rojo-35); border-radius:var(--r-ctrl); padding:10px 12px; font-size:13px; color:var(--sem-rojo-texto); line-height:1.45; }
  .reporte-respaldo { border:1px solid var(--border); border-radius:var(--r-ctrl); background:var(--bg); padding:12px; font-size:13px; color:var(--muted); line-height:1.45; }
  .reporte-respaldo b { color:var(--text); }
  .reporte-respaldo-acciones { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; }
  .reporte-respaldo-acciones .btn-ghost { text-decoration:none; }
  .reporte-respaldo .field-ayuda { margin-top:8px; }
  .reporte-copia { width:100%; min-height:96px; margin-top:10px; background:var(--bg); border:1px solid var(--border-ctl); border-radius:var(--r-ctrl); color:var(--text); font:12px/1.4 ui-monospace, Menlo, monospace; padding:8px 10px; resize:vertical; }

  /* ---------- Roster de proyecto: avatares agrupados por rol (Opcion D2,
     reporte-pendientes mejora 2026-09-09) -- reemplaza la tabla Consultor/Rol
     por grupos de chips con avatar. Mockup elegido:
     https://claude.ai/code/artifact/4ffc47a0-c4ab-4249-b37c-2ec865a8bbac */
  .roster-groups { margin-top:4px; }
  .grupo-block { margin-bottom:14px; }
  .grupo-block:last-child { margin-bottom:0; }
  .grupo-label { display:flex; align-items:center; gap:8px; margin-bottom:8px; }
  .grupo-label .rt { font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:var(--accent); }
  .grupo-label .rc { font-size:11px; color:var(--muted); }
  .grupo-label .ln { flex:1; height:1px; background:var(--border); }
  .chip-row { display:flex; flex-wrap:wrap; gap:7px; }
  .avatar-chip { display:inline-flex; align-items:center; gap:6px; background:rgba(255,255,255,.02); border:1px solid var(--border-ctl); border-radius:999px; padding:3px 5px; font-size:12px; color:var(--text); }
  .avatar-chip .avatar { width:24px; height:24px; border-radius:50%; flex:none; background:var(--accent-14); border:1px solid rgba(102,182,255,.3); color:var(--accent); display:inline-flex; align-items:center; justify-content:center; font-size:11px; font-weight:800; margin:0; }
  .chip-rm { width:18px; height:18px; border-radius:50%; border:none; background:none; color:var(--muted); font-size:11px; display:inline-flex; align-items:center; justify-content:center; cursor:pointer; margin-right:1px; padding:0; }
  .chip-rm:hover { color:var(--text); }

  /* ---------- Usuarios (rol/permisos) ---------- */
  .user-cell { display:flex; align-items:center; }
  .avatar { width:30px; height:30px; border-radius:50%; background:var(--border); display:inline-flex; align-items:center; justify-content:center; font-size:11.5px; font-weight:700; color:var(--muted); margin-right:10px; flex:none; }
  .you-tag { font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color:var(--accent); border:1px solid rgba(102,182,255,0.4); background: rgba(102,182,255,0.1); border-radius:5px; padding:1px 6px; margin-left:8px; }
  .role-select { background:var(--bg); border:1px solid var(--border-ctl); color:var(--text); font-family:inherit; font-size:13px; font-weight:600; border-radius:8px; padding:8px 30px 8px 12px; min-height:36px; cursor:pointer; appearance:none; -webkit-appearance:none; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23919191'/%3E%3C/svg%3E"); background-repeat:no-repeat; background-position:right 12px center; }
  .role-select:focus { border-color:var(--accent); }
  .role-select:focus:not(:focus-visible) { outline:none; }
  .role-select.admin { color:var(--accent); }
  .role-select:disabled { opacity:.6; cursor:default; }

  .note-banner { display:flex; gap:12px; align-items:flex-start; background: var(--accent-08); border:1px solid rgba(102,182,255,0.25); border-radius:12px; padding:14px 18px; margin-top:22px; font-size:12.5px; color:var(--muted); }
  .note-banner b { color:var(--text); }

  /* ---------- Selectores / filtros ---------- */
  .header-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
  .header-row .filters { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
  /* Asignaciones (Detalle por consultor / Preventa / Sin asignacion): filtros
     en la misma linea que el titulo, en vez de una fila propia debajo (mejora
     2026-09-09, ver skill report-design-system). Escapado con el modificador
     .header-row--filtros-en-linea para no afectar los demas header-row de la
     app (Proyectos - Plan, Asignaciones - Proyectos, etc.) que no pidieron
     este cambio. */
  .header-row--filtros-en-linea .filters { margin-left: auto; }
  .header-row--filtros-en-linea > div:not(.filters) { flex: 1 1 300px; min-width: 0; }
  .selector-wrap { display:flex; flex-direction:column; gap:4px; }
  .selector-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
  .selector { background: var(--superficie-solida); color: var(--text); border: 1px solid var(--superficie-borde); border-radius: 9px; padding: 8px 12px; font-size: 13px; font-family: inherit; cursor: pointer; min-width: 220px; min-height: 34px; }
  .selector.date-input { min-width: 130px; padding: 8px 10px; font-size: 12.5px; border-radius: 9px; }
  .estado-filter-group { display: flex; gap: 4px; flex-shrink: 0; padding-top: 0; }
  /* DIS-03 (2026-09-14): .estado-filter-btn y .periodo-filter-btn eran casi
     identicas (mismo patron visual, radios/paddings/tamanios apenas distintos)
     -- se fusiona el ESTILO en .filtro-chip-btn. Los nombres de clase
     originales SE MANTIENEN en el HTML y en el JS a proposito:
     initEstadoFilter() hace document.querySelectorAll('.estado-filter-btn')
     SIN scope (selecciona los 2 grupos de Incidentes/Resumen a la vez para que
     queden sincronizados) e initPeriodoFilter()/initPeriodoDateRange() escopan
     por #periodo-filter-group -- fusionar el nombre de clase hubiera roto esa
     seleccion. .filtro-chip-btn es solo estilo, conviven las dos clases en el
     mismo elemento. */
  .filtro-chip-btn { border: 1px solid var(--border-ctl); background: none; color: var(--text); border-radius: var(--r-pill); min-height: 26px; padding: 0 12px; font-size: 11px; font-family: inherit; cursor: pointer; }
  .filtro-chip-btn:hover { border-color: var(--accent); }
  .filtro-chip-btn.active { background: var(--accent); color: var(--accent-text); border-color: var(--accent); font-weight: 600; }
  .periodo-filter-group { display: flex; gap: 3px; margin-bottom: 0; }
    /* Mejora 11: agrupa Estado + Periodo en una sola linea, cada uno en su
     propia capsula con tinte propio -- misma tecnica "vidrio" que el resto
     de la app, sin inventar un componente nuevo. */
  .filtro-capsula { display: flex; flex-direction: column; gap: 3px; padding: 5px 9px; border-radius: 9px; }
  .filtro-capsula.estado { background: var(--accent-08); border: 1px solid var(--accent-14); }
  .filtro-capsula.periodo { background: rgba(255,255,255,.035); border: 1px solid var(--border-ctl); }
  .filtro-capsula-label { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
  .filtro-capsula-fila { display: flex; align-items: flex-end; gap: 6px; flex-wrap: wrap; }
  /* PER-01 (2026-09-15, Camino B, propuesta v2 aprobada por Lucho con Playwright):
     el selector de fecha nativo (.selector.date-input) es mas alto que un chip
     (.filtro-chip-btn, min-height:26px) por su propio padding -- eso hacia que la
     capsula de Periodo terminara 7-9px mas alta que la de Estado, pese a tener la
     misma estructura (label + una sola fila). Se achica el alto del date-input
     SOLO dentro de esta capsula puntual (no toca .selector.date-input base, usado
     en otras vistas) para que la fila completa mida lo mismo que la de Estado --
     el ancho de cada capsula sigue siendo el que necesite su propio contenido, sin
     forzar (pedido explicito: solo igualar alto, no ancho). Ver mobile mas abajo
     para el override que devuelve el alto a 44px (area tactil) en esa resolucion. */
  .filtro-capsula.periodo .selector.date-input { min-height: 26px; padding: 0 8px; }
  /* Chip "Filtros" comprimido para mobile/landscape (mejora 2026-09-09 v2, Opcion A de 3
     alternativas) -- en desktop el <summary> queda oculto y el panel se ve siempre abierto
     (open por default via JS, ver initFiltrosCompactos), en mobile/landscape se muestra el
     summary y el panel respeta el atributo open/cerrado nativo de <details>. */
  .filtros-compactos > summary { display: none; }
  .filtros-compactos-panel { display: flex; flex-direction: row; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
  input[type=date] { border: 1px solid var(--border-ctl); border-radius: 9px; padding: 8px 10px; font-family: inherit; font-size: 13px; background: var(--bg); color: var(--text); }

  /* ---------- KPIs ---------- */
  /* DIS-02 (2026-09-15): .kpis/.kpi (tarjeta plana, sin icono) quedaron sin
     ningun consumidor en toda la app -- verificado por grep antes de
     borrar. Historicamente su unico usuario declarado era #cro-kpis (que
     migro a .kpis-ico/.kpi-ico, ver comentario mas abajo); #view-incidente
     tambien las tenia documentadas en un comentario cercano ("no el de
     .kpis en general") pero ya usaba .kpis-ico de hecho -- ese comentario
     y su regla #view-incidente .kpis (huerfana) tambien se eliminan. Tres
     sistemas de KPI para el mismo rol visual (titulo original del item)
     quedan en uno solo: .kpis-ico/.kpi-ico. */

  /* KPI icono-izquierda GENERALIZADO al resto de la app (pedido de Lucho
     2026-09-11: "aplicá este mismo criterio de ajuste... para los
     siguientes KPIs" -- Asignación, Tickets/Incidentes, Detalle por
     consultor/Preventa/Sin asignación y Alertas). Icono a la izquierda del
     mismo alto que valor+etiqueta, valor que NUNCA se parte en 2 lineas via
     achicar+truncar (ver JS kpiIcoFitValues()/kpiIcoSyncIconHeights()).
     Clases (.kpis-ico/.kpi-ico/.kpi-ico-icon/.kpi-ico-text) reutilizables en
     una sola fila o en varios grupos apilados (DIS-02, 2026-09-15: #cro-kpis
     -- unico caso de 2 filas agrupadas -- tambien pasa a usar este mismo
     componente en vez de tener el suyo propio, ver comentario junto a
     #cro-kpis mas abajo). Mockups aprobados por Lucho: prop_asig/
     prop_tickets/prop_consultor/prop_preventa/prop_alertas.html. Iconos SVG:
     viewBox 24x24, stroke=currentColor, stroke-width 1.8, sin relleno salvo
     puntos/circulos chicos. */
  .kpis-ico { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 20px; }
  @media (min-width: 1100px) { .kpis-ico { grid-template-columns: repeat(4, minmax(0,1fr)); } }
  .kpi-ico { display: flex; align-items: stretch; gap: 10px; background: var(--superficie-solida); border: 1px solid var(--superficie-borde); border-radius: var(--r-card); padding: 8px 10px; min-width: 0; }
  .kpi-ico .num, .kpi-ico .num.small { font-size: 24px; font-weight: 700; color: var(--accent); line-height: 30px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* Si el valor no entra en una linea ni achicando la fuente hasta el piso
     minimo de legibilidad, se trunca con "..." y se agrega title= con el
     texto completo (tooltip). Ver kpiIcoFitValues() en el JS. */
  .kpi-ico .num.cro-num-truncado { text-overflow: ellipsis; cursor: default; }
  .kpi-ico .num.rojo { color: var(--sem-rojo); } /* DIS-04: idem .kpi .num.rojo */
  .kpi-ico .num.naranja { color: var(--sem-naranja); }
  /* DIS-07 (2026-09-15): unificacion de TODAS las etiquetas de la app en
     10px/.04em/uppercase/var(--muted) -- el item tambien citaba ".kpi .lbl"
     como uno de los 7 selectores a unificar, pero esa clase ya no existe
     desde DIS-02 (elimino .kpis/.kpi por completo, mismo dia): el unico
     selector real que queda para esa cita es .kpi-ico .lbl, de abajo. */
  .kpi-ico .lbl { font-size: 11px; line-height: 1.25; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; overflow-wrap: anywhere; }
  .kpi-ico-icon { flex: 0 0 auto; width: 44px; height: 44px; border-radius: 8px; background: var(--accent-14); border: 1px solid var(--accent-32); color: var(--accent); display: flex; align-items: center; justify-content: center; }
  .kpi-ico-icon svg { width: 20px; height: 20px; }
  .kpi-ico-text { min-width: 0; display: flex; flex-direction: column; justify-content: center; }

  /* KPIs de la pildora Resumen (#cro-kpis): 2 filas agrupadas
     (.cro-kpis-grupo-lbl como encabezado de cada grupo). DIS-02
     (2026-09-15): hasta esta fecha #cro-kpis tenia su propia estructura
     .kpi/.kpi-icon/.kpi-text con achicado+truncado y sincronizado de alto
     de icono por JS PROPIOS (croFitKpiValues()/croSyncKpiIconHeights()),
     casi identicos a los de .kpi-ico/kpiIcoCard() que ya usa el resto de
     la app (Asignacion, Tickets/Incidentes, Detalle por consultor,
     Alertas) -- 3 sistemas de KPI para el mismo rol visual. Se
     consolido en uno: los 2 grupos ahora son .kpis-ico (mismo componente
     que el resto), croRenderKpis() arma cada tarjeta con kpiIcoCard() y
     usa el kpiIcoFitAll() ya compartido -- ver comentario junto a
     kpiIcoCard() en el JS. Se elimina toda la CSS que era exclusiva de
     la version vieja de #cro-kpis (incluidos sus overrides de mobile
     landscape, mas abajo). */
  #cro-kpis .cro-kpis-grupo-lbl { font-size: 11px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; margin: 0 0 8px; }
  #cro-kpis .cro-kpis-grupo-lbl:not(:first-child) { margin-top: 14px; }

  /* ============================================================
     Pildora Resumen (modulo Proyecto -- ver claude/pildora-resumen-propuestas.md,
     Project "APP - Project Manager"). KPIs/Cronograma/Alertas del plan de
     proyecto importado desde MS Project XML (script/plan-proyecto-xml.js).
     ============================================================ */
  .charts-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }

  .nota-seccion { font-size: 12.5px; color: var(--muted); margin-bottom: 12px; line-height: 1.5; }

  /* Avance por fase -- barra de progreso con color de estado (celeste/rojo).
     Distinto de .barras-perfil/.bp-* (ese es "% real vs. teorica" con techo
     variable y resaltado de excedente -- otro concepto, no reusar). */
  .avance-fase { display: flex; flex-direction: column; gap: 12px; flex: 1; }
  .af-row { display: grid; grid-template-columns: 118px 1fr 46px; align-items: center; gap: 10px; }
  .af-label { font-size: 12px; color: var(--text); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .af-track { position: relative; height: 16px; background: rgba(255,255,255,0.08); border-radius: 4px; }
  .af-fill { position: absolute; left: 0; top: 0; height: 100%; border-radius: 4px 0 0 4px; box-sizing: border-box; }
  /* DIS-04: renombradas de .celeste/.rojo (nombres de estado que no aplicaban
     a esta barra de PROGRESO) a .en-curso/.completa -- .rojo en particular
     pintaba naranja, doble confusion. */
  /* Mejora reporte-pendientes 2026-09-15 (pedido de Lucho): vuelve al relleno
     translucido + borde solido -- la MISMA tecnica que .gantt-bar .fill
     (Cronograma) y .bp-fill (Plan de capacidad), ya documentada mas abajo en
     el comentario de .gantt-bar (linea ~766: "mismo estilo que Avance por
     fase") pero que habia quedado desactualizada cuando DIS-04 (2026-09-14)
     paso .af-fill a color solido plano al introducir 'en-curso'/'completa'.
     Mismos colores que ya tenia (--accent/--sem-verde), no se toca semantica
     de color -- solo la tecnica de relleno, para que las 3 barras (Avance
     por fase, Cronograma, Plan de capacidad) se vean con el mismo "vidriado". */
  .af-fill.en-curso { background: rgba(102,182,255,0.5); border: 1px solid var(--accent); }
  .af-fill.completa { background: rgba(62,207,110,0.5); border: 1px solid var(--sem-verde); }
  .af-refline { position: absolute; top: -3px; bottom: -3px; width: 2px; background: rgba(255,255,255,0.45); right: 0; }
  .af-value { font-size: 12px; font-variant-numeric: tabular-nums; }
  .af-marker { color: var(--muted); font-size: 11px; margin-left: 1px; }
  .af-footnotes { display: flex; flex-direction: column; gap: 4px; margin-top: auto; padding-top: 8px; border-top: 1px solid var(--border); }
  .af-footnote { font-size: 11px; color: var(--muted); line-height: 1.4; }
  .af-footnote b { color: var(--muted); font-weight: 700; margin-right: 3px; }

  /* Hitos del plan -- grilla de 2 columnas, sin scroll (crece en alto si hace falta) */
  .hito-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2px 24px; align-content: start; }
  .hito-item { display: flex; flex-direction: row; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--border); min-width: 0; }
  .hito-item:nth-last-child(-n+2) { border-bottom: none; }
  .hito-item .nm-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
  .hito-item .nm { color: var(--text); font-size: 12px; font-weight: 600; line-height: 1.25; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .hito-item .cro-ico { flex: none; }
  .hito-item .fecha { color: var(--muted); font-variant-numeric: tabular-nums; font-size: 11px; white-space: nowrap; flex: none; }

  /* Cronograma (Gantt) */
  .gantt-scroll { overflow-x: auto; overscroll-behavior-x: contain; } /* RES-04: deslizar el Gantt no arrastra la pagina entera */
  .gantt { min-width: 760px; --label-w: 230px; }
  .gantt-inner { position: relative; }
  .gantt-month-row, .gantt-week-row { margin-left: var(--label-w); position: relative; height: 20px; border-bottom: 1px solid var(--border); }
  .gantt-month-row span { position: absolute; top: 0; height: 100%; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text); border-left: 1px solid var(--border); }
  .gantt-month-row span:first-child { border-left: none; }
  .gantt-week-row { margin-bottom: 4px; }
  .gantt-week-row span { position: absolute; top: 0; transform: translateX(-50%); font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; }
  /* Semanas pares: una sola franja continua (no por renglon) que arranca en
     la fila de semana -- la banda de mes de arriba queda sin pintar (pedido
     explicito de Lucho, reporte-pendientes mejoras 2026-09-10). El paso de
     la franja se calcula con --gantt-semanas (seteado inline por proyecto). */
  .gantt-zebra-wrap { position: relative; }
  .gantt-zebra-wrap::before {
    content: "";
    position: absolute;
    top: 0; bottom: 0; left: var(--label-w); right: 0;
    background-image: repeating-linear-gradient(to right,
      transparent 0, transparent calc(100% / var(--gantt-semanas, 1)),
      rgba(255,255,255,.055) calc(100% / var(--gantt-semanas, 1)), rgba(255,255,255,.055) calc(200% / var(--gantt-semanas, 1)));
    z-index: 0;
  }
  .gantt-zebra-wrap > * { position: relative; z-index: 1; }
  .gantt-row { display: grid; grid-template-columns: var(--label-w) 1fr; align-items: center; min-height: 30px; }
  .gantt-row.fase .gantt-label { font-weight: 700; }
  .gantt-row.fase.has-children { cursor: pointer; }
  .gantt-chevron { display: inline-block; width: 9px; font-size: 11px; color: var(--muted); transition: transform .15s; }
  .gantt-row.fase.open .gantt-chevron { transform: rotate(180deg); }
  .gantt-children { display: none; }
  .gantt-children.open { display: block; }
  .gantt-label { font-size: 12.5px; padding-right: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
  .gantt-track { position: relative; height: 20px; }
  /* mismo estilo que "Avance por fase" (.af-track/.af-fill): pista neutra + relleno translucido con borde solido */
  .gantt-bar { position: absolute; top: 2px; height: 16px; border-radius: 4px; background: rgba(255,255,255,0.08); overflow: hidden; }
  .gantt-bar .fill { height: 100%; box-sizing: border-box; }
  .gantt-bar .fill.celeste { background: rgba(168,212,255,0.5); border: 1px solid var(--sem-celeste); }
  .gantt-bar .fill.completa { background: rgba(62,207,110,0.5); border: 1px solid var(--sem-verde); } /* mejora reporte-pendientes 2026-09-15: alinea con .af-fill.completa */
  .gantt-baseline-mark { position: absolute; top: -4px; bottom: -4px; width: 2px; background: #fff; opacity: .55; }
  .gantt-hito { position: absolute; top: 3px; width: 14px; height: 14px; background: var(--sem-celeste); transform: translateX(-50%) rotate(45deg); border-radius: 2px; }
  .gantt-hito.rojo { background: var(--sem-rojo); } /* fix 2026-09-15 (pedido de Lucho): estaba pintando naranja pese a llamarse .rojo -- mismo tipo de bug que DIS-04 ya corrigio en .af-fill. Hito vencido ahora usa el MISMO rojo que 'critico'/'problema' en el resto de la app. */
  .gantt-hito.pendiente { background: #fff; }
  .gantt-today { position: absolute; top: 0; bottom: 0; width: 0; border-left: 2px dashed var(--text); z-index: 3; }
  .gantt-today span { position: absolute; top: 2px; left: 6px; font-size: 11px; color: var(--text); font-weight: 700; white-space: nowrap; background: var(--bg); padding: 0 3px; line-height: 18px; }
  .gantt-legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 14px; font-size: 11px; color: var(--muted); }
  .gantt-legend span { display: inline-flex; align-items: center; gap: 6px; }
  .gantt-legend i { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
  .gantt-legend i.linea-base { background: #fff; width: 2px; height: 11px; border-radius: 0; } /* UI-015: ítem «Fin de línea base» (antes en el atributo style del HTML); i.linea-base le gana a .gantt-legend i */

  /* Alertas */
  .alerta-feed { display: flex; flex-direction: column; gap: 0; }
  .alerta-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--border); }
  .alerta-row:last-child { border-bottom: none; }
  .alerta-row .txt { flex: 1; font-size: 13px; color: var(--text); line-height: 1.4; }
  .alerta-row .txt b { color: var(--text); }
  .alerta-row .when { font-size: 11px; color: var(--muted); white-space: nowrap; }
  .alerta-row .cro-ico { flex: none; }
  /* DIS-02 (2026-09-15): .stat-row/.stat-tile se elimina -- su unico
     consumidor (la previsualizacion de import del Plan de Proyecto,
     fases/tareas/hitos) migro a .kpis-ico/kpiIcoCard(), verificado por
     grep antes de borrar. */
  .grupo-h { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--muted); margin: 16px 0 4px; }
  .grupo-h:first-child { margin-top: 0; }

  /* ---------- Filtro de meses en linea (Dashboard de Asignacion) ----------
     Mejora 2026-09-09 (Cowork, mockup aprobado en 3 rondas): el boton vive en
     .header-row (misma fila que titulo/subtitulo) para que .kpis use el 100%
     del ancho; el panel deja de ser el overlay position:absolute de
     .filtro-panel (crearComboFiltro/combo-filtro generico, sin tocar para el
     resto de sus usos en Explorador/vistas tipo consultor/Administracion) y
     pasa a insertarse en el flujo del documento, entre .header-row y .kpis --
     al abrirse empuja el contenido hacia abajo en vez de taparlo (motivo del
     cambio: tapaba la tarjeta "% de asignacion real vs. teorica"). Ocupa una
     sola linea de 44px: Limpiar/Todos fijos a la izquierda, chips de mes con
     scroll horizontal alineados a la derecha, buscador como icono en el
     extremo derecho (orden pedido por Lucho sobre el mockup). Ver
     crearFiltroMesesLinea() mas abajo. */
  .filtro-meses-panel { max-height: 0; overflow: hidden; opacity: 0; transition: max-height .22s ease, opacity .18s ease, margin .22s ease; margin-bottom: 0; }
  .filtro-meses-panel.abierto { max-height: 60px; opacity: 1; margin-bottom: 16px; }
  .filtro-meses-linea { display: flex; align-items: center; justify-content: flex-end; gap: 10px; height: 44px; background: var(--superficie); border: 1px solid var(--superficie-borde); border-radius: 10px; padding: 0 10px; white-space: nowrap; }
  .filtro-meses-chips { flex: 0 1 auto; min-width: 0; display: flex; gap: 6px; overflow-x: auto; scrollbar-width: thin; height: 100%; align-items: center; justify-content: flex-start; }
  .filtro-meses-chips::-webkit-scrollbar { height: 4px; }
  .filtro-meses-chips::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); border-radius: 4px; }
  .mes-chip { flex: 0 0 auto; font-family: inherit; font-size: 12px; padding: 6px 12px; border-radius: 999px; cursor: pointer; background: var(--card); border: 1px solid var(--border-ctl); color: var(--muted); }
  .mes-chip:hover { border-color: var(--accent); color: var(--text); }
  .mes-chip.activo { background: var(--superficie-borde); border-color: var(--accent); color: var(--accent-texto); font-weight: 600; } /* ACC-02: 5.05:1, --accent original se sigue usando en fondo/borde */
  .filtro-meses-buscar-input { flex: 0 0 auto; width: 0; min-height: 26px; background: var(--card); border: 1px solid var(--border-ctl); border-radius: 7px; color: var(--text); font-family: inherit; font-size: 12px; padding: 0; opacity: 0; transition: width .18s ease, padding .18s ease, opacity .12s ease; }
  .filtro-meses-buscar-input.visible { width: 130px; padding: 0 10px; opacity: 1; }
  .filtro-meses-buscar-btn { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: var(--h-ico); height: var(--h-ico); border-radius: var(--r-ico); background: var(--card); border: 1px solid var(--border-ctl); color: var(--muted); cursor: pointer; }
  .filtro-meses-buscar-btn:hover { color: var(--text); border-color: var(--accent); }
  .filtro-meses-buscar-btn.activo { color: var(--accent); border-color: var(--accent); }
  .filtro-meses-buscar-btn svg { width: 13px; height: 13px; }
  .filtro-meses-acciones { flex: 0 0 auto; display: flex; align-items: center; gap: 12px; padding-left: 10px; border-left: 1px solid var(--border); }

  /* ---------- Gráficos ---------- */
  .charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; margin-bottom: 16px; }
  .chart-card { background: var(--superficie-solida); border: 1px solid var(--superficie-borde); border-radius: var(--r-card); padding: 14px; display: flex; flex-direction: column; }
  .chart-card h3 { margin: 0 0 10px 0; font-size: 13px; color: var(--muted); font-weight: 600; }
  .chart-canvas { position: relative; flex: 1; min-height: 220px; }
  .chart-legend { display: flex; justify-content: center; gap: 16px; margin-top: 10px; font-size: 12px; color: var(--muted); }
  .chart-legend .legend-item { display: flex; align-items: center; gap: 6px; }
  .chart-legend .legend-dot { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
  .count-badge { font-weight: 400; color: var(--muted); }

  /* ---------- Resumen de situación (placeholder hasta que se genere con IA — ver
     project_summaries en architecture/schema.sql; todavía no se carga ningún dato ahí) ---------- */
  .resumen-ia-card { background: var(--superficie-solida); border: 1px solid var(--superficie-borde); border-radius: var(--r-card); padding: 10px 18px; margin-bottom: 9px; }
  .resumen-ia-header { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 3px; cursor: pointer; user-select: none; }
  .resumen-ia-header-right { display: flex; align-items: center; gap: 8px; }
  .colapsable-chevron { display: inline-block; color: var(--muted); font-size: 11px; transition: transform 0.15s ease; }
  .colapsable-header.open .colapsable-chevron { transform: rotate(180deg); }
  /* ACC-05 (2026-09-15): reset para los .colapsable-header, que son <button>
     -- deja el aspecto identico al de un encabezado de tarjeta (el resto del
     look sigue viniendo de .resumen-ia-header / .detalle-header). */
  .colapsable-header { background: none; border: none; width: 100%; text-align: left; font: inherit; color: inherit; cursor: pointer; }
  /* UI-014: encabezado colapsable que lleva una accion adentro (Importar,
     Cargar plan, Agregar al roster, Actualizar). Un <button> dentro de otro
     no existe, y un div con role="button" que contiene un boton rompe la
     regla nested-interactive de axe (y se comia el Enter/Espacio del boton
     interior). Por eso la franja es un div sin rol: el TITULO es un <button
     class="colapsable-toggle"> dentro del h3 (lleva el id, aria-expanded y
     aria-controls), la accion y el chevron son sus hermanos en
     .detalle-header-right, y un listener de click en la franja
     (initColapsableConAccion) sigue alternando con el mouse al tocar el
     hueco o el chevron, ignorando los clicks que vienen de un <button>. El
     aspecto en reposo es el de siempre; el anillo de foco rodea solo el
     titulo. El chevron gira con aria-expanded, no con .open de la franja.
     El padding de 4px con margen negativo del mismo valor agranda el area
     de toque a >= 24px de alto (invariante 4 del arnes responsive) sin
     mover nada en pantalla. */
  .colapsable-toggle { display: block; background: none; border: 0; padding: 4px 0; margin: -4px 0; font: inherit; color: inherit; text-align: left; cursor: pointer; }
  .colapsable-franja:has(.colapsable-toggle[aria-expanded="true"]) .colapsable-chevron { transform: rotate(180deg); }
  .colapsable-content { display: none; }
  .colapsable-content.open { display: block; }
  .resumen-ia-header h3 { margin: 0; font-size: 14px; line-height: 17px; color: var(--text); }
  .resumen-ia-meta { font-size: 11px; color: var(--muted); white-space: nowrap; }
  .resumen-ia-veredicto { font-size: 14px; font-weight: 600; color: var(--accent); margin-bottom: 14px; }
  .resumen-ia-section { margin-bottom: 12px; }
  .resumen-ia-section:last-child { margin-bottom: 0; }
  .resumen-ia-section h4 { margin: 0 0 6px 0; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; }
  .resumen-ia-section ul { margin: 0; padding-left: 18px; font-size: 13px; }
  .resumen-ia-section li { margin-bottom: 4px; }
  .resumen-ia-section p { margin: 0; font-size: 13px; }
  .resumen-ia-empty { color: var(--muted); font-size: 13px; font-style: italic; }
  .resumen-ia-badge-stale { background: var(--border); color: var(--muted); padding: 1px 8px; border-radius: 999px; font-size: 11px; margin-left: 6px; }
  /* Boton "Actualizar" manual (mejora 2026-09-11, ver claude/resumen-ia-incidentes-propuesta.md)
     -- mismo patron visual que #sync-redmine-btn (icono solo, se deshabilita
     mientras corre), pero propio para no acoplar ambos botones. */
  .resumen-ia-actualizar-btn { flex: none; display: inline-flex; align-items: center; justify-content: center; width: var(--h-ico); height: var(--h-ico); border-radius: var(--r-ico); background: var(--accent); color: var(--accent-text); border: 1px solid var(--accent); font-size: 13px; cursor: pointer; font-family: inherit; padding: 0; }
  .resumen-ia-actualizar-btn:hover { filter: brightness(1.08); }
  .resumen-ia-actualizar-btn:disabled { opacity: .6; cursor: default; filter: none; }
  .resumen-ia-actualizar-btn.error { background: transparent; color: var(--sem-rojo); border-color: var(--sem-rojo); }
  .resumen-ia-spin { display: inline-block; animation: resumenIaSpin 1s linear infinite; }
  @keyframes resumenIaSpin { to { transform: rotate(360deg); } }
  .resumen-ia-error-msg { font-size: 12px; color: var(--sem-rojo); margin-top: 8px; }

  /* ---------- Login (componente nuevo — no estaba en el sistema de diseño porque el
     standalone anterior no tenía backend ni usuarios; ver nota en el resumen para
     Lucho sobre incorporarlo a report-design-system si se reutiliza en Asignaciones).
     Misma paleta y radios que el resto: tarjeta .card centrada, inputs con el mismo
     patrón que .selector, botón que reutiliza el estado "activo" de .filter-btn. ---------- */
  /* RES-07 (2026-09-14): min-height: 100vh de fallback para navegadores viejos
     que no soportan la unidad svh -- ver min-height: 100svh mas abajo, que la
     pisa donde esta soportada. */
  #login-view { min-height: 100vh; min-height: 100svh; display: flex; align-items: center; justify-content: center; padding: 20px; }
  .login-card { background: var(--superficie-solida); border: 1px solid var(--superficie-borde); border-radius: var(--r-card); padding: 32px 30px; width: 100%; max-width: 360px; }
  .login-title { font-size: 20px; font-weight: 700; margin-bottom: 4px; }
  .login-subtitle { color: var(--muted); font-size: 13px; margin-bottom: 22px; }
  .login-field { margin-bottom: 14px; }
  .login-field label { display: block; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 5px; }
  .login-field input { width: 100%; background: var(--bg); color: var(--text); border: 1px solid var(--border-ctl); border-radius: 8px; padding: 9px 10px; font-size: 13px; font-family: inherit; }
  .login-field input:focus { border-color:var(--accent); }
  .login-field input:focus:not(:focus-visible) { outline:none; }
  .login-btn { width: 100%; background: var(--accent); color: var(--accent-text); border: 1px solid var(--accent); border-radius: var(--r-ctrl); min-height: var(--h-ctrl); padding: 0 16px; font-size: 13px; font-weight: 600; font-family: inherit; cursor: pointer; margin-top: 6px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
  .login-btn:disabled { opacity: .6; cursor: default; }
  .login-error { color: var(--sem-rojo-texto); font-size: 12px; margin-top: 12px; min-height: 14px; }
  .login-btn-google { display: flex; align-items: center; justify-content: center; gap: 10px; background: var(--bg); color: var(--text); border: 1px solid var(--border); margin-top: 0; }
  .login-btn-google:hover { border-color: var(--accent); }
  .login-btn-google:disabled { opacity: .6; cursor: default; }
  .login-google-icon { width: 16px; height: 16px; flex-shrink: 0; }
  .login-divider { display: flex; align-items: center; gap: 10px; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; margin: 18px 0; }
  .login-divider::before, .login-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }

  /* ---------- Distribución / Incidentes — CSS portado de report_template.html
     (mismas clases, mismos valores) ---------- */
  .view { display: none; }
  .view.active { display: block; }
  .date-range { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; font-size: 13px; flex-wrap: wrap; color: var(--muted); }
  .date-range-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .btn-reset { background: var(--bg); border: 1px solid var(--border); border-radius: var(--r-ctrl); min-height: var(--h-ctrl); padding: 0 16px; cursor: pointer; font-size: 13px; font-weight: 600; font-family: inherit; color: var(--text); display: inline-flex; align-items: center; gap: 8px; }
  .btn-reset:hover { border-color: var(--accent); }
  .filter-summary { color: var(--accent); font-size: 12px; }
  /* Reduccion 50% del espacio entre la barra "Limpiar filtros" y los graficos de la pildora Distribucion (pedido de Lucho 2026-09-15) -- acotado a #view-incidente para no afectar la otra barra .date-range (incidentes-chart-filter-bar, pestana Incidentes). */
  #view-incidente .date-range { margin-bottom: 8px; }

  .incidentes-cards { display: flex; flex-direction: column; gap: 16px; margin-bottom: 16px; }
  .incidentes-cards-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
  .incidentes-desc-card { margin-bottom: 16px; }
  /* El buscador de incidentes vive en .header-row (misma fila que titulo/subtitulo,
     alineado a la derecha con justify-content:space-between) para liberar el espacio
     vertical que antes ocupaba en su propia fila -- mismo criterio que el filtro de
     meses de Asignacion (ver comentario en "Filtro de meses en linea" mas arriba).
     El header-row es compartido por las 3 pestanas del modulo Incidentes, asi que el
     buscador se oculta por default y solo se muestra con la pestana "Incidentes"
     activa (mejora 2026-09-09). */
  .header-row .filters.header-search-incidentes { display: none; }
  .content:has(#view-incidentes.active) .header-row .filters.header-search-incidentes { display: flex; }
  .header-row .filters.filtros-resumen { display: none; }
  .content:has(#view-principal.active) .header-row .filters.filtros-resumen { display: flex; }
  .header-row .filters.filtros-distribucion { display: none; }
  .content:has(#view-incidente.active) .header-row .filters.filtros-distribucion { display: flex; }
  .incidentes-search { width: 100%; max-width: 320px; }

  .detalle-card { background: var(--superficie-solida); border: 1px solid var(--superficie-borde); border-radius: var(--r-card); padding: 10px 18px; margin-bottom: 9px; }
  .detalle-header { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 3px; cursor: pointer; user-select: none; min-height: 24px; }
  .detalle-header h3, .detalle-header .detalle-header-titulo { margin: 0; font-size: 13px; line-height: 16px; color: var(--muted); font-weight: 600; }
  .detalle-header-right { display: flex; align-items: center; gap: 8px; }
  .detalle-count { font-size: 12px; color: var(--muted); }
  .table-scroll { overflow-x: auto; }
  /* PERF-05 (2026-09-15): el Explorador de datos ya no pinta la tabla
     entera en la pagina -- con tamanos de pagina grandes (500) se volvia
     inmanejable. La tabla scrollea por dentro y el thead (con los combos
     de filtro) queda fijo arriba via position:sticky en los <th> --
     mas confiable entre navegadores que sticky en el propio <thead>. Los
     dos breakpoints de abajo (mobile portrait y landscape de celular) NO
     son excluyentes, asi que cada uno resetea max-height explicito.
  */
  .table-scroll--expl { max-height: min(70vh, 620px); overflow-y: auto; }
  .table-scroll--expl thead.expl-thead-sticky th { position: sticky; top: 0; z-index: 1; background: var(--superficie-solida); }
  /* RES-04 (2026-09-14): afordance de scroll horizontal -- la clase la
     enciende marcarScrollHorizontal() (JS, mas abajo) SOLO cuando el
     contenido realmente desborda, en cualquier tamano de pantalla (antes
     esto vivia adentro de @media max-width:768px y solo para .table-scroll,
     asi que en desktop/tablet el roster de 1452px de Plan de Capacidad, el
     Explorador y el Gantt scrolleaban sin ninguna pista). Mismo patron
     visual que .tabs-bar. No degrada la ultima columna cuando la tabla
     entra entera, porque la clase no se enciende en ese caso. */
  .scroll-x-activo { mask-image: linear-gradient(to right, #000 92%, transparent); -webkit-mask-image: linear-gradient(to right, #000 92%, transparent); }
  .tabla-detalle { width: 100%; border-collapse: collapse; font-size: 12.5px; }
  .tabla-detalle th { text-align: left; padding: 4px 14px; color: var(--muted); font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: .03em; border-bottom: 1px solid var(--border); white-space: nowrap; }
  .tabla-detalle td { padding: 2px 14px; border-bottom: 1px solid var(--border); white-space: nowrap; }
  .tabla-detalle tbody tr:hover { background: var(--accent-05); }
  .tabla-detalle td.td-asunto { max-width: 360px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tabla-detalle tfoot td { padding: 6px 10px; font-weight: 700; color: var(--accent); border-top: 1px solid var(--border); border-bottom: none; }
  .tabla-detalle-clickable tbody tr { cursor: pointer; }
  .tabla-detalle-clickable tbody tr.selected { background: var(--accent-14); }
  .detalle-pager { display: flex; align-items: center; justify-content: space-between; margin-top: 14px; font-size: 12px; color: var(--muted); flex-wrap: wrap; gap: 10px; }

  .info-row { margin-bottom: 12px; }
  .info-row:last-child { margin-bottom: 0; }
  .info-row .info-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 3px; }
  .info-row .info-value { font-size: 13px; color: var(--text); word-break: break-word; }
  .info-row .info-value a { color: var(--accent); text-decoration: none; }
  .info-row .info-value a:hover { text-decoration: underline; }
  .info-row .info-value.info-desc { white-space: pre-wrap; line-height: 1.5; }

  .incidente-hero { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding-bottom: 14px; margin-bottom: 14px; border-bottom: 1px solid var(--border); flex-wrap: nowrap; }
  .incidente-hero-left { min-width: 0; }
  .incidente-hero-left .incidente-id { font-size: 12px; color: var(--muted); letter-spacing: .04em; }
  .incidente-hero-left .incidente-titulo { font-size: 18px; font-weight: 700; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .incidente-hero-right { display: flex; gap: 10px; flex-wrap: nowrap; flex-shrink: 0; }
  .incidente-info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px 24px; }
  .incidente-info-grid .info-row { margin-bottom: 0; }
  .incidente-pill { display: inline-flex; align-items: center; padding: 6px 16px; border-radius: 999px; border: 1px solid var(--border); white-space: nowrap; }
  .incidente-pill .incidente-pill-val { font-size: 13px; font-weight: 700; }
  /* DIS-04: fondos y bordes ahora IDENTICOS a los de .pill.* de la misma
     familia semantica (el border-radius:999px ya coincidia) -- .incidente-pill
     sigue siendo una clase aparte (padding mas grande, valor en negrita via
     .incidente-pill-val) porque cumple un rol distinto (KPI destacado del
     encabezado de ticket, no un chip de listado), pero pinta con los mismos
     numeros que .pill.verde/.amarillo/.rojo/.naranja/.celeste/.azul/.neutral. */
  .incidente-pill.sem-verde    { background: var(--sem-verde-12);  border-color: rgba(62,207,110,0.4);  color: var(--sem-verde); }
  .incidente-pill.sem-amarillo { background: var(--sem-amarillo-12);  border-color: rgba(232,197,71,0.4);  color: var(--sem-amarillo); }
  .incidente-pill.sem-rojo     { background: var(--sem-rojo-10);   border-color: var(--sem-rojo-35);  color: var(--sem-rojo-texto); }
  .incidente-pill.sem-naranja  { background: var(--sem-naranja-10);  border-color: rgba(232,149,74,0.35); color: var(--sem-naranja); }
  .incidente-pill.sem-celeste  { background: var(--sem-celeste-12); border-color: rgba(168,212,255,0.4); color: var(--sem-celeste); }
  .incidente-pill.sem-azul     { background: var(--accent-16); border-color: var(--accent);         color: var(--accent); }
  .incidente-pill.sem-neutro   { background: var(--bg); border-color: var(--border); color: var(--muted); }

  /* ---------- Tarjetas de detalle para mobile (reemplazan la tabla de detalle
     por debajo del breakpoint -- decision Opcion B confirmada con Lucho
     2026-09-08, ver skill report-design-system / mockup responsive) ---------- */
  .tabla-cards { display: none; flex-direction: column; gap: 8px; margin-bottom: 10px; }
  .tabla-cards-totales { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; background: var(--accent-08); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; margin-bottom: 4px; }
  .tct-item { display: flex; flex-direction: column; }
  .tct-v { font-size: 15px; font-weight: 700; color: var(--accent); line-height: 20px; }
  .tct-l { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; margin-top: 2px; }
  /* DIS-06: .tabla-card queda a proposito con material solido var(--card) (no vidrio) -- va anidada sobre el vidrio de .table-card en el listado mobile y necesita contraste real, no el mismo material translucido de su contenedor. */
  .tabla-card { background: var(--card); border: 1px solid var(--border); border-left: 3px solid var(--border); border-radius: var(--r-card-sm); padding: 10px 12px; }
  .tabla-card[data-ticket-id] { cursor: pointer; }
  .tabla-card.selected { background: var(--accent-14); }
  .tabla-card.sem-verde    { border-left-color: var(--sem-verde); }
  .tabla-card.sem-amarillo { border-left-color: var(--sem-amarillo); }
  .tabla-card.sem-rojo     { border-left-color: var(--sem-rojo); }
  .tabla-card.sem-naranja  { border-left-color: var(--sem-naranja); }
  .tabla-card.sem-celeste  { border-left-color: var(--sem-celeste); }
  .tabla-card.sem-azul     { border-left-color: var(--accent); }
  .tabla-card.sem-neutro   { border-left-color: var(--border); }
  .tabla-card-top { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
  .tabla-card-asunto { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tabla-card-id { font-size: 11px; color: var(--muted); flex: none; }
  /* UI-010: las dos columnas llevan minmax(0,1fr) y no 1fr -- con 1fr el minimo
     automatico de la columna es el contenido mas ancho, y una pildora de estado
     larga (ej. «En espera de informacion del usuario», con nowrap) la empujaba
     fuera de la tarjeta y del ancho del celular. Dentro de la tarjeta la pildora
     corta en varias lineas con esquinas de 14px (999px la dejaria como una
     capsula deforme); el encabezado del ticket (.incidente-hero) conserva la suya
     de una sola linea porque la regla se acota a .tabla-card-field. */
  .tabla-card-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 12px; }
  .tabla-card-field { display: flex; flex-direction: column; min-width: 0; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
  .tabla-card-field .fv { min-width: 0; overflow-wrap: anywhere; color: var(--text); font-size: 12.5px; text-transform: none; letter-spacing: normal; margin-top: 3px; }
  .tabla-card-field .incidente-pill { white-space: normal; max-width: 100%; border-radius: 14px; }

  .topbar-right .sync-freshness { white-space: nowrap; cursor: default; }
  /* UI-017 (2026-10-05): en Celular la pildora de estado de la ultima sincronizacion
     pasa a un boton-icono de 32x32 con el color del semaforo (el mismo nodo, por CSS) y el texto
     completo queda en su title/aria-label y en un toast al tocarla (renderSyncFreshness y el
     listener de abajo). Antes el selector de proyecto (200px) y la pildora (107 a 130px) no
     entraban en los 358px de la fila y se pisaban hasta 63px. UI-018 (2026-10-05): lo mismo en
     Celular horizontal (el bloque de landscape repite estas reglas porque a 844px y 926px no
     matchea el de mobile portrait); en PC el icono no se ve y la pildora es la de siempre. */
  .sync-freshness .sync-ico { display: none; }

