/* ==========================================================================
   Mobile layer
   --------------------------------------------------------------------------
   Loaded last, after every other stylesheet, and scoped entirely inside
   @media (max-width: 767.98px) so nothing here can affect a desktop.

   The system was built for a counter machine and every listing screen is a
   wide table. On a phone those either scrolled sideways inside a box — the
   reader could see the first column and had to drag for the rest — or, on the
   three screens with no wrapper, pushed the page itself sideways.

   Horizontal scrolling is not the fix. A row of a repair listing is a record,
   and on a narrow screen a record reads as a stacked card: label on the left,
   value on the right, one line each. That is what this file does, to every
   table in the system at once, using the header text each table already has
   (see the small script in layouts/app.blade.php that copies it onto the
   cells). No per-page markup, so a table added later is covered too.
   ========================================================================== */

@media (max-width: 767.98px) {

    /* ── Tables become cards ─────────────────────────────────────────────── */

    /* The wrapper no longer needs to scroll; nothing inside it is wide now. */
    .table-responsive { overflow-x: visible !important; }

    table.table:not(.keep-table) { display: block; width: 100% !important; min-width: 0 !important; }
    table.table:not(.keep-table) thead { display: none; }
    table.table:not(.keep-table) tbody,
    table.table:not(.keep-table) tfoot { display: block; width: 100%; }

    table.table:not(.keep-table) > tbody > tr,
    table.table:not(.keep-table) > tfoot > tr {
        display: block;
        border: 1px solid #e6eaf0;
        border-radius: 10px;
        background: #fff;
        padding: 4px 2px;
        margin-bottom: 10px;
        box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
    }

    table.table:not(.keep-table) > tbody > tr > td,
    table.table:not(.keep-table) > tfoot > tr > td {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        gap: 14px;
        width: auto !important;
        border: 0 !important;
        border-bottom: 1px solid #f2f5f8 !important;
        padding: 9px 12px !important;
        text-align: right !important;
        white-space: normal !important;
        font-size: 13.5px;
    }
    table.table:not(.keep-table) > tbody > tr > td:last-child,
    table.table:not(.keep-table) > tfoot > tr > td:last-child { border-bottom: 0 !important; }

    /* The header text, copied onto the cell by the script in app.blade.php. */
    table.table:not(.keep-table) > tbody > tr > td[data-label]::before {
        content: attr(data-label);
        flex: 0 0 38%;
        text-align: left;
        font-size: 11px;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: .04em;
        color: #8b98a8;
        padding-top: 2px;
    }

    /* A cell with no header (the actions column) gets the full width, and its
       buttons spread across it rather than stacking into a narrow strip. */
    table.table:not(.keep-table) > tbody > tr > td.is-actions {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        justify-content: flex-start;
        background: #fafbfd;
        border-radius: 0 0 9px 9px;
        padding: 11px 12px !important;
    }
    table.table:not(.keep-table) > tbody > tr > td.is-actions .btn { flex: 1 1 auto; min-height: 38px; }

    /* An empty-state row must not be laid out as a label/value pair. */
    table.table:not(.keep-table) > tbody > tr > td[colspan] {
        display: block;
        text-align: center !important;
    }
    table.table:not(.keep-table) > tbody > tr > td[colspan]::before { content: none; }

    /* Column show/hide hides a header the cards no longer render, so the
       matching cell has to be hidden by its label instead — handled in JS. */

    /* ── Page furniture ──────────────────────────────────────────────────── */

    .pui-page .page-header-band,
    .pui-page .pui-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 12px;
        padding: 18px 16px;
    }
    .pui-page .page-header-band h4,
    .pui-page .pui-header h4 { font-size: 18px; line-height: 1.25; }
    .pui-page .page-header-band .btn,
    .pui-page .pui-header .btn { width: 100%; justify-content: center; min-height: 42px; }
    /* The button row under a page title. */
    .pui-page .page-header-band > div:last-child:not(:only-child) {
        display: flex; flex-wrap: wrap; gap: 8px; width: 100%;
    }
    .pui-page .page-header-band > div:last-child:not(:only-child) .btn { width: auto; flex: 1 1 44%; }

    /* Stat cards: two up, and the icon inside the card rather than pushed off
       the right of it. */
    .pui-page .stat-card { padding: 14px; gap: 10px; }
    .pui-page .stat-card .sc-icon { width: 38px; height: 38px; font-size: 17px; }
    .pui-page .stat-card .sc-value { font-size: 20px; }
    .pui-page .stat-card .sc-label { font-size: 10.5px; }

    /* Filter panels stack; every control is full width and thumb-sized. */
    .pui-page .card-body .row > [class*="col-"] { margin-bottom: 10px; }
    .pui-page .form-control,
    .pui-page .form-select,
    .pui-page select.form-control { min-height: 44px; font-size: 15px; }
    .pui-page .btn { min-height: 42px; }
    .pui-page .btn-sm { min-height: 36px; padding: 7px 12px; font-size: 13px; }

    /* ── Topbar ──────────────────────────────────────────────────────────── */

    /* Upgrade / POS / New / language / bell / avatar do not fit across 390px.
       The labels go, the icons stay, and the row scrolls if it still needs to
       rather than overlapping. */
    .top-nav { gap: 6px !important; padding-left: 8px !important; padding-right: 8px !important; }
    .top-nav .tb-upgrade span,
    .top-nav .tb-pos span { display: none; }
    .top-nav .tb-upgrade,
    .top-nav .tb-pos { padding: 0 11px !important; }
    .top-nav .tb-user__meta { display: none; }
    .top-nav .storeselect { max-width: 42vw; }

    /* ── Modals ──────────────────────────────────────────────────────────── */

    .modal-body { padding: 14px !important; }
    .modal-footer { flex-wrap: wrap; gap: 8px; }
    .modal-footer .btn { flex: 1 1 auto; }

    /* ── Charts ──────────────────────────────────────────────────────────── */

    canvas { max-width: 100% !important; }

    /* ── Anything else that will not fit ─────────────────────────────────── */

    /* A last line of defence: nothing may push the page sideways. Vertical
       scrolling is untouched, so this cannot trap the page. */
    html, body { overflow-x: clip; max-width: 100%; }
    .pui-page, .dashboard-area { min-width: 0; }

    /* A flex item defaults to min-width:auto, which means "never narrower
       than my own content". An <input> reports its intrinsic size from the
       size attribute and its font, so .ai-ask-input on the dashboard refused
       to shrink and pushed its send button clean out of the card and off the
       side of the screen — which is what was dragging the page sideways,
       since .pui-page scrolls on x even though html/body do not. Bootstrap's
       columns have the same default and the same problem. */
    .ai-ask-row > .ai-ask-input,
    .pui-page .row > [class*="col-"],
    .dashboard-area .row > [class*="col-"] { min-width: 0 !important; }
    .ai-ask-row { flex-wrap: nowrap; }
    pre, code { white-space: pre-wrap; word-break: break-word; }
    img, svg, video { max-width: 100%; height: auto; }
}

