/* mobile.css — overrides para viewports <= 1024 px.

   Toda regla debe estar dentro de @media (max-width: 1024px). No
   introducir reglas desktop aquí ni reglas globales sin breakpoint.

   El breakpoint 1024 px cubre iPhone (portrait + landscape), iPad mini
   e iPad estándar (portrait + landscape). Por encima quedan iPad pro,
   notebook y monitor. Ver tokens.css para documentación.

   Estructura interna (Fase Mobile-i, 2026-05-10):
     Block 1: layout base (sidebar oculta, content full width)
     Block 2: sidebar drawer (off-canvas + hamburger)
     Block 3: header controls (gear popover)
     Block 4: KPI truncation + pill chip overlap
     Block 5: chart containers full-width

   Estructura interna (Fase Mobile-ii, 2026-05-10):
     Block 6: tabla scroll horizontal con fade cue
     Block 7: ECharts media options (axis labels, legends, font sizes)
     Block 8: treemap → barras horizontales en /produccion

   Altura de /stock queda fuera de scope por owner decision. */

/* ─────────────────────────────────────────────────────────────────── */
/* Block 1 — layout base                                                */
/* ─────────────────────────────────────────────────────────────────── */

@media (max-width: 1024px) {
    /* Layout: una sola columna. Sidebar pasa a ser off-canvas (Block 2)
       — no se "stackea" arriba del contenido. */
    .layout {
        grid-template-columns: 1fr;
    }

    /* Sidebar oculto por default; el drawer (Block 2) la trae con
       transform al abrir. width explícito porque al sacar la sidebar
       del grid (.layout pasa a 1fr en mobile), el track de
       var(--sidebar-w) deja de aplicar — sin el width fijo el sidebar
       caería al ancho del contenido. */
    .sidebar {
        position: fixed;
        top: 0;
        left: 0;
        width: var(--sidebar-w);
        height: 100vh;
        z-index: 300;
        transform: translateX(-100%);
        transition: transform 220ms ease-out;
        overflow-y: auto;
        /* padding-top/bottom safe-area lo aporta el .sidebar de style.css
           (regla desktop con calc(20px + var(--safe-area-*))). Mobile no
           lo override acá para no romper la cascada. */
    }

    /* Content: full width en mobile, sin reservar espacio para sidebar.
       El padding lateral se relaja un poco para no apretar texto
       contra el borde del viewport. */
    .main {
        min-width: 0;
    }
    .content {
        padding: 20px 16px 40px 16px;
        max-width: 100%;
    }

    /* Header: en mobile el gap se reduce y el padding lateral se alinea
       con .content. El layout interno (hamburger | título | gear)
       queda definido en Block 3.
       Bugfix sesión 2026-05-09: sumamos var(--safe-area-top) al padding
       superior para que en iPhone 14 Pro / 15 Pro la Dynamic Island no
       tape el contenido del header sticky. En desktop y Android el inset
       es 0 → el padding queda en su valor original. */
    .header {
        padding: max(var(--space-2), var(--safe-area-top)) var(--space-3) var(--space-2);
        gap: var(--space-2);
    }
}

/* ─────────────────────────────────────────────────────────────────── */
/* Block 2 — sidebar drawer                                             */
/* ─────────────────────────────────────────────────────────────────── */

/* Hamburger button: por default oculto. mobile.css lo habilita en
   <= 1024 px. Vive en el header como primer flex child. */
.btn-hamburger {
    display: none;
}

/* Overlay del drawer: por default oculto. CSS-only fade-in usando
   pointer-events + opacity para evitar el "ghost click" durante la
   transición de leave que dejaría hueco si usáramos x-show con
   x-transition. */
.drawer-overlay {
    display: none;
}

