/* ========================================================
   MOBILE COMPATIBILITY LAYER
   ========================================================
   THE ONE RULE FOR THIS FILE: every declaration that changes an existing
   look must sit inside a `max-width` media query. This stylesheet is
   linked LAST — after layout/main.css — so anything unscoped here would
   silently win on desktop too, which is exactly what this file must never
   do. The only unscoped rules below define BRAND-NEW classes (.tab-nav,
   .entry-picker, .chart-frame sizes) that no desktop rule touches.

   Breakpoints, and why these three:
     <= 1024px   the sidebar has already collapsed to a drawer (main.css),
                 so the page is a single column and gets tablet padding.
     <=  767px   the bottom nav appears (main.css) and the viewport is a
                 phone held one-handed. This is where the real work is.
     <=  400px   small/older handsets — a 320-360px viewport where a
                 two-up grid stops fitting.

   WHY THIS FILE EXISTS AT ALL
   The app was authored desktop-first with mobile patches sprinkled through
   six stylesheets and five <style> blocks. Collecting the mobile rules in
   one place means a future "why is this squashed on a phone?" has exactly
   one file to read, and a mistake here can never leak onto the desktop
   layout that the office actually uses for reporting.
   ======================================================== */


/* ========================================================
   1. NEW COMPONENTS (shared by desktop and mobile)
   ========================================================
   These classes did not exist before, so defining them unscoped changes
   nothing that was already rendering. */

/* The grid/flex item minimum that briefly lived here in v2.7.0 is in
   layout/grid.css, and it belongs there. `.panel` and `.card` are styled for
   the desktop in other files, so an UNSCOPED rule naming them in this file is
   exactly what test_mobile_css_rules_are_all_scoped_to_a_max_width forbids —
   and the test is right: the safety property of this whole stylesheet is that
   nothing in it outside a max-width query can reach a desktop. A statement
   about how CSS Grid sizes its items is a grid-system rule, so it now sits
   beside the .grid definition. The phone-only half of the original rule is
   still below, unchanged. */

/* ---- Chart frames ----
   Chart.js needs a parent with a RESOLVED height to fill; a responsive
   canvas in an auto-height box grows without bound on every redraw. The
   dashboard used to carry `style="height: 340px; position: relative;"`
   inline on each wrapper, which meant a mobile override could only win
   with !important. The heights are now classes, so the phone can simply
   restate them. */
.chart-frame { position: relative; width: 100%; height: 340px; }
.chart-frame--sm { height: 300px; }
.chart-frame--md { height: 320px; }
/* v2.2.0. The panels that were split out of shared rows — Sales Breakdown,
   Price Realisation, and the two commodity charts — are full width now, and a
   340px frame across 1200px is a very wide, very short strip. It also matters
   for Price Realisation specifically, whose y axis deliberately does not start
   at zero: a short frame compresses exactly the drift that panel exists to
   show. */
.chart-frame--lg { height: 420px; }
/* v2.7.0 — for the Sales Breakdown doughnut, and sized from a measurement
   rather than a guess. A doughnut is sized by the SHORTER dimension of its
   chart area, so a 420px frame across 1,042px drew a 354px circle and left
   688px of the panel — two thirds of it — empty. Height is the only
   dimension that can make it bigger, and the .breakdown-layout below turns
   the width that is left over into a table instead of whitespace. */
.chart-frame--xl { height: 560px; }
.chart-skeleton--xl { height: 560px; }

/* ---- Sales Breakdown: the chart beside its figures (v2.7.0) ----
   The panel has been full width since v2.2.0, so there was no column span
   left to widen — the component was small INSIDE a container that was
   already as wide as the page. Two things were wrong instead:

     * the doughnut was sized by the frame's height, so all the extra width
       went to empty canvas; and
     * the exact figures sat in a collapsed <details> underneath, which is
       the one place on the panel where there genuinely was no room.

   Splitting the panel puts the table in the space the circle could never
   use. The chart column is the larger of the two because it is what the
   panel is for; the figures column is capped so a long item name cannot
   take width back off the chart. */
.breakdown-layout {
    display: grid;
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    gap: var(--space-5);
    align-items: start;
}
.breakdown-layout__figures {
    /* Its own scroll at the point the item list outgrows the chart beside
       it, rather than stretching the panel. Twelve items is the most this
       mill sells and they fit; a thirteenth must not push the doughnut
       taller. */
    max-height: 560px;
    overflow-y: auto;
}
.breakdown-layout__figures table {
    margin: 0;
    /* Beats `.responsive-wrapper table { min-width: 500px }` in grid.css.
       Same specificity (0,1,1) — this wins on ORDER, because mobile.css is
       last in the bundle. That 500px floor is right for a records table in a
       full-width wrapper and wrong here: the figures column is ~440px, so
       the table overflowed into its own horizontal scroller and the SHARE
       column — the one a reader wants beside a doughnut, because a doughnut
       is a picture of proportion — was scrolled off the right-hand edge
       where nothing suggested it existed. */
    min-width: 0;
}
/* Enough for "Untreated Transmission Poles" to wrap to two lines instead of
   taking width off the figures beside it. */