/* Small phones: the label column costs too much of the line. */
@media (max-width: 400px) {
    table.table:not(.keep-table) > tbody > tr > td[data-label] {
        display: block;
        text-align: left !important;
    }
    table.table:not(.keep-table) > tbody > tr > td[data-label]::before {
        display: block;
        flex: none;
        margin-bottom: 3px;
    }
}

/* ==========================================================================
   Second pass — the things the first one did not reach
   ========================================================================== */

@media (max-width: 767.98px) {

    /* ── Topbar right-hand group ─────────────────────────────────────────── */

    /* Store picker, Upgrade, POS, New, language, bell, avatar. Seven controls
       do not fit across 390px: the group was 311px wide starting 43px off the
       left of the screen, so the store name was clipped and the avatar sat
       under the edge. It wraps to its own line under the store picker now,
       which is the only arrangement where all of it is reachable. */
    .top-nav { flex-wrap: wrap !important; row-gap: 8px !important; padding: 8px !important; }
    .dashoptions {
        flex-wrap: wrap;
        justify-content: flex-end;
        width: 100%;
        gap: 6px !important;
    }
    .dashoptions .tb-upgrade,
    .dashoptions .tb-pos { flex: 0 0 auto; }

    /* ── Repair wizard ───────────────────────────────────────────────────── */

    /* Six steps across a phone means either a scrolling rail nobody notices or
       labels too small to read. The numbers stay, the words go, and the row
       fits without moving. */
    .repair-wizard-progress { overflow-x: visible; padding: 10px 6px; }
    .wiz-step-item { min-width: 0; }
    .wiz-label { display: none !important; }
    .wiz-circle { width: 26px !important; height: 26px !important; font-size: 12px !important; }
    .wiz-step-item:not(:last-child)::after { top: 12px; }

    /* Cart header: the title and the count fought for the same line. */
    .cart-head-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

    /* ── Customer display ────────────────────────────────────────────────── */

    /* /display is built for a second monitor facing the customer and assumes
       it has one. On a phone it must at least fit. */
    .dh, .dm, .ticker { max-width: 100% !important; min-width: 0 !important; }

    /* ── Tap targets ─────────────────────────────────────────────────────── */

    /* Icon-only buttons in a card's action row were 28px squares. The WCAG
       floor is 24px and the practical one is about 40 — a thumb is 45. */
    td.is-actions .btn,
    td.is-actions a.btn,
    .btn-icon,
    .btn-group-sm > .btn { min-height: 40px; min-width: 40px; }

    /* Checkboxes and radios in lists and forms. */
    input[type="checkbox"], input[type="radio"] { min-width: 22px; min-height: 22px; }

    /* Pagination arrows. */
    .pagination .page-link { min-width: 40px; min-height: 40px; display: inline-flex; align-items: center; justify-content: center; }

    /* ── Forms ───────────────────────────────────────────────────────────── */

    /* iOS zooms the whole page in when a focused field is under 16px. */
    input, select, textarea { font-size: 16px !important; }

    /* Two-up form rows stack rather than squeezing to 45px each. */
    .form-row > [class*="col-"], .row.g-2 > [class*="col-"] { flex: 0 0 100%; max-width: 100%; }
}

