/* ═══════════════════════════════════════════════════════════════════════════
   css/mobile.css — Capa móvil de ENNAT INTEL
   ───────────────────────────────────────────────────────────────────────────
   TODO lo que hay acá vive dentro de @media (max-width:768px). El escritorio
   queda intacto por construcción: si una regla no está dentro del media query,
   está mal puesta.

   Por qué un archivo aparte y no más CSS en index.html:
     · index.html pesa ~2MB y vercel.json lo sirve con no-cache → se
       re-descarga entero en cada carga. Este .css se cachea.
     · Hay 10 branches activos tocando index.html (ver CLAUDE.md). Un archivo
       propio del branch móvil hace que los conflictos de merge sean ~cero.
     · Al cargar último gana los empates de especificidad → menos !important.

   Excepción a la regla: Elena y Coach inyectan su CSS en runtime (después de
   que cargó este archivo). Sus reglas móviles van en sus propios archivos
   (js/elena-ui/_primitives.jsx, js/coach-ui/) para respetar la cascada.

   Orden del archivo:
     0. Shell (app, sidebar/drawer, topbar, tabs, content-area)
     1. Primitivas globales (KPIs, cards, grids, tipografía, targets táctiles)
     2. Tablas (utilidad .m-tbl-scroll + wrapper automático)
     3. Modales → bottom sheet
     4. Bottom nav
     5. Utilidades (.m-only, .m-hide, aviso "mejor desde computador")
     6. Overrides por módulo (Expenses ya estaba; el resto se suma por fase)
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

/* ── 0. SHELL ─────────────────────────────────────────────────────────────── */

/* Una sola columna: el sidebar sale del flujo y pasa a ser drawer. */
.app { grid-template-columns: 1fr !important; }

/* Sidebar → drawer off-canvas (entra desde la izquierda con ☰). */
.sidebar {
  display: flex !important;
  position: fixed; top: 0; bottom: 0; left: 0;
  width: 84vw; max-width: 300px; min-width: 0;
  transform: translateX(-100%);
  transition: transform .25s ease;
  z-index: 2100;
  box-shadow: 2px 0 18px rgba(0,0,0,.22);
  padding-bottom: env(safe-area-inset-bottom);
}
.sidebar.mobile-open { transform: translateX(0); }
#mobile-menu-btn { display: inline-flex !important; }

/* El drawer se usa con el pulgar: filas más altas que en escritorio. */
.sb-view-head { padding: 12px 14px; min-height: 44px; }
.sb-subtab    { padding: 11px 14px 11px 28px; min-height: 44px; font-size: 13px; }
.sidebar-user-btn, .sidebar-user-menu-item { min-height: 48px; }

/* Topbar compacta. El bloque derecho (QB, PD, usuario) no cabe en un teléfono. */
.topbar {
  padding: 0 8px; height: 48px; gap: 6px; overflow: hidden;
  padding-top: env(safe-area-inset-top);
  height: calc(48px + env(safe-area-inset-top));
}
.topbar-left { flex: 0 0 auto; min-width: 0; }
/* Se esconde SÓLO mientras vive en la topbar. _mobileRelocateTopbarRight() lo
   muda al drawer, y ahí este selector deja de matchear y se ve. Antes era
   `.topbar-right{display:none}` a secas, o sea que país, QB, PD, glosario,
   usuarios y logout quedaban inalcanzables en el teléfono. */
.topbar .topbar-right { display: none !important; }

/* Bloque de cuenta al pie del drawer, colapsable: el botón sólo muestra la
   identidad (#user-identity, movido acá aparte del resto) + una flecha; el
   resto de .topbar-right vive en el body y arranca colapsado — antes el
   bloque entero se mostraba siempre abierto y se comía media pantalla. */
#sidebar-account { flex-shrink: 0; border-top: 1px solid var(--border); background: #fafbfc; }