.breakdown-layout__figures td:first-child,
.breakdown-layout__figures th:first-child { white-space: normal; }
.breakdown-layout__figures thead th {
    /* The header stays put while the rows scroll under it. A scrolling
       figures table whose column headings have gone is a column of numbers
       with no units. */
    position: sticky; top: 0;
    background: var(--surface);
    z-index: 1;
}

/* The grey shimmer shown while a chart is being built. Must match the
   frame it replaces or the page jumps when the chart appears. */
.chart-skeleton { height: 340px; }

/* ---- Tab navigation wrapper ----
   `.tab-nav` holds BOTH representations of one tab group: the horizontal
   button strip (desktop) and a native <select> (phone). Only one is ever
   displayed. They are kept in sync by static/js/main.js, and both drive
   the same openTab() function, so the URL hash, the DataTables redraw and
   the linked "recent records" panes behave identically either way.

   A native <select> was chosen over a custom dropdown deliberately: it
   opens the phone's own full-height picker, which is operable with gloves,
   readable in sunlight, and needs no focus-trap or outside-click code. */
.tab-select-wrap { display: none; }

/* ---- Entry form card picker ----
   Hidden entirely on desktop, where the seven tabs fit on one line. */
.entry-picker { display: none; }
.entry-back { display: none; }


/* ========================================================
   2. TABLET AND BELOW  (<= 1024px)
   ========================================================
   The sidebar is a drawer from here down, so the content column is the
   full width of the screen. */
@media (max-width: 1024px) {

    /* The phone half of the item-minimum rule. `.grid > *` and `.flex > *`
       are handled unscoped in layout/grid.css since v2.7.0; these two are
       not grid or flex items in every place they appear, and letting a
       PANEL shrink below its content is a small-screen decision rather than
       a correctness one. */
    .panel,
    .card { min-width: 0; }

    /* main.css already sets `padding: 80px 20px 20px`. Only the horizontal
       value is retuned here, via the spacing scale rather than a literal,
       and the top value is derived from the actual header height instead
       of the hardcoded 80 (which was 20px of guesswork on a notched
       phone, where the header is 60px PLUS the safe-area inset). */
    .main-content {
        padding-left: var(--space-3);
        padding-right: var(--space-3);
        padding-top: calc(60px + env(safe-area-inset-top, 0px) + var(--space-3));
    }

    /* Panels lose the desktop's 28px inset. At 28px a side, a 360px screen
       spent 32% of its width on padding before any content was drawn. */
    .panel, .card {
        padding: var(--space-3);
        margin-bottom: var(--space-4);
    }
}


/* ========================================================
   3. PHONE  (<= 767px)
   ======================================================== */
