/* ============================================================
   Majors Group Dashboard — custom CSS overrides
   Loaded automatically by Dash from the assets/ folder.
   ============================================================ */

/* DatePickerRange — force black text on white background so dates
   are readable. Default Dash/react-dates styling shows light grey
   text which is invisible on the dark-mode dashboard. */
.DateInput,
.DateInput_input,
.DateInput_input_1 {
    background-color: #ffffff !important;
    color: #000000 !important;
    font-weight: 500 !important;
}

/* The arrow / separator between start and end inputs */
.DateRangePickerInput_arrow_svg {
    fill: #000000 !important;
}

/* The whole DateRangePickerInput wrapper — keep white so text is on
   white background, not the dark card */
.DateRangePickerInput {
    background-color: #ffffff !important;
    border: 1px solid #334155 !important;
    border-radius: 4px !important;
}

/* ============================================================
   Dropdown styling — dashboard uses dark theme, so dropdowns need
   LIGHT text on the dark control background, NOT black.
   Targets both legacy react-select v1 (.Select-*) and Dash 3.x's
   BEM-style react-select v5 (.Select__*) so it works regardless
   of Dash version.
   ============================================================ */

/* --- The dropdown control box (the "input" the user clicks) ---
   Committed to a WHITE control with BLACK text.  react-select v5
   renders the control on a light background by default and the older
   "dark control + light text" rules kept losing that fight, leaving
   the selected value invisible (white-on-white).  White + black is
   guaranteed readable and matches the open menu panel below. */
.Select-control,
.Select__control,
div.Select__control {
    background-color: #ffffff !important;
    border: 1px solid #cbd5e1 !important;
    box-shadow: none !important;
}
.Select__control--is-focused {
    border-color: #2563eb !important;
}

/* --- Selected single value, placeholder, and search input ---
   `-webkit-text-fill-color` is set alongside `color` because Safari
   paints the text fill separately and will otherwise keep the old
   light colour. */
.Select-value-label,
.Select-input > input,
.Select__single-value,
.Select__input-container,
.Select__input-container input,
.Select__input,
.Select__input input {
    color: #0f172a !important;                /* black text on white */
    -webkit-text-fill-color: #0f172a !important;
}
/* Placeholder text — mid-grey, clearly visible on white */
.Select__placeholder,
.Select-placeholder {
    color: #64748b !important;
    -webkit-text-fill-color: #64748b !important;
}
/* Multi-select chips: light-blue chip, dark label */
.Select__multi-value {
    background-color: #e0f2fe !important;
}
.Select__multi-value__label {
    color: #0f172a !important;
    -webkit-text-fill-color: #0f172a !important;
}

/* --- Dropdown menu panel (when you click to open) ---
   react-select v5 uses Emotion CSS-in-JS with high specificity, so we
   need very specific selectors here. Doubling class names increases
   specificity without adding `!important` arms races. */
div.Select__menu,
div.Select-menu-outer {
    background-color: #ffffff !important;
    border: 1px solid #cbd5e1 !important;
}
div.Select__menu-list,
.Select__menu .Select__menu-list {
    background-color: #ffffff !important;
    padding: 4px 0 !important;
}

/* Option rows: high-specificity to beat Emotion's runtime styles */
.Select__menu .Select__option,
div.Select__option,
.Select-menu .Select-option {
    background-color: #ffffff !important;
    color: #0f172a !important;
    -webkit-text-fill-color: #0f172a !important;
    cursor: pointer !important;
    padding: 8px 12px !important;
}

/* Hover row */
.Select__menu .Select__option--is-focused,
div.Select__option--is-focused,
.Select-menu .Select-option.is-focused {
    background-color: #e0f2fe !important;
    color: #0f172a !important;
    -webkit-text-fill-color: #0f172a !important;
}

/* Currently-selected option (the one you picked previously) */
.Select__menu .Select__option--is-selected,
div.Select__option--is-selected,
.Select-menu .Select-option.is-selected {
    background-color: #3b82f6 !important;
    color: #ffffff !important;
    -webkit-text-fill-color: #ffffff !important;
}

/* Disabled options (rare but cover anyway) */
.Select__menu .Select__option--is-disabled,
div.Select__option--is-disabled {
    color: #94a3b8 !important;
    -webkit-text-fill-color: #94a3b8 !important;
    cursor: not-allowed !important;
}

/* "No options" / "Loading" message */
.Select__menu-notice,
.Select__menu-notice--no-options,
.Select__menu-notice--loading {
    color: #64748b !important;
    background-color: #ffffff !important;
}

/* --- Indicator separator + arrow (dark so they show on white) --- */
.Select__indicator-separator {
    background-color: #cbd5e1 !important;
}
.Select__indicator,
.Select__dropdown-indicator,
.Select__clear-indicator {
    color: #475569 !important;
}
.Select__indicator svg,
.Select__dropdown-indicator svg,
.Select__clear-indicator svg {
    fill: #475569 !important;
}
.Select__indicator:hover {
    color: #0f172a !important;
}
/* react-select v1 arrow (the little triangle) */
.Select-arrow {
    border-color: #475569 transparent transparent !important;
}

/* --- Multi-select chips --- */
.Select--multi .Select-value,
.Select__multi-value {
    background-color: #0f172a !important;
    border: 1px solid #334155 !important;
    color: #f1f5f9 !important;
}
.Select--multi .Select-value-label,
.Select--multi .Select-value-label *,
.Select__multi-value__label,
.Select__multi-value__label * {
    color: #f1f5f9 !important;
    -webkit-text-fill-color: #f1f5f9 !important;
}
.Select--multi .Select-value-icon,
.Select__multi-value__remove {
    color: #f1f5f9 !important;
    -webkit-text-fill-color: #f1f5f9 !important;
}
.Select__multi-value__remove:hover {
    background-color: #ef4444 !important;
    color: #ffffff !important;
}

/* ============================================================
   Tab nav — streamlined hover state + tight spacing
   ============================================================ */
.tab-parent .tab {
    transition: color 0.15s ease, border-color 0.15s ease;
}
.tab-parent .tab:hover:not(.tab--selected) {
    color: #f1f5f9 !important;
    border-bottom: 2px solid rgba(34, 211, 238, 0.4) !important;
}
.tab-parent ::-webkit-scrollbar {
    height: 4px;
}
.tab-parent ::-webkit-scrollbar-thumb {
    background: rgba(34, 211, 238, 0.3);
    border-radius: 2px;
}

/* ============================================================
   Quick-switch pill buttons (Products tab category multi-select)
   Works for both dcc.RadioItems (radio) and dcc.Checklist (checkbox).
   ============================================================ */
.cat-pill .form-check {
    display: inline-block;
    margin-right: 0;
}
.cat-pill input[type="radio"],
.cat-pill input[type="checkbox"] {
    display: none;
}
.cat-pill label {
    padding: 6px 14px;
    margin-right: 6px;
    margin-bottom: 6px;
    background-color: #1e293b;
    color: #94a3b8;
    border: 1px solid #334155;
    border-radius: 16px;
    cursor: pointer;
    font-size: 12px;
    font-weight: 600;
    user-select: none;
    transition: all 0.15s ease;
    display: inline-block;
}
.cat-pill label:hover {
    color: #f1f5f9;
    border-color: #22d3ee;
}
.cat-pill input[type="radio"]:checked + label,
.cat-pill input[type="checkbox"]:checked + label {
    background-color: #22d3ee;
    color: #0f172a;
    border-color: #22d3ee;
}

/* ============================================================
   MOBILE NATIVE POLISH (iPhone 17 Pro target ~402x874)
   These rules layer on top of the inline styles in dashboard.py
   to give the dashboard a high-end mobile-app feel.
   ============================================================ */