.sidebar-account-toggle {
  width: 100%; display: flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 10px 12px;
  background: transparent; border: none; cursor: pointer;
  font-family: inherit; text-align: left; -webkit-tap-highlight-color: transparent;
}
.sidebar-account-toggle:hover { background: var(--bg); }
.sidebar-account-avatar { font-size: 15px; flex-shrink: 0; }
/* #user-identity sigue llegando acá vía JS (_mobileRelocateTopbarRight) porque otros
   módulos lo leen por id (js/coach.js), pero desde v9.20 NO se muestra: la identidad
   está al pie del sidebar, unos centímetros más abajo en el mismo drawer, y verla dos
   veces sobraba. El botón ahora tiene su propia etiqueta. */
.sidebar-account-toggle #user-identity { display: none !important; }
.sidebar-account-label {
  flex: 1; min-width: 0;
  font-size: 12.5px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidebar-account-chevron { font-size: 10px; color: var(--light); transition: transform .15s; flex-shrink: 0; }
.sidebar-account-toggle.open .sidebar-account-chevron { transform: rotate(180deg); }

.sidebar-account-body {
  max-height: 0; overflow: hidden;
  transition: max-height .22s ease;
  padding: 0 12px;
}
.sidebar-account-body.open {
  max-height: 480px;
  padding: 0 12px calc(12px + env(safe-area-inset-bottom));
}
.sidebar-account-body .topbar-right {
  display: flex !important;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
/* País a ancho completo: es contexto, no una acción. */
.sidebar-account-body #country-switcher { flex: 1 1 100%; margin-right: 0 !important; }
.sidebar-account-body #country-switcher select { width: 100%; min-height: 44px; }
/* Los botones se reparten la fila, con target de pulgar. */
.sidebar-account-body .topbar-btn {
  flex: 1 1 auto;
  min-height: 44px;
  font-size: 13px !important;
  padding: 8px 12px !important;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.sidebar-account-body .topbar-btn svg { width: 18px; height: 18px; }

/* Recalcular histórico: sobrescribe meses ya congelados — una herramienta de
   reparación, no de uso rutinario, y en un teléfono el riesgo de un toque
   accidental es mayor. Se saca sólo de la versión móvil (pedido explícito);
   en escritorio sigue exactamente igual, gateada a Martín por su propio JS
   (ver metrics-backfill-btn en index.html). El selector se autoacota: sólo
   pega cuando el botón vive dentro del drawer móvil. */
.sidebar-account-body #metrics-backfill-btn { display: none !important; }
.sidebar-account-body [data-lang-btn] { min-height: 40px; padding: 6px 18px !important; font-size: 14px !important; }

/* Face ID / huella: fila propia, separada de .topbar-right (que es un nodo que se
   mueve/devuelve completo — esto es estático, sólo existe acá). */
.webauthn-account-row {
  padding: 10px 0;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.webauthn-account-row .topbar-btn {
  width: 100%;
  min-height: 44px;
  font-size: 13px !important;
}
.webauthn-registered-note {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 12px;
  color: var(--green, #059669);
  padding: 8px 2px;
}
.webauthn-registered-note button {
  background: none; border: none; color: var(--muted); font-size: 12px;
  text-decoration: underline; cursor: pointer; font-family: inherit; padding: 4px;
  min-height: 32px;
}
.topbar-center {
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  justify-content: flex-start; gap: 0;
}
.topbar-center::-webkit-scrollbar { display: none; }
/* Filtros en un teléfono. Dos cosas que en escritorio no pasan:
   1. La etiqueta "Filtros" no entra al lado del pill de versión y el botón de menú —
      queda el ícono con su contador, que es lo que se mira.
   2. El panel pasa a position:fixed. En absolute lo recortaban DOS ancestros a la vez
      (.topbar tiene overflow:hidden y .topbar-center overflow-x:auto), así que se abría
      dentro de una franja de 48px y no se veía nada. Fixed escapa del clipping porque
      ninguno de los dos crea contexto de contención (no hay transform en el medio). */
.topbar-filter-btn .topbar-filter-btn-label { display: none; }
.topbar-filter-btn { min-height: 34px; padding: 4px 8px; }
.topbar-filter-panel {
  position: fixed; left: 8px; right: 8px; width: auto; transform: none;
  top: calc(48px + env(safe-area-inset-top) + 4px);
  max-height: 62vh;
}
#pipeline-version-pill, #proy-version-pill { font-size: 10px !important; padding: 2px 6px !important; }
.mode-btn { padding: 4px 10px !important; font-size: 11px !important; white-space: nowrap; }

/* Barra de tabs: tira horizontal con snap, para que un tab nunca quede a medias. */
.tabs {
  padding: 0 8px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  padding: 11px 14px; font-size: 12px;
  min-height: 44px; display: flex; align-items: center;
  scroll-snap-align: start;
}

/* Área de contenido. Sin bottom nav, el padding-bottom sólo respeta el home
   indicator del iPhone — no hace falta reservar los 64px de antes. */
.content-area {
  padding: 10px 10px calc(14px + env(safe-area-inset-bottom));
}

/* Búsqueda a ancho completo. */
.search-bar { padding: 8px 10px; }
.search-input { max-width: none; }


/* ── 1. PRIMITIVAS GLOBALES ───────────────────────────────────────────────── */

/* KPIs — antes: display:none, o sea que en el teléfono desaparecían justo los
   números que uno quiere mirar. Ahora: carrusel horizontal con snap. Se ven 2
   a la vez y se desliza para el resto, ocupando UNA sola fila de alto. */
/* OJO: acá NO se declara `display`. setMode() controla la visibilidad de la
   fila con un style inline (`display:none` fuera de Pipeline y Proyección), y
   un `display:grid !important` le ganaría al inline y mostraría los KPIs en
   todos los módulos. Este bloque sólo cambia el LAYOUT; quién la muestra sigue
   siendo el JS. */
.kpi-row {
  grid-template-columns: none !important;
  grid-auto-flow: column;
  grid-auto-columns: 46%;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}
.kpi-row::-webkit-scrollbar { display: none; }
.kpi-cell { scroll-snap-align: start; padding: 10px 12px; min-height: 44px; }
.kpi-label { font-size: 9.5px; margin-bottom: 4px; }
.kpi-value { font-size: 19px; }
.kpi-sub   { font-size: 10px; margin-top: 3px; }

/* Grillas multi-columna → una sola columna. */
.grid-2, .grid-3 { grid-template-columns: 1fr !important; gap: 8px; }

/* Cards a ancho completo y algo más compactas. */
.card { padding: 12px; border-radius: 10px; }
.card-title { font-size: 10px; margin-bottom: 8px; }

/* Targets táctiles: mínimo 44px (guía de Apple/Material) en los elementos de
   NAVEGACIÓN, que son los que uno persigue con el pulgar.
   A propósito NO se aplica a `button` en general: min-height gana sobre un
   height explícito, así que una regla global infla también los botones de
   ícono compactos (la × de borrar de una card de gasto, .pl-toggle, el
   "marcar todo leído" del panel de notificaciones) y rompe justo las cards
   densas que otras reglas de este archivo intentan compactar. Cada módulo
   agranda sus propios targets en su fase. */
.chip-row, .stage-item, .nav-item, .mf-pill { min-height: 44px; }
.mf-pill { padding: 10px 14px; font-size: 12px; }
.chip-row, .stage-item { padding-top: 10px; padding-bottom: 10px; }

/* Inputs a 16px: por debajo de eso iOS hace zoom automático al enfocar y
   deja la página descuadrada. Aplica a TODO el app, no sólo a un módulo. */
input, select, textarea { font-size: 16px !important; }
.app input[type=checkbox], .app input[type=radio] { width: 22px !important; height: 22px !important; }

/* Evita que Safari reescale el texto al rotar el teléfono. */
html { -webkit-text-size-adjust: 100%; }

/* Gráficos: son SVG inline hechos a mano (no Chart.js). Esta regla sólo pone
   un techo al ancho — no toca el alto, así que los íconos lucide no se rompen.
   Los charts de cada módulo se vuelven realmente fluidos en su propia fase. */
.content-area svg { max-width: 100%; }


/* ── 2. TABLAS ────────────────────────────────────────────────────────────── */

/* OJO: .content-area tiene overflow-x:hidden, así que una tabla ancha NO
   scrollea sola — queda cortada. Toda tabla ancha necesita este wrapper.
   _mobileWrapTables() (index.html) lo aplica solo a las que se pasan de ancho. */
.m-tbl-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--white);
  /* Sombra sutil en el borde derecho: pista visual de que hay más para el lado. */
  background-image: linear-gradient(to left, rgba(0,0,0,.06), rgba(0,0,0,0) 12px);
  background-repeat: no-repeat;
  background-position: right center;
  background-size: 12px 100%;
}
.m-tbl-scroll > table { margin: 0; }
/* Combinadores directos a propósito: con selector descendiente esto alcanzaría
   también a las tablas anidadas dentro de una tabla envuelta. */
.m-tbl-scroll > table > * > tr > th,
.m-tbl-scroll > table > * > tr > td { white-space: nowrap; }

/* Tabla de muchas columnas con width:100%: en vez de desbordar se aplasta y
   quedan columnas de 30px, ilegibles. El min-width la obliga a ocupar su ancho
   natural para que el wrapper le dé scroll. Va en CSS y no como estilo inline
   a propósito: al rotar el teléfono a horizontal (>768px) la regla deja de
   aplicar sola y el escritorio nunca la ve. */
.m-tbl-scroll.m-tbl-wide > table { min-width: 560px; }

/* Primera columna fija: sin esto uno pierde de vista de qué fila se trata.
   La celda pegada tiene que ser opaca (si no, el contenido pasa por debajo al
   scrollear), pero pintarla de blanco fijo rompe las filas de total, que traen
   su color como estilo inline en el <tr>. La cadena de `inherit` resuelve las
   dos cosas: la tabla pinta el blanco de base, el <tr> lo hereda salvo que
   traiga el suyo (el inline gana), y la celda hereda el del <tr>. */
.m-tbl-scroll.m-tbl-sticky > table { background: var(--white); }
.m-tbl-scroll.m-tbl-sticky > table > * > tr { background-color: inherit; }
.m-tbl-scroll.m-tbl-sticky > table > * > tr > th:first-child,
.m-tbl-scroll.m-tbl-sticky > table > * > tr > td:first-child {
  position: sticky; left: 0; z-index: 2;
  background-color: inherit;
  box-shadow: 1px 0 0 var(--border);
}
.m-tbl-scroll.m-tbl-sticky > table > thead > tr > th:first-child { z-index: 3; }


/* ── 3. MODALES → BOTTOM SHEET ────────────────────────────────────────────── */

/* No existe una clase .modal compartida: los modales se arman con style.cssText
   inline. Por eso se sobrescriben por ID y con !important (los estilos inline
   pierden contra !important). Cada fase suma los IDs de su módulo acá.
   .m-sheet es la clase para modales nuevos que sí podamos marcar. */
.m-sheet {
  align-items: flex-end !important;
}
.m-sheet > div {
  width: 100vw !important;
  max-width: 100vw !important;
  max-height: 92dvh !important;
  border-radius: 14px 14px 0 0 !important;
  overflow-y: auto;
  padding-bottom: env(safe-area-inset-bottom);
}


/* ── 4. BOTTOM NAV ────────────────────────────────────────────────────────── */

/* Sacado a pedido explícito: la navegación completa ya está en el drawer ☰
   (que además ahora abre con un toque desde el bottom nav mismo, redundante
   con el propio ☰ de la topbar). _buildMobileNav() en index.html lo sigue
   construyendo — es barato y así queda un solo lugar para reactivarlo si
   hiciera falta — pero acá se lo esconde, volviendo al mismo criterio que
   tenía el proyecto antes de la Fase 0: "la navegación va por el drawer". */
#mobile-bottom-nav { display: none !important; }

/* Avisos fijos abajo a la derecha: se arman con style inline y z-index 9998.
   Sin bottom nav no hay nada que tapar — sólo se respeta el home indicator. */
#flow-progress, .app-toast {
  bottom: calc(14px + env(safe-area-inset-bottom)) !important;
  left: 10px !important; right: 10px !important;
  min-width: 0 !important;
}


/* ── 5. UTILIDADES ────────────────────────────────────────────────────────── */

.m-hide { display: none !important; }   /* oculto en móvil, visible en escritorio */
/* .m-only NO declara display acá a propósito: el elemento se queda con el que
   le da su módulo (flex, grid, lo que sea) y sólo se oculta en escritorio, en
   el bloque de ≥768px del final. Declarar `display:revert` retrocedería al
   valor del user-agent (block/inline), pisando el layout del módulo. */

/* Aviso para las pantallas que son de edición pesada y no tienen sentido en un
   teléfono (configs, prompts, campañas, edición de catálogo). */
.m-desktop-notice {
  display: block !important;
  margin: 24px 8px;
  padding: 18px 16px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--orange);
  border-radius: 10px;
  background: var(--orange-bg);
  font-size: 13px;
  line-height: 1.5;
  color: var(--text);
}
.m-desktop-notice strong { display: block; margin-bottom: 4px; }
.m-desktop-notice span { color: var(--muted); }


/* ── 6. OVERRIDES POR MÓDULO ──────────────────────────────────────────────── */

/* ── Catálogo (ecom) ── */
/* Su grilla ya usa repeat(auto-fill,minmax(280px,1fr)), que en un teléfono da
   una columna sola — no hay nada que aplastar. El catálogo que ve el cliente
   es otra cosa: la PWA de catalogo.html, que ya es mobile-first. Este módulo
   es la administración de fotos, así que acá sólo se recupera ancho. */
/* Nada de tocar paddings por selector de atributo acá: `padding:14px 18px`
   matchea por prefijo y pega también en `padding:14px 18px 80px`, que es el
   contenedor raíz del módulo — reemplazar el shorthand entero le borraría la
   reserva de 80px de abajo. */
[data-mode="ecom"] { padding-left: 0; padding-right: 0; }

/* ── Leads ── */
/* Ojo si se compara con versiones anteriores de este archivo: el módulo dejó de
   ser una lista de tarjetas y pasó a ser una tabla de 10 columnas con funnel,
   búsqueda, filtros e importación de Excel. Todo lo de acá apunta a esa forma.
   Se acota con [data-mode="leads"] (atributo que escribe setMode). */

[data-mode="leads"] > div { padding: 10px 2px !important; }

/* La tabla ya viene dentro de un overflow-x:auto propio, así que el envoltorio
   automático la saltea — bien. Pero trae width:100% y <colgroup> en
   porcentajes: en 390px no se desborda, se APLASTA a ~37px por columna, que es
   ilegible. El min-width la obliga a su ancho real para que ese contenedor
   tenga algo que scrollear. */
[data-mode="leads"] table { min-width: 900px; }
[data-mode="leads"] table th,
[data-mode="leads"] table td { white-space: nowrap; }

/* Cabecera del card: el buscador tiene width:210px fijo y comparte fila con el
   título, los conteos y dos botones. En un teléfono va a fila completa. */
[data-mode="leads"] #ld-search {
  width: 100% !important;
  flex: 1 1 100%;
  min-height: 44px;
}
[data-mode="leads"] #ld-refresh,
[data-mode="leads"] #ld-import-btn { flex: 1 1 auto; min-height: 44px; }