@media (max-width: 767px) {

    /* ---- 3.1 The twelve-column grid ----
       THE SINGLE WORST MOBILE BUG IN THE APP, and the reason the dashboard
       scrolled sideways. `.grid-cols-12` is defined UNSCOPED in grid.css,
       so a phone got `repeat(12, 1fr)` with `gap-5` (2.5rem). Eleven gaps
       at 40px is 440px of pure gutter — already wider than a 360px phone
       before a single pixel of content. Everything spanning those columns
       inherited that overflow.

       Below 768px there is only ever room for one column, so the track
       list collapses and every span becomes automatic. */
    .grid-cols-12,
    .grid-cols-6,
    .grid-cols-4 { grid-template-columns: 1fr; }

    [class*="col-span-"] { grid-column: auto; }

    /* Gaps scale down with the screen. `gap-5` (2.5rem) between stacked
       full-width panels is a third of a phone screen of empty space. */
    .gap-4, .gap-5, .gap-6 { gap: var(--space-3); }

    /* ---- 3.2 Page chrome ---- */
    .main-content {
        padding-left: var(--space-2);
        padding-right: var(--space-2);
    }

    .page-title { margin-bottom: var(--space-4); }

    .panel, .card {
        border-radius: var(--radius-md);
        padding: var(--space-3);
        margin-bottom: var(--space-3);
    }

    /* ---- 3.3 Typography ----
       Page headings drop a step. `clamp()` is not used because the sizes
       here are set by utility classes (.text-3xl, .text-2xl) that the
       templates apply directly. */
    h1, .text-3xl { font-size: 1.375rem; line-height: 1.25; }
    h2, .text-2xl { font-size: 1.25rem; }
    h3 { font-size: 1rem; }

    /* A heading with a right-aligned meta label ("Weekly", "Monthly YTD")
       must be allowed to wrap rather than squeeze the label off-screen. */
    .panel h3 { flex-wrap: wrap; }
    .panel h3 .ml-auto { margin-left: 0; width: 100%; }

    /* ---- 3.4 Forms ----
       Inputs stay at 16px (set in main.css) on purpose: iOS Safari zooms
       the whole page in when a field smaller than 16px receives focus, and
       it never zooms back out. */
    .fieldset-group {
        padding: var(--space-3) var(--space-2);
        margin-bottom: var(--space-3);
        border-radius: var(--radius-md);
    }
    .fieldset-group legend {
        font-size: 13px;
        padding: 0 var(--space-1);
    }
    .form-group { margin-bottom: var(--space-3); }

    /* Every action button at the foot of a form goes full width. A 44px
       target in the middle of a row is reachable; a full-width one is
       unmissable, which matters when the other hand is holding a docket. */
    .form-actions .btn,
    .form-actions .btn-action { flex: 1 1 100%; }
    .form-actions { gap: var(--space-2); }

    /* ---- 3.5 Toolbars (Records) ----
       `justify-content: space-between` on a two-item toolbar put Delete on
       the far left and Import/Export on the far right with a canyon
       between them. Stacked and full-width instead. */
    .toolbar {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-2);
    }
    .toolbar-right {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: var(--space-2);
    }
    .toolbar .btn-action { width: 100%; font-size: 12px; padding: 0 var(--space-2); }

    /* ---- 3.6 Tables ----
       Two different kinds of table live in this app and they need opposite
       treatment.

       (a) DataTables with the Responsive extension COLLAPSE their surplus
           columns into an expandable child row — but only if they are
           allowed to measure themselves against the real screen width.
           `.responsive-wrapper table { min-width: 500px }` from grid.css
           lied to them: the plugin saw 500px of room on a 360px phone and
           kept every column, so the table overflowed AND the collapse
           feature silently never triggered. Releasing the min-width is
           what makes the responsive plugin actually work. */
    .responsive-wrapper table.dataTable { min-width: 0; }

    /*   (b) Plain tables (audit log, user list, waste breakdown) have no
           such plugin, so they keep a min-width and scroll horizontally
           inside their own box — which is correct: the surrounding page
           must not scroll, only the table. */
    .responsive-wrapper {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        /* A hairline on the right edge tells the reader there is more
           table out of view; without it a cut-off column looks like a
           rendering fault. */
        background:
            linear-gradient(to right, var(--surface) 30%, rgba(0,0,0,0)),
            linear-gradient(to right, rgba(0,0,0,0), var(--surface) 70%) 100% 0,
            radial-gradient(farthest-side at 0 50%, rgba(0,0,0,.12), rgba(0,0,0,0)),
            radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.12), rgba(0,0,0,0)) 100% 0;
        background-repeat: no-repeat;
        background-size: 32px 100%, 32px 100%, 12px 100%, 12px 100%;
        background-attachment: local, local, scroll, scroll;
    }

    table.dataTable thead th { padding: 10px 8px !important; font-size: 11px; }
    table.dataTable tbody td { padding: 10px 8px !important; font-size: 13px; }

    /* DataTables' own controls are laid out with floats, which on a narrow
       screen overlap each other. Stacked and full width. */
    .dataTables_wrapper .dataTables_length,
    .dataTables_wrapper .dataTables_filter {
        float: none;
        text-align: left;
        justify-content: flex-start;
        width: 100%;
        margin-bottom: var(--space-2);
    }
    .dataTables_wrapper .dataTables_filter { display: block; }
    .dataTables_wrapper .dataTables_filter label { display: flex; align-items: center; width: 100%; }
    .dataTables_wrapper .dataTables_filter input {
        width: 100% !important;
        flex: 1;
        min-height: 40px;
    }
    .dataTables_wrapper .dataTables_info,
    .dataTables_wrapper .dataTables_paginate {
        float: none;
        text-align: center;
        width: 100%;
        padding-top: var(--space-2);
    }
    .dataTables_wrapper .dataTables_paginate .paginate_button {
        padding: 6px 10px !important;
        min-width: 36px;
    }

    /* ---- 3.7 Modals become bottom sheets ----
       A centred dialog on a phone sits under the keyboard the moment a
       field is focused. Anchoring to the bottom keeps the first field and
       the submit button in the visible half of the screen. */
    .modal { align-items: flex-end; padding: 0; }
    .modal-content {
        margin: 0;
        max-width: 100%;
        max-height: 88vh;
        overflow-y: auto;
        border-radius: var(--radius-xl) var(--radius-xl) 0 0;
        padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
    }
    .confirm-dialog { max-height: 85vh; overflow-y: auto; }

    /* ---- 3.8 Date filter (Records) ---- */
    .date-filter-field { flex: 1 1 100%; min-width: 0; }
    .date-filter-actions { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); width: 100%; }
    .date-filter-actions .btn { width: 100%; }
    .date-filter-presets { gap: var(--space-1); }
    .preset-btn { flex: 1 1 calc(50% - var(--space-1)); }
}