@media (max-width: 768px) {

    /* --- Cards: rounded, slightly elevated --- */
    .kpi-card,
    div[id^="drill-panel"] > div,
    div[style*="border-radius"][style*="background"] {
        border-radius: 14px !important;
    }

    /* --- Containers/tab content: padding tweaks --- */
    #tab-content { padding: 14px !important; }
    /* Many sub-tabs use a flex row of children with gap. Widen spacing slightly. */
    #tab-content > div { gap: 12px !important; }

    /* --- KPI rows wrap nicely --- */
    #tab-content > div:first-child {
        gap: 10px !important;
    }

    /* --- Plotly chart cards: reduce inner padding --- */
    .js-plotly-plot {
        border-radius: 12px !important;
    }
    .js-plotly-plot,
    div[class*="dash-graph"] {
        margin: 4px 0 !important;
    }

    /* --- DataTable polish on mobile --- */
    .dash-table-container {
        border-radius: 12px;
        overflow: hidden;
    }
    .dash-table-container .dash-spreadsheet-container {
        border-radius: 12px;
    }
    .dash-cell-value {
        font-size: 11px !important;
    }
    /* sticky header */
    .dash-spreadsheet-inner thead th,
    .dash-header-cell-value {
        background: #1e293b !important;
        font-weight: 700 !important;
        font-size: 10px !important;
        text-transform: uppercase;
        letter-spacing: 0.4px;
    }
    /* Row hover (subtle, since touch doesn't really hover) */
    .dash-cell:active {
        background: rgba(34,211,238,0.12) !important;
    }

    /* --- Dropdowns: pill shape on mobile --- */
    .Select-control,
    .Select__control {
        border-radius: 12px !important;
        min-height: 44px !important;
    }
    .Select__menu {
        border-radius: 12px !important;
        box-shadow: 0 12px 32px rgba(0,0,0,0.4) !important;
    }
    .Select__option {
        font-size: 14px !important;
        padding: 12px 14px !important;
    }

    /* --- Inputs: rounded + comfortable. ALL inputs must be >=16px on iOS
       Safari, otherwise focus triggers an auto-zoom that doesn't always
       unzoom on blur. Was 15px → caused user-reported zoom-stuck bug. --- */
    input[type="text"], input[type="search"], input[type="email"],
    input[type="number"], input[type="password"], input[type="url"],
    input[type="tel"], textarea, select {
        border-radius: 12px !important;
        font-size: 16px !important;
        padding: 12px 14px !important;
    }

    /* --- Buttons: tactile + pill rounded --- */
    button {
        border-radius: 12px !important;
        font-weight: 600 !important;
        letter-spacing: 0.2px;
    }
    button:active {
        opacity: 0.85;
    }

    /* --- DatePickerRange: full width, vertical stack on tight screens --- */
    .DateRangePickerInput {
        display: flex !important;
        align-items: center;
        width: 100% !important;
        border-radius: 10px !important;
        gap: 8px;
        padding: 4px 8px !important;
    }
    .DateInput {
        flex: 1 1 0 !important;
        width: auto !important;
    }

    /* --- Tab bar: scroll fade indicators --- */
    .tab-parent {
        position: relative;
    }
    .tab-parent::before,
    .tab-parent::after {
        content: "";
        position: absolute;
        top: 0; bottom: 0;
        width: 18px;
        pointer-events: none;
        z-index: 1;
    }
    .tab-parent::before {
        left: 0;
        background: linear-gradient(90deg, #0f172a, transparent);
    }
    .tab-parent::after {
        right: 0;
        background: linear-gradient(-90deg, #0f172a, transparent);
    }

    /* --- Bookmark panel: full-width on mobile --- */
    #bookmark-panel {
        margin: 0 !important;
        border-radius: 0 !important;
    }

    /* --- Drill panel: native-style "back" treatment --- */
    #drill-panel > div {
        border-radius: 0 !important;
        border-left: none !important;
        border-right: none !important;
    }
    #drill-panel h2 {
        font-size: 18px !important;
        margin: 4px 0 8px !important;
    }
    #drill-panel h3 {
        font-size: 15px !important;
    }

    /* --- Cat-pills look like iOS segmented control on mobile --- */
    .cat-pill {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
    }
    .cat-pill label {
        border-radius: 10px !important;
        padding: 10px 14px !important;
        font-size: 13px !important;
        margin: 0 !important;
    }

    /* --- Forms inside the resources panel etc --- */
    label {
        font-size: 12px !important;
    }
}

/* ============================================================
   PHONE BREAKPOINT (<= 480px) — refinements
   ============================================================ */
@media (max-width: 480px) {
    #tab-content { padding: 12px 10px !important; }

    /* KPI: tighter, centered */
    .kpi-card { padding: 12px 12px !important; }
    .kpi-card h3 { font-size: 20px !important; }

    /* Chart heights: cap at viewport-aware */
    .js-plotly-plot,
    .js-plotly-plot .main-svg {
        max-height: 320px !important;
    }
    /* Bigger charts (where height was set explicitly) cap to a sane mobile height */
    div[style*="height"] > .js-plotly-plot {
        max-height: 360px !important;
    }

    /* Drill-panel buttons full-width-ish */
    #drill-panel button {
        font-size: 13px !important;
    }
    /* Many drill panels lay out two-col on desktop; force single-col on phone */
    #drill-panel > div > div[style*="display: flex"] > div,
    #drill-panel > div > div[style*="display:flex"] > div {
        flex-basis: 100% !important;
        min-width: 0 !important;
    }
}

/* ============================================================
   VERY SMALL (<=400px)
   ============================================================ */
@media (max-width: 400px) {
    .kpi-card { flex: 1 1 100% !important; }
    .tab-parent .tab { padding: 8px 12px !important; font-size: 13px !important; }
}

/* ============================================================
   FILTER-SHEET-WRAP CONTENT POLISH (mobile)
   The .filter-sheet-wrap container holds the period-bar / filters /
   cust-lookup-bar. On mobile it slides up as a bottom sheet. The
   inline styles in dashboard.py are mostly hidden via the rules in
   index_string; this block adds extra polish.
   ============================================================ */
@media (max-width: 768px) {
    .filter-sheet-wrap .DateInput_input {
        font-size: 14px !important;
    }
    .filter-sheet-wrap .Select-control {
        min-height: 44px !important;
    }
    /* Hide the Period-bar's leading "Period:" label on mobile (we already
       have a section title from .filter-sheet-chrome) */
    .filter-sheet-wrap .period-bar > label {
        display: none !important;
    }
}

/* ============================================================
   MOBILE MENU SHEET POLISH (grid layout)
   The Dash-rendered #mobile-menu-sheet gets `.mobile-sheet` styles
   from index_string for slide-up behaviour; this block adds the
   grid layout, sticky handle, close button, and Done button.
   ============================================================ */
#mobile-menu-sheet,
#mobile-bottom-tabs {
    display: none;  /* hidden on desktop */
}
.filter-sheet-done,
.sheet-done-btn {
    display: none;  /* shown only on mobile, see @media block */
}

@media (max-width: 768px) {
    #mobile-menu-sheet {
        display: block;
        padding: 6px 16px calc(80px + env(safe-area-inset-bottom)) !important;
    }
    /* Sticky handle stays visible while scrolling a tall sheet */
    #mobile-menu-sheet > .sheet-handle {
        position: sticky; top: 0;
        z-index: 2;
        background: #0f172a;
        margin: 0 auto 6px;
    }
    #mobile-menu-sheet > .sheet-close,
    .filter-sheet-chrome > .sheet-close {
        position: absolute;
        top: 12px; right: 14px;
        width: 36px; height: 36px;
        border-radius: 18px;
        background: #1e293b;
        color: #f1f5f9;
        font-size: 22px;
        border: 1px solid #334155;
        cursor: pointer;
        z-index: 30;
        display: flex; align-items: center; justify-content: center;
        line-height: 1; font-weight: 400;
        padding: 0;
    }
    .filter-sheet-chrome > .sheet-close { top: 8px; right: 4px; }

    /* GRID layouts ------------------------------------------------ */
    .mobile-nav-grid {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: 10px;
        margin: 0;
    }
    .mobile-action-grid {
        display: grid;
        grid-template-columns: repeat(4, 1fr);
        gap: 10px;
        margin: 0;
    }
    .mobile-grid-btn:active,
    .mobile-action-btn:active {
        transform: scale(0.96);
        opacity: 0.92;
    }

    /* "Done" button at the foot of any sheet — full-width, prominent */
    .sheet-done-btn,
    .filter-sheet-done {
        display: block !important;
    }

    /* BOTTOM TAB BAR --------------------------------------------- */
    #mobile-bottom-tabs {
        display: flex;
        position: fixed;
        bottom: 0; left: 0; right: 0;
        background: rgba(15, 23, 42, 0.95);
        -webkit-backdrop-filter: saturate(180%) blur(20px);
        backdrop-filter: saturate(180%) blur(20px);
        border-top: 1px solid rgba(51, 65, 85, 0.6);
        padding: 0 0 env(safe-area-inset-bottom);
        z-index: 8500;  /* below sheets (9700) and floating buttons (9500) */
        box-shadow: 0 -2px 12px rgba(0,0,0,0.3);
    }
    .mobile-bottom-tab {
        transition: color 0.15s ease, transform 0.1s ease;
    }
    .mobile-bottom-tab:active {
        transform: scale(0.92);
    }
    .mobile-bottom-tab.is-active::before {
        /* Tiny pill at the top of the active tab */
        content: "";
        position: absolute;
        top: 0; left: 50%;
        transform: translateX(-50%);
        width: 28px; height: 3px;
        border-radius: 0 0 3px 3px;
        background: #22d3ee;
    }
    .mobile-bottom-tab { position: relative; }

    /* Reserve space at the bottom of every page so content isn't hidden
       behind the fixed bottom tab bar */
    #tab-content {
        padding-bottom: calc(80px + env(safe-area-inset-bottom)) !important;
    }
}