/* Filtros y chips: tiras que se deslizan con el pulgar en vez de envolver en
   varias filas y comerse la pantalla antes de llegar a los datos. */
[data-mode="leads"] .ld-filter { min-height: 40px; flex: 0 0 auto; }
[data-mode="leads"] .ld-qk,
[data-mode="leads"] .ld-country { min-height: 36px; flex: 0 0 auto; }

/* Modal de importación → hoja a pantalla completa. */
#ld-import-overlay { padding: 0 !important; align-items: flex-end !important; }
#ld-import-overlay > div {
  max-width: none !important;
  max-height: 92dvh !important;
  border-radius: 14px 14px 0 0 !important;
  padding: 18px 14px calc(18px + env(safe-area-inset-bottom)) !important;
}

/* ── Commercial Activity ── */
/* #tab-content lleva data-mode/data-tab (los setea setMode/renderTab en
   index.html). Es la forma de acotar reglas a un módulo sin tener que meterle
   una clase envolvente a cada función de render. */

/* Las grillas de layout de este módulo también son estilos inline en divs
   anónimos, igual que en Overview. */
/* repeat(2..6 y no repeat( a secas: `repeat(` suelto matchea también
   repeat(auto-fill,minmax(…)), que hay 13 veces en index.html y ya es
   responsive por sí sola — forzarla a una columna sería empeorarla. */