/* ========================================================
   4. TAB GROUPS -> NATIVE DROPDOWN  (<= 767px)
   ========================================================
   Applies to Records and the Admin Panel. The horizontal strip is
   replaced, not merely made scrollable: a scrolling strip still hides its
   last option behind a gesture nobody discovers, and on Records the last
   option is Maintenance, which operators open every day.
   ======================================================== */
@media (max-width: 767px) {
    .tab-nav .tab-container { display: none; }

    .tab-select-wrap {
        display: block;
        margin-bottom: var(--space-3);
    }

    .tab-select-label {
        display: block;
        font-size: 11px;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.06em;
        color: var(--text-muted);
        margin-bottom: var(--space-1);
    }

    /* Styled as a prominent control rather than a plain field: it is the
       primary navigation for the page it sits on. */
    .tab-select {
        width: 100%;
        min-height: 48px;
        font-size: 16px;         /* 16px or iOS zooms on focus */
        font-weight: 600;
        color: var(--text-main);
        background: var(--input-bg);
        border: 1px solid var(--brand);
        border-radius: var(--radius-md);
        padding: 10px var(--space-3);
    }
    .tab-select:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
}


/* ========================================================
   5. DATA ENTRY: CARD PICKER -> FOCUSED FORM  (<= 767px)
   ========================================================
   Seven forms behind seven tabs is roughly 700px of tab strip on a 360px
   screen. Scrolled horizontally, four of the seven are invisible and the
   operator has no way to know they exist.

   The replacement is a two-step drill-in:

     Step 1  a 2-up grid of large cards, one per record type. Everything
             the page can do is visible without scrolling sideways.
     Step 2  tap a card and that ONE form takes the whole screen, with a
             sticky "All forms" bar to come back.

   Nothing about the underlying tab machinery changes: the cards call the
   same openTab() the desktop buttons call, so the URL hash, the linked
   "Recent Records" pane and the AJAX submit handlers are untouched. On a
   desktop none of this renders at all.
   ======================================================== */