/* ==========================================================================
   The rail as a drawer
   --------------------------------------------------------------------------
   72px of permanent chrome is a fifth of a 390px viewport, and the content was
   living in what was left. On a phone the rail slides in over the page from
   the hamburger in the topbar; the page itself never moves.
   ========================================================================== */

.tb-burger { display: none; }

.rail-scrim {
    position: fixed; inset: 0; z-index: 1035;
    background: rgba(8, 24, 38, .5);
    -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}

@media (max-width: 767.98px) {

    .tb-burger {
        display: inline-flex; align-items: center; justify-content: center;
        width: 40px; height: 40px; flex-shrink: 0;
        border: 1px solid #e2e8f0; border-radius: 9px;
        background: #fff; color: #334155; font-size: 20px;
        cursor: pointer; margin-right: 8px;
    }
    .tb-burger:active { background: #f1f5f9; }

    /* ── One row, one control ────────────────────────────────────────────
       These grew up separately and it showed: three heights (40, 44 and 36),
       three near-identical border greys, two corner radii, and the language
       and bell buttons with no box at all next to five that had one. At this
       size a header is mostly chrome, so the chrome has to look decided. One
       height, one radius, one border. The POS button keeps its dark fill —
       it is the one control that is meant to stand out. */
    /* 6px, not the burger's 9 or the bell's 8: till-theme.css already forces
       6 on .btn, .card, .tb-store, .tb-pos-btn, .qa-btn and the avatar, so it
       is the radius the rest of the app is speaking in. Matching it also
       avoids a specificity fight with that file's
       `.dashoptions button#btn.tb-user`, which is !important at (1,2,1). */
    .top-nav .tb-burger,
    .top-nav .tb-store,
    .dashoptions .tb-pos-btn,
    .dashoptions .qa-btn,
    .dashoptions .lang-btn,
    .dashoptions .notif-btn,
    .dashoptions .reg-topbar-btn,
    .dashoptions button#btn.tb-user {
        height: 38px !important;
        min-height: 38px !important;
        border-radius: 6px !important;
        box-shadow: none !important;
    }
    .top-nav .tb-burger,
    .dashoptions .tb-pos-btn,
    .dashoptions .qa-btn,
    .dashoptions .lang-btn,
    .dashoptions .notif-btn,
    .dashoptions .reg-topbar-btn {
        width: 38px !important;
        /* Centring is asserted, display is not: the register buttons are shown
           and hidden by setTopbarOpen/setTopbarClosed through element.style,
           and a normal inline declaration loses to an !important one — a
           display here made all three visible at once and left the store name
           29px wide. Their own base rules already give them a flex box. */
        align-items: center !important;
        justify-content: center !important;
        flex: 0 0 auto;
    }
    .top-nav .tb-store,
    .dashoptions .qa-btn,
    .dashoptions .lang-btn,
    .dashoptions .notif-btn,
    .dashoptions button#btn.tb-user {
        background: #fff !important;
        border: 1px solid #e2e8f0 !important;
    }
    /* The register buttons keep their status tint — amber for closed, red for
       open — but stop being the only bare-edged things in the row. */
    .dashoptions .reg-topbar-btn { border: 1px solid rgba(15, 23, 42, .08) !important; }

    /* Off-canvas until asked for. Labels are always shown here — a drawer wide
       enough to open is wide enough to read. */
    .sidebar,
    .sidebar.close {
        position: fixed !important;
        top: 0; bottom: 0; left: 0;
        width: 268px !important;
        z-index: 1040;
        transform: translateX(-100%);
        transition: transform .26s cubic-bezier(.32, .72, 0, 1);
        overflow-y: auto !important;
        align-items: stretch !important;
        padding: 14px 12px !important;
    }
    body.rail-mobile-open .sidebar,
    body.rail-mobile-open .sidebar.close { transform: translateX(0); }

    /* The page keeps the full width whether the drawer is open or not. */
    .sidebar ~ .home,
    .sidebar.close ~ .home {
        left: 0 !important;
        right: 0 !important;
        width: 100% !important;
    }

    /* Inside the drawer every item reads as a labelled row, the same shape
       body.rail-open gives it on a desktop.

       These selectors carry `body` and the list container on purpose.
       rail-nav.css has, in its own max-width:767px block:

           body.rail-open .rail-wordmark,
           body.rail-open .sidebar .nav-link:not(.has-submenu) > a > .nav-text
               { display: none !important; }

       — there to keep the *rail* icon-only on a phone, from before the rail
       became a drawer. `rail-open` is remembered in localStorage, so anyone
       who had ever expanded the rail on a laptop opened this drawer on their
       phone and got a column of unlabelled icons: every label was
       display:none except the submenu parents', which that rule does not
       match. Both rules are !important, so specificity decides — theirs is
       (0,5,2), these are (0,5,3). */
    body .sidebar .menu-links > li.nav-link:not(.has-submenu) > a > .nav-text,
    body .sidebar .bottom-content > li.nav-link:not(.has-submenu) > a > .nav-text,
    body .sidebar .menu-links > li.has-submenu > a.submenu-toggle > .nav-text {
        position: static !important;
        transform: none !important;
        opacity: 1 !important;
        visibility: visible !important;
        background: transparent !important;
        box-shadow: none !important;
        padding: 0 !important;
        font-size: 14px !important;
        font-weight: 400 !important;
        color: #fff !important;
        white-space: nowrap;
        display: flex !important;
        align-items: center;
        flex: 1;
        min-width: 0;
        pointer-events: auto;
    }
    /* ── The drawer is a drawer, whatever a laptop session saved ──────────
       Everything down to the next comment re-states, without the rail-open
       hook, the row layout rail-nav.css only gives `body.rail-open`. That
       flag is the desktop chevron's remembered position, so with the rail
       collapsed on a laptop this drawer centred every icon, shrank the
       dividers to 30px stubs and ran the labels off the right edge; with it
       expanded, the labels vanished entirely. Neither state is a decision
       about a phone. The extra `body` on each selector is what carries them
       past the collapsed-rail rules in rail-nav.css and app.blade.php. */
    body .sidebar .menu-links,
    body .sidebar .bottom-content { align-items: stretch; }

    body .sidebar .rail-divider { width: auto; margin: 10px 6px; }

    body .sidebar .menu-links > li.nav-link,
    body .sidebar .menu-links > li.has-submenu,
    body .sidebar .bottom-content > li.nav-link { align-items: stretch; }

    body .sidebar .menu-links > li > a,
    body .sidebar .menu-links > li.has-submenu > a.submenu-toggle,
    body .sidebar .bottom-content > li > a {
        width: 100% !important;
        min-width: 0 !important;
        height: 46px !important;
        justify-content: flex-start;
        padding: 0 12px !important;
        gap: 12px;
        border-radius: 10px;
    }
    body .sidebar .icon { min-width: 22px !important; }
    body .sidebar i.icon { width: 22px !important; text-align: center; }

    /* Submenu parents open underneath rather than flying out sideways. The
       tap used to navigate to the first child instead, because the JS could
       not trust which layout the CSS had drawn — which also meant
       Manufacturers, Suppliers, Employees and Customers had no route in from
       a phone at all. */
    body .sidebar li.has-submenu > a.submenu-toggle::after {
        content: '\ea5f';
        font-family: 'tabler-icons';
        font-size: 15px; opacity: .7;
        margin-left: auto;
        transition: transform .2s ease;
    }
    body .sidebar li.has-submenu.open > a.submenu-toggle::after { transform: rotate(180deg); }
    body .sidebar li.has-submenu > .submenu {
        display: none;
        position: static !important;
        left: auto !important; top: auto !important;
        width: 100% !important; min-width: 0 !important;
        transform: none !important;
        border: 0 !important;
        box-shadow: none !important;
        background: rgba(0, 0, 0, .18) !important;
        border-radius: 9px;
        margin: 4px 0 6px !important;
        padding: 5px !important;
    }
    /* Beats rail-nav.css's :hover display:none — a tap leaves :hover stuck on
       a touch screen, which would otherwise shut the panel it just opened. */
    body .sidebar.close li.has-submenu.open > .submenu,
    body .sidebar.close li.has-submenu.open:hover > .submenu { display: block !important; }
    body .sidebar li.has-submenu > .submenu::before { display: none !important; }
    body .sidebar.close .submenu-header { display: none !important; }
    body .sidebar.close .submenu > li:not(.submenu-header) {
        height: auto !important; margin: 2px 0 !important; overflow: visible;
    }
    body .sidebar.close .submenu > li:not(.submenu-header) > a {
        display: flex !important; align-items: center;
        width: 100% !important; height: auto !important; min-width: 0 !important;
        min-height: 42px;
        padding: 9px 12px !important;
        border-radius: 8px;
        font-size: 13.5px; font-weight: 400;
        color: rgba(255, 255, 255, .82) !important;
    }
    body .sidebar.close .submenu > li.sub-active > a {
        background: rgba(255, 255, 255, .16) !important;
        color: #fff !important;
    }
    body .sidebar.close .submenu .nav-text {
        position: static !important;
        transform: none !important;
        opacity: 1 !important;
        visibility: visible !important;
        background: transparent !important;
        box-shadow: none !important;
        padding: 0 !important;
        font-size: 13.5px !important;
        font-weight: 400 !important;
        display: block !important;
        white-space: normal;
        pointer-events: auto;
    }
    /* ── end of the rail-open restatement ─────────────────────────────── */

    .sidebar header { flex-direction: row !important; align-items: center; gap: 10px; }
    /* Same fight, lower stakes: body.rail-open .rail-wordmark is (0,2,0). */
    body .sidebar header .rail-wordmark { display: block !important; flex: 1; color: #fff; font-size: 14px; font-weight: 700; }
    .sidebar a { min-height: 44px; }
    .sidebar .nav-text .badge { margin-left: auto; }

    /* The desktop label toggle has no job inside a drawer. */
    .rail-toggle { display: none !important; }

    /* Submenus open in place, not as a flyout off the side of the screen. */
    .sidebar li.has-submenu:hover > .submenu { left: auto !important; top: auto !important; transform: none !important; }

    /* The scrim covers the demo bar too, so the drawer is the only thing
       interactive while it is open. */
    body.rail-mobile-open .demo-bar { z-index: 1030; }
}