[data-mode="commercial"] [style*="grid-template-columns:1fr 1fr"],
[data-mode="commercial"] [style*="grid-template-columns:repeat(2"],
[data-mode="commercial"] [style*="grid-template-columns:repeat(3"],
[data-mode="commercial"] [style*="grid-template-columns:repeat(4"],
[data-mode="commercial"] [style*="grid-template-columns:repeat(5"],
[data-mode="commercial"] [style*="grid-template-columns:repeat(6"] {
  grid-template-columns: 1fr !important;
}

/* Barras de filtros y acciones: en vez de amontonarse en varias filas, pasan a
   ser una tira que se desliza con el pulgar. */
[data-mode="commercial"] .chip-group,
[data-mode="commercial"] .mf-chips {
  flex-wrap: nowrap;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: 2px;
}
[data-mode="commercial"] .chip-group::-webkit-scrollbar,
[data-mode="commercial"] .mf-chips::-webkit-scrollbar { display: none; }
[data-mode="commercial"] .chip-row { flex: 0 0 auto; }

/* Tabs de edición pesada: en un teléfono no tiene sentido escribir el Rep Voice
   o armar un reporte de board. Se muestra el aviso y se esconde la herramienta.
   (El aviso lo inyecta _mobileDesktopOnlyNotice() en index.html.) */