@media (max-width: 767px) {

    /* The desktop tab strip is gone; the picker takes its place. */
    .tab-group-scope .tab-container { display: none; }

    .entry-picker {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: var(--space-2);
        margin-bottom: var(--space-2);
    }

    .entry-card {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        justify-content: flex-start;
        gap: 6px;
        min-height: 96px;
        padding: var(--space-3) var(--space-2);
        text-align: left;
        background: var(--input-bg);
        border: 1px solid var(--input-border);
        border-radius: var(--radius-md);
        color: var(--text-main);
        font-family: inherit;
        cursor: pointer;
        transition: border-color var(--dur-fast, 120ms) ease,
                    background var(--dur-fast, 120ms) ease;
        -webkit-tap-highlight-color: transparent;
    }
    .entry-card:active { background: rgba(var(--accent-blue-rgb), 0.08); border-color: var(--brand); }
    .entry-card:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

    .entry-card .icon {
        width: 22px; height: 22px;
        color: var(--brand);
        flex-shrink: 0;
    }
    .entry-card-name { font-size: 14px; font-weight: 700; line-height: 1.25; }
    .entry-card-hint {
        font-size: 11px;
        font-weight: 500;
        color: var(--text-muted);
        line-height: 1.3;
    }

    /* ---- Step 1: picker showing ----
       No form is rendered at all, whatever `.active` says. `.tab-content`
       carries `display:block` when active, so this needs the same
       specificity weight to win — hence the compound selector rather than
       !important. */
    .tab-group-scope:not(.is-focused) .tab-content.active { display: none; }
    .tab-group-scope:not(.is-focused) .recent-panel { display: none; }

    /* ---- Step 2: one form showing ---- */
    .tab-group-scope.is-focused .entry-picker { display: none; }

    /* Sticky so the way back is always one thumb-tap away, however long
       the form is. Sits directly under the fixed mobile header. */
    .tab-group-scope.is-focused .entry-back {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        position: sticky;
        top: calc(60px + env(safe-area-inset-top, 0px));
        z-index: 5;
        min-height: 48px;
        /* Pulled out to the panel's edges (top -16px, sides -8px) so the bar
           reads as chrome attached to the panel rather than as a control
           floating inside it. The width compensates for the two negative
           side margins: 2 x 8px = one --space-3. */
        margin: calc(var(--space-3) * -1) calc(var(--space-2) * -1) var(--space-3);
        width: calc(100% + var(--space-3));
        padding: 0 var(--space-3);
        background: var(--surface);
        border: none;
        border-bottom: 1px solid var(--border);
        border-radius: var(--radius-md) var(--radius-md) 0 0;
        color: var(--brand);
        font-family: inherit;
        font-size: 14px;
        font-weight: 700;
        cursor: pointer;
    }
    .entry-back .icon { width: 18px; height: 18px; }
    .entry-back-current {
        margin-left: auto;
        color: var(--text-muted);
        font-weight: 600;
        font-size: 12px;
        text-transform: uppercase;
        letter-spacing: 0.04em;
    }

    /* ---- Sticky save bar (§5.2, v1.19.0) ----
       The primary action, always reachable, without scrolling to the bottom
       of a form that is fourteen fields long on a 667px screen. The Raw
       Receipt form is the worst of them: an operator who has filled in
       everything has to scroll past four more fieldsets to find Save, and on
       a phone held in one hand at a yard gate that is where entries get
       abandoned.

       `position: sticky`, not `fixed`: the row keeps its place in the
       document, so it does not overlap the last field, it does not need a
       spacer element underneath it, and it stops sticking naturally when the
       form ends rather than hovering over the Recent Records pane below.

       Only when a form is open. On the picker screen there is no form and
       nothing to save. */
    .tab-group-scope.is-focused .entry-actions {
        position: sticky;
        /* Clear of the fixed 56px bottom navigation, which would otherwise
           sit on top of the save button — the one control this rule exists to
           keep reachable. Keyed off the attribute base.html already sets,
           exactly as the toast container is, so the two can never disagree
           about whether the nav is on the page. Data Entry is behind a login,
           so in practice it always is. */
        bottom: 0;
        z-index: 4;
        gap: var(--space-2);
        /* Pulled to the panel's edges and re-padded, so the bar reads as a
           bar rather than as two buttons floating over the content that
           scrolls under them. */
        margin: var(--space-4) calc(var(--space-2) * -1) 0;
        padding: var(--space-3) var(--space-3)
                 calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
        background: var(--surface);
        border-top: 1px solid var(--border);
    }
    body:not([data-user-role=""]) .tab-group-scope.is-focused .entry-actions {
        bottom: calc(56px + env(safe-area-inset-bottom, 0px));
        padding-bottom: var(--space-3);
    }

    /* ---- Recent Records ----
       Demoted to a collapsed <details> below the form. It is reference
       material; on a 667px-tall screen it should never push the form the
       operator came here to fill in below the fold. */
    .recent-panel {
        margin-top: var(--space-3);
        padding: 0;
        border: 1px solid var(--card-border);
        border-radius: var(--radius-md);
        background: var(--card-bg-solid);
    }
    .recent-panel > summary {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        min-height: 48px;
        padding: 0 var(--space-3);
        font-weight: 700;
        font-size: 14px;
        cursor: pointer;
        list-style: none;
    }
    .recent-panel > summary::-webkit-details-marker { display: none; }
    .recent-panel > summary::after {
        content: '▾';
        margin-left: auto;
        color: var(--text-muted);
        transition: transform var(--dur-fast, 120ms) ease;
    }
    .recent-panel[open] > summary::after { transform: rotate(180deg); }
    .recent-panel-body { padding: 0 var(--space-3) var(--space-3); }
    .recent-item { padding: var(--space-2); margin-bottom: var(--space-2); }
}

/* On desktop the <details> is permanently open and its summary is just the
   panel heading — the disclosure affordance would be a lie, since the
   element cannot be closed there (main.js re-opens it on `toggle`). So the
   marker triangle goes, and so does the pointer cursor that would invite a
   click. The heading must look and read exactly as the <h3> it replaced. */
@media (min-width: 768px) {
    .recent-panel > summary {
        display: block;
        list-style: none;
        cursor: default;
        font-size: 1.17em;
        font-weight: bold;
    }
    .recent-panel > summary::-webkit-details-marker { display: none; }
    .recent-panel > summary::marker { content: ''; }
}


/* ========================================================
   6. DASHBOARD  (<= 767px)
   ======================================================== */
