﻿:root {
    --col-info: 240px;
    --col-accion: 165px;
    --col-tipo: 128px;
    --col-km: 90px;
    --col-metrado: 130px;
    --col-espesor: 90px;
    --col-hh: 120px;
    --col-terminado: 80px;
    --col-guardado: 150px;
    --col-quitar: 32px;
    /* Ancho total del grid plano de escritorio (info+accion+tipo+km+km+metrado+espesor+hh+
       terminado+guardado+quitar, 11 columnas, 10 gaps) - lo único que lo necesita es
       .modo-escritorio .form-main, para que su max-width alcance sin desbordar (ver más abajo). */
    --ancho-tabla-escritorio: calc(
        var(--col-info) + var(--col-accion) + var(--col-tipo) + (var(--col-km) * 2) +
        var(--col-metrado) + var(--col-espesor) + var(--col-hh) + var(--col-terminado) +
        var(--col-guardado) + var(--col-quitar) + (12px * 10)
    );
}

* { box-sizing: border-box; }
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    margin: 0;
    background-color: #eef0f2;
    color: #1c1e21;
}

/* ============================== LOGIN ============================== */
.login-body {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    padding: 20px;
}
.login-card {
    background: #fff;
    padding: 40px 30px;
    border-radius: 12px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.08);
    width: 100%;
    max-width: 400px;
    text-align: center;
}
.logo-img { max-width: 140px; margin-bottom: 20px; }
.login-card h2 { margin: 0 0 5px; font-size: 24px; }
.login-card p { color: #606770; font-size: 14px; margin-bottom: 25px; }
.form-group { margin-bottom: 16px; text-align: left; }
.form-group label { display: block; font-size: 13px; color: #444; margin-bottom: 6px; font-weight: 600; }
.form-group input[type="email"],
.form-group input[type="password"],
.form-group input[type="text"],
.form-group input[type="file"],
.form-group select {
    width: 100%; padding: 12px 14px; font-size: 15px;
    border: 1px solid #ccd0d5; border-radius: 6px; outline: none;
    transition: border-color .2s; background: #fff;
}
.form-group input:focus { border-color: #1877f2; box-shadow: 0 0 0 2px rgba(24,119,242,.2); }
.checkbox-group { display: flex; align-items: center; margin-bottom: 20px; text-align: left; }
.checkbox-group input { margin-right: 8px; cursor: pointer; width: 16px; height: 16px; }
.checkbox-group label { font-size: 13px; color: #444; cursor: pointer; user-select: none; }
.btn-submit {
    background: #1877f2; color: #fff; border: none; width: 100%;
    padding: 12px; font-size: 16px; font-weight: 600; border-radius: 6px;
    cursor: pointer; transition: background .2s;
}
.btn-submit:hover { background: #166fe5; }
.error-msg {
    background: #ffebe8; color: #c22915; padding: 12px; border-radius: 6px;
    font-size: 13px; margin-bottom: 20px; font-weight: 600;
    border: 1px solid #f9d1cc; text-align: left;
}
/* Aviso de configuración incompleta (falta config/secretos.php -> sin cookie "recuérdame"). */
.aviso-config {
    background: #fff8e1; color: #8a6100; padding: 10px 12px; border-radius: 6px;
    font-size: 12px; margin: 0 0 16px; border: 1px solid #ffe0a3; text-align: left;
}
.aviso-config code { font-size: 11px; background: rgba(0,0,0,.06); padding: 1px 4px; border-radius: 3px; }

/* ============================== TOPBAR ============================== */
.topbar {
    background: #3d3d3d; color: #fff;
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px 20px;
}
.topbar-back { color: #fff; text-decoration: none; font-size: 20px; font-weight: 700; }
.topbar-user { font-size: 14px; }

/* ============================== DASHBOARD ============================== */
.dashboard-main { max-width: 900px; margin: 30px auto; padding: 0 20px; }
.menu-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; margin-top: 20px; }
.menu-card {
    display: block; background: #fff; border-radius: 10px; padding: 20px;
    text-decoration: none; color: inherit; box-shadow: 0 2px 8px rgba(0,0,0,.06);
    transition: transform .15s;
}
.menu-card:hover { transform: translateY(-2px); }
.menu-card-title { display: block; font-weight: 700; font-size: 16px; margin-bottom: 6px; }
.menu-card-desc { display: block; font-size: 13px; color: #606770; }

/* Pantallas exclusivas del permiso "*" (herramientas BIM/admin) - se distinguen en amarillo del resto del dashboard. */
.menu-card--admin { background: #fffaeb; border: 1px solid #f5d67e; border-left: 4px solid #f0ad00; }
.logout-link { margin-top: 30px; }
.logout-link a { color: #c22915; text-decoration: none; font-size: 13px; }

/* ============================== FORM MAIN ============================== */
/* El layout tipo móvil (apilado) es el DEFAULT siempre, sin importar el ancho de pantalla -ver
   ".btn-modo-escritorio" y ".modo-escritorio" más abajo-, así que en el default el ancho cómodo
   es el de un formulario apilado normal, no el de la tabla de escritorio (que solo se activa con
   el botón flotante). */
.form-main {
    max-width: 720px;
    margin: 24px auto 60px; padding: 0 20px;
    transition: max-width .2s ease;
}
/* Con "modo escritorio" activo, el máximo se recalcula a partir de --ancho-tabla-escritorio (más
   el padding de .fila-escritorio y de este contenedor): así siempre alcanza para la tabla
   completa sin desbordar -si desborda, el contenido se sale del contenedor y el centrado
   (margin:auto) se ve roto/asimétrico-. */
.modo-escritorio .form-main {
    max-width: calc(var(--ancho-tabla-escritorio) + 76px);
}
.form-main h1 { font-size: 24px; margin-bottom: 16px; }
/* Título + botón de acción en la misma línea (ej. "Limpieza de Datos BIM" + "Refrescar limpieza"
   en cargar-bronce.php, tras juntar esa sección con la carga de CSV) - el h1 pierde su propio
   margin-bottom, lo hereda este contenedor, para no duplicar el espacio de abajo. */
.cabecera-con-boton { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; margin-bottom: 16px; }
.cabecera-con-boton h1 { margin-bottom: 0; }
/* Páginas de tabla ancha (ej. resumen-planeamiento.php) - no dependen del cálculo de modo
   escritorio de arriba, que es específico de avance-diario.php. */
.form-main--ancho { max-width: 1400px; }

.semana-box {
    background: #4a4a4a; color: #fff; border-radius: 8px;
    padding: 14px 18px; display: flex; flex-direction: column; gap: 4px;
    margin-bottom: 18px; max-width: 420px;
}
.semana-box span { font-size: 14px; }

.campo-semana-manual { display: flex; align-items: center; gap: 10px; margin-bottom: 18px; }
.campo-semana-manual label { font-size: 13px; font-weight: 600; color: #444; }

/* ============================== PLANEAMIENTO ============================= */
.elemento-card-header-plan { margin-bottom: 10px; }
.elemento-card-header-plan strong { font-size: 15px; }
.meta-plan { font-size: 13px; color: #444; margin-top: 4px; }
.elemento-card-header-plan .tramos-tabla { margin-top: 8px; max-width: 420px; }
/* A pedido: la cascada de planeamiento.php ya no arranca eligiendo Proceso Constructivo (arranca
   en Fase, que puede agrupar varios procesos a la vez), así que cada tarjeta necesita mostrar el
   suyo para que quede claro a qué proceso pertenece. */
.badge-proceso-plan {
    display: inline-block; padding: 2px 10px; border-radius: 12px; margin-right: 8px;
    background: #0e183b; color: #fff; font-size: 12px; font-weight: 700; vertical-align: middle;
}
/* Tipo Elemento de la tarjeta - a pedido: cada tarjeta agrupa por un único Tipo Elemento (va en la
   clave de agrupación, ver agruparEnTarjetas() en avance-diario.js/planeamiento.js), pero antes no
   se mostraba en ningún lado, así que no había forma de distinguir a simple vista dos tarjetas del
   mismo Elemento con distinto Tipo Elemento. Se omite del todo si la tarjeta no tiene (celda vacía
   en tabla_plata), en vez de mostrar un badge vacío. */
.badge-tipo-elemento {
    display: inline-block; padding: 2px 10px; border-radius: 12px; margin-right: 8px;
    background: #eef1f5; color: #2f3742; border: 1px solid #c7cdd6;
    font-size: 12px; font-weight: 700; vertical-align: middle;
}
/* Semanas YA guardadas (planeamientoDeCard()/renderFilasGuardadasPlan()) - separado de
   ".filas-plan"/".fila-plan", que son SOLO para agregar semanas nuevas (ver construirFila()). */
.plan-guardadas { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; }
.plan-guardada-fila {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    background: #f7f8fa; border: 1px solid #e3e6ea; border-radius: 6px; padding: 6px 10px;
    font-size: 13px;
}
.plan-guardada-semana { font-weight: 700; }
.plan-guardada-accion {
    display: inline-block; padding: 1px 8px; border-radius: 10px;
    background: #eef1f5; color: #2f3742; border: 1px solid #c7cdd6; font-size: 11px; font-weight: 600;
}
.plan-guardada-info { margin-left: auto; font-size: 11px; color: #666; font-style: italic; }
.btn-eliminar-plan {
    background: none; border: none; color: #c22915; font-size: 16px; font-weight: 700;
    cursor: pointer; line-height: 1; padding: 0 2px;
}
.filas-plan { display: flex; flex-direction: column; gap: 10px; margin-bottom: 8px; }
.fila-plan {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
}
.fila-plan select, .fila-plan input[type="text"], .fila-plan input[type="date"], .fila-plan input[type="number"] {
    padding: 8px 10px; border-radius: 6px; border: 1px solid #ccd0d5; font-size: 13px;
}
.fila-plan .in-km-ini-plan, .fila-plan .in-km-fin-plan { width: 90px; }
.fila-plan .in-metrado-directo-plan { width: 130px; }
.tramo-plan-block { display: flex; flex-direction: column; gap: 6px; flex-basis: 100%; }
.tramo-plan-selector-row .sel-tramo-plan {
    max-width: 170px; font-size: 12px; padding: 4px 6px;
}
.tramo-plan-campos-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.fila-plan .campo-tramo-plan { display: inline-flex; flex-direction: column; }
.metrado-calc-plan { font-size: 13px; font-weight: 600; color: #1e7e34; }
.btn-agregar-fila {
    background: none; border: 1px dashed #999; border-radius: 6px; padding: 6px 14px;
    font-size: 13px; cursor: pointer; color: #444;
}
.btn-agregar-fila:hover { background: #f0f0f0; }

.filtros-top {
    display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
    margin-bottom: 20px;
}
/* min-width:0 permite que el item de flex se achique por debajo del ancho intrínseco del
   select (por defecto un flex item no se achica más que su contenido); el select en sí trunca
   con ellipsis en vez de forzar scroll horizontal de toda la página en pantallas angostas -pasaba
   con textos largos de Fase como "CONSTRUCCIÓN / TRABAJOS DE RENOVACIÓN - REPARACIÓN DE PAVIMENTO"-. */
.campo-filtro { min-width: 0; max-width: 100%; }
.campo-filtro select {
    padding: 10px 14px; border-radius: 20px; border: 1px solid #ccd0d5;
    font-size: 14px; background: #fff; min-width: 160px; max-width: 100%;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Sin ancestro ".campo-filtro" requerido a propósito: la Acción por fila de avance (ver
   agregarFilaAvanceMovil/Escritorio en avance-diario.js) también usa esta píldora y no cuelga de
   ningún ".campo-filtro". */
.valor-auto {
    display: inline-block; padding: 10px 16px; border-radius: 20px;
    background: #e4e6eb; color: #444; font-size: 14px; font-weight: 600;
}
.valor-auto--noaplica { color: #8a8d91; font-style: italic; font-weight: 400; }
.campo-dia { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.campo-dia label { font-weight: 700; }
.campo-dia select { padding: 10px 14px; border-radius: 6px; border: 1px solid #ccd0d5; }

.elementos-section h2 { font-size: 16px; margin-bottom: 12px; }
.hint-msg { color: #606770; font-style: italic; }
.hint-msg.error { color: #c22915; font-style: normal; font-weight: 600; }
/* Tarjeta sin planificación activa hoy (ver resolverEstadoPlan() en avance-diario.js) - reemplaza
   las filas de avance y el botón "+ Agregar avance" mientras no haya nada planificado vigente. */
.sin-plan-msg { padding: 8px 0; font-weight: 600; }

/* Sincronizar Power BI (sincronizar-powerbi.php). */
.lista-tablas-powerbi { margin: 12px 0 20px; padding-left: 20px; color: #444; font-size: 14px; line-height: 1.7; }
.lista-tablas-powerbi strong { color: #2f3742; }

/* Cabecera de columnas -solo aparece con "modo escritorio" activo, ver más abajo-. */
.elementos-header { display: none; }

/* ============================== TARJETA DE ELEMENTO ============================== */
/* Colapsada por defecto: no todos los elementos se llenan cada día, así que
   no tiene sentido mostrar los campos de todos de entrada. Un click en el
   encabezado (botón) recién despliega Acción + filas de avance. */
.elemento-card {
    background: #eaeaea; border-radius: 8px; padding: 4px 18px 12px; margin-bottom: 14px;
}
.elemento-card-toggle {
    display: block; width: 100%; background: none; border: none; padding: 12px 0 0;
    margin: 0; text-align: left; cursor: pointer; font: inherit; color: inherit;
}
.elemento-card-header {
    display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
}
.elemento-nombre { font-weight: 700; font-size: 15px; letter-spacing: .3px; }
.elemento-avance-pct { font-size: 13px; margin-left: auto; }
.elemento-card-chevron {
    font-size: 18px; color: #888; transition: transform .15s;
}
.elemento-card.abierta .elemento-card-chevron { transform: rotate(90deg); }
/* Termómetro chico y fijo, pegado al nombre -antes ocupaba TODO el ancho de la tarjeta (una
   línea aparte debajo del encabezado), lo que además hacía que creciera junto con la tarjeta en
   escritorio. flex-shrink:0 + un ancho fijo en px aseguran que se quede así de chico siempre. */
.elemento-progressbar {
    width: 90px; flex-shrink: 0; height: 8px; border-radius: 6px;
    background: #f5a623; overflow: hidden;
}
.elemento-progressbar-fill { height: 100%; background: #7fe0ab; }

.elemento-meta-info { font-size: 13px; }
.meta-label { font-weight: 700; }

/* Tabla compacta "Tramos | Metrado | %" -una fila por tramo YA FUSIONADO (rangos que se tocan o
   se solapan se agrupan en uno solo, ver fusionarTramos() en avance-diario.js), en vez del rango
   min-max de todos los tramos crudos (que contaba de más cualquier hueco entre tramos separados
   como si fuera parte del elemento). La columna "%" ya no repite el rango -antes "Avance" volvía
   a escribir el mismo texto que "Tramos"-, solo el porcentaje de ESE tramo puntual. */
.tramos-tabla-fila {
    display: grid; grid-template-columns: 1fr auto auto; column-gap: 14px; align-items: baseline;
}
.tramos-tabla-fila + .tramos-tabla-fila { margin-top: 2px; }
.tramos-tabla-header {
    font-weight: 700; color: #666; font-size: 11px;
    text-transform: uppercase; letter-spacing: .3px; margin-bottom: 2px;
}
.tramos-tabla-fila span:nth-child(2), .tramos-tabla-fila span:nth-child(3) {
    text-align: right; font-variant-numeric: tabular-nums;
}
/* Segmentos planificado/ejecutado (segmentarTramos() en avance-diario.js/planeamiento.js) - a
   pedido: planificado y sin ejecutar todavía = azul, ya ejecutado (dentro de lo planificado) =
   verde oscuro, ambos con letras blancas; lo que NO está planificado se deja sin color (estilo
   "normal" de siempre). Un poco de padding/radio para que se note como una fila resaltada, no
   solo texto de color. */
.tramos-tabla-fila--planificado, .tramos-tabla-fila--ejecutado {
    color: #fff; padding: 2px 6px; border-radius: 4px;
}
.tramos-tabla-fila--planificado { background: #2f6fd6; }
.tramos-tabla-fila--ejecutado { background: #1f6b3a; }
/* Código de 2 letras de la Acción (ver codigoAccion()) a la izquierda de cada fila de "Tramos
   existentes" - a pedido, para poder distinguir de un vistazo a qué Acción corresponde cada
   tramo cuando el elemento tiene más de una (ej. "CO" Concreto, "EN" Encofrado, "AC" Acero) -
   fondo semitransparente para que se lea bien tanto sobre una fila coloreada (planificado/
   ejecutado) como sobre una fila "normal" sin color. */
.tramos-tabla-accion {
    display: inline-block; min-width: 20px; padding: 0 4px; margin-right: 4px;
    border-radius: 3px; background: rgba(0, 0, 0, .12); font-size: 10px; font-weight: 700;
    letter-spacing: .3px; text-align: center;
}
.tramos-tabla-fila--planificado .tramos-tabla-accion, .tramos-tabla-fila--ejecutado .tramos-tabla-accion {
    background: rgba(255, 255, 255, .25);
}

/* Colapsado/expandido animado: en vez de alternar display:none (no se puede animar, quedaba
   brusco) o un max-height fijo (queda desproporcionado: si el tope es mucho más grande que el
   contenido real, el despliegue se "revela" casi entero al toque y el resto de la transición no
   se nota), se anima grid-template-rows 0fr->1fr sobre el propio contenido -técnica estándar de
   acordeón en CSS Grid- para que el tiempo de la animación se sienta proporcional a lo que
   realmente se despliega, sin importar si la tarjeta tiene 1 o 10 filas de avance.
   El body siempre existe en el DOM; la clase "abierta" la pone alternarTarjeta() en
   assets/js/avance-diario.js. */
.elemento-card-body {
    display: grid; grid-template-rows: 0fr; margin-top: 0; opacity: 0;
    transition: grid-template-rows .3s ease, opacity .25s ease, margin-top .3s ease;
}
.elemento-card.abierta .elemento-card-body {
    grid-template-rows: 1fr; opacity: 1; margin-top: 12px;
}
.elemento-card-body-inner {
    overflow: hidden; min-height: 0;
    display: flex; flex-direction: column; gap: 8px;
}

/* Píldora azul compartida por los selects de Acción y Tipo de Avance de cada fila (ambos son
   ahora por fila, no por tarjeta -ver agregarFilaAvanceMovil/Escritorio()-). */
.sel-accion,
.sel-tipo-avance,
.sel-accion-plan {
    background: #4f8ef7; color: #fff; border: none; border-radius: 20px;
    padding: 8px 16px; font-size: 13px; font-weight: 600; cursor: pointer;
    max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.avances-rows { display: flex; flex-direction: column; gap: 10px; margin-bottom: 8px; }
.avance-row { background: #d9d9d9; border-radius: 6px; padding: 10px 12px; }
/* flex-wrap: en móvil ahora hay 2 píldoras por fila (Acción y Tipo de Avance, ver
   agregarFilaAvanceMovil() en avance-diario.js) además de la etiqueta y el botón "×" -en
   pantallas angostas se acomodan en una segunda línea en vez de desbordar-. */
.avance-row-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 8px; }
.avance-row-label { font-weight: 700; font-size: 13px; }
.btn-quitar-fila {
    margin-left: auto; background: none; border: none; color: #c22915;
    font-size: 18px; font-weight: 700; cursor: pointer; line-height: 1;
}
/* Botón "Editar" de una fila YA guardada (ver establecerModoFila() en avance-diario.js) - solo
   visible en modo lectura; en modo edición se oculta y los campos vuelven a ser normales. */
.btn-editar-fila {
    background: none; border: 1px solid #4f8ef7; color: #2f5fb8; border-radius: 14px;
    padding: 4px 10px; font-size: 12px; font-weight: 600; cursor: pointer;
}
.btn-editar-fila:hover { background: #e8f0fe; }
.btn-eliminar-fila {
    background: none; border: 1px solid #c22915; color: #c22915; border-radius: 14px;
    padding: 4px 10px; font-size: 12px; font-weight: 600; cursor: pointer; margin-left: 6px;
}
.btn-eliminar-fila:hover { background: #fbe6e2; }
.celda-acciones-fila { white-space: nowrap; }
.celda-acciones-fila .fase-guardado-msg { margin-left: 8px; color: #c22915; font-size: 12px; }
.fila-en-edicion select, .fila-en-edicion input[type="text"] {
    width: 100%; max-width: 160px; padding: 4px 6px; font-size: 13px;
}
/* Modo lectura: los mismos <input>/<select> quedan deshabilitados (fila.disabled = true en JS) y
   acá solo se les saca el aspecto de campo editable, para que se vean como texto plano -sin
   cambiar la estructura del DOM, clave en escritorio porque ".avance-row" participa directo del
   grid plano de la fila (display:contents) y no puede perder/ganar celdas entre un modo y otro. */
.avance-row--lectura input:disabled, .avance-row--lectura select:disabled {
    background: transparent; border: none; color: #222; opacity: 1; cursor: default;
    -webkit-text-fill-color: #222; /* Safari/Chrome opacan el texto de los disabled por defecto */
}
.avance-row--lectura input[type="checkbox"]:disabled { opacity: .8; }
.avance-row--lectura .sel-accion:disabled, .avance-row--lectura .sel-tipo-avance:disabled {
    background: #4f8ef7; color: #fff; -webkit-text-fill-color: #fff; opacity: .85;
}
.avance-row--lectura .btn-quitar-fila { opacity: 1; }
.avance-row-body {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: 10px; align-items: end;
}
.avance-row-body label { display: block; font-size: 11px; color: #333; margin-bottom: 4px; font-weight: 600; }
/* "Guardado por / cuándo" de una fila YA guardada (registroExistente) - vacío para una fila nueva
   sin guardar todavía. En móvil se ve abajo a la derecha de la fila (hay caja real, .avance-row);
   en escritorio ".avance-row" es "display:contents" (no genera caja propia, ver el comentario
   grande de ".fila-escritorio" más abajo), así que ahí es su PROPIA columna del grid (.fe-guardado)
   en vez de una esquina superpuesta. */
.avance-guardado-info {
    text-align: right; font-size: 11px; color: #555; font-style: italic; margin-top: 6px;
}
.fe-guardado { font-size: 11px; color: #555; line-height: 1.3; }
/* Selector por clase (no por ancestro ".avance-row-body"): en escritorio estos mismos <input>
   cuelgan directo de ".avance-row" (display:contents), sin ese ancestro -si el estilo dependiera
   de ".avance-row-body input", en escritorio se verían sin fondo ni ancho, como <input> nativos-.
   ".in-metrado" queda AFUERA de este grupo a propósito -ver más abajo-: es el único que comparte
   su celda con la unidad (m3, m2, kg...) al costado, así que necesita un ancho distinto (no 100%,
   que dejaba a la unidad sin espacio y se veía descuadrada). */
.in-km-ini, .in-km-fin, .in-espesor, .in-hh {
    width: 100%; padding: 8px 10px; border-radius: 4px; border: none;
    background: #4a4a4a; color: #fff; font-size: 13px;
}
.in-km-ini::placeholder, .in-km-fin::placeholder,
.in-espesor::placeholder, .in-hh::placeholder { color: #bbb; font-style: italic; }

/* Aviso (no bloqueante) cuando el km inicial/final tipeado cae fuera de los tramos planificados
   del elemento -ver validarProgresivaFila() en avance-diario.js-. El motivo completo va en el
   title (tooltip nativo al pasar el mouse), acá solo el color para que salte a la vista. */
.in-km-ini.progresiva-invalida, .in-km-fin.progresiva-invalida {
    outline: 2px solid #f5a623; outline-offset: -1px;
}

/* Vista previa en vivo del formato progresiva (ej. tipear "1240" muestra "1+240" acá abajo) -ver
   actualizarPreview() dentro de llenarCamposFilaAvance() en avance-diario.js-. */
.progresiva-preview {
    display: block; font-size: 11px; color: #606770; margin-top: 3px; min-height: 14px;
}

/* Nota chica dentro del resumen del elemento (ej. "sin detalle de tramos en Oro_2 todavía"). */
.meta-nota { font-size: 11px; color: #8a8d91; font-style: italic; }

/* Metrado Ejecutado + unidad lado a lado: el input se achica lo que haga falta (min-width:0) para
   dejarle espacio fijo a la unidad, en vez de taparla con un input a 100% de ancho. Es un div
   PROPIO, nunca el mismo elemento que ".campo-metrado"/".fe-metrado" -dos razones-: (1) en móvil
   ese contenedor también lleva el <label> "Metrado Ejecutado" arriba, y si el flex fuera en ese
   nivel el label quedaría al lado del input en vez de arriba; (2) ".campo-metrado" es el que
   actualizarVisibilidadTipo() oculta/muestra según Tramo/Metrado con el atributo "hidden" -si ESE
   mismo elemento tuviera "display:flex" declarado acá, la regla de autor le gana en el cascade al
   "display:none" por defecto de [hidden] del navegador (pasó en escritorio: el input de Metrado
   quedaba visible SIEMPRE, aunque se eligiera Tramo). Por eso el flex va en un hijo aparte. */
.metrado-input-wrap { display: flex; align-items: center; gap: 6px; }
.in-metrado {
    flex: 1 1 auto; min-width: 0; padding: 8px 10px; border-radius: 4px; border: none;
    background: #4a4a4a; color: #fff; font-size: 13px;
}
.in-metrado::placeholder { color: #bbb; font-style: italic; }
.unidad-metrado { flex-shrink: 0; font-size: 12px; color: #444; }

.campo-terminado { display: flex; flex-direction: column; align-items: flex-start; }
.in-terminado { width: 20px; height: 20px; cursor: pointer; }

.btn-agregar-avance {
    background: none; border: none; color: #4f8ef7; font-weight: 700;
    cursor: pointer; padding: 4px 0; font-size: 14px;
}

/* Depuración por tarjeta (misma idea visual que .debug-entry, solo visible con DEBUG_MODE
   -usuario "soporte"-, ver debugAvanceTarjeta() en avance-diario.js). */
.card-debug {
    margin-top: 10px; padding: 6px 10px; background: #fffaf0; border: 1px solid #f0e0b0;
    border-radius: 6px; font-size: 12px;
}
.card-debug summary { cursor: pointer; font-weight: 600; color: #a15c00; padding: 4px 0; }

/* ============================== GUARDAR ============================== */
.btn-guardar {
    display: block; width: 100%; margin-top: 20px; padding: 16px;
    background: #5b8fef; color: #fff; font-size: 18px; font-weight: 700;
    border: none; border-radius: 8px; cursor: pointer; transition: background .2s;
}
.btn-guardar:disabled { background: #a9c3ec; cursor: not-allowed; }
.btn-guardar:not(:disabled):hover { background: #4a7ee0; }

.mensaje-guardado { margin-top: 14px; padding: 12px 16px; border-radius: 6px; font-weight: 600; font-size: 14px; }
.mensaje-ok { background: #e6f7ec; color: #1e7e34; border: 1px solid #bfe8cd; }
.mensaje-error { background: #ffebe8; color: #c22915; border: 1px solid #f9d1cc; }
.mensaje-info { background: #eef2f7; color: #444; border: 1px solid #dde3ea; }
.mensaje-warning { background: #fff6e0; color: #8a6100; border: 1px solid #f2debb; }

/* ============================== DEPURACIÓN ============================== */
.debug-panel {
    margin-top: 30px; padding: 16px 18px; border-radius: 8px;
    background: #fffaf0; border: 1px solid #f0e0b0;
}
.debug-panel h2 { margin: 0 0 6px; font-size: 18px; }
.debug-intro { font-size: 13px; color: #7a6a3a; margin: 0 0 14px; }
.debug-entry {
    background: #fff; border: 1px solid #eee1bd; border-radius: 6px;
    margin-bottom: 10px; padding: 6px 12px;
}
.debug-entry summary { cursor: pointer; font-weight: 600; font-size: 13px; padding: 6px 0; }
.debug-block { margin: 8px 0; font-size: 13px; }
.debug-block-google { background: #f7f9fc; border-radius: 6px; padding: 8px 10px; }
.debug-url { font-family: monospace; font-size: 12px; color: #555; word-break: break-all; margin: 2px 0 6px; }
.debug-esquema { font-size: 12px; color: #555; margin-bottom: 6px; }
.debug-nota { font-size: 12px; color: #a15c00; font-style: italic; margin: 4px 0; }
.debug-campo { margin: 4px 0 4px 8px; }
.debug-block pre {
    background: #f3f3f3; padding: 8px; border-radius: 4px; overflow-x: auto;
    font-size: 12px; margin: 4px 0;
}
.debug-table-wrap { overflow-x: auto; margin: 4px 0; }
.debug-table { border-collapse: collapse; font-size: 12px; width: 100%; }
.debug-table th, .debug-table td {
    border: 1px solid #ddd; padding: 4px 8px; text-align: left; white-space: nowrap;
}
.debug-table th { background: #f0f0f0; }
.debug-table input[type="text"] { padding: 4px 6px; border: 1px solid #ccd0d5; border-radius: 4px; font-size: 12px; }
.debug-table button { padding: 4px 10px; font-size: 12px; border-radius: 4px; border: 1px solid #ccd0d5; background: #fff; cursor: pointer; }
.debug-table button:hover { background: #f0f0f0; }
.debug-table select { padding: 4px 6px; border: 1px solid #ccd0d5; border-radius: 4px; font-size: 12px; }

/* asignar-fases.php: resalta las combinaciones sin fase asignada dentro de .debug-table. */
tr.fila-sin-fase { background: #fff3cd; }
tr.fila-sin-fase:hover { background: #ffe8a3; }
.fase-guardado-msg { font-size: 12px; margin-left: 6px; color: #606770; }
.chk-mostrar-sin-fase-label { display: flex; align-items: center; gap: 8px; font-weight: 600; margin: 8px 0; }

/* gestionar-acciones.php: fila de campos en línea para agregar una acción nueva al catálogo. */
.form-nueva-accion { display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: flex-end; margin-bottom: 8px; }
.form-nueva-accion .form-group { margin-bottom: 8px; min-width: 170px; flex: 1 1 170px; }
.form-nueva-accion .form-group input,
.form-nueva-accion .form-group select { width: 100%; padding: 10px 12px; font-size: 14px; }
.form-nueva-accion-submit { flex: 0 0 auto; min-width: 160px; }
.form-nueva-accion-submit .btn-submit { width: 100%; padding: 10px 16px; white-space: nowrap; }

/* Checkboxes de permisos (gestionar-usuarios.php) y las etiquetas que los resumen en la tabla. */
.permisos-checks { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 4px; }
.permisos-checks label { display: flex; align-items: center; gap: 6px; font-weight: 400; font-size: 14px; }
.permiso-badge {
    display: inline-block; background: #eef2f7; color: #2f3742; border: 1px solid #dde3ea;
    border-radius: 10px; padding: 2px 8px; font-size: 11px; font-weight: 600; margin: 1px;
}

.drop-zone {
    border: 2px dashed #ccd0d5; border-radius: 8px; padding: 24px 16px;
    text-align: center; color: #606770; font-size: 14px; cursor: pointer;
    transition: border-color .2s, background .2s;
}
.drop-zone:hover { border-color: #9aa3ac; }
.drop-zone.drop-zone--activa { border-color: #1877f2; background: #eef4fd; color: #1877f2; }
.lista-archivos { list-style: none; margin: 10px 0 0; padding: 0; font-size: 13px; }
.lista-archivos li {
    display: flex; justify-content: space-between; align-items: center;
    padding: 6px 10px; border: 1px solid #e4e6eb; border-radius: 6px; margin-bottom: 6px;
}
.lista-archivos .btn-quitar-archivo {
    border: none; background: none; color: #c22915; cursor: pointer; font-size: 14px; padding: 0 4px;
}

/* ====================== AUTOCOMPLETAR PROCESO CONSTRUCTIVO ================== */
.autocompletar-wrap { position: relative; }
.lista-sugerencias {
    position: absolute; z-index: 20; top: 100%; left: 0; right: 0; margin: 4px 0 0;
    padding: 4px; list-style: none; background: #fff; border: 1px solid #ccd0d5;
    border-radius: 6px; box-shadow: 0 4px 14px rgba(0,0,0,.12);
    max-height: 220px; overflow-y: auto; font-size: 14px;
}
.lista-sugerencias li { padding: 8px 10px; border-radius: 4px; cursor: pointer; }
.lista-sugerencias li:hover { background: #eef4fd; }

/* ====================== CSV YA CARGADOS (reemplazar por completo) =========== */
.lista-archivos-existentes { list-style: none; margin: 10px 0 0; padding: 0; font-size: 13px; }
.lista-archivos-existentes li {
    border: 1px solid #e4e6eb; border-radius: 6px; margin-bottom: 6px;
}
.archivo-existente-item {
    display: flex; align-items: center; gap: 10px; padding: 8px 10px;
    cursor: pointer; user-select: none;
}
.archivo-existente-item input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; flex-shrink: 0; }
.archivo-existente-nombre { font-weight: 600; word-break: break-all; }
.archivo-existente-meta { margin-left: auto; color: #606770; white-space: nowrap; font-size: 12px; }

@media (max-width: 640px) {
    .campo-dia { margin-left: 0; width: 100%; }
    .elemento-meta-info { gap: 14px; }
}

/* ============================== BOTÓN FLOTANTE "MODO ESCRITORIO" ==============================
   El layout apilado tipo móvil es el default SIEMPRE (sin importar el ancho de pantalla) - la
   responsividad automática por @media fue la fuente de casi todos los bugs de alineación de las
   últimas sesiones (columnas invadiéndose entre sí, márgenes rotos, etc.). En vez de eso, el
   layout de tabla de escritorio (ver bloque de abajo) solo se activa a pedido, con la clase
   "modo-escritorio" en <body> que pone/quita initModoEscritorio() en avance-diario.js. El botón
   solo se muestra en pantallas >=900px (ahí es donde la tabla realmente entra); en pantallas más
   angostas ni siquiera aparece y el body nunca lleva la clase. */
.btn-modo-escritorio {
    position: fixed; right: 20px; bottom: 20px; z-index: 50;
    background: #3d3d3d; color: #fff; border: none; border-radius: 24px;
    padding: 12px 20px; font-size: 14px; font-weight: 700; cursor: pointer;
    box-shadow: 0 4px 14px rgba(0,0,0,.25); transition: background .2s;
}
.btn-modo-escritorio:hover { background: #2a2a2a; }

/* ============================== MODO ESCRITORIO: TABLA DE ELEMENTOS ============================
   DOM completamente distinto al de las tarjetas móviles (construirFilaEscritorio() en
   avance-diario.js, no el mismo HTML de .elemento-card reacomodado) - por eso ya no hace falta
   ningún grid anidado ni "subgrid": cada elemento (.fila-escritorio) es UN SOLO CSS Grid plano,
   con las mismas 10 columnas que la cabecera (.elementos-header comparte el mismo
   grid-template-columns). Dentro de esa única grilla:
     - .fe-info (columna 1) no lleva grid-row explícito, así que el auto-placement de CSS Grid lo
       deja fijo en la fila 1 y no se repite para cada "Avance N".
     - Cada fila "Avance N" (.avance-row, con "display:contents" para que sus campos participen
       directo de este grid) declara columna (2 a 10, incluida su propia Acción en .fe-accion -a
       pedido, cada avance elige la suya, no una única por elemento-) para cada uno de sus campos,
       sin fila explícita: el auto-placement por columna hace que la 2da fila de avance caiga sola
       en la siguiente fila libre del grid, y quitar una fila del medio compacta las demás solas
       -sin tener que renumerar nada a mano-. */
.elementos-header,
.fila-escritorio {
    grid-template-columns: var(--col-info) var(--col-accion) var(--col-tipo)
        var(--col-km) var(--col-km) var(--col-metrado) var(--col-espesor) var(--col-hh)
        var(--col-terminado) var(--col-guardado) var(--col-quitar);
    column-gap: 12px;
}
.modo-escritorio .elementos-header {
    display: grid;
    padding: 6px 18px;
    font-size: 12px; font-weight: 700; color: #555;
    text-transform: uppercase; letter-spacing: .3px;
}

.fila-escritorio {
    display: grid;
    row-gap: 8px;
    align-items: center;
    background: #eaeaea; border-radius: 8px; padding: 12px 18px; margin-bottom: 14px;
}
.fe-info { grid-column: 1; align-self: start; }
.fila-escritorio .avances-rows { display: contents; }
.fila-escritorio .avance-row { display: contents; }
.fe-accion { grid-column: 2; width: 100%; }
.fe-tipo { grid-column: 3; width: 100%; padding: 6px 10px; }
.fe-kmini { grid-column: 4; }
.fe-kmfin { grid-column: 5; }
.fe-metrado { grid-column: 6; }
.fe-espesor { grid-column: 7; }
.fe-hh { grid-column: 8; }
.fe-terminado { grid-column: 9; justify-self: center; }
.fe-guardado { grid-column: 10; }
.fe-quitar { grid-column: 11; justify-self: center; margin-left: 0; }
.fe-acciones-fila { display: flex; align-items: center; gap: 6px; }
.fe-acciones-fila .btn-editar-fila { padding: 2px 8px; font-size: 11px; }
.fe-agregar { grid-column: 2; justify-self: start; }
.fila-escritorio > .card-debug { grid-column: 1 / -1; }

/* ============================== MODAL GENÉRICO (ventana emergente) ==============================
   Reusable en cualquier página que ya cargue styles.css - a pedido, para avisos "bien ordenados"
   que no caben en el mensaje de texto de abajo del formulario (ej. solape de tramos al guardar
   avance, ver mostrarModalConfirmacion() en avance-diario.js). Vanilla JS, sin librería. */
.modal-overlay {
    position: fixed; inset: 0; background: rgba(20, 22, 26, .55);
    display: flex; align-items: center; justify-content: center;
    z-index: 200; padding: 16px;
}
.modal-caja {
    background: #fff; border-radius: 10px; max-width: 560px; width: 100%;
    max-height: 85vh; display: flex; flex-direction: column;
    box-shadow: 0 12px 36px rgba(0,0,0,.3);
}
.modal-titulo {
    padding: 18px 22px 0; margin: 0; font-size: 18px; font-weight: 700; color: #2f3742;
}
.modal-cuerpo { padding: 12px 22px 4px; overflow-y: auto; font-size: 14px; color: #333; }
.modal-cuerpo p { margin: 0 0 10px; }
.modal-lista-solapes { list-style: none; margin: 0 0 8px; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.modal-lista-solapes li {
    background: #fff6e0; border: 1px solid #f2debb; border-radius: 8px;
    padding: 10px 12px; font-size: 13px; color: #6b4e00;
}
.modal-lista-solapes .modal-solape-elemento { font-weight: 700; color: #2f3742; display: block; margin-bottom: 4px; }
.modal-acciones {
    padding: 14px 22px 20px; display: flex; justify-content: flex-end; gap: 10px;
}
.modal-btn {
    border: none; border-radius: 6px; padding: 10px 18px; font-size: 14px; font-weight: 700;
    cursor: pointer; transition: background .15s;
}
.modal-btn-cancelar { background: #eceff2; color: #333; }
.modal-btn-cancelar:hover { background: #dfe3e8; }
.modal-btn-confirmar { background: #e3572c; color: #fff; }
.modal-btn-confirmar:hover { background: #c94a22; }

/* ============================== CARGA GRANDE (pantallas pesadas, ej. Resumen de Avances) ========
   A pedido: para reportes que tardan en cargar más que un fetch instantáneo, un indicador grande
   y visible en vez del texto chico de ".hint-msg" - para que quede claro que sigue avanzando. */
.carga-grande {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 16px; padding: 60px 20px; color: #444; font-size: 16px; font-weight: 600;
}
/* BUG REAL corregido: el atributo "hidden" (usado vía "carga.hidden = true" en JS) pierde contra
   un "display:flex" propio con la misma especificidad -por eso el spinner se quedaba pegado en
   pantalla sin desaparecer nunca-. Hace falta este override explícito. */
.carga-grande[hidden] { display: none; }
.carga-grande-spinner {
    width: 42px; height: 42px; border-radius: 50%;
    border: 5px solid #dde3ea; border-top-color: #5b8fef;
    animation: carga-grande-girar 0.8s linear infinite;
}
@keyframes carga-grande-girar { to { transform: rotate(360deg); } }

/* ============================================================================
   Resumen de Planeamiento (resumen-planeamiento.php): tabla ordenable + con filtro por
   columna (tipo autofiltro simple de Sheets, ver assets/js/resumen-planeamiento.js).
   ============================================================================ */
/* ============================== ANALÍTICA DE DATOS ========================= */
.analitica-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0; }
.analitica-tab {
    padding: 9px 16px; border-radius: 20px; border: 1px solid #ccd0d5;
    background: #fff; color: #444; font-size: 14px; font-weight: 600; cursor: pointer;
}
.analitica-tab--activa { background: #0e183b; border-color: #0e183b; color: #fff; }
/* Marca las filas de "Oro - Agrupada por Acción" que usan la progresiva por defecto
   (0+000-1+047) porque ese elemento no tiene ningún tramo real - ver csv_retorno_columnas.md. */
.analitica-fila-default { background: #fff6e0 !important; }
.analitica-fila-default:hover { background: #ffedbf !important; }

/* Alto acotado + overflow-y:auto (además del overflow-x:auto ya existente) - sin esto la tabla
   crecía sin límite y "position: sticky" en el encabezado (ver ".resumen-tabla thead th" más abajo)
   no tenía ningún contenedor con scroll propio al que pegarse, así que no quedaba sticky -se iba
   con el resto de la tabla-; y como el scroll horizontal de un "overflow-x:auto" sin alto acotado
   aparece pegado al borde INFERIOR del contenido (no de la ventana), con una tabla larga había que
   bajar hasta el final de la página para poder usarlo. Mismo patrón ya usado en
   "#resumenAvancesTablaWrap" (ver comentario grande más abajo sobre esa tabla). */
.resumen-tabla-wrap {
    overflow-x: auto; overflow-y: auto; max-height: calc(100vh - 320px); min-height: 240px;
    border: 1px solid #dde1e6; border-radius: 8px;
}
.resumen-tabla { border-collapse: collapse; width: 100%; font-size: 13px; white-space: nowrap; }
/* Sin líneas horizontales entre filas a pedido (se distinguen alternando el fondo, ver
   tbody tr:nth-child(even) más abajo) - en su lugar, una línea VERTICAL muy tenue entre columnas
   (border-right), quitada del último <th>/<td> de cada fila para no dejar un borde colgando al
   final de la tabla. */
.resumen-tabla th, .resumen-tabla td { padding: 8px 10px; text-align: left; border-right: 1px solid rgba(0,0,0,.06); }
.resumen-tabla th:last-child, .resumen-tabla td:last-child { border-right: none; }
.resumen-tabla thead th {
    position: sticky; top: 0; background: #2f3742; color: #fff; z-index: 1;
    user-select: none; border-right-color: rgba(255,255,255,.15);
}
.resumen-tabla tbody tr:nth-child(even) { background: #f7f8fa; }
.resumen-tabla tbody tr:hover { background: #eef3fb; }

/* ---- Edición de avances desde "Resumen por Elemento" (rol "*", resumen-elemento.js) ---- */
.resumen-edit-toolbar { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; flex-wrap: wrap; }
.resumen-edit-toolbar .hint-msg { flex-basis: 100%; margin: 0; }
.btn-secundario {
    border: 1px solid #ccd0d5; background: #fff; color: #2f3742; border-radius: 6px;
    padding: 8px 14px; font-size: 13px; font-weight: 700; cursor: pointer;
}
.btn-secundario:hover:not(:disabled) { background: #f2f4f7; }
.btn-secundario:disabled { opacity: .5; cursor: not-allowed; }
.resumen-edit-input {
    width: 96px; padding: 3px 6px; border: 1px solid #5b93e6; border-radius: 4px;
    font: inherit; text-align: right; background: #fff;
}
.resumen-tabla tbody tr.fila-edit-editable td { background: #eef5ff; }
.resumen-tabla tbody tr.fila-edit-contrario td { background: #fdeff0; color: #b3261e; }
.resumen-tabla tbody tr.fila-edit-contrario td:first-child { box-shadow: inset 3px 0 0 #d3352b; }
.modal-detalle-tabla { width: 100%; border-collapse: collapse; font-size: 12.5px; margin: 6px 0 10px; white-space: nowrap; }
.modal-detalle-tabla th, .modal-detalle-tabla td { border: 1px solid #e3e6ea; padding: 4px 8px; text-align: right; }
.modal-detalle-tabla th:first-child, .modal-detalle-tabla td:first-child { text-align: left; }
.modal-aviso-extra {
    background: #fff6e0; border: 1px solid #f2debb; border-radius: 8px;
    padding: 10px 12px; color: #6b4e00; font-size: 13px; margin-top: 4px;
}
/* Alto de fila FORZADO e IDÉNTICO en las 2 tablas (fija/scroll de Resumen de Avances) - antes cada
   tabla calculaba su propio alto de fila de forma independiente (según su propio contenido), y con
   columnas distintas en cada lado (tras reducir el sticky a solo Sector/Margen/Elemento) los altos
   dejaban de coincidir por casualidad -filas de una tabla más altas que las de la otra, desalineando
   todo hacia abajo-. Con un alto explícito en TODAS las celdas de AMBAS tablas (mismo selector
   ".resumen-tabla", compartido) el alto de cada fila queda garantizado igual sin depender del
   contenido ni de cuántas columnas tenga cada lado. */
.resumen-tabla tbody th, .resumen-tabla tbody td { height: 34px; box-sizing: border-box; padding-top: 0; padding-bottom: 0; vertical-align: middle; }
.resumen-th-inner { display: flex; align-items: center; gap: 6px; }
.resumen-th-label { cursor: pointer; flex: 1; }
.resumen-th-label:hover { text-decoration: underline; }
.resumen-th-orden { font-size: 11px; opacity: .8; min-width: 10px; }
.resumen-filtro-btn {
    background: none; border: none; color: #cfd6e0; cursor: pointer; font-size: 12px;
    padding: 2px 4px; border-radius: 4px; line-height: 1;
}
.resumen-filtro-btn:hover { background: rgba(255,255,255,.15); }
.resumen-filtro-btn.activo { color: #ffd166; font-weight: 700; }
.resumen-filtro-popover {
    position: absolute; top: 100%; left: 0; margin-top: 4px; background: #fff; color: #222;
    border: 1px solid #ccd0d5; border-radius: 8px; box-shadow: 0 6px 18px rgba(0,0,0,.18);
    padding: 8px; width: 220px; max-height: 280px; display: flex; flex-direction: column;
    z-index: 5; white-space: normal;
}
.resumen-filtro-acciones { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 6px; }
.resumen-filtro-acciones button {
    background: none; border: none; color: #2b6cb0; cursor: pointer; padding: 0; font-size: 12px;
}
.resumen-filtro-lista { overflow-y: auto; display: flex; flex-direction: column; gap: 4px; font-size: 12px; }
.resumen-filtro-lista label { display: flex; align-items: center; gap: 6px; font-weight: 400; cursor: pointer; }
.resumen-filtro-lista input[type="checkbox"] { margin: 0; }
#resumenPlanContFase, #resumenAvancesContFase {
    display: flex; align-items: center; gap: 10px; margin-bottom: 16px;
}

/* ============================== RESUMEN DE AVANCE POR ELEMENTO (resumen-elemento.php) ==============================
   Panel de filtros (Fase/Elemento obligatorios via <select>, el resto opcionales de selección
   múltiple via botón-píldora + popover) + 3 bloques de resumen (total general, por Acción, por
   Zona) + 2 tablas de detalle debajo (planificaciones/avances). Reutiliza el mismo componente de
   popover de checkboxes que las columnas ".resumen-tabla" (".resumen-filtro-popover" y afines,
   ver arriba) pero colgado de un botón-píldora en vez de un ícono de encabezado -por eso
   ".filtro-multi-btn" es el que necesita "position: relative" (el popover se posiciona "absolute"
   respecto de él), cosa que un <th> ya tenía gratis por ser "position: sticky". */
.filtros-panel {
    display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px 22px;
    margin-bottom: 20px; padding: 14px 16px; background: #f7f8fa; border: 1px solid #dde1e6;
    border-radius: 10px;
}
.filtro-grupo { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.filtro-grupo label { font-size: 12px; font-weight: 700; color: #444; }
.filtro-grupo--opcional { position: relative; }
.filtro-multi-btn {
    padding: 9px 14px; border-radius: 20px; border: 1px solid #ccd0d5; background: #fff;
    font-size: 13px; color: #2f3742; cursor: pointer; min-width: 170px; max-width: 260px;
    text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.filtro-multi-btn:hover { background: #eef3fb; }
.filtro-multi-btn:disabled { color: #aaa; cursor: not-allowed; background: #f0f1f3; }
.filtro-multi-btn.activo { border-color: #2b6cb0; color: #2b6cb0; font-weight: 600; }
.filtro-grupo--opcional .resumen-filtro-popover { top: calc(100% + 4px); }

.resumen-stats-row { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 22px; }
.resumen-stat-tile {
    flex: 1 1 180px; background: #fff; border: 1px solid #dde1e6; border-radius: 10px;
    padding: 14px 18px;
}
.resumen-stat-label { font-size: 12px; font-weight: 700; color: #767974; text-transform: uppercase; letter-spacing: .03em; }
.resumen-stat-valor { font-size: 26px; font-weight: 700; color: #14171a; margin-top: 4px; }
.resumen-stat-valor.resumen-stat-valor--pct { color: #1f8fb0; }
.resumen-stat-unidad { font-size: 13px; font-weight: 400; color: #767974; margin-left: 4px; }

.resumen-bloque-titulo { font-size: 15px; font-weight: 700; margin: 22px 0 10px; color: #2f3742; }
/* Los cuadros "Por Acción"/"Por Zona" son chicos (pocas filas) - sin el alto/scroll pensado para
   las tablas grandes de detalle (".resumen-tabla-wrap" normal, ver arriba). */
.resumen-tabla-wrap--chica { max-height: none; min-height: 0; margin-bottom: 6px; }

/* Barra superior de Resumen de Avances: navegación semana por semana + toggle diario/progresivas. */
.resumen-barra-superior {
    display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
    gap: 10px; margin-bottom: 12px;
}
.resumen-nav-semana { display: flex; align-items: center; gap: 10px; }
.resumen-nav-btn {
    background: #2f3742; color: #fff; border: none; border-radius: 6px;
    width: 32px; height: 32px; font-size: 13px; cursor: pointer;
}
.resumen-nav-btn:hover { background: #1f252d; }
.resumen-nav-btn:disabled { background: #ccd0d5; cursor: not-allowed; }
.resumen-nav-label { font-weight: 700; color: #2f3742; font-size: 13px; min-width: 220px; text-align: center; }
.resumen-btn-vista {
    background: #5b8fef; color: #fff; border: none; border-radius: 6px;
    padding: 8px 16px; font-size: 13px; font-weight: 700; cursor: pointer;
}
.resumen-btn-vista:hover { background: #4a7ee0; }
.resumen-btn-exportar { background: #1e7e42; }
.resumen-btn-exportar:hover { background: #17632f; }

/* Resumen de Avances (#resumenAvancesTabla): table-layout:fixed + <colgroup> real (armado en JS,
   ver construirColgroupHtml()) en vez del ancho automático que usa el resto de tablas ".resumen-
   tabla" -a pedido: que las columnas de fecha/progresiva NUNCA se aplasten por debajo de su ancho
   fijo (siempre al menos una semana entera visible), y que en cambio aparezca el scroll horizontal
   de ".resumen-tabla-wrap" para ver el resto-. Con table-layout:fixed el ancho de cada columna lo
   define el <col> del colgroup, no el contenido ni el "width"/"min-width" de sus celdas -por eso
   además cada celda de columnas de texto usa ".resumen-celda-meta" (white-space:normal) para que
   el contenido largo pase a la línea siguiente en vez de desbordar el ancho fijo de su columna-.
   Resumen de Planeamiento (#resumenPlanTabla/#resumenPlanTablaFija) usa el mismo modo, por el
   mismo motivo (columnas del Gantt semanal con ancho fijo, mismo patrón de 2 tablas). */
#resumenAvancesTabla, #resumenAvancesTablaFija, #resumenPlanTabla, #resumenPlanTablaFija { table-layout: fixed; width: auto; }
#resumenAvancesTabla, #resumenPlanTabla { min-width: 100%; }
.resumen-celda-meta { white-space: normal; overflow-wrap: break-word; }
/* Dentro de Resumen de Avances (ambas tablas) el texto NUNCA pasa a una segunda línea -a
   diferencia del resto de tablas ".resumen-tabla" (ej. resumen-planeamiento.php), que sí dejan
   envolver texto largo-. Es lo que garantiza que cada fila mida EXACTAMENTE lo mismo de alto en
   las 2 tablas por separado (izquierda fija / derecha con scroll, ver el comentario grande en
   resumen-avances.php sobre por qué son 2 tablas) sin tener que medir ni sincronizar nada por
   JS: mismo padding + mismo alto de línea + siempre 1 sola línea = mismo alto de fila en ambas,
   garantizado por el navegador. El texto que no entra se recorta con "..." en vez de tapar la
   columna de al lado (ver el ancho fijo de cada columna en COLUMNAS, resumen-avances.js). */
#resumenAvancesTabla .resumen-celda-meta, #resumenAvancesTablaFija .resumen-celda-meta,
#resumenPlanTabla .resumen-celda-meta, #resumenPlanTablaFija .resumen-celda-meta {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Altura acotada a la ventana, a pedido -"limita la altura... para que siempre el scroll esté
   visible"-: sin esto la tabla podía crecer sin límite y el scroll quedaba en la PÁGINA entera
   (perdiendo de vista la barra de navegación de semanas de más arriba). El scroll VERTICAL vive
   acá, en el contenedor de AFUERA de las 2 tablas, así ambas bajan juntas a la vez sin necesitar
   ningún JS que las sincronice; el scroll HORIZONTAL vive en ".resumen-tabla-scroll-inner" (ver
   más abajo), que envuelve SOLO la tabla de la derecha - la de la izquierda nunca se mueve, ni
   siquiera está adentro de un contenedor con scroll horizontal. Bajado de 300/320 a 380/240 a
   pedido -"ponle un max height más corto... que se vea el scroll [y] casi que se pierda y hay que
   usar la ruedita"-: menos alto visible = el scroll vertical entra en juego antes, en vez de que
   la tabla completa quepa sin necesitar scroll con pocas filas. */
#resumenAvancesTablaWrap, #resumenPlanTablaWrap {
    max-height: calc(100vh - 380px); min-height: 240px; overflow-y: auto; overflow-x: hidden;
    border: 1px solid #dde1e6; border-radius: 8px;
}
/* 2 tablas lado a lado: la fija no crece ni se achica (flex 0 0 auto); la de scroll ocupa el
   resto del ancho disponible y es la ÚNICA con scroll horizontal propio (min-width:0 es
   necesario para que un hijo flex pueda encogerse por debajo de su ancho de contenido natural,
   si no, el flexbox nunca deja aparecer su scroll interno). */
.resumen-tabla-doble { display: flex; align-items: flex-start; }
.resumen-tabla-fija { flex: 0 0 auto; border-right: 2px solid #dde1e6; }
/* BUG REAL corregido: "overflow-x: auto" solo (sin overflow-y explícito) hace que el navegador
   promueva el overflow-y calculado a "auto" también (regla de CSS Overflow: si un eje no es
   "visible", el otro deja de ser "visible" también) - eso convertía a este div en su PROPIO
   contenedor de scroll para position:sticky, así que el encabezado sticky de la tabla de la
   derecha se quedaba "pegado" a la caja de ESTE div (que nunca necesita scroll vertical propio,
   crece con el contenido) en vez de a #resumenAvancesTablaWrap (el que realmente scrollea) - el
   efecto visible era "el encabezado no es sticky, se va con el resto de la tabla". "overflow-y:
   clip" evita esa promoción sin agregar una segunda barra de scroll vertical (clip no crea un
   contenedor de scroll para estos fines).  */
.resumen-tabla-scroll-inner { flex: 1 1 auto; min-width: 0; overflow-x: auto; overflow-y: clip; }

/* Encabezado de dos filas (semana + día) en la vista diaria. */
.resumen-tabla thead th.resumen-th-semana { text-align: center; background: #232a34; }
.resumen-tabla thead th.resumen-th-dia { text-align: center; font-size: 11px; }
.resumen-tabla td.resumen-td-dia { text-align: right; font-variant-numeric: tabular-nums; }
.resumen-td-dia.resumen-td-dia-vacio { color: #bbb; }

/* Diagrama de Gantt semanal de Resumen de Planeamiento (resumen-planeamiento.js) - una celda por
   semana visible, sin texto: vacía si no hay nada planificado en esa semana para esta fila, o
   pintada (barra sólida) si sí -la fila colapsada pinta la UNIÓN de todos sus tramos/semanas, cada
   fila de detalle pinta SOLO su propia semana, ver celdaGanttGrupo()/celdaGanttDetalle()-. */
.resumen-td-gantt { padding: 4px 3px; }
.resumen-td-gantt-activa { padding: 0; }
.resumen-td-gantt-activa::before {
    content: ''; display: block; height: 100%; min-height: 22px; border-radius: 4px;
    background: #5b8fef;
}
.resumen-fila-detalle .resumen-td-gantt-activa::before { background: #8fb3f5; }

/* Fila de detalle tipo "brecha" (tramo real sin ningún tramo planificado encima, ver
   construirSegmentosGrupo()) - en cursiva y más tenue, para distinguirla de un tramo SÍ
   planificado de un vistazo. */
.resumen-fila-brecha { font-style: italic; color: #8a8d91; }

/* Resumen de Avances: 5 colores de encabezado según el grupo temático de cada columna (ver
   "grupo" en COLUMNAS, resumen-avances.js), a pedido - SOLO el encabezado se colorea, el cuerpo de
   la tabla se queda con el fondo normal (alternado/hover) para no recargar la vista. La fila
   "espaciadora" de la tabla fija (ver renderEncabezadosDiario()) usa las MISMAS clases, para que
   el bloque de color se vea continuo en los 2 renglones del encabezado. */
.resumen-tabla thead th.resumen-th-grupo-verde { background: #2f7a4d; color: #fff; }
.resumen-tabla thead th.resumen-th-grupo-naranja { background: #d9822b; color: #fff; }
.resumen-tabla thead th.resumen-th-grupo-celeste { background: #1f8fb0; color: #fff; }
.resumen-tabla thead th.resumen-th-grupo-lila { background: #8a63c9; color: #fff; }
.resumen-tabla thead th.resumen-th-grupo-amarillo { background: #d4b106; color: #2b2408; }

/* Columnas numéricas (Metrado BIM/Ejecutado/Faltante, % Avance - ver "numerico" en COLUMNAS):
   alineadas a la derecha, a pedido, con números tabulares para que las cifras calcen en columna. */
.resumen-tabla td.resumen-td-numerico { text-align: right; font-variant-numeric: tabular-nums; }

/* Liberado?/Terminado?: check o vacío, nunca texto/número crudo (ver valorCeldaGrupo() en resumen-avances.js). */
.resumen-check { color: #2f7a4d; font-weight: 700; }

/* ---------------------------------------------------------------------------
   Vista "Avance por progresivas" (toggle, tabla plana sin filas desplegables):
   columnas fijas de 20m a lo largo de toda la progresiva, oscuras donde el elemento
   no tiene tramo real, con el % de avance de ESE pedazo de 20m donde sí lo tiene.
   --------------------------------------------------------------------------- */
.resumen-tabla thead th.resumen-th-chunk { text-align: center; font-size: 10px; white-space: normal; }
.resumen-td-chunk { text-align: center; font-variant-numeric: tabular-nums; }
.resumen-td-chunk-sin-tramo { background: #2a2e35 !important; color: #2a2e35; }
.resumen-tabla tbody tr:hover td.resumen-td-chunk-sin-tramo { background: #2a2e35 !important; }
.resumen-td-chunk-0 { color: #b3312c; }
.resumen-td-chunk-parcial { color: #a8760a; font-weight: 700; }
.resumen-td-chunk-completo { color: #1e7e34; font-weight: 700; }
.resumen-th-expandir { width: 28px; }
.resumen-td-expandir { text-align: center; }
.resumen-btn-expandir {
    background: none; border: 1px solid #ccd0d5; border-radius: 4px; width: 22px; height: 22px;
    cursor: pointer; font-size: 11px; line-height: 1; color: #2f3742;
}
.resumen-btn-expandir:hover { background: #eef3fb; }
.resumen-fila-detalle { background: #fbfbfc !important; font-size: 12px; color: #555; }
.resumen-fila-detalle:hover { background: #f2f5f9 !important; }
.resumen-detalle-etiqueta { color: #999; font-style: italic; text-align: right; }
.resumen-desglose { display: flex; gap: 18px; padding: 10px 4px; white-space: normal; }
.resumen-desglose-panel { flex: 1; min-width: 0; }
.resumen-desglose-panel:first-child { border-right: 1px dashed #dde1e6; padding-right: 18px; }
.resumen-desglose-titulo { font-weight: 700; font-size: 12px; color: #2f3742; margin-bottom: 6px; }
.resumen-desglose-tabla { border-collapse: collapse; width: 100%; font-size: 12px; }
.resumen-desglose-tabla th {
    text-align: left; padding: 4px 8px; background: #eef1f4; color: #444; font-weight: 600;
}

/* ============================== DASHBOARDS GERENCIALES (dashboards.php) ==============================
   Paleta categórica validada (skill dataviz, orden fijo, no ciclada) + tokens de texto/superficie
   propios para esta pantalla (pensada como pantalla de presentación para gerencia, reemplazo del
   Power BI "SICAP-BIM") - nunca texto en color de dato: las etiquetas van siempre en tinta oscura
   sobre superficie neutra, nunca superpuestas a un relleno de ancho variable (esa superposición
   era la causa de "las letras se pierden con el fondo" en la versión anterior). */
.dash-body {
    --dash-page: #eef0f2;
    --dash-surface: #ffffff;
    --dash-ink: #14171a;
    --dash-ink-secundaria: #52514e;
    --dash-ink-muted: #767974;
    --dash-borde: rgba(11,11,11,0.08);
    --dash-track: #e3ecf9;
    --dash-c1: #2a78d6; /* azul - serie primaria (ejecutado, progreso) */
    --dash-c2: #eb6834; /* naranja */
    --dash-c3: #1baf7a; /* aqua */
    --dash-c4: #eda100; /* amarillo */
    --dash-c5: #e87ba4; /* magenta */
    --dash-c6: #008300; /* verde */
    --dash-c7: #4a3aa7; /* violeta */
    --dash-c8: #e34948; /* rojo */
    --dash-good: #0ca30c;
    --dash-warning: #c98200;
    --dash-critical: #d03b3b;
    background: var(--dash-page);
    color: var(--dash-ink);
    min-height: 100vh;
    padding-bottom: 90px; /* espacio para el dock flotante */
}

/* ---------- Encabezado fijo ---------- */
.dash-header {
    position: sticky; top: 0; z-index: 60;
    display: flex; align-items: center; gap: 18px;
    background: var(--dash-surface); border-bottom: 1px solid var(--dash-borde);
    box-shadow: 0 2px 10px rgba(0,0,0,.06);
    padding: 10px 22px;
}
.dash-logo { height: 40px; width: auto; object-fit: contain; flex-shrink: 0; }
.dash-logo-saceem { height: 26px; }
.dash-header-titulos { flex: 1; min-width: 0; text-align: center; }
.dash-header-titulos h1 { margin: 0; font-size: 17px; color: var(--dash-ink); line-height: 1.25; }
.dash-header-titulos p { margin: 2px 0 0; font-size: 12px; color: var(--dash-ink-secundaria); }
.dash-header-acciones { display: flex; align-items: center; gap: 14px; flex-shrink: 0; }
.dash-header-user { font-size: 12px; color: var(--dash-ink-secundaria); }
.dash-header-back {
    font-size: 12px; font-weight: 700; color: var(--dash-c1); text-decoration: none;
    border: 1px solid var(--dash-c1); border-radius: 6px; padding: 6px 10px;
}
.dash-header-back:hover { background: var(--dash-c1); color: #fff; }

.dash-main { max-width: 1180px; margin: 0 auto; padding: 22px; }
.dash-vista { display: flex; flex-direction: column; gap: 20px; }
/* BUG REAL corregido: [hidden] es un selector de atributo, misma especificidad que la clase
   .dash-vista de arriba - sin esta regla, "display:flex" del author stylesheet le gana al
   "display:none" del user-agent stylesheet para [hidden], así que dashboards.js's mostrarVista()
   (que solo hace elemento.hidden = true/false) nunca ocultaba de verdad la vista anterior: cada
   click en el dock dejaba todo apilado y la página crecía sin parar. */
.dash-vista[hidden] { display: none; }

/* ---------- Tarjetas base ---------- */
.dash-card {
    background: var(--dash-surface); border-radius: 12px; padding: 20px 22px;
    box-shadow: 0 1px 3px rgba(0,0,0,.06); border: 1px solid var(--dash-borde);
}
.dash-card-titulo { margin: 0 0 16px; font-size: 15px; color: var(--dash-ink); font-weight: 700; }

/* ---------- Resumen Gerencial: hero + cardinal ---------- */
.dash-hero-row { display: flex; gap: 20px; flex-wrap: wrap; align-items: stretch; }
.dash-hero-tile {
    background: var(--dash-surface); border-radius: 12px; padding: 24px 28px;
    box-shadow: 0 1px 3px rgba(0,0,0,.06); border: 1px solid var(--dash-borde);
    display: flex; flex-direction: column; gap: 10px; justify-content: center;
    min-width: 260px; flex: 0 0 260px;
}
.dash-hero-label { font-size: 13px; color: var(--dash-ink-secundaria); font-weight: 600; }
.dash-hero-valor { font-size: 44px; font-weight: 700; color: var(--dash-ink); line-height: 1; }

.dash-meter {
    height: 14px; border-radius: 7px; background: var(--dash-track); overflow: hidden; position: relative;
}
.dash-meter--grande { height: 18px; border-radius: 9px; }
.dash-meter-fill {
    height: 100%; background: var(--dash-c1); border-radius: 7px 4px 4px 7px;
    min-width: 4px; transition: width .3s ease;
}

/* Avance General: el % va centrado DENTRO de la barra (a pedido). isolation + mix-blend-mode:
   difference dejan el texto legible tanto sobre el track claro como sobre el relleno de color,
   sin importar el ancho del relleno - única excepción a "ningún texto sobre relleno variable"
   del resto del dashboard (ver cabecera de dashboards.js). */
.dash-meter--con-pct { height: 20px; border-radius: 10px; isolation: isolate; }
.dash-meter--con-pct .dash-meter-fill { border-radius: 10px 4px 4px 10px; }
.dash-meter-pct {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 11px; font-weight: 700; color: #fff; mix-blend-mode: difference;
    font-variant-numeric: tabular-nums; letter-spacing: .2px; pointer-events: none;
}

.dash-cardinal-grid {
    flex: 1; min-width: 280px; display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 12px;
}
.dash-cardinal-card {
    background: var(--dash-surface); border: 1px solid var(--dash-borde); border-top: 4px solid var(--dash-cor, var(--dash-c1));
    border-radius: 10px; padding: 14px 16px; box-shadow: 0 1px 3px rgba(0,0,0,.06);
    display: flex; flex-direction: column; gap: 8px;
}
.dash-cardinal-card-cabecera { display: flex; align-items: center; gap: 8px; }
.dash-cardinal-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--dash-cor, var(--dash-c1)); flex-shrink: 0; }
.dash-cardinal-card-nombre { font-size: 12px; font-weight: 700; color: var(--dash-ink-secundaria); text-transform: uppercase; }
.dash-cardinal-card-pct { font-size: 24px; font-weight: 700; color: var(--dash-ink); }

/* ---------- Listas "meter" reutilizadas por Resumen Gerencial / Avance General / Avance Semanal ---------- */
.dash-lista-meter { display: flex; flex-direction: column; gap: 14px; }
.dash-fila-meter { display: grid; grid-template-columns: 220px 1fr auto; align-items: center; gap: 12px; }
.dash-fila-meter-label {
    font-size: 12.5px; color: var(--dash-ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap;
}
.dash-fila-meter-valor {
    font-size: 12.5px; color: var(--dash-ink-secundaria); white-space: nowrap; font-variant-numeric: tabular-nums;
    text-align: right;
}
.dash-fila-meter-valor strong { color: var(--dash-ink); font-weight: 700; }
.dash-vacio { color: var(--dash-ink-muted); font-size: 13px; }

/* ---------- Filtros ---------- */
.dash-filtros-row { display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-end; }
.dash-filtro { display: flex; flex-direction: column; gap: 5px; }
.dash-filtro label { font-size: 12px; font-weight: 700; color: var(--dash-ink-secundaria); }
.dash-filtro select {
    padding: 8px 12px; border: 1px solid #ccd0d5; border-radius: 6px; font-size: 13px;
    background: var(--dash-surface); min-width: 200px; color: var(--dash-ink);
}
.dash-pills { display: flex; gap: 6px; }
.dash-pill {
    border: 1px solid #ccd0d5; background: var(--dash-surface); color: var(--dash-ink-secundaria);
    border-radius: 999px; padding: 7px 14px; font-size: 12.5px; font-weight: 700; cursor: pointer;
}
.dash-pill:hover { border-color: var(--dash-c1); color: var(--dash-c1); }
.dash-pill--activa, .dash-pill--activa:hover { background: var(--dash-c1); border-color: var(--dash-c1); color: #fff; }

/* La tabla Unidad>Elemento>Acción y la de una fila por partida (#agTabla/#agEstadoTabla) se
   quitaron a pedido ("esas tablas larguísimas... quiero gráficos") - ambas vistas ahora son
   dash-lista-meter, como el resto del dashboard (ver renderAvanceGeneral()/renderEstadoPartidas()/
   COLOR_ESTADO en dashboards.js). */

/* ---------- Avance Semanal: barra proporcional + leyenda ---------- */
.dash-barra-proporcional {
    display: flex; width: 100%; height: 30px; border-radius: 8px; overflow: hidden; gap: 2px;
    background: var(--dash-page);
}
.dash-barra-proporcional-seg { height: 100%; min-width: 3px; }
.dash-leyenda { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 12px; }
.dash-leyenda-item { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--dash-ink); }
.dash-swatch { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
.dash-swatch--track { background: var(--dash-track); border: 1px solid var(--dash-borde); }
.dash-leyenda-svg { display: flex; gap: 20px; font-size: 12.5px; color: var(--dash-ink-secundaria); margin: -6px 0 14px; flex-wrap: wrap; }
.dash-leyenda-svg span { display: inline-flex; align-items: center; gap: 6px; }

/* ---------- Avance Semanal vs Acumulado (SVG) ---------- */
.dash-svg-wrap { width: 100%; overflow-x: auto; }
.dash-svg-wrap svg { display: block; }
.dash-svg-eje-texto { font-size: 10px; fill: var(--dash-ink-muted); }
.dash-svg-valor-texto { font-size: 10px; fill: var(--dash-ink-secundaria); font-weight: 600; }

/* ---------- Planeamiento Semanal (Gantt) ---------- */
.dash-gantt-wrap { overflow-x: auto; }
.dash-gantt-grid { display: grid; row-gap: 16px; column-gap: 6px; align-items: start; min-width: 760px; }
.dash-gantt-celda-header {
    font-size: 11px; font-weight: 700; color: var(--dash-ink-secundaria); text-align: center;
    text-transform: capitalize; padding-bottom: 8px; border-bottom: 2px solid var(--dash-borde);
}
.dash-gantt-fila-wrap { display: flex; flex-direction: column; gap: 4px; }
.dash-gantt-celda-label {
    font-size: 12px; color: var(--dash-ink); display: flex; align-items: center;
    padding: 6px 8px; background: #f7f8fa; border-radius: 4px;
}
.dash-gantt-barra { background: var(--dash-c1); border-radius: 5px; min-height: 22px; }
.dash-gantt-caption {
    font-size: 11px; color: var(--dash-ink-secundaria); padding: 0 2px; font-variant-numeric: tabular-nums;
}

/* ---------- Dock flotante (menú de vistas) ---------- */
.dash-dock {
    position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
    display: flex; gap: 4px; background: var(--dash-surface); border-radius: 999px;
    box-shadow: 0 6px 24px rgba(0,0,0,.18); border: 1px solid var(--dash-borde);
    padding: 6px; z-index: 55; max-width: calc(100vw - 24px); overflow-x: auto;
}
.dash-dock-item {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    background: none; border: none; border-radius: 999px; padding: 8px 16px; cursor: pointer;
    color: var(--dash-ink-secundaria); font-size: 10.5px; font-weight: 700; white-space: nowrap;
}
.dash-dock-icono { font-size: 16px; line-height: 1; }
.dash-dock-item:hover { background: var(--dash-page); }
.dash-dock-item--activo { background: var(--dash-c1); color: #fff; }
.dash-dock-item--activo:hover { background: var(--dash-c1); }

@media (max-width: 720px) {
    .dash-header { flex-wrap: wrap; }
    .dash-header-titulos { order: 3; flex-basis: 100%; text-align: left; }
    .dash-fila-meter { grid-template-columns: 140px 1fr; }
    .dash-fila-meter-valor { grid-column: 1 / -1; text-align: left; }
}