/* El `:has(> .m-desktop-notice)` es un cinturón de seguridad, no un adorno:
   ata el esconder a que el JS haya puesto el aviso. Sin eso, el CSS se activa
   solo al bajar de 768px mientras que el JS puede no haber corrido, y el tab
   queda completamente en blanco sin explicación. Si el navegador no soporta
   :has(), descarta la regla entera y el contenido se ve — que es el fallo
   correcto para este caso. */
[data-mode="commercial"][data-tab="comm-config"]:has(> .m-desktop-notice) > *:not(.m-desktop-notice),
[data-mode="commercial"][data-tab="board-reports"]:has(> .m-desktop-notice) > *:not(.m-desktop-notice),
[data-mode="commercial"][data-tab="policy-ai"]:has(> .m-desktop-notice) > *:not(.m-desktop-notice),
[data-mode="elena"][data-tab="elena-config"]:has(> .m-desktop-notice) > *:not(.m-desktop-notice),
[data-mode="elena"][data-tab="elena-studio"]:has(> .m-desktop-notice) > *:not(.m-desktop-notice) {
  display: none !important;
}
/* En Elena el aviso queda dentro de un #tab-content con padding:0, así que
   necesita su propio margen para no pegarse a los bordes. */
[data-mode="elena"] > .m-desktop-notice { margin: 16px 10px; }