@media (max-width: 767px) {

    /* ---- Header + period filter ----
       Three controls (mode toggle, month picker, print) in one flex row
       overflowed. Each becomes full width and stacks. */
    /* box-sizing is deliberate: this stylesheet has no global border-box
       reset, so `width: 100%` plus padding and a 1px border made the panel
       ~34px wider than the KPI cards below it — it sat proud of the grid on
       the right-hand edge. Same arithmetic applied to the toggle inside it.
       Set here rather than globally: a site-wide reset would resize every
       fixed-width element in the app at once. */
    .filter-panel {
        box-sizing: border-box;
        width: 100%;
        padding: var(--space-3);
        border-radius: var(--radius-md);
        gap: var(--space-2);
    }
    .toggle-switch-container { box-sizing: border-box; width: 100%; }
    /* 10px made the toggle visibly taller than the month picker sitting
       directly under it; 8px lines the two rows up. */
    .toggle-option { flex: 1; text-align: center; padding: 8px var(--space-2); }
    .filter-panel > .flex { flex: 1; min-width: 0; }
    /* Both the native input and the v2.4.0 trigger that replaces it. Only one
       of the two is ever displayed — which one depends on whether the script
       loaded — so the rule has to name both or the field stops filling its
       pill on exactly the pages where the enhancement failed. */
    .filter-panel input[type="month"],
    .filter-panel .monthpick,
    .filter-panel .monthpick-trigger { width: 100%; min-width: 0; }

    /* Printing from a phone is not a thing anyone does on a factory floor,
       and the button was stealing a row of width from the controls that
       are. Hidden rather than deleted — it is still there on any screen
       big enough to be attached to a printer. */
    .filter-panel .btn-secondary[onclick*="print"] { display: none; }

    /* ---- KPI cards ----
       Two up rather than one: four full-width cards is four screens of
       scrolling before the first chart. */
    /* ⚠ THESE WERE DEAD UNTIL v2.8.0. .kpi-card and the sixteen rules that
       follow it in this file were out-specified at every viewport width by
       templates/dashboard.html's own <style> block, which base.html emits
       AFTER every stylesheet — and a media query adds no specificity. The
       components moved to components/dashboard.css in v2.8.0 and these
       apply for the first time. See that file's header for the full list.
       Anything that looks wrong on a phone here has never been seen. */
    .kpi-card { padding: var(--space-3); border-radius: var(--radius-md); }
    .kpi-icon-box { width: 36px; height: 36px; border-radius: var(--radius-md); font-size: 16px; margin-bottom: 8px; }
    .kpi-title { font-size: 11px; }
    .kpi-value { font-size: 1.125rem; }
    .kpi-delta { font-size: 11px; }
    /* The "View sales →" hint duplicates what tapping the card already
       does, and costs a line of height on every card. */
    .kpi-link-hint { display: none; }

    /* ---- Charts ----
       Shorter frames. A 340px chart plus its heading is more than half a
       phone screen, and the dashboard has five of them. */
    .chart-frame,
    .chart-frame--sm,
    .chart-frame--md { height: 240px; }
    /* Still the tallest of the four on a phone, because the charts that use
       it carry a legend and an axis title, but nothing like 420px — that is
       most of a phone screen for one chart. */
    .chart-frame--lg { height: 300px; }
    /* Taller than --lg even on a phone: this is the one chart that is now
       the whole panel rather than one element of it, and a doughnut with a
       centre label and a legend inside 300px is a ring of slivers. */
    .chart-frame--xl { height: 360px; }
    .chart-skeleton { height: 240px; }
    .chart-skeleton--xl { height: 360px; }

    /* One column. Two 45%-wide columns would give the doughnut ~170px. */
    .breakdown-layout { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
    .breakdown-layout__figures { max-height: none; overflow-y: visible; }

    /* ---- Target achievement rows ---- */
    .machine-target-row {
        padding: var(--space-3);
        border-radius: var(--radius-md);
        gap: var(--space-3);
    }
    .progress-labels { font-size: 11px; gap: var(--space-2); }
    /* The label and the "12 / 30 (40%)" figure must be free to sit on two
       lines rather than colliding. */
    .progress-labels > span:last-child { text-align: right; white-space: nowrap; }

    /* ---- Inventory tiles ----
       MOVED to the <style> block in templates/dashboard.html (v2.5.0), and
       these four rules are gone rather than kept "just in case".

       They never applied. The inventory tiles are styled in that block, which
       base.html emits AFTER every linked stylesheet including this one, and a
       media query adds no specificity — so `.inv-grid` there beat `.inv-grid`
       here at every viewport width. Four dead rules that read as the phone
       layout are worse than none, because the next person to widen a tile
       edits them and sees nothing change.

       The rule for the file: a component styled in a page's <style> block
       keeps its responsive rules in that same block. Only components styled
       from a stylesheet belong here. */

    /* ---- Order cards ----
       Item name and status badge side by side truncated the name. Stacked,
       with the badge on its own line. */
    .order-card {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-2);
        padding: var(--space-3);
    }
    .order-meta { flex-wrap: wrap; gap: var(--space-2); font-size: 11px; }

    /* ---- Downtime / achievement pills ---- */
    .breakdown-list { padding-left: 0; gap: var(--space-3); }
    .breakdown-item { padding-left: var(--space-3); }
    .achievement-pill { font-size: 11px; }

    /* ---- Maintenance counters ---- */
    .machine-target-title { font-size: 14px; }

    /* The v2.2.0 treatment-card and outlook components are NOT overridden
       here, and that is deliberate. Their base rules live in the <style>
       block in templates/dashboard.html, which the browser applies AFTER
       every linked stylesheet — so a same-specificity rule in this file loses
       the cascade no matter what media query it sits in. That is the same
       reason .chart-frame lives up in §1 rather than in the template. Their
       phone overrides are in the template beside the rules they override. */
}