/* Phone refinement: shrink grid to 2-cols on very narrow screens */
@media (max-width: 360px) {
    .mobile-nav-grid {
        grid-template-columns: repeat(2, 1fr) !important;
    }
}

/* ============================================================
   LOGIN PAGE — phone polish
   ============================================================ */
@media (max-width: 480px) {
    /* The login card uses a fixed width; let it size to viewport */
    div[id="login-passcode"], #login-passcode {
        font-size: 24px !important;
    }
    body > div > div > div[style*="width: 360px"],
    body > div > div > div[style*="width:360px"] {
        width: calc(100vw - 32px) !important;
        max-width: 360px !important;
        padding: 32px 24px !important;
    }
}

/* ============================================================
   SUBTLE: long-press ripple feel on cards (visual only)
   ============================================================ */
.kpi-card,
button,
.tab,
.Select-control {
    -webkit-tap-highlight-color: rgba(34, 211, 238, 0.15);
}

/* Reduce visual jank on iOS during scroll */
* {
    -webkit-overflow-scrolling: touch;
}

/* Hide focus ring on touch (visible only on keyboard) */
@media (hover: none) {
    button:focus, .Select-control:focus-within {
        outline: none !important;
    }
}

/* ============================================================
   AGGRESSIVE MOBILE LAYOUT FIXES
   The render_* functions in dashboard.py use lots of inline
   `style={"display":"flex"}` rows that don't naturally stack on
   phones. These rules force responsive behaviour without
   touching the Python.
   ============================================================ */
@media (max-width: 768px) {

    /* --- HARD STOP: prevent ANY horizontal scroll on the body --- */
    html, body {
        overflow-x: hidden !important;
        max-width: 100vw !important;
    }

    /* --- HEADER: shrink so it doesn't eat 25% of the screen --- */
    .app-header {
        padding: calc(8px + env(safe-area-inset-top)) 52px 8px 14px !important;
    }
    .app-header h1 {
        font-size: 16px !important;
        line-height: 1.1 !important;
    }
    /* Re-tune the sticky tab-parent offset (now hidden, but keep predictable) */
    .tab-parent { display: none !important; }

    /* --- TAB CONTENT: tight padding, generous bottom gap for the bar --- */
    #tab-content {
        padding: 12px 12px calc(86px + env(safe-area-inset-bottom)) !important;
        max-width: 100% !important;
        overflow-x: hidden !important;
    }
    /* All direct children get a vertical rhythm */
    #tab-content > div {
        gap: 12px !important;
        margin-bottom: 12px !important;
    }
    /* But avoid double-margin on the very last child */
    #tab-content > div:last-child {
        margin-bottom: 0 !important;
    }

    /* ===========================================================
       FLEX → STACK: turn every horizontal flex row inside the tab
       content (and drill panel) into a column on phones. This is
       what makes pairs of charts, KPI rows, and side-by-side cards
       actually fit on a 402px screen.
       =========================================================== */
    #tab-content [style*="display: flex"]:not(.kpi-row),
    #tab-content [style*="display:flex"]:not(.kpi-row),
    #drill-panel [style*="display: flex"]:not(.kpi-row),
    #drill-panel [style*="display:flex"]:not(.kpi-row) {
        flex-wrap: wrap !important;
    }
    /* Children of those flex rows: full-width unless they're KPI cards */
    #tab-content [style*="display: flex"] > div:not(.kpi-card),
    #tab-content [style*="display:flex"] > div:not(.kpi-card),
    #drill-panel [style*="display: flex"] > div:not(.kpi-card),
    #drill-panel [style*="display:flex"] > div:not(.kpi-card) {
        flex-basis: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
    }
    /* KPI cards stay at 50% so we get a 2-col grid */
    #tab-content [style*="display: flex"] > .kpi-card,
    #tab-content [style*="display:flex"] > .kpi-card,
    #drill-panel [style*="display: flex"] > .kpi-card,
    #drill-panel [style*="display:flex"] > .kpi-card {
        flex: 1 1 calc(50% - 6px) !important;
        max-width: calc(50% - 6px) !important;
        min-width: 0 !important;
    }

    /* --- CHARTS always fit: width 100%, sane height --- */
    .js-plotly-plot,
    .dash-graph,
    .dash-graph > div,
    .js-plotly-plot > div,
    .plot-container {
        width: 100% !important;
        max-width: 100% !important;
    }
    .js-plotly-plot,
    .dash-graph .plot-container .svg-container {
        max-height: 320px !important;
    }
    /* dcc.Graph wrapper sometimes has its own flex; force block */
    .dash-graph { display: block !important; }

    /* --- TABLES: constrain to viewport, scroll inside --- */
    .dash-table-container,
    .dash-spreadsheet,
    .dash-spreadsheet-container,
    .dash-spreadsheet-inner {
        max-width: 100% !important;
    }
    .dash-spreadsheet-container {
        overflow-x: auto !important;
        -webkit-overflow-scrolling: touch;
    }
    /* Container that holds a DataTable usually has padding; don't let it expand */
    [class*="dash-table"] table {
        max-width: 100%;
    }

    /* --- IMAGES: always responsive --- */
    img { max-width: 100% !important; height: auto !important; }

    /* --- KPI CARDS: tighter padding & numbers visible --- */
    .kpi-card {
        padding: 12px !important;
        margin: 0 !important;
    }
    .kpi-card h3 { font-size: 20px !important; }
    .kpi-card p  { font-size: 11px !important; }

    /* --- DRILL PANEL header rows: stack and let the H2 wrap --- */
    #drill-panel h2 {
        font-size: 17px !important;
        line-height: 1.2 !important;
        word-break: break-word;
    }
    #drill-panel h3 { font-size: 14px !important; }

    /* --- Inline pill rows / section labels can wrap --- */
    h1, h2, h3, h4 {
        word-break: break-word;
        overflow-wrap: anywhere;
    }

    /* --- Map iframe / container: full width, fixed height --- */
    .mapboxgl-map, .mapboxgl-canvas, .js-plotly-plot.scatter-mapbox,
    div[id^="map-"] {
        width: 100% !important;
        max-width: 100% !important;
    }

    /* --- Hide non-essential header chrome on phone (already partly done) --- */
    .app-header > div:nth-child(2) { display: none !important; }
}

/* Phone (<=480px): more aggressive */
@media (max-width: 480px) {
    /* KPI cards become single-column on very narrow screens IF there are many */
    #tab-content [style*="display: flex"] > .kpi-card:nth-of-type(n + 5),
    #tab-content [style*="display:flex"] > .kpi-card:nth-of-type(n + 5) {
        /* keep 2-col, but allow the last odd one to span */
    }
    /* DatePickerRange goes inline-block on tight phones */
    .DateRangePickerInput { padding: 2px 6px !important; }
    .DateInput_input { font-size: 14px !important; padding: 8px !important; }
}