/* ── Overview (executive) ── */
/* El contenedor trae padding:20px 56px inline: en 390px eso deja 278px útiles.
   Se recupera casi el ancho completo. */
.exec-wrap { padding: 12px 10px !important; }

/* Los layouts de los 5 tabs son grillas de 2-4 columnas puestas como estilo
   INLINE en divs anónimos, y además viven repartidas entre renderExecutive() y
   los constructores de cards (_growthMixCard, _execExpoCompareCard, etc.).
   En vez de tocar cada una, se aplastan por selector de atributo, acotado a
   Overview. Sólo matchea las grillas de layout (1fr repetido) — las grillas
   `auto auto` de leyendas y chips se dejan como están, que en móvil se leen
   bien y en una sola columna quedarían larguísimas. */
.exec-wrap [style*="grid-template-columns:1fr 1fr"],
.exec-wrap [style*="grid-template-columns:repeat(2"],
.exec-wrap [style*="grid-template-columns:repeat(3"],
.exec-wrap [style*="grid-template-columns:repeat(4"],
.exec-wrap [style*="grid-template-columns:repeat(5"],
.exec-wrap [style*="grid-template-columns:repeat(6"] {
  grid-template-columns: 1fr !important;
}

/* Comparativa de expos: no es una <table>, es una grilla de flex con columnas
   de ancho fijo (~885px), así que el envoltorio automático de tablas no la
   agarra. Scrollea como unidad para que la cabecera no se desalinee. */