/* ========================================================
   6b. NOTIFICATION PANEL  (<= 767px)
   ========================================================
   A 380px drawer on a 360px screen is a drawer with a 20px strip of page
   showing down one side — enough to look like a mistake, not enough to be
   useful. On a phone the panel is the whole screen instead, which is what
   every other full-screen surface in this app already does (see the entry
   picker in §5 and the bottom-sheet modals in §3.7).
   ======================================================== */
@media (max-width: 767px) {
    .notif-panel {
        width: 100%;
        border-left: none;
    }

    /* The list is the only thing that should scroll; the header, the filter
       chips and the footer stay put so "Clear all" is reachable without
       scrolling to the end of a long list. */
    .notif-list { overscroll-behavior: contain; }

    /* Two lines of 11px metadata under every message is a lot of a phone
       screen spent on chrome. The absolute time is dropped, leaving the
       relative one ("12 min ago"), which is what anyone actually reads. */
    .notif-exact { display: none; }

    .notif-item { padding: var(--space-3) var(--space-2); }
}


/* ========================================================
   7. SMALL HANDSETS  (<= 400px)
   ========================================================
   320-360px viewports. Anything two-up here has about 150px per column,
   which is below the point where a card is readable. */
@media (max-width: 400px) {
    .entry-picker { grid-template-columns: 1fr; }
    .entry-card { min-height: 0; flex-direction: row; align-items: center; }
    .entry-card-hint { display: none; }

    .toolbar-right { grid-template-columns: 1fr; }
    .date-filter-actions { grid-template-columns: 1fr; }

    /* KPI cards go one-up; two 150px cards cannot hold "Rs. 4,250,000.00". */
    .grid-cols-2-sm { grid-template-columns: 1fr; }
    /* .inv-grid removed here too — see the note in §"Inventory tiles" above.
       The one-column fallback now lives beside the rest of the tile's rules,
       in templates/dashboard.html. */
}


/* ========================================================
   8. LANDSCAPE PHONES
   ========================================================
   A phone turned sideways is ~360px TALL. A 240px chart plus the fixed
   header and the bottom nav leaves nothing. Charts shrink further; the
   sticky back bar stops being sticky, because a sticky bar on a 360px-tall
   viewport eats an eighth of it. */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) {
    .chart-frame,
    .chart-frame--sm,
    .chart-frame--md,
    .chart-skeleton { height: 200px; }

    .tab-group-scope.is-focused .entry-back { position: static; }
    .entry-picker { grid-template-columns: repeat(3, 1fr); }
}


/* ========================================================
   9. SAFE AREAS (notches, home indicators, punch-holes)
   ========================================================
   Applies in the installed Android app / PWA, where the page really does
   run edge to edge and there is no browser chrome to absorb the inset.
   ======================================================== */
@media (max-width: 767px) {
    /* main.css already reserves 72px for the bottom nav. Anything that
       positions itself against the bottom of the viewport must clear BOTH
       the nav and the home indicator. */
    .modal-content { margin-bottom: calc(56px + env(safe-area-inset-bottom, 0px)); }

    .main-content {
        padding-left: max(var(--space-2), env(safe-area-inset-left, 0px));
        padding-right: max(var(--space-2), env(safe-area-inset-right, 0px));
    }
}


/* ========================================================
   10. TOUCH DEVICES OF ANY SIZE
   ========================================================
   Keyed on the INPUT METHOD, not the screen width, so a touchscreen
   laptop or a wall-mounted panel in the mill gets these too. */
@media (hover: none) {
    /* :hover sticks after a tap on a touchscreen, leaving rows and cards
       visibly lifted until something else is tapped. main.css already
       guards .card/.panel/.kpi-card; these three were missed. */
    .order-card:hover,
    .recent-item:hover,
    .inv-item:hover { transform: none; box-shadow: none; }

    /* Anything that opens or submits gets the WCAG 2.5.5 target size. */
    .preset-btn,
    .tab-btn,
    .btn-sm,
    /* v2.8.0. .btn-action is 40px on a pointer device so a five-control
       toolbar does not out-weigh the table under it; on a touch screen it
       has to clear the WCAG 2.5.8 minimum like everything else. */
    .btn-action,
    .btn-edit { min-height: 44px; }
}