/* ============================================================
   ============================================================
                  MOBILE-ONLY DASHBOARD STYLES
   Served when User-Agent matches a phone. The .m-app root and
   all .m-* classes are unique to this layout — they don't
   affect the desktop dashboard at all.
   ============================================================
   ============================================================ */

/* Standalone PWA mode (when user has added to home screen) */
@media (display-mode: standalone) {
    body { padding-top: 0; }
}

/* ===== iOS ZOOM PREVENTION =====
   iOS Safari auto-zooms ANY input < 16px on focus and doesn't always
   zoom back. This rule ensures every single text input, textarea,
   select, AND react-select internal input is at least 16px so the
   zoom never triggers in the first place. */
.m-app input, .m-app textarea, .m-app select,
.m-app input[type="text"], .m-app input[type="search"],
.m-app input[type="email"], .m-app input[type="number"],
.m-app input[type="password"], .m-app input[type="tel"],
.m-app input[type="url"], .m-app input[type="date"],
.m-app .Select-input > input,
.m-app .Select__input,
.m-app .Select__input input,
.m-app .Select__input-container input,
#m-scan-input,
#m-scan-sheet input,
.m-search-modal-input,
.DateInput_input,
#login-passcode {
    font-size: 16px !important;
}
/* Also apply at the body level on any mobile-sized viewport — catches
   inputs in the desktop dashboard (rendered in narrow responsive view). */
@media (max-width: 768px) {
    input, textarea, select,
    .Select-input > input, .Select__input input {
        font-size: 16px !important;
    }
}

.m-app {
    background: #0f172a;
    color: #f1f5f9;
    min-height: 100vh;
    font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
    /* Hard width cap — kills any horizontal overflow from charts / tables /
       wide flex children. Anything that wants more space scrolls inside
       its own container, never pushing the page sideways. */
    width: 100vw;
    max-width: 100vw;
    overflow-x: hidden;
    box-sizing: border-box;
    /* Top padding clears the fixed header (avatar 36 + 8+8 padding +
       safe-area). Bottom padding clears the fixed bottom nav. */
    padding-top: calc(56px + env(safe-area-inset-top));
    padding-bottom: calc(76px + env(safe-area-inset-bottom));
}
/* Just box-sizing — no universal max-width clamp (was over-constraining
   layouts and pushing content off the viewport). Horizontal-scroll
   prevention is handled at the app + content + body level instead. */
.m-app *, .m-app *::before, .m-app *::after {
    box-sizing: border-box;
}
/* Body itself shouldn't scroll horizontally for mobile UA users */
body:has(.m-app), body.has-m-app {
    overflow-x: hidden !important;
    max-width: 100vw;
}

/* When the mobile dashboard is rendered, hide the desktop's floating
   menu/filter buttons + sheet backdrop that live in index_string body —
   otherwise they overlap with the .m-header cog and the .m-backdrop.
   Uses both :has() (modern) and .has-m-app body class (JS-set, works in all). */
body:has(.m-app) #mobile-menu-toggle,
body:has(.m-app) #mobile-filter-toggle,
body:has(.m-app) #sheet-backdrop,
body.has-m-app   #mobile-menu-toggle,
body.has-m-app   #mobile-filter-toggle,
body.has-m-app   #sheet-backdrop {
    display: none !important;
}

/* ===== iOS TOUCH FIXES =====
   Every interactive element in the mobile UI gets these properties so that
   taps register reliably on iOS Safari and Android Chrome. */
.m-app button,
.m-list-row,
.m-chip,
.m-nav-btn,
.m-icon-btn,
.m-back-btn,
.m-list-button {
    cursor: pointer;
    touch-action: manipulation;          /* kills the 300ms double-tap delay */
    -webkit-tap-highlight-color: rgba(34, 211, 238, 0.18);
    -webkit-touch-callout: none;
    user-select: none;
    -webkit-user-select: none;
}
/* Children of buttons should never absorb the tap — let it bubble */
.m-app button > *,
.m-list-button > * {
    pointer-events: none;
}

/* ===== HEADER ===== */
/* Fixed (not sticky) so it can never scroll out of view, regardless of
   parent overflow / transform / containment quirks. Pinned to viewport
   top with safe-area inset baked into the padding. */
