  /* ---------- Responsive -- un solo breakpoint, ver skill report-design-system ---------- */
  @media (max-width: 768px) {
    /* PERF-05: mobile portrait sigue teniendo bastante alto disponible
       (a diferencia del landscape de celular de abajo) -- se achica un
       poco igual para dejar mas lugar al selector de tabla/footer. */
    .table-scroll--expl { max-height: min(60vh, 480px); }
    .topbar { flex-wrap: wrap; height: auto; padding: 12px 16px; row-gap: 8px; }
    .topbar { min-height: 0; }
    .topbar-right { width: 100%; justify-content: flex-start; flex: 0 1 auto; min-width: auto; }
    /* UI-011: reset del tope de PC del selector del topbar (ver .topbar-right base) */
    .topbar-project-wrap .selector { width: auto; max-width: none; text-overflow: revert; overflow: revert; white-space: revert; }
    .topbar-right .sync-freshness { font-size: 11px; padding: 4px 9px; }
    /* UI-017: la pildora de sincronizacion es un boton-icono; el selector toma el ancho que sobra */
    .topbar-right { gap: 10px; }
    .topbar-right .sync-freshness { position: relative; flex: none; justify-content: center; width: var(--h-ico); height: var(--h-ico); padding: 0; border-radius: 50%; cursor: pointer; }
    .sync-freshness .pill-dot { display: none; }
    .sync-freshness .sync-ico { display: block; }
    .sync-freshness-txt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); }
    #sync-redmine-btn { flex: none; }
    .tabs-bar { padding: 12px 16px; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; -ms-overflow-style: none; mask-image: linear-gradient(to right, #000 88%, transparent); -webkit-mask-image: linear-gradient(to right, #000 88%, transparent); }
    .tabs-bar::-webkit-scrollbar { display: none; }
    .nav-btn { flex: none; }
    .content { padding: 16px 16px 24px 16px; }
    .header-row { flex-direction: column; align-items: stretch; }
    .header-row .filters { flex-direction: column; align-items: stretch; width: 100%; }
    .header-row > div:first-child { display: none; } /* FIX 2026-09-09 (reporte-pendientes): antes solo .header-row--filtros-en-linea (4/11 pildoras) ocultaba titulo+subtitulo en mobile portrait -- las otras 7 (header-row simple) nunca lo hacian. Se usa :first-child en vez de :not(.filters) porque en "Proyecto -> Asignacion" el filtro de mes vive en un div sin la clase .filters (mount aparte), que con :not(.filters) se ocultaria por error; el bloque de titulo+subtitulo es siempre el primer div hijo de .header-row en las 11 pildoras reales. Verificado con Playwright (ver auditoria 2026-09-09, propuesta aprobada por Lucho). */
    .filtros-compactos { border: 1px solid var(--border); border-radius: 9px; background: rgba(255,255,255,.035); }
    .filtros-compactos > summary { display: flex; align-items: center; gap: 7px; padding: 9px 12px; font-size: 12px; cursor: pointer; list-style: none; color: var(--text); }
    .filtros-compactos > summary::-webkit-details-marker { display: none; }
    .filtros-compactos > summary .chev { margin-left: auto; color: var(--muted); font-size: 11px; transition: transform .15s; }
    .filtros-compactos[open] > summary .chev { transform: rotate(180deg); }
    .filtros-compactos-panel { flex-direction: column; align-items: stretch; padding: 4px 12px 12px; border-top: 1px solid var(--border); margin-top: 2px; }
    .header-row--filtros-en-linea .filters { margin-left: 0; }
    .selector, .selector.date-input { width: 100%; min-width: 0; }
    .estado-filter-group { flex-wrap: wrap; }
    .periodo-filter-group { flex-wrap: wrap; }
    /* PER-01: en mobile las 2 capsulas van apiladas (una debajo de la otra, no
       lado a lado) -- igualar el alto entre ellas no aplica ahi, asi que se
       revierte al alto de area tactil de 44px (igual que el resto de los
       controles de la app en esta resolucion, ver mas abajo en este bloque). */
    .filtro-capsula.periodo .selector.date-input { min-height: 44px; padding: 0 10px; }
    /* Generalizacion del componente icono-izquierda (ver comentario junto a
       .kpis-ico mas arriba) -- mismo criterio de mobile compacto que
       #cro-kpis (2 lineas mas abajo): padding/tipografia vuelven a los
       valores chicos, la Opcion B de padding/tipografia grande es solo
       para notebook/tablet horizontal. */
    .kpis-ico { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .kpi-ico { padding: 12px 10px; }
    .kpi-ico .num, .kpi-ico .num.small { font-size: 22px; line-height: 28px; }
    .kpi-ico .lbl { font-size: 11px; min-height: 26px; }
    .kpi-ico-icon { width: 54px; height: 54px; }
    .kpi-ico-icon svg { width: 22px; height: 22px; }
    /* DIS-02 (2026-09-15): #cro-kpis paso a usar .kpis-ico/.kpi-ico (ver
       comentario junto a esas clases en la hoja de estilos base) -- el
       reset de mobile de la linea de arriba ya cubre #cro-kpis por igual,
       sin necesitar un override propio (que antes tenia los MISMOS
       valores duplicados aca). */
    /* Proyecto -> Asignacion: el filtro de meses paso a vivir en .header-row
       (mejora 2026-09-09, reemplaza a .kpis-filtro-row) -- el header-row ya
       apila a columna con la regla de arriba, este boton pasa a ancho
       completo, mismo criterio que .header-row--filtros-en-linea .filters
       .selmes-btn (Detalle por consultor / Preventa / Sin asignacion). */
    .filtro-meses-btn-mount .selmes-btn { width: 100%; justify-content: space-between; }
    /* Filtro de meses (Asignacion, modulo Proyecto): mismo cambio que el selector de
       modulos (mejora 2026-09-10, pedida por Lucho: "replicar el mismo cambio... para
       el filtro de mes de la pildora Asignacion") -- en vez de la linea unica de 44px
       con scroll horizontal para los meses, en mobile vertical se apilan en una lista
       completa (sin scroll), un mes por renglon. A diferencia del selector de modulos,
       ES multiseleccion: elegir un mes NO cierra el panel (solo el boton .selmes-btn
       lo abre/cierra), para poder tildar varios seguidos. Desktop y landscape de
       celular no cambian -- reset explicito mas abajo, mismo gotcha que .modules.
       Se pisa el max-height:60px de .abierto (pensado para la linea unica) porque la
       lista apilada supera ese limite -- max-height:none tambien pierde la transicion
       de apertura en mobile, aceptado (unico efecto: no anima, no rompe nada). */
    .filtro-meses-panel.abierto { max-height: none; }
    .filtro-meses-linea { flex-direction: column; align-items: stretch; height: auto; padding: 10px; gap: 8px; }
    .filtro-meses-chips { flex-direction: column; overflow: visible; height: auto; align-items: stretch; gap: 6px; }
    .mes-chip { width: 100%; text-align: left; padding: 11px 14px; min-height: 44px; display: flex; align-items: center; justify-content: space-between; }
    .mes-chip.activo::after { content: "\2713"; font-weight: 700; margin-left: 8px; }
    .filtro-meses-buscar-btn { order: -2; align-self: flex-end; }
    .filtro-meses-buscar-input { order: -1; }
    .filtro-meses-buscar-input.visible { width: 100%; }
    .filtro-meses-buscar-input:not(.visible) { display: none; }
    .filtro-meses-acciones { border-left: none; border-top: 1px solid var(--border); padding-left: 0; padding-top: 10px; justify-content: flex-end; }
    .charts { grid-template-columns: 1fr; }
    .incidentes-cards-row { grid-template-columns: 1fr; }

    /* Incidentes -> Listado: el asunto del ticket quedaba truncado casi de
       inmediato porque las pastillas de estado/prioridad no bajaban de renglon
       (auditoria mobile 2026-09-09). */
    .incidente-hero { flex-direction: column; align-items: flex-start; flex-wrap: nowrap; }
    .incidente-hero-left { width: 100%; }
    .incidente-hero-left .incidente-titulo { white-space: normal; overflow: visible; text-overflow: clip; }
    .incidente-hero-right { width: 100%; margin-top: 10px; flex-shrink: 1; }

    /* Tabla de detalle -> tarjetas apiladas (Opcion B). Acotado con :has() a la
       tabla que realmente tiene reemplazo en tarjetas -- ver hallazgo (1) de
       la auditoria mobile 2026-09-09: antes esto apagaba *cualquier*
       .table-scroll de la app, dejandolas invisibles en mobile sin ningun
       reemplazo -- no se veian mal, directamente no se veian.
       RES-03 (2026-09-14) sumo tarjetas (via tablaCardsDesdeTabla(), helper
       generico) a Incidentes (ya estaba), Administracion -> Proyectos y
       Usuarios, y Asignaciones -> Detalle por mes (Detalle por consultor /
       Preventa / Sin asignacion, template compartido). Siguen en modo tabla
       con scroll horizontal (ver el comentario de tablaCardsDesdeTabla() mas
       arriba para el porque de cada una): Administracion -> Consultores,
       Explorador de datos, Roster/plan de capacidad, preview de "Cargar plan
       (pegado)", Detalle por perfil y el mapeo de Carga de horas paso 2. */
    .table-scroll:has(+ .tabla-cards) { display: none; }
    .tabla-cards { display: flex; }
    /* RES-04 (2026-09-14): la afordance de scroll horizontal para las tablas
       que se quedan en modo tabla en mobile (Explorador, roster/plan de
       capacidad, etc.) ya no vive aca -- .scroll-x-activo (base de :root,
       arriba del todo del <style>) cubre esto mismo en CUALQUIER tamano de
       pantalla, encendida por JS solo cuando hace falta. */

    /* Selector de módulos y panel de Administración */
    .topbar-left { width: 100%; justify-content: space-between; flex-wrap: wrap; row-gap: 6px; flex: 0 1 auto; }
    /* Hallazgo (2) de la auditoria mobile 2026-09-09 (.modules se apilaba en vez de
       deslizar): resuelto en su momento con scroll horizontal, superado ahora por la
       botonera desplegable de abajo (mejora 2026-09-09, pedida por Lucho: "las pildoras
       del top level tienen que verse como una botonera que te permita desplegar y
       seleccionar el valor"). */
    .modules-details { flex: 1 1 auto; min-width: 0; order: 2; }
    /* padding lateral mas chico que el default (16px) para darle mas margen
       a "Administracion" antes de necesitar el ellipsis de arriba -- ver
       fix reporte-pendientes 2026-09-22 junto a #modules-summary-current. */
    .modules-summary { display: flex; padding: 0 11px; }
    /* Fix reporte-pendientes 2026-09-22 (pedido de Lucho): "APP" del titulo se
       saca en mobile (queda solo "Project Manager") y la pildora DEV baja a su
       propio renglon -- entre los dos liberan bastante ancho para que el pill
       de modulo activo entre sin recortarse (ver #modules-summary-current mas
       arriba, que ahora casi no necesita achicar texto). order:1/2 fuerza
       titulo+pill de modulo en el primer renglon, DEV solo en el segundo.
       El salto de renglon lo fuerza un pseudo-elemento invisible con
       flex-basis:100% (order:3, entre modules-details y el badge) -- NO el
       flex-basis:100% puesto directo en .env-badge-dev, porque eso estira la
       pildora a lo ancho de todo el renglon en vez de dejarla compacta como
       el resto de los badges de la app. */
    .app-name-prefix { display: none; }
    #app-name-home { order: 1; }
    .topbar-left::after { content: ''; order: 3; flex-basis: 100%; height: 0; }
    .env-badge-dev { order: 4; flex: none; }
    .modules { flex-direction: column; flex-wrap: nowrap; gap: 6px; margin-top: 8px; padding: 8px; background: rgba(47,49,52,.5); border: 1px solid var(--superficie-borde); border-radius: 14px; }
    .mod-btn { flex: none; width: 100%; justify-content: space-between; padding: 11px 14px; min-height: 44px; }
    .mod-btn.active { background: var(--accent-14); color: var(--text); border-color: rgba(102,182,255,.35); box-shadow: none; }
    .topbar-project-wrap { flex: 1 1 0; min-width: 0; } /* UI-017: base 0 para que el selector solo ceda ancho y los botones no se aprieten */
    .form-grid { grid-template-columns: 1fr; }

    /* Asignaciones (Detalle por consultor / Preventa / Sin asignacion): los
       filtros wrapeaban pero no seguian el patron "un filtro por renglon,
       ancho completo" del resto de la app (auditoria mobile 2026-09-09).
       Mejora 2026-09-09: los filtros pasaron de .filtros-vista-row (fila
       propia debajo del titulo) a .header-row--filtros-en-linea .filters
       (misma linea que el titulo en desktop) -- este ancho completo por
       boton se mantiene igual en mobile. */
    .header-row--filtros-en-linea .filters .selmes-btn { width: 100%; justify-content: space-between; }

    /* Administracion -> Proyectos/Consultores: el panel de filtro por columna
       tiene ancho fijo y puede cortarse contra el borde de una pantalla chica
       (auditoria mobile 2026-09-09). */
    .filtro-panel { width: min(220px, calc(100vw - 64px)); }

    /* Carga de horas -> Mejora 5: la fila 60/40 dropzone/historial vuelve a
       apilarse en una sola columna, mismo criterio que .charts/.kpis. */
    .carga-fila { grid-template-columns: 1fr; }

    /* DIS-05 (2026-09-14): el stepper de Carga de horas (.carga-steps) usa
       gap:0 + conector horizontal ::after pensado para una sola fila -- en
       mobile portrait los 4 badges no entran en el ancho disponible y
       wrapean; con gap:0 las filas envueltas quedan pegadas sin espacio
       vertical, y el conector del ultimo badge de cada fila apunta al
       vacio (el siguiente badge salto a la fila de abajo). Se apila en
       columna con espacio vertical propio y se oculta el conector
       horizontal, que no tiene sentido en un layout de una sola columna. */
    .carga-steps { flex-direction: column; gap: 6px; }
    .carga-step-badge:not(:last-child)::after { display: none; }

    /* Roster de proyecto + Plan de capacidad (reporte-pendientes mejora
       2026-09-09, ronda 2 -- mockup aprobado por Lucho:
       propuesta_boton_cargar_plan.html): se sacaron las pildoras de resumen
       de ambos headers ("322.0 dias de plan" / cantidad de roster), y
       "Cargar plan (pegado)" paso a vivir en el header igual que "Agregar
       al roster" (antes vivia suelto arriba de la tabla). Sin pildora, el
       bloque titulo+boton entra en la mayoria de los anchos de mobile real
       -- ya no se fuerza el salto de linea a ancho completo, el header solo
       envuelve si el contenido de verdad no entra (flex-wrap nativo,
       medido con Playwright). Se recorta ademas el gap en estos 2 headers
       puntuales (title<->boton 10px->6px, boton<->chevron 8px->4px) para
       que "Agregar al roster" tambien entre a partir de ~390px; por debajo
       de eso sigue necesitando su propio renglon (fallback ya existente,
       envuelve pegado a la derecha via margin-left:auto, nunca se
       superpone). */
    .roster-header, .plan-header { flex-wrap: wrap; row-gap: 8px; gap: 6px; }
    .roster-header .detalle-header-right,
    .plan-header .detalle-header-right { width: auto; flex: 0 1 auto; justify-content: flex-end; flex-wrap: wrap; margin-left: auto; gap: 4px; }
  
    /* Pildora Resumen: 1 sola columna en mobile portrait (charts-2 e hito-list
       vienen a 2 columnas por default, pensadas para desktop). */
    .charts-2 { grid-template-columns: 1fr; }
    .hito-list { grid-template-columns: 1fr; }
    .af-row { grid-template-columns: 90px 1fr 40px; }

    /* ACC-03 (2026-09-14) -- objetivos tactiles chicos en mobile (medido con
       Playwright a 390px de ancho, /home/claude/work/res-harness/measure.json):
       .alias-pill button y .alias-add-form button de borrar miden 16x16 y 20x20
       (aparecen 8+8 veces en Consultores/Explorador), .chip-rm 18x18, .pagina-btn
       30x30, .icon-btn 32x32, .filtro-limpiar/.filtro-todos ~12px de alto. El
       minimo recomendado para el dedo es 44x44 css px. Patron: pseudo-elemento de
       area extendida (::after invisible, 44x44, centrado) para NO cambiar el
       tamaño visual del icono ni romper los layouts de chips/pildoras -- .row-actions
       ya tenia gap:8px de base, insuficiente para separar del todo las 2 zonas de
       44px de los botones Guardar/Cancelar del Explorador (que quedan a 32px de
       ancho con 8px de gap, area tactil de 44px c/u): se sube a 14px en mobile
       para que las dos zonas de 44px no se toquen (32+14+32, zonas de 6px por
       lado no se superponen: 14 > 2*6). Verificado con Playwright a 390x844 que
       no queda superposicion. */
    .alias-pill button, .chip-rm, .icon-btn, .pagina-btn, .filtro-meses-buscar-btn,
    .alias-add-form button { position: relative; }
    .alias-pill button::after, .chip-rm::after, .icon-btn::after, .pagina-btn::after,
    .filtro-meses-buscar-btn::after, .alias-add-form button::after {
      content: ""; position: absolute; left: 50%; top: 50%;
      transform: translate(-50%, -50%);
      width: 44px; height: 44px;
    }
    .row-actions { gap: 14px; }
    .filtro-limpiar, .filtro-todos { min-height: 44px; padding: 0 10px; }
    .filtro-panel-footer { align-items: stretch; }
    .expl-tab-btn, .radio-pill, .periodo-filter-btn, .estado-filter-btn,
    .nav-btn, .selmes-btn, .btn-ghost, .selector, .tabla-input, .tabla-select,
    .plan-preview-select, .role-select { min-height: 44px; }
    tr.fila-cruce-clic td { padding-top: 12px; padding-bottom: 12px; }

    /* UI-010: encabezado del «Resumen de situacion» en celular. En una sola fila
       la meta (`Generado el ... · basado en N tickets, M comentarios` + avisos,
       con nowrap, ~510 px o mas) empujaba el encabezado a ~670 px y el boton de
       actualizar quedaba fuera de pantalla. Aca el contenedor de la derecha pasa a
       display:contents para que titulo, boton y chevron compartan la primera fila
       y la meta baje a ancho completo, con salto de linea. Se aplica a todo el
       rango <=768px, incluido un celular angosto en horizontal (667x375): ahi la
       fila de escritorio desborda ~10 px, asi que queda apilado a proposito y el
       bloque landscape de abajo no lo resetea. Los selectores van por clase (no
       por la etiqueta del titulo) y el order del chevron queda acotado a esta
       tarjeta. */
    .resumen-ia-header { flex-wrap: wrap; align-items: center; column-gap: 8px; row-gap: 4px; }
    .resumen-ia-header-right, .resumen-ia-header .detalle-header-right { display: contents; }
    .resumen-ia-header h3 { flex: 1 1 auto; min-width: 0; }
    .resumen-ia-header .resumen-ia-actualizar-btn { order: 1; }
    .resumen-ia-header .colapsable-chevron { order: 2; }
    .resumen-ia-header .resumen-ia-meta { order: 3; flex: 1 1 100%; min-width: 0; white-space: normal; overflow-wrap: anywhere; line-height: 15px; }
  }

  /* ---------- Responsive -- landscape de celular (tercer estado adicional a mobile
     <768px y desktop; ver seccion "Landscape de celular" del skill report-design-system).
     Gatillo: orientation:landscape + max-height, no ancho -- lo que distingue a un
     celular acostado (alto fisico chico, ~360-430px) de una tablet o notebook en
     landscape (alto tipicamente >500px) es el alto disponible, no el ancho (la mayoria
     de los celulares en horizontal ya supera los 768px del breakpoint de arriba, asi
     que sin esta regla caian directo en CSS de escritorio). reporte-pendientes mejora
     2026-09-09, opcion C de 3 alternativas (mockup:
     https://claude.ai/code/artifact/0d7fd591-38a3-450d-944e-ccaa14819ab7). A diferencia
     del bloque de arriba, esto NO reutiliza el layout angosto de mobile -- landscape
     tiene de sobra el ancho que a mobile portrait le falta, asi que .header-row,
     .form-grid, .incidente-hero, etc. se quedan
     en su distribucion de escritorio A PROPOSITO Y AHORA DE FORMA EXPLICITA (RES-01,
     2026-09-14): hasta esta fecha era cierto por omision (nada los reseteaba), y un
     celular angosto acostado (ej. 667x375) matchea TAMBIEN el bloque
     @media (max-width:768px) de arriba, asi que sin resets propios 10 de las 16
     vistas heredaban en silencio el layout de una sola columna de mobile portrait
     -- ver el grupo de resets explicitos al final de este bloque. Solo se toca lo
     que realmente no entra en el alto/ancho real de un celular acostado. La
     excepcion previa ya cubierta es .header-row--filtros-en-linea (Asignaciones --
     Detalle por consultor / Preventa / Sin asignacion), que SI necesitaba un reset
     explicito -- ver el bloque de esa clase mas abajo. */
  @media (orientation: landscape) and (max-height: 500px) {
    /* PERF-05: el reset de mobile portrait (max-width:768px) de arriba
       tambien aplica aca (los dos breakpoints no son excluyentes) -- con
       tan poco alto de viewport, min(60vh,480px) del portrait seria casi
       toda la pantalla. Se resetea a un valor bajo, pensado para dejar
       lugar al selector de tabla y al footer de paginacion. */
    .table-scroll--expl { max-height: min(45vh, 200px); }
    /* Login: la tarjeta no entra en un celular acostado bajo (viewport ~375px
       de alto) -- el boton Ingresar quedaba abajo del pliegue. Se compactan
       espacios (no se oculta nada) y la tarjeta se ancla arriba en vez de
       centrarse. Verificado con el arnes responsive (test/responsive). */
    #login-view { align-items: flex-start; padding: 10px 20px; }
    .login-card { padding: 14px 20px; }
    .login-subtitle { margin-bottom: 10px; }
    .login-divider { margin: 10px 0; }
    .login-field { margin-bottom: 8px; }
    .login-field input { padding: 6px 10px; }
    .login-btn { margin-top: 4px; }
    .login-error { margin-top: 6px; }

    /* Topbar (UI-018, 2026-10-05): una sola fila de ~54px en vez de dos renglones (161px a 667x375
       con la etiqueta DEV, 111px sin ella; 92px a 844x390 y 926x428). Antes el titulo, los 4 botones
       de modulo y el selector con los iconos no entraban juntos y se apilaban (topbar-right con
       width:100%). Ahora los 4 modulos pasan al <details> de mobile portrait (mas abajo), el selector
       toma el ancho que sobra, la pildora de sincronizacion es un icono de 32x32 (como en mobile
       portrait, UI-017) y «APP ·» se saca del titulo. Un celular angosto acostado (ej. 667px) matchea
       tambien el bloque de mobile portrait de arriba: lo que ese bloque cambio se pisa o se resetea
       aca. position:relative + z-index + contain:none: el panel de modulos abierto flota debajo de la
       barra, y el contain:paint de la regla base lo recortaria. */
    .topbar { flex-wrap: wrap; height: auto; min-height: 0; padding: 8px 20px; row-gap: 6px; column-gap: 12px; position: relative; z-index: 30; contain: none; }
    .topbar-left { width: auto; flex: 0 1 auto; flex-wrap: nowrap; row-gap: 0; justify-content: flex-start; gap: 12px; }
    .topbar-right { width: auto; flex: 1 1 0; min-width: 236px; justify-content: flex-end; gap: 10px; margin-left: 0; }
    .topbar-project-wrap { flex: 1 1 0; min-width: 0; }
    /* UI-011: el selector vuelve a cortar con «...» si no entra el nombre; UI-018: tambien resetea el
       min-height de 44px que le deja mobile portrait (con 44px a 667px la fila crecia 7px). */
    .topbar-project-wrap .selector { width: 100%; min-width: 0; max-width: none; min-height: 34px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
    /* UI-017 + UI-018: la pildora de sincronizacion es el icono de 32x32 tambien en horizontal (a 667px
       ya lo hacia el bloque de mobile portrait; a 844px y 926px no matchea ese bloque, asi que se
       repite aca). El texto completo va en su title/aria-label y en un toast (aplicarModoPildoraSync()). */
    .topbar-right .sync-freshness { position: relative; flex: none; justify-content: center; width: var(--h-ico); height: var(--h-ico); padding: 0; border-radius: 50%; cursor: pointer; }
    .sync-freshness .pill-dot { display: none; }
    .sync-freshness .sync-ico { display: block; }
    .sync-freshness-txt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); }
    #sync-redmine-btn, .logout-link { flex: none; }
    .env-badge-dev { padding: 3px 9px; font-size: 11px; }

    /* Selector de modulos (UI-018): la botonera <details> de mobile portrait aplica tambien en
       Celular horizontal (modulosDebenIrCerrados() la deja cerrada de entrada). Abierta, la fila de
       4 botones de ancho parejo flota debajo de la barra sin agrandarla; se cierra al elegir un
       modulo, con Escape y con un clic afuera (initModulosCompactos()). Mismo gotcha de siempre:
       a 667px matchea tambien el bloque de mobile portrait (columna apilada, botones de 44px, de
       ancho completo) -- se resetea explicitamente. */
    .modules-summary { display: flex; min-width: 0; gap: 10px; padding: 0 12px; }
    .modules-details { flex: 0 1 auto; min-width: 0; }
    .modules-details[open] > .modules { position: absolute; left: 12px; right: 12px; top: calc(100% - 2px); z-index: 1; flex-direction: row; flex-wrap: nowrap; gap: 6px; margin: 0; padding: 8px; background: var(--superficie-solida); border: 1px solid var(--superficie-borde); border-radius: 14px; box-shadow: 0 10px 28px rgba(0,0,0,.5); }
    .mod-btn { flex: 1 1 0; width: auto; min-width: 0; min-height: var(--h-pill); padding: 0 16px; justify-content: center; }
    .mod-btn.active { background: var(--accent); color: var(--accent-text); border-color: var(--accent); box-shadow: 0 0 28px var(--accent-55); }
    /* UI-018: las pestañas del modulo tambien heredaban los 44px de mobile portrait a 667px (64px de
       barra, contra 52px a 844px): vuelven a la altura de pildora. */
    .nav-btn { min-height: var(--h-pill); }
    /* Reset explicito de lo que mobile portrait cambio en el titulo (fix reporte-pendientes
       2026-09-22): el salto de renglon y el orden ya no hacen falta en una sola fila, y «APP ·» se queda afuera
       tambien a 844px y 926px (donde mobile portrait no matchea) para que la barra sea igual en todo el rango. */
    .topbar-left::after { content: none; }
    #app-name-home, .modules-details, .env-badge-dev { order: 0; }
    .env-badge-dev { flex: none; }
    .app-name-prefix { display: none; }

    /* Tabs de Administracion (5 pestañas) son las que mas ancho piden de toda la
       app -- con el padding de escritorio (84px por lado) no entran ni en el celular
       en landscape mas ancho de este rango. Mismo afordance de scroll que ya usa
       .modules/.tabs-bar en mobile. */
    .tabs-bar { padding: 10px 20px; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; -ms-overflow-style: none; mask-image: linear-gradient(to right, #000 88%, transparent); -webkit-mask-image: linear-gradient(to right, #000 88%, transparent); }
    .tabs-bar::-webkit-scrollbar { display: none; }
    .nav-btn { flex: none; }
    .content { padding: 14px 20px 20px 20px; }
    .header-row > div:first-child { display: none; } /* FIX 2026-09-09 (reporte-pendientes): este bloque de landscape de celular nunca tuvo NINGUNA regla que ocultara titulo+subtitulo -- afectaba a las 11 pildoras por igual (incluidas las 4 header-row--filtros-en-linea que si funcionaban bien en mobile portrait). Se agrega la misma regla universal que en el bloque de mobile portrait de arriba. Verificado con Playwright a 926x428 (ver auditoria 2026-09-09, propuesta aprobada por Lucho). */

    /* KPIs y graficos: a diferencia de mobile portrait, hay ancho real de sobra --
       se fija la grilla en vez de dejarla en auto-fit para que no varie de 3 a 5
       columnas segun el celular puntual. DIS-02 (2026-09-15): la regla .kpi
       de esta linea quedo huerfana (0 consumidores) cuando se elimino
       .kpis/.kpi del resto del archivo -- se saca aca tambien. */
    .charts { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; }
    .chart-card { padding: 10px; }
    /* RES-08 (2026-09-15): el alto minimo de 220px del canvas (regla base,
       .chart-canvas mas arriba) hace que una sola tarjeta de grafico ocupe el
       78% de los 375px utiles de un celular acostado. Como los Chart se crean
       con maintainAspectRatio:false, el alto lo decide enteramente el CSS. No
       se toca la regla base: desktop y mobile portrait estan bien con 220px. */
    .chart-canvas { min-height: 150px; }

    /* Asignaciones (Detalle por consultor / Preventa / Sin asignacion): a
       diferencia del resto de header-row de la app, este SI necesita
       reforzar flex-direction:row explicitamente -- un celular angosto en
       landscape (ej. 667px) matchea tambien el breakpoint de mobile portrait
       de arriba, y sin este reset se hereda su flex-direction:column (ver
       skill report-design-system, gotcha de breakpoints superpuestos). */
    .header-row--filtros-en-linea { flex-direction: row; align-items: flex-end; }
    .header-row--filtros-en-linea .filters { flex-direction: row; align-items: flex-end; width: auto; margin-left: 0; }
    /* El width:100% que el bloque mobile de arriba fuerza sobre .selmes-btn
       (para que ocupe todo el renglon en columna) hace que cada boton siga
       ocupando el ancho completo aunque el contenedor ya este en flex-direction:
       row -- con flex-wrap:wrap eso los vuelve a apilar en renglones propios,
       aunque el contenedor mismo ya sea una fila (hallazgo real verificando
       este mismo cambio con Playwright contra el archivo real, 2026-09-09).
       Hay que resetear el ancho del boton tambien, no solo el del contenedor. */
    .header-row--filtros-en-linea .filters .selmes-btn { width: auto; justify-content: flex-start; }
    /* Filtro de meses (Asignacion, modulo Proyecto): la version apilada de mobile
       portrait de arriba no aplica aca -- reset explicito, mismo gotcha que .modules
       (celular angosto en landscape matchea tambien el breakpoint de mobile portrait).
       Vuelve a la linea unica de 44px con scroll horizontal, igual que en desktop hoy. */
    .filtro-meses-panel.abierto { max-height: 60px; }
    .filtro-meses-linea { flex-direction: row; align-items: center; height: 44px; padding: 0 10px; }
    .filtro-meses-chips { flex-direction: row; overflow-x: auto; overflow-y: visible; height: 100%; align-items: center; }
    .mes-chip { width: auto; padding: 6px 12px; min-height: auto; display: inline-flex; justify-content: flex-start; }
    .filtro-meses-buscar-btn { order: 0; align-self: auto; }
    .filtro-meses-buscar-input { order: 0; }
    .filtro-meses-buscar-input.visible { width: 130px; }
    .filtro-meses-buscar-input:not(.visible) { display: inline-block; }
    .filtro-meses-acciones { border-left: 1px solid var(--border); border-top: none; padding-left: 10px; padding-top: 0; }
    .filtros-compactos { border: 1px solid var(--border); border-radius: 9px; background: rgba(255,255,255,.035); }
    .filtros-compactos > summary { display: flex; align-items: center; gap: 7px; padding: 9px 12px; font-size: 12px; cursor: pointer; list-style: none; color: var(--text); }
    .filtros-compactos > summary::-webkit-details-marker { display: none; }
    .filtros-compactos > summary .chev { margin-left: auto; color: var(--muted); font-size: 11px; transition: transform .15s; }
    .filtros-compactos[open] > summary .chev { transform: rotate(180deg); }
    .filtros-compactos-panel { flex-direction: row; align-items: flex-end; gap: 16px; padding: 4px 12px 12px; border-top: 1px solid var(--border); margin-top: 2px; }

    /* RES-01 (2026-09-14) -- Reset explicito de la herencia portrait -> landscape.
       Un celular angosto acostado (ej. 667x375) matchea TAMBIEN el bloque
       @media (max-width:768px) de arriba, asi que cada propiedad que ese bloque
       toca y que aca no corresponde hay que devolverla explicitamente. Antes de
       este bloque, 10 de las 16 vistas quedaban con el layout de una sola columna
       de mobile portrait al girar el celular. Verificado con Playwright a 667x375
       y 926x428 sobre las 16 vistas: sin overflow horizontal nuevo y con
       flex-direction:row confirmado en .header-row.
       OJO: .kpis-ico va con auto-fit minmax(190px,1fr) y NO con un numero
       fijo de columnas -- con columnas fijas angostas el numero se corta
       (mismo motivo que llevo a DIS-01, 2026-09-14, a usar auto-fit en la
       regla base de .kpis-ico en vez de un grid fijo). DIS-02 (2026-09-15):
       #cro-kpis ya no tiene su propio grid (.cro-kpis-row1/row2) -- pasa a
       usar .kpis-ico como el resto de la app, asi que este reset la cubre
       igual sin un override propio. NO se resetea la tipografia compacta
       que mobile le pone a .kpi-ico (eso sigue siendo deseable con el alto
       limitado de landscape de celular); solo se resetean las grillas. */
    .header-row { flex-direction: row; align-items: flex-end; }
    .header-row .filters { flex-direction: row; align-items: flex-end; width: auto; }
    .selector, .selector.date-input { width: auto; }
    .selector { min-width: 220px; }
    .selector.date-input { min-width: 130px; }
    .kpis-ico { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
    .charts-2 { grid-template-columns: 1fr 1fr; }
    .hito-list { grid-template-columns: repeat(2, 1fr); }
    .form-grid { grid-template-columns: 1fr 1fr; }
    .carga-fila { grid-template-columns: 3fr 2fr; }
    .incidentes-cards-row { grid-template-columns: 1fr 1fr; }
    .incidente-hero { flex-direction: row; align-items: center; flex-wrap: wrap; }
    .incidente-hero-left { width: auto; }
    .incidente-hero-right { width: auto; margin-top: 0; }
    .filtro-meses-btn-mount .selmes-btn { width: auto; justify-content: flex-start; }
    .table-scroll:has(+ .tabla-cards) { display: block; }
    .tabla-cards { display: none; }

    /* DIS-05 (2026-09-14): un celular angosto en landscape matchea tambien el
       breakpoint de mobile portrait de arriba (mismo gotcha ya documentado
       para .modules/.header-row) -- landscape tiene ancho de sobra para que
       los 4 badges entren en una sola fila, asi que se resetea explicitamente
       la columna apilada y se vuelve a mostrar el conector. */
    .carga-steps { flex-direction: row; gap: 0; }
    .carga-step-badge:not(:last-child)::after { display: block; }
  }
  .banner-esquema{display:flex;gap:10px;align-items:flex-start;background:#3a2326;border:1px solid var(--sem-rojo);
  border-radius:10px;color:var(--text);padding:10px 14px;margin:10px 16px 0;font-size:13px;line-height:1.45}
  .banner-esquema-ico{color:var(--sem-rojo);font-size:15px;line-height:1.3;flex:0 0 auto}
  .banner-esquema .mono{color:var(--sem-rojo)}
  @media (max-width:768px){.banner-esquema{margin:8px 12px 0;font-size:12px;padding:8px 10px}}
  @media (orientation:landscape) and (max-height:500px){.banner-esquema{margin:6px 12px 0;font-size:12px;padding:6px 10px}}

  /* UI-029: el boton de reportes sale de la barra en Celular y Celular horizontal y pasa a ser una
     entrada del menu de modulos (.mod-extra): con 5 iconos a 375 px y a 667x375 con la etiqueta DEV
     el selector de proyecto queda por debajo de los 200 px de UI-017. Dialogo de reportes.
     Mobile portrait: la revision apila etiqueta y valor (una sola
     columna). Celular horizontal: no hay alto -- cabecera, cuerpo y acciones se compactan, el
     textarea baja y la revision vuelve a dos columnas (el bloque landscape resetea lo que toco el
     de mobile, porque un celular angosto acostado matchea los dos). */
  @media (max-width: 768px) {
    #reportar-btn { display: none; }
    .mod-extra { display: inline-flex; align-items: center; gap: 8px; flex: none; width: 100%; min-height: 44px; padding: 11px 14px; 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; }
    .reporte-revision { grid-template-columns: minmax(0, 1fr); gap: 2px; }
    .reporte-revision dd { margin-bottom: 10px; }
  }
  @media (orientation: landscape) and (max-height: 500px) {
    #reportar-btn { display: none; }
    .mod-extra { display: inline-flex; align-items: center; gap: 8px; flex: none; width: auto; 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; }
    .dialogo-cabecera { padding: 14px 20px 0; }
    .dialogo-cuerpo { padding: 12px 20px; }
    .dialogo-acciones { padding: 10px 20px 12px; }
    .field--prosa textarea { min-height: 56px; }
    .reporte-revision { grid-template-columns: minmax(0, 118px) minmax(0, 1fr); gap: 8px 14px; }
    .reporte-revision dd { margin-bottom: 0; }
  }

  /* ACC-09: respeta el ajuste de movimiento reducido del sistema (iOS/Android) --
     no toca los backdrop-filter:blur() (el blur no es movimiento, forma parte de
     la identidad visual "Carbon"), solo transiciones/animaciones/scroll suave.
     El comodin agarra TODA animacion/transicion de la app -- incluida
     .resumen-ia-spin (gira sin parar mientras dura la llamada a
     functions/api/resumen-ia.js) y .dis08-spin -- sin tener que listar cada una. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: .01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: .01ms !important;
      scroll-behavior: auto !important;
    }
    .resumen-ia-spin { animation: none; }
  }