.exec-expo-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.exec-expo-scroll::-webkit-scrollbar { display: none; }
.exec-expo-scroll > * { min-width: 760px; }

/* Cabecera: título, fecha del snapshot y el selector de fecha. El selector
   tiene margin-left:auto y en móvil se iba solo a una fila rarísima. */
.exec-head { row-gap: 8px; }
.exec-head > span[style*="margin-left:auto"] { margin-left: 0 !important; }
.exec-head input[type="date"] { font-size: 16px !important; padding: 6px 8px !important; }

/* Expenses (gastos) — ya estaba adaptado antes de este trabajo; se migró tal
   cual desde index.html:571-590. Es el patrón que replican las demás fases. */
#gastos-view { padding: 0; }
#gastos-bulk-bar { flex-wrap: wrap; gap: 6px; }
#gastos-bulk-bar select, #gastos-bulk-bar input { min-width: 0 !important; flex: 1; }
.gastos-topbar { flex-wrap: nowrap !important; gap: 6px !important; }
.gastos-topbar button { flex: 1 !important; font-size: 13px !important; padding: 12px 6px !important; min-height: 48px !important; }
.gastos-card { flex-wrap: wrap !important; padding: 12px !important; gap: 8px !important; }
.gastos-card-body { flex: 1 1 140px !important; min-width: 0 !important; }
.gastos-card-vendor { font-size: 15px !important; font-weight: 700 !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; }
.gastos-card-meta { white-space: normal !important; line-height: 1.4 !important; font-size: 11px !important; }
.gastos-card-right { flex-direction: row !important; align-items: center !important; width: 100% !important; flex-basis: 100% !important; justify-content: space-between !important; padding-top: 8px !important; border-top: 1px solid var(--border) !important; margin-top: 0 !important; }
.gastos-card-actions { display: flex !important; align-items: center !important; gap: 6px !important; }
#gastos-modal { align-items: flex-end !important; }
#gastos-modal > div { width: 100vw !important; max-height: 100dvh !important; border-radius: 0 !important; overflow-y: auto; }

} /* ── fin @media (max-width:768px) ───────────────────────────────────────── */


/* ── Contrapartida de escritorio ──────────────────────────────────────────── */
/* .m-only existe sólo para móvil; en escritorio se esconde. Es la única regla
   del archivo fuera del media query, y es deliberada.
   768.02px y no 769px: con anchos fraccionarios (típico con escalado de
   pantalla al 125% en Windows, que da cosas como 768.5px) un corte en 769
   dejaba una franja donde no matcheaba ninguno de los dos bloques y los
   elementos sólo-móvil se colaban en escritorio. */
@media (min-width: 768.02px) {
  .m-only, .m-desktop-notice { display: none !important; }
}