@media (max-width: 1024px) {
    /* Sidebar fixed off-canvas — ya está en Block 1 con
       transform: translateX(-100%) por default. Acá agregamos el estado
       open: cuando body.x-data tiene drawerAbierto=true, el sidebar
       toma la clase .drawer-open vía Alpine :class y se desliza. */
    .sidebar.drawer-open {
        transform: translateX(0);
        box-shadow: 4px 0 24px rgba(0, 0, 0, 0.45);
    }

    /* Hamburger button: tres barras horizontales. 44×44 mínimo touch.
       margin-right pequeño para separar del logo. Sin border ni bg —
       integra con el header como icon-only button. */
    .btn-hamburger {
        display: inline-flex;
        flex-direction: column;
        justify-content: space-between;
        align-items: stretch;
        width: 24px;
        height: 18px;
        padding: 0;
        background: none;
        border: none;
        cursor: pointer;
        flex: 0 0 auto;
        /* Touch target real con pseudo-padding (mantiene visual de 24×18
           pero el tap area se expande a ≥ 44×44 sin alterar layout). */
        position: relative;
    }
    .btn-hamburger::before {
        content: "";
        position: absolute;
        inset: -13px;
    }
    .btn-hamburger:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 4px;
        border-radius: var(--radius-btn);
    }
    .btn-hamburger__bar {
        display: block;
        width: 100%;
        height: 2px;
        background: var(--fg);
        border-radius: 2px;
        transition: background 140ms var(--ease);
    }
    .btn-hamburger:hover .btn-hamburger__bar {
        background: var(--accent);
    }

    /* Overlay backdrop: dim oscuro, fade-in/out controlado por opacity.
       pointer-events: none cuando cerrado evita que capture clicks
       accidentales mientras se desvanece. */
    .drawer-overlay {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.55);
        z-index: 290;
        opacity: 0;
        pointer-events: none;
        transition: opacity 220ms ease-out;
    }
    .drawer-overlay--abierto {
        opacity: 1;
        pointer-events: auto;
    }

    /* Reduce motion: elimina las transiciones del drawer. Sigue
       funcional — solo sin animación. */
    @media (prefers-reduced-motion: reduce) {
        .sidebar,
        .drawer-overlay {
            transition: none;
        }
    }
}

/* ─────────────────────────────────────────────────────────────────── */
/* Block 3 — header controls gear popover                               */
/* ─────────────────────────────────────────────────────────────────── */

/* Default (desktop + mobile): .header-config es contenedor real con
   position: relative y ancla el popover absoluto. El gear button es
   visible. .header-controls-pop queda hidden hasta que Alpine flippea
   .header-controls-pop--abierto.

   Bloque 3 (sesión 2026-05-10): el patrón se aplica en TODAS las
   anchuras. El header desktop quedó reducido a hamburger (mobile only)
   + logo + título + sincro + ⚙️. Antes (Mobile-i) este patrón era
   exclusivo de @media ≤1024px; el resto de los controles renderizaban
   inline en desktop con `display: contents`. */
.header-config {
    display: inline-flex;
    align-items: center;
    position: relative;
    margin-left: auto;
    flex: 0 0 auto;
}

.btn-configuracion {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-btn);
    cursor: pointer;
    color: var(--fg-muted);
    padding: 0;
    transition: color 140ms var(--ease),
                border-color 140ms var(--ease),
                background 140ms var(--ease);
    position: relative;
}
.btn-configuracion::before {
    content: "";
    position: absolute;
    inset: -4px;
}
.btn-configuracion:hover,
.btn-configuracion[aria-expanded="true"] {
    color: var(--accent);
    border-color: var(--border-hover);
    background: var(--bg-soft);
}
.btn-configuracion:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Popover: dropdown abierto debajo del gear, ancla derecha. Hidden
   por default; toggle vía .header-controls-pop--abierto. */
.header-controls-pop {
    display: none;
    position: absolute;
    top: calc(100% + var(--space-2));
    right: 0;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    padding: var(--space-3);
    z-index: 250;
    min-width: 280px;
    max-width: calc(100vw - var(--space-3) * 2);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
    /* fade + slide entrance: animación corta, respetando reduced motion
       más abajo si el usuario lo configura. Como Alpine no aplica el
       transition al display:block, usamos animation-name al toggle. */
    animation: header-pop-in 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
.header-controls-pop--abierto {
    display: block;
}
@keyframes header-pop-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
    .header-controls-pop { animation: none; }
}

/* `.controls` dentro del popover: stack vertical, full-width.
   Pills, refresh y palette trigger quedan apilados y fáciles de
   tocar (mantiene el patrón de mobile previo). */
.header-controls-pop .controls {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-3);
    flex-wrap: nowrap;
}
.header-controls-pop .pill-toggle {
    width: 100%;
    height: 38px;
}
.header-controls-pop .pill-toggle .pill {
    flex: 1;
    height: 34px;
    justify-content: center;
    font-size: var(--text-sm);
}
.header-controls-pop form {
    width: 100%;
}
.header-controls-pop .header button.refresh,
.header-controls-pop button.refresh {
    width: 100%;
    height: 38px;
    font-size: var(--text-sm);
}
.header-controls-pop .cmd-palette-trigger {
    width: 100%;
    justify-content: center;
    height: 38px;
}
.header-controls-pop select[name="rango"] {
    width: 100%;
    height: 38px;
}