/* ── Topbar on a phone: one tidy row ─────────────────────────────────────── */

@media (max-width: 767.98px) {
    /* It was wrapping into a vertical stack of floating controls because the
       inner Bootstrap grid keeps its columns. Flatten the grid and let the row
       lay itself out. */
    .top-nav { display: flex !important; align-items: center; flex-wrap: nowrap !important; }
    .top-nav .storeselect { flex: 1 1 auto; min-width: 0; max-width: none; }
    .top-nav .storeselect .row { display: flex !important; flex-wrap: nowrap; margin: 0; }
    /* Not both columns growing — that is what put the POS button on top of
       the store name. The store shrinks, the controls keep their width. */
    .top-nav .storeselect .row { gap: 8px !important; }
    .top-nav .storeselect .row > [class*="col-"]:first-child {
        flex: 0 1 auto; min-width: 0; max-width: none; width: auto; padding: 0;
    }
    .top-nav .storeselect .row > [class*="col-"]:last-child {
        flex: 0 0 auto; max-width: none; width: auto; padding: 0;
        display: flex; justify-content: flex-end;
    }
    /* The right-hand group is one line of icons, not a wrapped block. */
    .dashoptions {
        flex-wrap: nowrap !important;
        width: auto !important;
        justify-content: flex-end;
        gap: 4px !important;
        flex-shrink: 0;
    }
    .dashoptions .btn,
    .dashoptions button,
    .dashoptions a { flex: 0 0 auto; }
    /* Upgrade is the least urgent control on a phone and the widest — it lives
       in the drawer and the demo bar already. */
    .dashoptions .tb-upgrade { display: none !important; }
    .tb-store__name { max-width: 34vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* The demo bar was four lines of copy across the foot of every screen. */
    .demo-bar { font-size: 11.5px; }
    .demo-bar__text { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
}

/* The store picker has to actually shrink, not just be told it may.
   Its own min-width (and the flex default of min-width:auto on the column
   above it) kept it at full size, so the controls beside it were laid over
   the top of the name rather than beside it. */
@media (max-width: 767.98px) {
    .top-nav .storeselect,
    .top-nav .storeselect .row,
    .top-nav .storeselect .row > [class*="col-"] { min-width: 0 !important; }

    /* No overflow:hidden here. The dropdown is absolutely positioned inside
       this button, so clipping the button clipped the panel — which is why
       the store could not be changed on a phone at all. The name has its own
       ellipsis; that is the only thing that needed trimming. */
    .tb-store {
        min-width: 0 !important;
        max-width: 100%;
        padding: 6px 8px !important;
    }
    /* Pinned to the viewport, not to the button.
       Anchored to a 90px button it opened 300px wide from the button’s left
       edge and ran off the right of the screen, so the far store in the list
       could not be reached. Fixed positioning takes it out of the button
       entirely and lets it span the width with a margin each side. */
    .tb-store-dropdown {
        position: fixed !important;
        left: 10px !important;
        right: 10px !important;
        top: 62px !important;
        width: auto !important;
        min-width: 0 !important;
        max-height: 55vh;
        z-index: 1200;
    }
    /* 360px of panel anchored to the right edge of a button that sits ~310px
       into a 390px screen puts its left edge at -46px, so the heading and the
       "View all" link were both cut off the side. Same treatment as the store
       list above: take it out of the button and pin it to the viewport. */
    .notif-dropdown {
        position: fixed !important;
        left: 10px !important;
        right: 10px !important;
        top: 62px !important;
        width: auto !important;
        max-width: none !important;
        max-height: 72vh;
        overflow-y: auto;
        z-index: 1200;
    }

    /* The revenue block is a shrink-to-fit flex item with text-align:right,
       so once the header wrapped onto its own line it sat at the left of the
       card with its figure right-aligned inside its own 108px box — which is
       why the number looked randomly indented with 250px of empty banner
       beside it. Across the full width instead: label at the left margin
       every other line starts from, figure and change together at the right. */
    .db-welcome .db-rev {
        width: 100%;
        display: flex;
        align-items: baseline;
        gap: 10px;
        text-align: left !important;
        margin-top: 14px;
        padding-top: 12px;
        border-top: 1px solid rgba(255, 255, 255, .18);
    }
    .db-welcome .db-rev > .db-rev__label { margin-right: auto; }

    .tb-store__label { display: none !important; }
    /* Six controls at 40px with a 65px profile button came to 247px of a
       390px row, so the store chip got 66px — the shop icon, the chevron and
       no room at all for the name. On a POS where the store you are ringing
       into decides which till the money lands in, that is the wrong thing to
       squeeze. 36px keeps a comfortable target on glass while giving the name
       back about 45px; the profile chevron and the divider are decoration and
       go entirely. */
    .dashoptions > .tb-pos-btn,
    .dashoptions .qa-btn,
    .dashoptions .lang-btn,
    .dashoptions .notif-btn {
        min-width: 0 !important;
        padding: 0 !important;
    }
    .dashoptions .lang-btn img,
    .dashoptions .notif-btn img { width: 21px !important; height: 21px !important; }
    .dashoptions .tb-user { padding: 3px !important; gap: 0 !important; }
    .dashoptions .tb-user__av { width: 30px !important; height: 30px !important; }
    .dashoptions .tb-user__chev,
    .dashoptions .tb-divider { display: none !important; }

    /* A hard 22vw cap guessed at the space left over. Let the flex row work
       it out instead — the controls beside it are flex:0 0 auto, so whatever
       they do not use belongs to the name. Every ancestor needs min-width:0
       or the default min-width:auto stops the shrink and the name runs under
       the buttons. */
    /* The shop glyph and its 10px gap cost 38px of a 110px chip — more
       than the name itself was getting. The chevron already says this opens,
       and the row is not short of icons. */
    .tb-store__ic { display: none !important; }
    .tb-store { gap: 0 !important; }
    .tb-store-btn { gap: 4px !important; }
    .tb-store__meta,
    .tb-store-wrap,
    .tb-store-btn { min-width: 0 !important; max-width: 100%; }
    .tb-store-btn { overflow: hidden; }
    .tb-store__name {
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 13px;
    }
}

/* ==========================================================================
   POS on a phone
   --------------------------------------------------------------------------
   The till is a two-column screen: products on the left, cart on the right.
   Bootstrap 4's .col-lg-* only stacks below 992px if the grid is allowed to —
   here both columns kept their widths, so the cart was squeezed to about 160px
   with "Sub Total" wrapping onto two lines and the category heading rendering
   one letter per line, which is what a 20px-wide block does to a word.
   ========================================================================== */

@media (max-width: 991.98px) {
    .pos-col, .cart-col {
        flex: 0 0 100% !important;
        max-width: 100% !important;
        width: 100% !important;
        min-width: 0 !important;
    }
    .cart-col { margin-top: 16px; }

    /* Nothing inside either column may be narrower than its own words. */
    .pos-col .pos-cont, .cart-col .card, .cart-col .card-body { min-width: 0 !important; }
    .pos-col *, .cart-col * { word-break: normal; overflow-wrap: anywhere; }

    /* The wizard's step heading was the worst of it. */
    .step-info, .step-info h4, .step-header { min-width: 0; white-space: normal; }
}

@media (max-width: 767.98px) {
    /* The filter bar was a column of full-width buttons taking half the
       screen before a product was visible. Two up, search across the top. */
    .filter-bar {
        display: grid !important;
        grid-template-columns: 1fr 1fr;
        gap: 8px;
        align-items: stretch;
    }
    .filter-bar .filter-search { grid-column: 1 / -1; order: -1; }
    .filter-bar .filter-btn { width: 100%; }
    .filter-bar .filter-btn button,
    .filter-bar .filter-btn .pos-add-product { width: 100%; justify-content: center; }

    /* ── Six nested boxes, each taking its own gutter ─────────────────────
       Measured at 390px before this block: .pui-page 15+15, .pos-col 15+15,
       .pos-cont 22+22 and a 1px border, the inner .card's border, then
       .tab-content 16+16 — 140px of chrome around 250px of content, so more
       than a third of the phone was padding before a product tile got a
       pixel. On a laptop that reads as breathing room; here it is the reason
       everything looks miniature. One gutter is enough on a phone; the boxes
       are still there, they just stop each charging for the same margin. */
    .pui-page { padding-left: 10px !important; padding-right: 10px !important; }

    /* Bootstrap's .row pulls back 15px to cancel a container's 15px padding.
       The gutter is 10 here, so a row sitting straight in the page hung 5px
       past the edge — small, but enough to make the whole page draggable
       sideways, which is what "fields going outside" looks like on a phone.
       Match the two. Rows inside a nested container are already balanced by
       that container's own padding and are left alone. */
    .pui-page .row { margin-left: -10px !important; margin-right: -10px !important; }
    .pui-page .row > [class*="col-"] { padding-left: 10px !important; padding-right: 10px !important; }

    /* And a backstop, the same one html and body already carry further down.
       clip rather than hidden: it does not make this a scroll container, so
       nothing inside loses position:sticky, and the y axis stays visible.
       A row's negative-margin strip is empty by definition, so there is
       nothing here to clip that anyone was meant to see. */
    .pui-page { overflow-x: clip; }

    /* A handful of pages nest a second .pui-page inside the layout's one, so
       they pay the gutter twice. */
    .pui-page .pui-page { padding-left: 0 !important; padding-right: 0 !important; }

    /* Those same pages build their header as a bare d-flex rather than
       .pui-header, so the mobile header rules above never reached them and a
       140px "Add Manufacturer" sat beside a title on a 390px screen. */
    .pui-page .d-flex:has(> div > .pui-page-title),
    .pui-page .d-flex:has(> .pui-page-title) { flex-wrap: wrap; gap: 10px; }
    .pui-page .d-flex:has(> div > .pui-page-title) > .btn,
    .pui-page .d-flex:has(> .pui-page-title) > .btn {
        width: 100%; justify-content: center; min-height: 42px;
    }
    /* Written with the attribute selector so it outranks the generic
       .pui-page .row > [class*="col-"] gutter above, which would otherwise
       hand this column back the padding .pos-cont is already providing. */
    .pui-page .row > [class*="col-"].pos-col,
    .pui-page .row > [class*="col-"].cart-col { padding-left: 0 !important; padding-right: 0 !important; }
    .pos-col > .pos-cont { padding: 10px !important; }
    .pos-cont > .card.p-0 { border-left: 0 !important; border-right: 0 !important; }
    .pos-cont #nav-tabContent.tab-content { padding-left: 0 !important; padding-right: 0 !important; }
    .pos-cont .tabs-bar { margin-left: 0 !important; margin-right: 0 !important; }

    /* The grid's own -15px gutters are wider than the tiles can spare. */
    #productsGrid.products-grid {
        margin-left: -4px !important; margin-right: -4px !important;
        padding-left: 0 !important; padding-right: 0 !important;
    }
    #productsGrid > * { padding-left: 4px !important; padding-right: 4px !important; }

    /* The register buttons the POS pushes into the topbar kept their desktop
       size and label, which squeezed everything else in that row into
       coloured circles. Icon only, same 36px as the rest. */
    .dashoptions .reg-topbar-btn {
        min-width: 0 !important; padding: 0 !important;
    }
    .dashoptions .reg-topbar-label { display: none !important; }
    .dashoptions .reg-topbar-ic { display: inline-flex !important; font-size: 18px; }
    .dashoptions .reg-topbar-btn .ti { font-size: 18px; }
    .dashoptions .reg-topbar-btn .reg-dot { position: absolute; top: 5px; right: 5px; }
    .dashoptions .reg-topbar-btn { position: relative; }

    /* The till adds two more controls to a row that was already full, and the
       store name — the one thing that says which shop this sale belongs to —
       is what got squeezed out. Both of these earn their removal here rather
       than paying for it: the POS shortcut points at the page you are on, and
       every entry in the + menu (New Repair, New Sale, Add Product) is a
       full-width button a thumb's reach below it. */
    .dashoptions:has(#regTopBar) .tb-pos-btn,
    .dashoptions:has(#regTopBar) .qa-wrap { display: none !important; }

    /* style.css gives each filter button an 8px vertical margin, from when
       they were a stacked column. They are a two-column grid with its own
       gap now, so that margin is just dead space above the fold. */
    .filter-bar .filter-btn button,
    .filter-bar .filter-btn .pos-add-product { margin: 0 !important; }
    .dashoptions #regTopBar { gap: 4px !important; }

    /* style.css turns .items-cont into display:block below 768, which put the
       repair wizard's device tiles one per row — one and a half categories
       visible before the fold, on the very first step of the flow. */
    .items-cont {
        display: grid !important;
        grid-template-columns: 1fr 1fr;
        gap: 8px;
    }
    .items-cont > * { width: auto !important; margin: 0 !important; }

    /* ── The till shell is a desktop idea ────────────────────────────────
       till-theme.css turns the app into a fixed-height shell — html and body
       overflow:hidden, .pui-page as the single scroll region — and the POS
       panel is a viewport-height column that scrolls inside itself. Both are
       right at a counter, where nothing should ever move but the list you
       are looking at. On a phone they meant the repair wizard's own footer,
       laid out below a box with overflow:hidden, was simply never drawn:
       "Continue to Details" on the parts step and "Create Repair & Add to
       Cart" on the details step both existed in the DOM 200px past a clipped
       edge, so the flow had no way forward at all. Let the column grow and
       let the page scroll, which is what a phone does anyway. */
    .pos-col .pos-cont,
    .pos-col .pos-cont > .card,
    .pos-col .pos-cont > .card > form,
    .pos-col .pos-cont > .card > form > fieldset,
    .pos-col #nav-tabContent,
    .pos-col #nav-tabContent > .tab-pane,
    .pos-col #repairWizardContainer,
    .pos-col .repair-wizard-step {
        height: auto !important;
        max-height: none !important;
        min-height: 0 !important;
        overflow: visible !important;
        flex: 0 1 auto !important;
    }
    /* The list keeps a cap and its own scroll so the footer stays a thumb
       away rather than below ten rows of parts — it just is not clipped by
       anything above it any more. */
    .pos-col #repairWizardContainer .items-categories,
    .pos-col .repair-wizard-step .step-scroll {
        height: auto !important;
        max-height: 52vh !important;
        min-height: 200px;
        overflow-y: auto !important;
        flex: 0 1 auto !important;
    }

    /* ── Prices do not break ─────────────────────────────────────────────
       The rule further up gives everything in the POS columns
       overflow-wrap:anywhere, so a stubborn word cannot push the layout
       sideways. A price is exactly the wrong thing to apply that to: as a
       flex item with no minimum, "£150.00" was breaking a character per line
       — the problem cards were showing a price as three stacked lines. */
    #problemSelectionContainer .items-box strong,
    #problemSelectionContainer .items-box .text-primary,
    #partsContainer .part-card__price,
    .product-card .product-price {
        white-space: nowrap !important;
        overflow-wrap: normal !important;
        flex-shrink: 0;
    }
    #problemSelectionContainer .items-box .d-flex > small { min-width: 0; }

    /* ── Parts cards: two per row like every other step ──────────────────
       The stock chip and the price are both white-space:nowrap inside a
       flex row, which set the card's min-content width at 188px — and a
       flex item defaults to min-width:auto, so it could not shrink to the
       163px its basis asked for and every part sat alone on its line. */
    #partsContainer .items-box,
    #problemSelectionContainer .items-box,
    #deviceModelsContainer .items-box { min-width: 0 !important; }
    #partsContainer .part-card__foot { flex-wrap: wrap; row-gap: 4px; }
    #partsContainer .part-card__name,
    #partsContainer .part-card__sku { min-width: 0; }

    /* Product tiles: two up, and tall enough to hit. */
    #productsGrid > * { flex: 0 0 50% !important; max-width: 50% !important; }

    /* The cart's own table is a real table and must stay one — a receipt line
       read as a stacked card is unreadable. It opts out of the global rule. */
    .cart-col table { display: table !important; }
    .cart-col table thead { display: table-header-group !important; }
    .cart-col table tbody { display: table-row-group !important; }
    .cart-col table tr { display: table-row !important; border: 0 !important; margin: 0 !important; box-shadow: none !important; }
    .cart-col table td {
        display: table-cell !important;
        text-align: left !important;
        border-bottom: 1px solid #f2f5f8 !important;
        padding: 8px 6px !important;
        font-size: 12.5px;
    }
    .cart-col table td::before { content: none !important; }
    .cart-col table td.is-actions { display: table-cell !important; background: transparent !important; }
}

/* ── The drawer must not be blurred by its own scrim ──────────────────────
   backdrop-filter on the scrim creates a stacking context, and the rail is
   rendered inside a wrapper rather than at the top level, so it was landing
   under the blur instead of over it. The dim alone does the job. */
.rail-scrim { -webkit-backdrop-filter: none; backdrop-filter: none; }

@media (max-width: 767.98px) {
    .sidebar, .sidebar.close { z-index: 2147483000 !important; filter: none !important; }
    .rail-scrim { z-index: 2147482000; }
    /* The demo pill sat over the drawer. */
    body.rail-mobile-open .demo-pill { display: none !important; }
}

/* ── Modal actions must be reachable without scrolling ───────────────────
   till-theme.css opens every modal full-screen with .modal-content pinned to
   100vh and .modal-body scrolling inside it. On a short phone viewport the
   footer was being pushed past the bottom edge, so Delete and Cancel could
   only be reached by scrolling a page that did not look scrollable. Pin the
   footer instead and let the body take what is left. */
@media (max-width: 767.98px) {
    .modal-content {
        height: 100dvh !important;
        max-height: 100dvh !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .modal-header, .modal-footer { flex: 0 0 auto !important; }
    .modal-body { flex: 1 1 auto !important; overflow-y: auto !important; min-height: 0 !important; }
    .modal-footer {
        position: sticky;
        bottom: 0;
        background: #fff;
        border-top: 1px solid #eef1f5;
        padding: 12px 14px calc(12px + env(safe-area-inset-bottom)) !important;
        box-shadow: 0 -6px 18px -12px rgba(16,24,40,.25);
    }
    /* The demo bar sits over the bottom of every screen; a modal's actions
       must clear it. */
    body.modal-open .demo-bar, body.modal-open .demo-pill { display: none !important; }
}

/* ── The drawer's own gap ────────────────────────────────────────────────
   The rail is a flex column with the menu at the top and a bottom block
   pushed down by margin-top:auto. In a 268px drawer that left a band of empty
   navy between them. Inside the drawer the list simply follows the header. */
@media (max-width: 767.98px) {
    .sidebar .menu { flex: 0 0 auto !important; }
    .sidebar .bottom-content { margin-top: 0 !important; }
    .sidebar { justify-content: flex-start !important; }
}

/* ── Queue board on touch ────────────────────────────────────────────────
   Long-press to pick a card up (see the Sortable config in repairs/board).
   The browser must not offer its own text selection or callout while that
   press is happening, or the two gestures fight. */
@media (max-width: 991.98px) {
    .qb-card {
        -webkit-user-select: none; user-select: none;
        -webkit-touch-callout: none;
        touch-action: pan-y;
    }
    /* Columns scroll horizontally as a set rather than shrinking to nothing. */
    .qb-board { display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; }
    .qb-col { flex: 0 0 86%; scroll-snap-align: start; min-width: 0; }
}