/* ========================================================
   11. THE DASHBOARD COMPONENTS (moved here in v2.8.0)
   ========================================================
   These blocks came out of templates/dashboard.html's <style> block along
   with the components they override, which now live in
   components/dashboard.css. They were written in the template because a
   max-width rule in THIS file could not win against a template rule at any
   width — see §1's note above and the header of components/dashboard.css.
   With the components in a real stylesheet, their responsive rules belong
   in the override layer with everything else. */

@media (max-width: 640px) {
    /* One panel per row on a phone, so a five-line list is never
       squeezed into half a 390px screen. The money column keeps its
       own track — dropping to two columns and stacking the rupees
       under the quantity is what makes a five-row list read as ten. */
    .kpi-line dt { font-size: 11.5px; }
    .kpi-line dd { font-size: 12.5px; }
    .kpi-line__money { min-width: 5.5rem; }
    /* The two chips go one under the other rather than shrinking. A
       span caption is already at 10px; taking it lower to keep two
       chips on one 390px line would make the thing the chip exists to
       explain the least legible part of it. */
    .kpi-line__vs { gap: 3px 5px; }
    .kpi-vs { padding: 1px 6px 2px; }
    .kpi-vs__label { font-size: 9.5px; }
    .kpi-vs__delta { font-size: 10.5px; }
}

/* ---- Phone layout for the Production tab's cards --------------------
   THESE RULES WERE DEAD UNTIL v2.8.0 — or rather, their siblings were.
   They lived in templates/dashboard.html's <style> block, which base.html
   emits AFTER every linked stylesheet; a media query adds no specificity,
   so anything the same components declared in THIS file lost at every
   viewport width. Seventeen phone rules here — the KPI card's padding, its
   title, value and delta sizes, the icon box, the "View sales" hint's
   `display: none`, the machine target row, the order card, the breakdown
   list — had never once applied.

   The components are in static/css/components/dashboard.css now, so their
   responsive rules belong here, in the override layer that is loaded last
   and is the only file in the app allowed to carry a max-width query. */
@media (max-width: 767px) {
    /* The reserved empty note keeps cards in a ROW the same height. Stacked
       there is nothing to line up with, and it is just a gap under every
       card. (.pick-card's own phone padding is set below and covers the
       static variant too, which is one of the things folding the two
       components into one bought.) */
    .pick-card-foot--empty { display: none; }

    /* Three cards across a 360px screen gives each about 100px, which is
       not enough for "Rs. 2,309,880" — the third card was clipped by the
       panel edge. Stacked, each keeps its figure and its range on one
       line. */
    .outlook-grid { grid-template-columns: 1fr; gap: 10px; }
    .outlook-head, .outlook-foot {
        flex-direction: column; align-items: flex-start; gap: 4px;
    }

    /* ---- The picker strips (v2.5.0) ----
       Three machine cards at 230px do not fit a 360px screen side by
       side, and a horizontally scrolling tab strip hides the card you
       have not thought to look for. Stacked, each card keeps its figure,
       its bar and its footnote on their own lines. */
    .pick-grid, .pick-grid--pair { grid-template-columns: 1fr; gap: 12px; }
    .pick-card { padding: 12px 14px; }
    .detail-row { padding: 10px 12px; }

    /* ---- Inventory (v2.5.0) ---- */
    .inv-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
    .inv-item { padding: 14px 14px 12px 20px; border-radius: var(--radius-md); }
    .inv-item-status { left: 8px; top: 14px; bottom: 12px; width: 3px; }
    .inv-value { font-size: 22px; }
    .inv-title { font-size: 12px; }
    /* The status word does not fit beside the dot at 150px, and the dot
       plus the stripe already carry the state; the word returns at the
       first size where there is room for it. */
    .inv-eyebrow { font-size: 10px; letter-spacing: 0.03em; }
    .inv-group { margin-bottom: 20px; }
}

/* ONE COLUMN below 400px, and this is a legibility call rather than an
   arithmetic one — two 150px tiles and a 12px gap do fit inside a 390px
   phone's panel, with about 18px to spare. What does not fit is the
   CONTENT: "BELOW REORDER POINT" wraps to two lines at 150px and
   "Untreated Bridge Sleepers" to three, so a two-up grid buys back a
   screen of scrolling by making every tile in it a paragraph.

   Stacked, each tile keeps its status on one line and its name on one or
   two. The panel is taller; it is also readable at arm's length on a
   shop floor, which is where it is read. */
@media (max-width: 399px) {
    .inv-grid { grid-template-columns: 1fr; }
}