@media (max-width: 1024px) {
    /* En mobile el popover puede ocupar más ancho de pantalla. El resto
       del comportamiento es igual al default. */
    .header-controls-pop {
        min-width: 240px;
    }

    /* Header layout en mobile: hamburger + brand + h1 + sincro + gear.
       El h1 trunca por overflow porque el espacio es premium. */
    .header h1 {
        font-size: var(--text-base);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        min-width: 0;
    }
    .header .brand-logo {
        height: 22px;
    }
    /* Sincro-badge en mobile: ocultamos el texto, solo dot — el header
       ya está apretado entre brand, h1 truncado y gear. */
    .sincro-badge--header .sincro-text {
        display: none;
    }
    /* Rango-badge en mobile (3H — visible también en mobile): el chip
       sigue presente pero en versión compacta (padding y font
       reducidos) para no apretar el resto del header. h1 con
       text-overflow: ellipsis absorbe lo que sobra. */
    .rango-badge--header {
        padding: 0 7px;
        font-size: 10px;
        height: 20px;
    }
}

/* ─────────────────────────────────────────────────────────────────── */
/* Block 4 — KPI truncation + pill overlap                              */
/* ─────────────────────────────────────────────────────────────────── */

/* Pre-condición: el sidebar ya no roba 180 px en mobile (Block 1-2),
   por lo cual el ancho de contenido pasó de 213 px a ≈ 393 px. Esto
   solo ya resolvió la mayoría de los truncates que el audit reportó.
   Block 4 endurece los casos remanentes: garantizar que ningún label
   ni value se corte; permitir wrap de pills sobre títulos largos en
   chart cards; bajar a 1-col debajo de 600 px viewport. */

@media (max-width: 1024px) {
    /* KPI grid genérico (.kpis): por default es 4-col, en style.css
       baja a 2-col a partir de 1100 px. En mobile (≤1024 px) sigue
       en 2-col porque ya hay espacio (≈393 px / 2 = 165 px por card),
       suficiente para "$298 mil" y "▲ 510". */
    .kpis,
    .kpis.kpis--3col {
        gap: var(--space-2);
    }

    /* Card a prueba de overflow: explicit overflow: visible y
       min-width: 0 evitan que un padre (grid track o flex column)
       les recorte el contenido. */
    .card {
        min-width: 0;
        overflow: visible;
        padding: 12px 14px;
    }
    .card .label {
        white-space: normal;
        overflow: visible;
        text-overflow: clip;
        line-height: 1.25;
    }
    .card .value {
        font-size: 22px;
        white-space: normal;
        overflow: visible;
        min-width: 0;
    }

    /* Cockpit hero — sólo /inicio. Ya estaba en 1-col a partir de 700
       px (style.css:627). Acá apretamos padding y aseguramos overflow
       visible. El value queda en 32 px (regla preexistente a 700 px). */
    .cockpit-hero__card {
        overflow: visible;
        min-width: 0;
    }
    .cockpit-hero__label {
        white-space: normal;
    }
    .cockpit-hero__value {
        white-space: nowrap;
        overflow: visible;
    }

    /* Chart card header: flex-wrap explícito por si el viewport es
       angosto. min-width: 0 al título permite que el flex shrinker
       no expulse las actions. */
    .chart-header {
        flex-wrap: wrap;
        gap: var(--space-2);
    }
    .chart-header h3 {
        min-width: 0;
        flex: 1 1 auto;
        font-size: var(--text-md);
        line-height: 1.25;
    }
    .chart-actions {
        flex-shrink: 0;
        flex-wrap: wrap;
    }
    /* Pill toggles dentro de chart cards: garantizar que no rompen el
       layout cuando el título wrappea. */
    .chart-actions .pill-toggle {
        height: 28px;
    }
    .chart-actions .pill-toggle .pill {
        height: 24px;
        padding: 0 10px;
        font-size: var(--text-xs);
    }
}

/* Sub-breakpoint a 600 px: bajo este ancho los KPIs pasan a 1-col
   para asegurar legibilidad total. iPhone portrait (393) cae acá. */
@media (max-width: 600px) {
    .kpis,
    .kpis.kpis--3col {
        grid-template-columns: 1fr;
    }

    /* "Guardar vista" se oculta en móvil: en pantallas angostas la isla de
       filtros lo bajaba a una línea propia (mucha altura). Guardar vistas es
       una acción de escritorio; el acceso sigue por desktop. Sólo afecta al
       botón del toolbar (.vistas-save), no a la lista del sidebar
       (.sidebar-vistas). 2026-05-29. */
    .vistas-save {
        display: none;
    }
}

/* ─────────────────────────────────────────────────────────────────── */
/* Block 5 — chart containers full-width                                */
/* ─────────────────────────────────────────────────────────────────── */

/* Pre-condición: el sidebar drawer (Block 1-2) ya recuperó 180 px de
   ancho. Los charts pasaron de 115 px (audit) a ~327 px en mobile —
   ratio 1:1 en lugar de 1:2.8. La ResizeObserver añadida en app.js se
   encarga de re-render en cambios de orientación.

   Block 5 NO simplifica series, NO cambia tipos, NO ajusta ejes. Eso
   es Fase Mobile-ii. Acá solo aseguramos full-width container. */