.m-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 9000;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: calc(8px + env(safe-area-inset-top)) 14px 8px;
    background: rgba(15, 23, 42, 0.96);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
    border-bottom: 1px solid rgba(51, 65, 85, 0.5);
    width: 100vw;
    max-width: 100vw;
    box-sizing: border-box;
}
.m-avatar {
    width: 36px;
    height: 36px;
    border-radius: 18px;
    background: linear-gradient(135deg, #22d3ee, #3b82f6);
    color: #0f172a;
    font-weight: 700;
    font-size: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.m-header-titles {
    flex: 1;
    min-width: 0;
}
.m-brand-text {
    font-size: 16px;
    font-weight: 700;
    line-height: 1.1;
}
.m-fresh-label {
    font-size: 11px;
    color: #94a3b8;
    margin-top: 2px;
}
.m-icon-btn {
    width: 40px;
    height: 40px;
    border-radius: 12px;
    background: #1e293b;
    color: #f1f5f9;
    border: 1px solid #334155;
    font-size: 18px;
    cursor: pointer;
    flex-shrink: 0;
}
.m-icon-btn:active { transform: scale(0.94); }

/* ===== CONTENT =====
   Locked to viewport width. Anything wider (charts / tables) gets a
   horizontal scroll *inside* its own container — never on the page. */
.m-content {
    padding: 14px 14px 12px;
    width: 100%;
    max-width: 100vw;
    box-sizing: border-box;
    overflow-x: hidden;
}
.m-content > div {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
}
/* Plotly graphs / Dash tables — let them scroll inside, never push the page */
.m-content .js-plotly-plot,
.m-content .dash-graph,
.m-content .dash-table-container {
    width: 100% !important;
    max-width: 100% !important;
    box-sizing: border-box;
}
.m-content .dash-spreadsheet-container {
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch;
}
.m-section {
    display: flex;
    flex-direction: column;
    gap: 16px;
    /* Opaque background + viewport-filling min-height so the new tab's
       tree fully covers the previous one while Dash is still reconciling
       the render.  Without this, swiping from Products → Customers
       leaves the product list visible beneath the (still-rendering)
       customer list for a few hundred milliseconds. */
    background: #0f172a;
    min-height: calc(100vh - 132px);
    position: relative;
    z-index: 1;
}
/* Belt-and-braces — if React ever leaves two children mounted in
   m-content during a transition, only the most recent one paints. */
.m-content > div ~ div { display: none; }
/* While a section is loading (class added by JS the moment a nav tap
   fires, removed when MutationObserver sees children update) the OLD
   tree fades so the user sees an immediate response instead of a
   frozen products list while waiting on the new render. */
.m-content.is-loading > * {
    opacity: 0.25;
    transition: opacity 80ms ease-out;
    pointer-events: none;
}
.m-section-header {
    margin: 4px 2px 4px;
}
.m-section-title {
    font-size: 18px;
    font-weight: 700;
    color: #f1f5f9;
}
.m-section-sub {
    font-size: 12px;
    color: #94a3b8;
    margin-top: 2px;
}
.m-section-label {
    font-size: 11px;
    font-weight: 700;
    color: #94a3b8;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    margin: 12px 2px 8px;
}

/* ===== CHIP ROW (period / status / brand filters) ===== */
.m-chip-row {
    display: flex;
    flex-wrap: nowrap;
    gap: 8px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding: 4px 2px;
    margin: 0 -2px;
}
.m-chip-row::-webkit-scrollbar { display: none; }
.m-chip {
    flex-shrink: 0;
    padding: 9px 16px;
    border-radius: 18px;
    background: #1e293b;
    color: #94a3b8;
    border: 1px solid #334155;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s ease, color 0.15s ease;
}
.m-chip:active { transform: scale(0.94); }
.m-chip.is-active {
    background: linear-gradient(135deg, #22d3ee, #3b82f6);
    color: #0f172a;
    border-color: transparent;
}

/* ===== KPI GRID (2-col cards) ===== */
.m-kpi-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
}
.m-kpi {
    background: linear-gradient(180deg, #1e293b, #1a253b);
    border: 1px solid rgba(51, 65, 85, 0.6);
    border-radius: 14px;
    padding: 14px;
    min-height: 84px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.m-kpi-full   { grid-column: span 2; }
.m-kpi-half   { grid-column: span 1; }
.m-kpi-label  {
    font-size: 11px;
    color: #94a3b8;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    margin-bottom: 4px;
}
.m-kpi-value  {
    font-size: 22px;
    font-weight: 700;
    line-height: 1.1;
    word-break: break-word;
}
.m-kpi-sub    {
    font-size: 11px;
    color: #94a3b8;
    margin-top: 4px;
    font-weight: 500;
}

/* ===== BIG CARD (Sales hero) ===== */
.m-big-card {
    background: linear-gradient(135deg, rgba(34, 211, 238, 0.10), rgba(59, 130, 246, 0.10));
    border: 1px solid rgba(34, 211, 238, 0.30);
    border-radius: 16px;
    padding: 20px;
}
.m-big-label {
    font-size: 12px;
    color: #94a3b8;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    margin-bottom: 8px;
}
.m-big-value {
    font-size: 36px;
    font-weight: 800;
    line-height: 1.0;
    color: #f1f5f9;
    word-break: break-word;
}
.m-big-delta {
    font-size: 13px;
    font-weight: 600;
    margin-top: 8px;
}

/* ===== SEARCH INPUT ===== */
.m-search-wrap {
    position: relative;
}
.m-search-input {
    width: 100%;
    padding: 12px 14px;
    border-radius: 12px;
    background: #1e293b;
    color: #f1f5f9;
    border: 1px solid #334155;
    font-size: 16px;  /* ≥16px prevents iOS auto-zoom */
    font-family: inherit;
    box-sizing: border-box;
    -webkit-appearance: none;
    appearance: none;
}
.m-search-input::placeholder { color: #64748b; }
.m-search-input:focus { outline: none; border-color: #22d3ee; }

/* ===== FILTER ROW (mobile customer / product filters) =====
   Two side-by-side dropdowns (e.g. Status + "Active in" period).  Native
   <select> via dcc.Dropdown gives the OS picker on iOS, which is way
   faster than a chip-row that re-renders on every tap. */
.m-filter-row {
    display: flex;
    gap: 8px;
    margin-bottom: 4px;
}
.m-filter-cell {
    flex: 1;
    min-width: 0;
}
.m-filter-label {
    display: block;
    color: #94a3b8;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 4px;
}
/* ========================================================================
   .dark-dropdown — historically a "dark theme" override, now folded into
   the SAME white-control + black-text scheme as every other dropdown so
   "everything" is consistently readable (user report: closed value text
   was invisible / white-on-white).  react-select v5 uses Emotion CSS-in-JS
   which wins specificity wars unless we use !important on every internal,
   so we target BOTH v1 (.Select-*) and v5 (.Select__*) hierarchies and set
   -webkit-text-fill-color for Safari.  This block sits LATE in the file so
   it also re-asserts the white menu portal (which renders at body level,
   outside any wrapper).
   ======================================================================== */
.dark-dropdown .Select-control,
.dark-dropdown .Select__control,
.dark-dropdown div.Select__control {
    background-color: #ffffff !important;
    border: 1px solid #cbd5e1 !important;
    color: #0f172a !important;
    box-shadow: none !important;
}
.dark-dropdown .Select-value,
.dark-dropdown .Select-value *,
.dark-dropdown .Select-value-label,
.dark-dropdown .Select__value-container,
.dark-dropdown .Select__value-container *,
.dark-dropdown .Select__single-value,
.dark-dropdown .Select__single-value *,
.dark-dropdown .Select__input,
.dark-dropdown .Select__input input,
.dark-dropdown .Select__input-container,
.dark-dropdown .Select__input-container input {
    color: #0f172a !important;
    background-color: transparent !important;
    -webkit-text-fill-color: #0f172a !important;
}
.dark-dropdown .Select-placeholder,
.dark-dropdown .Select__placeholder {
    color: #64748b !important;
    -webkit-text-fill-color: #64748b !important;
}
.dark-dropdown .Select-arrow,
.dark-dropdown .Select__indicator,
.dark-dropdown .Select__dropdown-indicator,
.dark-dropdown .Select__dropdown-indicator svg {
    color: #475569 !important;
    fill: #475569 !important;
}
.dark-dropdown .Select__indicator-separator { display: none !important; }

/* Multi-select chips inside .dark-dropdown (which is actually our
   white-bg theme) need a contrasting light-blue chip with dark label,
   not the global dark chip — the dark chip + dark label from the
   value-container catch-all above made selected pills invisible. */
.dark-dropdown .Select__multi-value,
.dark-dropdown .Select--multi .Select-value {
    background-color: #e0f2fe !important;
    border: 1px solid #93c5fd !important;
}
.dark-dropdown .Select__multi-value__label,
.dark-dropdown .Select__multi-value__label *,
.dark-dropdown .Select--multi .Select-value-label,
.dark-dropdown .Select--multi .Select-value-label * {
    color: #0f172a !important;
    -webkit-text-fill-color: #0f172a !important;
    background-color: transparent !important;
}
.dark-dropdown .Select__multi-value__remove,
.dark-dropdown .Select--multi .Select-value-icon {
    color: #475569 !important;
    -webkit-text-fill-color: #475569 !important;
}
.dark-dropdown .Select__multi-value__remove:hover {
    background-color: #ef4444 !important;
    color: #ffffff !important;
    -webkit-text-fill-color: #ffffff !important;
}

/* The menu panel often renders into a portal at body-level, so it can't
   inherit any wrapper styles.  These rules are unscoped — they apply
   wherever a .Select__menu lands — white panel, dark options. */
.Select-menu-outer,
.Select__menu,
div.Select__menu {
    background-color: #ffffff !important;
    border: 1px solid #cbd5e1 !important;
    color: #0f172a !important;
}
.Select__menu-list,
div.Select__menu-list { background-color: #ffffff !important; }
.Select-option,
.Select__option,
div.Select__option {
    background-color: #ffffff !important;
    color: #0f172a !important;
    -webkit-text-fill-color: #0f172a !important;
}
.Select__option--is-focused,
div.Select__option--is-focused {
    background-color: #e0f2fe !important;
    color: #0f172a !important;
    -webkit-text-fill-color: #0f172a !important;
}
.Select__option--is-selected,
div.Select__option--is-selected {
    background-color: #3b82f6 !important;
    color: #ffffff !important;
    -webkit-text-fill-color: #ffffff !important;
}

/* Native <html.Select> styled to match the dark theme.  iOS opens its
   native picker on tap — system handles font color in the picker itself.
   The closed control still needs styling so it sits cleanly on dark BG. */
.m-app .m-native-select {
    width: 100%;
    background-color: #1e293b;
    color: #f1f5f9;
    border: 1px solid rgba(51, 65, 85, 0.8);
    border-radius: 10px;
    padding: 11px 36px 11px 12px;
    font-size: 15px;
    font-weight: 600;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    /* Cyan caret arrow on the right side */
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' stroke='%2322d3ee' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 12px 8px;
    cursor: pointer;
}
.m-app .m-native-select:focus {
    outline: none;
    border-color: #22d3ee;
    box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.15);
}
/* iOS / Android render <option> elements in their NATIVE picker UI which
   ignores most CSS, but the option text colour is set directly to handle
   the rare desktop browsers that DO use custom dropdown chrome. */
.m-app .m-native-select option {
    background-color: #1e293b;
    color: #f1f5f9;
    font-size: 15px;
}

/* dcc.Dropdown internals — force dark theme on every react-select piece.
   v3 reports gave WHITE backgrounds on iOS Safari; root cause was that
   (a) the menu panel renders in a portal outside .m-app on some Dash
   builds (so .m-app-scoped selectors didn't reach it), and (b) Emotion
   injects high-specificity rules at runtime that needed broader matches
   to win. We now target both v1 (.Select-*) and v5 (.Select__*) class
   names everywhere AND duplicate the rules unscoped so portal-rendered
   menus are also covered. */
.m-app .m-filter-dropdown .Select-control,
.m-app .m-filter-dropdown .Select__control,
.m-app .m-filter-dropdown div.Select__control {
    background-color: #1e293b !important;
    background-image: none !important;
    border: 1px solid rgba(51, 65, 85, 0.8) !important;
    border-radius: 10px !important;
    min-height: 44px !important;
    color: #f1f5f9 !important;
    box-shadow: none !important;
}
.m-app .m-filter-dropdown .Select-value,
.m-app .m-filter-dropdown .Select-value-label,
.m-app .m-filter-dropdown .Select-placeholder,
.m-app .m-filter-dropdown .Select__value-container,
.m-app .m-filter-dropdown .Select__single-value,
.m-app .m-filter-dropdown .Select__placeholder,
.m-app .m-filter-dropdown .Select__input,
.m-app .m-filter-dropdown .Select__input input {
    color: #f1f5f9 !important;
    font-size: 15px !important;
    font-weight: 600 !important;
    background-color: transparent !important;
}
.m-app .m-filter-dropdown .Select-arrow,
.m-app .m-filter-dropdown .Select__indicator,
.m-app .m-filter-dropdown .Select__dropdown-indicator {
    color: #22d3ee !important;
}
.m-app .m-filter-dropdown .Select__indicator-separator { display: none !important; }
.m-app .m-filter-dropdown .Select-input,
.m-app .m-filter-dropdown .Select__input-container {
    background-color: transparent !important;
    color: #f1f5f9 !important;
}

/* MOBILE ONLY — dark-themed dropdown menu (white text on dark panel).
   2026-05-18: previously these rules were unscoped because of an old
   concern about react-select v5 portal menus rendering outside .m-app.
   That made desktop dropdown options invisible (these rules won the
   cascade and forced light text on light bg).  Desktop uses
   react-select v1 which renders menus INLINE inside the dropdown div,
   so .m-app scoping works correctly on both platforms.  If you ever
   add a v5 mobile menu that portals to body, anchor it with a class
   like .m-filter-dropdown-menu and re-scope this block. */
.m-app .Select-menu-outer,
.m-app .Select__menu,
.m-app div.Select__menu {
    background-color: #1e293b !important;
    background-image: none !important;
    border: 1px solid rgba(51, 65, 85, 0.9) !important;
    border-radius: 10px !important;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55) !important;
    color: #f1f5f9 !important;
}
.m-app .Select__menu-list,
.m-app div.Select__menu-list {
    background-color: #1e293b !important;
    padding: 4px 0 !important;
}
.m-app .Select-option,
.m-app .Select__option,
.m-app div.Select__option {
    background-color: #1e293b !important;
    color: #f1f5f9 !important;
    -webkit-text-fill-color: #f1f5f9 !important;
    font-size: 15px !important;
    padding: 12px 14px !important;
    cursor: pointer !important;
}
.m-app .Select-option.is-focused,
.m-app .Select__option--is-focused,
.m-app div.Select__option--is-focused {
    background-color: rgba(34, 211, 238, 0.18) !important;
    color: #ffffff !important;
    -webkit-text-fill-color: #ffffff !important;
}
.m-app .Select-option.is-selected,
.m-app .Select__option--is-selected,
.m-app div.Select__option--is-selected {
    background-color: #22d3ee !important;
    color: #0f172a !important;
    -webkit-text-fill-color: #0f172a !important;
}

/* ===== LIST (uniform tappable rows) ===== */
.m-result-count {
    font-size: 12px;
    color: #94a3b8;
    padding: 4px 2px;
}
.m-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.m-list-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    background: #1e293b;
    border: 1px solid rgba(51, 65, 85, 0.6);
    border-radius: 12px;
    color: #f1f5f9;
    text-align: left;
    width: 100%;
}
/* Buttons (= drillable rows) get the tactile treatment */
button.m-list-row,
.m-list-button {
    cursor: pointer;
    transition: background 0.15s ease, transform 0.1s ease;
    font-family: inherit;
    font-size: inherit;
}
.m-list-button:active {
    transform: scale(0.98);
    background: #243149;
}
.m-list-main {
    flex: 1;
    min-width: 0;
}
.m-list-title {
    font-size: 14px;
    font-weight: 600;
    color: #f1f5f9;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.m-list-sub {
    font-size: 12px;
    color: #94a3b8;
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.m-list-right {
    font-size: 14px;
    font-weight: 700;
    color: #22d3ee;
    flex-shrink: 0;
    margin-left: 6px;
}
.m-list-badge {
    display: inline-block;
    padding: 3px 8px;
    border-radius: 8px;
    color: #0f172a;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    flex-shrink: 0;
}
.m-list-chev {
    font-size: 18px;
    color: #475569;
    margin-left: 4px;
    flex-shrink: 0;
    line-height: 1;
}
.m-list-row:not(.m-list-button) .m-list-chev { display: none; }

/* ===== EMPTY STATES ===== */
.m-empty {
    padding: 24px 16px;
    text-align: center;
    color: #94a3b8;
    font-size: 13px;
    background: rgba(30, 41, 59, 0.5);
    border-radius: 12px;
    border: 1px dashed rgba(51, 65, 85, 0.6);
}
.m-empty-state {
    padding: 40px 16px;
    background: rgba(30, 41, 59, 0.4);
    border: 1px solid rgba(51, 65, 85, 0.6);
    border-radius: 14px;
}
.m-loading {
    padding: 60px 16px;
    text-align: center;
    color: #94a3b8;
}

/* ===== A-Z INDEXED CUSTOMER LIST (iOS Settings → Apps style) =====
   Index strip is FIXED on the right edge of the viewport so it's always
   reachable while scrolling the customer list. The current letter (the
   section currently scrolled into the top of the viewport) gets a cyan
   pill highlight, updated by an IntersectionObserver in index_string. */
/* No global scroll-behavior:smooth — iOS Safari blocks click events while a
   smooth scroll is animating, which broke rapid A-Z letter taps.  The A-Z
   index now uses instant window.scrollTo from JS instead. */

.m-az-wrap {
    position: relative;
    display: block;       /* index is fixed-positioned, no flex needed */
}
.m-az-list {
    width: 100%;
    padding-right: 28px;  /* leave a strip on the right so cards don't
                              hide behind the floating index */
}
.m-letter-header {
    position: sticky;
    top: calc(56px + env(safe-area-inset-top));  /* under fixed header */
    /* When the user taps an A-Z letter, scrollIntoView aligns the top edge
       of this header with the top of the viewport.  Without scroll-margin-top
       the header would land BEHIND the 56px fixed app header.  Adding the
       margin tells the browser to leave that much space above the target. */
    scroll-margin-top: calc(64px + env(safe-area-inset-top));
    z-index: 5;
    background: rgba(15, 23, 42, 0.95);
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
    color: #94a3b8;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    padding: 8px 12px;
    border-radius: 6px;
    margin: 8px 0 4px;
}
/* Floating fixed strip on the right edge, vertically centred */
.m-az-index {
    position: fixed;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
    max-height: calc(100vh - 200px);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 6px 4px;
    z-index: 800;
    background: rgba(15, 23, 42, 0.55);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    border-radius: 14px;
    border: 1px solid rgba(51, 65, 85, 0.4);
}
.m-az-letter {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 16px;
    font-size: 10px;
    font-weight: 700;
    color: #22d3ee;
    text-decoration: none;
    border-radius: 4px;
    cursor: pointer;
    -webkit-tap-highlight-color: rgba(34, 211, 238, 0.4);
    user-select: none;
    -webkit-user-select: none;
    transition: background 0.15s ease, color 0.15s ease;
}
.m-az-letter:active {
    background: #22d3ee;
    color: #0f172a;
}
.m-az-letter.is-dim {
    color: #475569;
    cursor: default;
    pointer-events: none;
}
/* Current letter — set by JS when its section header is at the top */
.m-az-letter.is-current {
    background: #22d3ee;
    color: #0f172a;
    font-size: 11px;
    transform: scale(1.15);
}

/* Infinite-scroll sentinel — sits at the bottom of the customer list.
   Three triggers all push +80 into m-cust-shown: scroll listener (40%
   of doc), IntersectionObserver (within 600px of viewport), or
   manual tap on the sentinel itself.  Visual is a tappable card so
   the user always has a backup if auto-load gets eaten. */
.m-cust-sentinel {
    padding: 18px 14px;
    text-align: center;
    border: 1px dashed rgba(34, 211, 238, 0.45);
    border-radius: 14px;
    background: rgba(34, 211, 238, 0.06);
    margin: 12px 0;
    cursor: pointer;
    -webkit-tap-highlight-color: rgba(34, 211, 238, 0.25);
    user-select: none;
    -webkit-user-select: none;
    transition: background 0.15s ease, transform 0.1s ease;
}
.m-cust-sentinel:active {
    background: rgba(34, 211, 238, 0.18);
    transform: scale(0.985);
}
.m-cust-sentinel-title {
    color: #22d3ee;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.3px;
}
.m-cust-sentinel-sub {
    color: #94a3b8;
    font-size: 11px;
    font-weight: 500;
    margin-top: 3px;
}

/* ===== ACCORDION (native <details>/<summary>) ===== */
.m-accordion {
    background: #1e293b;
    border: 1px solid rgba(51, 65, 85, 0.6);
    border-radius: 14px;
    overflow: hidden;
}
.m-acc-summary {
    list-style: none;
    cursor: pointer;
    padding: 14px 16px;
    display: flex;
    align-items: center;
    gap: 10px;
    user-select: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: rgba(34, 211, 238, 0.18);
}
.m-acc-summary::-webkit-details-marker { display: none; }
.m-acc-summary::marker { content: ""; }
.m-acc-title {
    flex: 1;
    font-size: 14px;
    font-weight: 700;
    color: #f1f5f9;
}
.m-acc-badge {
    font-size: 11px;
    color: #94a3b8;
    font-weight: 600;
}
.m-acc-marker {
    font-size: 22px;
    color: #475569;
    transform: rotate(90deg);
    transition: transform 0.18s ease;
    line-height: 0.6;
    margin-right: 2px;
}
.m-accordion[open] .m-acc-marker { transform: rotate(-90deg); }
.m-acc-body {
    padding: 4px 14px 14px;
    border-top: 1px solid rgba(51, 65, 85, 0.4);
}

/* ===== DETAIL ROWS (Contact card key/value pairs) ===== */
.m-detail-list {
    display: flex;
    flex-direction: column;
}
.m-detail-row {
    display: grid;
    grid-template-columns: 92px 1fr auto;
    align-items: center;
    column-gap: 12px;
    padding: 10px 0;
    border-bottom: 1px solid rgba(51, 65, 85, 0.35);
}
.m-detail-row:last-child { border-bottom: none; }
.m-detail-label {
    color: #94a3b8;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.4px;
}
.m-detail-value {
    color: #f1f5f9;
    font-size: 14px;
    font-weight: 500;
    word-break: break-word;
    overflow-wrap: anywhere;
    min-width: 0;
}
.m-detail-action {
    color: #22d3ee;
    font-size: 12px;
    font-weight: 700;
    text-decoration: none;
    padding: 6px 12px;
    border-radius: 8px;
    background: rgba(34, 211, 238, 0.12);
    border: 1px solid rgba(34, 211, 238, 0.3);
    flex-shrink: 0;
}
.m-detail-action:active { opacity: 0.8; }

/* ===== QUICK SEARCH BUTTON ICON =====
   Magnifying-glass icon, drawn via CSS mask so colour inherits from the
   parent button's `color`. */
.m-scan-icon {
    display: inline-block;
    width: 22px; height: 22px;
    background-color: currentColor;
    -webkit-mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
    -webkit-mask-size: contain; mask-size: contain;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center; mask-position: center;
}

/* ===== SEARCH MODAL (replaces QR scanner overlay in v2.8.0) ===== */
.m-search-modal-input {
    width: 100%;
    padding: 14px 16px;
    border-radius: 12px;
    background: #1e293b;
    color: #f1f5f9;
    border: 1px solid #475569;
    font-size: 16px;
    font-family: inherit;
    box-sizing: border-box;
    outline: none;
    margin-bottom: 8px;
}
.m-search-modal-input:focus {
    border-color: #22d3ee;
    box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.15);
}
.m-search-results {
    margin-top: 10px;
    max-height: calc(100vh - 220px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding-bottom: env(safe-area-inset-bottom);
}
.m-search-result-row {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 4px !important;
    padding: 12px 14px !important;
    text-align: left !important;
}
.m-search-result-chip {
    display: inline-block;
    color: #0f172a;
    font-size: 9px;
    font-weight: 800;
    letter-spacing: 0.6px;
    padding: 2px 8px;
    border-radius: 8px;
    flex-shrink: 0;
    line-height: 1.4;
}

/* "Search last 3 years / 5 years / all catalogue" fallback buttons —
   shown both when the empty-state has zero hits and as a small footer
   under existing results so the user can widen without retyping. */
.m-search-fallback {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 10px 0 4px;
}
.m-search-fallback-footer {
    border-top: 1px dashed rgba(51, 65, 85, 0.5);
    margin-top: 12px;
    padding-top: 12px;
    align-items: center;
}
.m-search-fallback-label {
    color: #94a3b8;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    margin-right: 4px;
}
.m-search-fallback-btn {
    appearance: none;
    -webkit-appearance: none;
    background: rgba(34, 211, 238, 0.12);
    border: 1px solid rgba(34, 211, 238, 0.4);
    color: #22d3ee;
    font-size: 13px;
    font-weight: 700;
    padding: 9px 14px;
    border-radius: 22px;
    cursor: pointer;
    -webkit-tap-highlight-color: rgba(34, 211, 238, 0.3);
}
.m-search-fallback-btn:active {
    background: #22d3ee;
    color: #0f172a;
    transform: scale(0.97);
}

/* Full-screen scanner overlay. Hidden by default; shown when JS sets
   body.m-scan-open. Uses position:fixed inset:0 so it covers everything,
   including the bottom nav and any sheets. */
.m-scan-sheet {
    position: fixed;
    inset: 0;
    background: #000;
    z-index: 9900;
    display: flex;
    flex-direction: column;
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.18s ease, visibility 0.18s ease;
}
body.m-scan-open .m-scan-sheet {
    visibility: visible;
    opacity: 1;
}
.m-scan-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: calc(12px + env(safe-area-inset-top)) 16px 12px;
    color: #f1f5f9;
    background: rgba(15, 23, 42, 0.85);
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
    z-index: 2;
}
.m-scan-title { font-size: 16px; font-weight: 700; }
.m-scan-close-btn {
    background: rgba(30, 41, 59, 0.85);
    color: #f1f5f9;
    border: 1px solid #334155;
    border-radius: 10px;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}
.m-scan-video {
    flex: 1;
    min-height: 0;  /* let html5-qrcode size its own video element */
    width: 100%;
    background: #000;
    overflow: hidden;
    position: relative;
}
/* html5-qrcode injects <video> + a scan-region overlay. Constrain them. */
.m-scan-video > div, .m-scan-video > video {
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
}
.m-scan-bottom {
    padding: 14px 16px calc(20px + env(safe-area-inset-bottom));
    background: rgba(15, 23, 42, 0.92);
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
    color: #f1f5f9;
}
.m-scan-input {
    width: 100%;
    padding: 12px 14px;
    border-radius: 12px;
    background: #0f172a;
    color: #f1f5f9;
    border: 1px solid #334155;
    font-size: 16px;  /* >= 16 prevents iOS auto-zoom */
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    box-sizing: border-box;
    outline: none;
}
.m-scan-input:focus { border-color: #22d3ee; }
.m-scan-status {
    margin-top: 8px;
    font-size: 12px;
    color: #94a3b8;
    text-align: center;
}

/* Hide the scanner sheet entirely on desktop unless explicitly opened
   (the desktop scan button uses the same sheet). */
@media (min-width: 769px) {
    /* still position:fixed; CSS shows it via body.m-scan-open same as mobile */
}

/* ===== STOCK-BY-LOCATION ACCORDION ===== */
.m-loc-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.m-loc-card {
    background: #0f172a;
    border: 1px solid rgba(51, 65, 85, 0.6);
    border-radius: 10px;
    padding: 10px 12px;
}
.m-loc-card.m-loc-total {
    background: rgba(34, 211, 238, 0.08);
    border-color: rgba(34, 211, 238, 0.30);
    margin-top: 4px;
}
.m-loc-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 4px;
}
.m-loc-name {
    font-size: 13px;
    font-weight: 700;
    color: #f1f5f9;
    word-break: break-word;
    min-width: 0;
    flex: 1;
}
.m-loc-metrics {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    font-size: 11px;
    margin-top: 4px;
}
.m-loc-chip {
    color: #f1f5f9;
    white-space: nowrap;
}

/* ===== MAP ACCORDION ===== */
.m-map-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 10px;
}
.m-map-link {
    display: block;
    text-align: center;
    padding: 12px 14px;
    background: #1e293b;
    color: #22d3ee;
    text-decoration: none;
    border: 1px solid #334155;
    border-radius: 12px;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
    width: 100%;
    box-sizing: border-box;
}
.m-map-link:active {
    transform: scale(0.98);
    opacity: 0.85;
}
/* Primary CTA — "Use My Location" before GPS is granted */
.m-map-link-primary {
    background: linear-gradient(135deg, #22d3ee, #3b82f6) !important;
    color: #0f172a !important;
    border-color: transparent !important;
    box-shadow: 0 4px 12px rgba(34,211,238,0.25);
}

/* GPS status banner above the map */
.m-gps-banner {
    background: rgba(34, 197, 94, 0.10);
    border: 1px solid rgba(34, 197, 94, 0.35);
    color: #f1f5f9;
    border-radius: 12px;
    padding: 10px 14px;
    font-size: 13px;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
}
.m-gps-banner.m-gps-error {
    background: rgba(239, 68, 68, 0.10);
    border-color: rgba(239, 68, 68, 0.35);
    color: #fecaca;
}

/* ===== DRILL VIEW =====
   The drill section paints its own opaque background so that during the brief
   transition between a list view (Products / Customers) and a drill-in view
   (customer profile / product detail), the OLD tree can never bleed through.
   Without this you can momentarily see product cards "on top of" the new
   customer profile while Plotly is still initialising. */
.m-drill-section {
    padding-top: 0;
    background: #0f172a;       /* same as page background — full opaque */
    position: relative;
    z-index: 1;
    min-height: calc(100vh - 132px);  /* fill viewport so list peeks die */
}
.m-back-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 8px 14px 8px 8px;
    background: transparent;
    color: #22d3ee;
    border: none;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    margin: 0 0 4px -4px;
}
.m-back-btn:active { opacity: 0.7; }

/* Top row of a drill page: back button on the left, action button(s)
   on the right.  Currently used by the customer profile to host the
   "PDF Snapshot" button next to Back, mirroring the desktop's print
   button placement.  Stretches across the page so the PDF chip pins
   to the trailing edge. */
.m-back-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    margin-bottom: 4px;
}
.m-pdf-btn {
    appearance: none;
    -webkit-appearance: none;
    background: rgba(59, 130, 246, 0.18);
    border: 1px solid rgba(59, 130, 246, 0.5);
    color: #60a5fa;
    font-size: 13px;
    font-weight: 700;
    padding: 7px 12px;
    border-radius: 22px;
    cursor: pointer;
    -webkit-tap-highlight-color: rgba(59, 130, 246, 0.3);
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.m-pdf-btn:active {
    background: #3b82f6;
    color: #fff;
    transform: scale(0.97);
}
.m-pdf-btn-label { font-size: 12px; font-weight: 700; }
.m-profile-header {
    background: linear-gradient(135deg, rgba(34, 211, 238, 0.10), rgba(59, 130, 246, 0.10));
    border: 1px solid rgba(34, 211, 238, 0.20);
    border-radius: 14px;
    padding: 16px;
    margin-bottom: 4px;
}
.m-profile-title {
    font-size: 19px;
    font-weight: 700;
    color: #f1f5f9;
    word-break: break-word;
    margin-bottom: 6px;
}
.m-profile-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px;
}

/* ===== BOTTOM NAV =====
   Fixed at the viewport bottom — never moves on scroll. Same z-tier as
   header (9000) so it always sits above sliding content but below sheets. */
.m-bottom-nav {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    display: flex;
    background: rgba(15, 23, 42, 0.96);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
    border-top: 1px solid rgba(51, 65, 85, 0.6);
    padding: 0 0 env(safe-area-inset-bottom);
    z-index: 9000;
    box-shadow: 0 -2px 12px rgba(0,0,0,0.3);
    width: 100vw;
    max-width: 100vw;
    box-sizing: border-box;
}
.m-nav-btn {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 8px 4px 6px;
    min-height: 56px;
    background: transparent;
    border: none;
    color: #94a3b8;
    font-size: 11px;
    font-weight: 600;
    cursor: pointer;
    transition: color 0.15s ease, transform 0.1s ease;
    position: relative;
}
.m-nav-btn:active { transform: scale(0.92); }
.m-nav-btn.is-active { color: #22d3ee; }
.m-nav-btn.is-active::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 28px;
    height: 3px;
    border-radius: 0 0 3px 3px;
    background: #22d3ee;
}
.m-nav-btn span:last-child {
    line-height: 1.1;
    margin-top: 2px;
}

/* ===== FILTER SHEET ===== */
.m-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
    z-index: 9600;
}
.m-filter-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    transform: translateY(100%);
    transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
    background: #0f172a;
    border-top-left-radius: 22px;
    border-top-right-radius: 22px;
    border-top: 1px solid #334155;
    box-shadow: 0 -10px 30px rgba(0,0,0,0.5);
    padding: 6px 16px calc(20px + env(safe-area-inset-bottom));
    max-height: 85vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    z-index: 9700;
    visibility: hidden;
}
body.m-sheet-open .m-backdrop {
    opacity: 1;
    pointer-events: auto;
}
body.m-sheet-open .m-filter-sheet {
    transform: translateY(0);
    visibility: visible;
}
.m-sheet-handle {
    width: 40px; height: 5px;
    border-radius: 3px;
    background: #475569;
    margin: 8px auto 12px;
}
.m-sheet-close-btn {
    position: absolute;
    top: 12px; right: 14px;
    width: 36px; height: 36px;
    border-radius: 18px;
    background: #1e293b;
    color: #f1f5f9;
    border: 1px solid #334155;
    font-size: 22px;
    cursor: pointer;
    line-height: 1;
    display: flex; align-items: center; justify-content: center;
    z-index: 30;
}
.m-sheet-title {
    font-size: 17px;
    font-weight: 700;
    color: #f1f5f9;
    padding: 0 4px 8px;
}
.m-account-card {
    background: #1e293b;
    border: 1px solid rgba(51, 65, 85, 0.6);
    border-radius: 12px;
    padding: 14px;
    margin: 16px 0 12px;
}
.m-account-name {
    font-size: 15px;
    font-weight: 700;
    color: #f1f5f9;
}
.m-account-role {
    font-size: 12px;
    color: #94a3b8;
    margin-top: 4px;
}
.m-logout-btn {
    width: 100%;
    padding: 14px;
    border-radius: 12px;
    background: #1e293b;
    color: #fca5a5;
    border: 1px solid #7f1d1d;
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
    margin-bottom: 10px;
}
.m-logout-btn:active { opacity: 0.85; }
.m-sheet-done-btn {
    width: 100%;
    padding: 14px;
    border-radius: 12px;
    background: linear-gradient(135deg, #22d3ee, #3b82f6);
    color: #0f172a;
    border: none;
    font-size: 16px;
    font-weight: 700;
    cursor: pointer;
}
.m-sheet-done-btn:active { opacity: 0.9; }