@media (max-width: 1024px) {
    /* charts-grid: 1 col forzado en mobile (en style.css ya pasa a 1
       col bajo 1100 px; reaffirmamos por si se introduce otro grid
       de charts en una vista futura). */
    .charts-grid {
        grid-template-columns: 1fr;
        gap: var(--space-3);
    }

    /* chart-card / chart-wrapper: full-width con min-width: 0 para
       que el flex/grid parent no clipe. */
    .chart-card,
    .chart-wrapper {
        width: 100%;
        min-width: 0;
        padding: var(--space-2);
    }

    /* ECharts canvas: máximo 100% del contenedor. ECharts setea el
       width inline cuando llama a resize() — el max-width: 100%
       previene overflows si la instancia no se actualizó tras un
       cambio de viewport. */
    .chart-card canvas,
    .chart-wrapper canvas {
        max-width: 100% !important;
    }

    /* charts-grid de produccion (.chart-yoy-stack) y similares: heredan
       el chart-card padding compactado. No tocamos heights — eso queda
       para Fase Mobile-ii cuando re-evaluemos series y aspect-ratio. */

    /* ── Mobile-ii Block 5b — normalización de heights ─────────────
       En mobile el card-header (título + tooltip + actions) consume
       50-100 px del card. Sin override, el canvas queda fijo en 320 px
       y deja "wasted space" del card. Subimos el canvas a 380 px
       (default `.chart`) y proporcionalmente los demás. Política única:
       cada chart aprovecha más del card en mobile sin tocar desktop.

       Fase 3E: stock migró sus `.chart-canvas` a `.chart`, así que un
       solo override sirve para todas las rutas. */
    .chart {
        height: 380px;
    }
    .chart.chart-tall {
        height: 720px;
    }
    #chart-saldo-flujo {
        height: 480px;
    }
    .chart-yoy-panel {
        height: 280px;
    }
    .rotacion-panel #chart-rotacion {
        height: 520px;
    }
}

/* ─────────────────────────────────────────────────────────────────── */
/* Block 6 — tabla scroll horizontal con fade cue                       */
/* ─────────────────────────────────────────────────────────────────── */

/* Pre-condición: `.tbl-wrap` ya define `overflow-x: auto` en style.css
   (línea 1717). Mobile-i agregó wrappers a todas las tablas con riesgo
   de overflow durante el audit del 2026-05-10.

   IMPORTANTE — fix 2026-05-10: el ::after directo sobre `.tbl-wrap`
   se posicionaba relativo al SCROLL CONTENT (1032 px), no al viewport
   visible (361 px), porque WebKit/Chrome anclan los hijos absolutos de
   un overflow:auto al box del contenido. Resultado visual: el fade
   aparecía pegado al borde derecho del contenido scrollable, NO al
   borde derecho visible — al scrollear, el fade se "movía" hasta el
   left edge del viewport (off-stage del lado correcto).

   Fix: app.js wrappea cada `.tbl-wrap` en un `<div class="tbl-fade-wrap">`
   externo (idempotente). El fade vive en `.tbl-fade-wrap::after` (no en
   `.tbl-wrap::after`) — el outer NO scrollea, así que el ::after se
   posiciona relativo al PADDING BOX visible. La clase `.scroll-fin` se
   togglea en el outer (no en el inner), leyendo `scrollLeft` del inner. */

@media (max-width: 1024px) {
    .tbl-fade-wrap {
        position: relative;
        /* Importante: NO usar overflow aquí — el scroll vive en el
           hijo .tbl-wrap. El outer mantiene su box completo. */
    }

    /* -webkit-overflow-scrolling para momentum scroll iOS. overflow-x
       sigue en style.css :1717. */
    .tbl-wrap {
        -webkit-overflow-scrolling: touch;
    }

    .tbl-fade-wrap::after {
        content: "";
        position: absolute;
        top: 0;
        right: 0;
        width: 48px;
        height: 100%;
        background: linear-gradient(to right, transparent, var(--bg-soft));
        pointer-events: none;
        opacity: 1;
        transition: opacity 180ms ease;
    }

    /* .scroll-fin → fade hidden. Cubre dos casos: (a) usuario scrolleó
       hasta el final, (b) la tabla cabe sin overflow. JS togglea sobre
       el OUTER `.tbl-fade-wrap`. */
    .tbl-fade-wrap.scroll-fin::after {
        opacity: 0;
    }

    @media (prefers-reduced-motion: reduce) {
        .tbl-fade-wrap::after {
            transition: none;
        }
    }
}
