/* ===========================================================================
   PS_RESPFIX 2026-11-02 — responsive repairs for the PlayStation / cafe POS.

   Measured with a Playwright sweep: 21 screens x 7 device sizes (360, 390, 414,
   768, 1024, 1280, 1920). 38 of those 147 combinations scrolled sideways, the
   worst by 523px. Every fix below names the measurement it answers.

   Loaded last, after modern-override.css, so it wins without !important wars.
   Nothing here changes markup — it is safe to drop in and safe to remove.
   =========================================================================== */


/* ---------------------------------------------------------------------------
   1. THE BIG ONE — reports hub +523px, day report +238px, month +115px

   css/mobile-fix.css has:

       .span10:has(> .top-blockk) { display: flex !important;
                                    flex-wrap: nowrap !important; }

   That was written for the device board, where the direct children are small
   `.well.span3.top-blockk` badges that should sit in a row (193 of them across
   the app). But reports.php, reports_day.php and friends put a full-width
   content panel in the same position — `.well.span11.top-blockk` — so the
   selector catches those too. A flex item defaults to `min-width: auto`, which
   means it will not shrink below its min-content width; the hospitality panel's
   min-content is 703px, so on a 390px phone the row refused to shrink and the
   whole document became 898px wide.

   Two parts: stop treating a wide panel as a badge row, and let any flex item
   in that row shrink.
   --------------------------------------------------------------------------- */
.span10:has(> .well.span11),
.span10:has(> .well.span10),
.span10:has(> .well.span5) {
    display: block !important;
}

/* a flex item that cannot shrink is the single most common cause of sideways
   scrolling in this codebase */
#content,
.span10:has(> .top-blockk),
.span10:has(> .top-blockk) > *,
.row-fluid > [class*="span"] {
    min-width: 0;
}


/* ---------------------------------------------------------------------------
   2. Wide tables scroll inside their own box — day report +238, users +119,
      cafe bill, halls (thead/tbody measured up to 759px)

   The report tables are genuinely wider than a phone and that is fine: a table
   of figures should scroll horizontally rather than be squashed to
   illegibility. What is not fine is the PAGE scrolling, which drags the whole
   interface sideways. Give the table's container the scroll instead.
   --------------------------------------------------------------------------- */
@media (max-width: 1100px) {
    .box-content,
    .box.all_details,
    .reports-hospitality-inner,
    .dataTables_wrapper {
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    /* NOTE: do not add `min-width: max-content` to the table here. It pins the
       table to its natural width, and because .box-content is itself a flex item
       with the default `min-width: auto` the constraint propagates upward and
       widens the container instead of scrolling inside it. That turned
       control_settings.php from 0 into 477px of overflow. overflow-x: auto on
       the container is sufficient. */
}


/* ---------------------------------------------------------------------------
   3. Device cards overshot by 4px — login / device board / kitchen (+12 to +13)

   `.span33` is a custom card width. Bootstrap 2 does not use border-box, so the
   card's padding and border were added on top of its width and each card sat a
   few pixels over; with margins that became 12-13px of page overflow.
   --------------------------------------------------------------------------- */
.span33,
.well.span33,
a.well.span33 {
    box-sizing: border-box;
    max-width: 100%;
}


/* ---------------------------------------------------------------------------
   4. Touch targets — every screen had 8 controls at 41px

   41px is just under the point where thumbs start missing, and this is used
   one-handed while carrying a tray. 44px is the usual floor; the navigation
   links were the consistent offender.
   --------------------------------------------------------------------------- */
@media (hover: none), (max-width: 991px) {
    .nav-link,
    .navbar .nav > li > a,
    .btn,
    .mobile-dock-item,
    .mobile-nav-drawer-link {
        min-height: 44px;
    }

    /* keep the label centred now that the box is taller */
    .navbar .nav > li > a {
        display: flex;
        align-items: center;
    }
}


/* ---------------------------------------------------------------------------
   5. Text below 12px — settings and attendance hint lines measured 11px

   Read at arm's length on a tablet mounted behind a counter. 12px is the floor.
   --------------------------------------------------------------------------- */
.help-block,
.text-muted,
small,
.hint,
.muted {
    font-size: max(12px, 0.8rem);
}


/* ---------------------------------------------------------------------------
   6. The notification panel covered the whole till on a phone

   #notification-container is position:fixed, width:300px, right:20px. On a
   360px screen that leaves 40px of usable interface behind it. Narrow screens
   get a panel inset from both edges and capped in height instead.
   --------------------------------------------------------------------------- */
@media (max-width: 575px) {
    #notification-container {
        inset-inline: 8px !important;
        width: auto !important;
        max-width: none !important;
        max-height: 55vh !important;
    }
}


/* ---------------------------------------------------------------------------
   7. Long unbroken strings — device names, product names, barcodes

   A single long token (a pasted URL, a barcode, a product name with no spaces)
   cannot wrap and pushes its container wide. This was behind the `<b>` element
   measured at 379px inside a 360px viewport on the devices-admin screen.
   --------------------------------------------------------------------------- */
.box-content,
.well,
td,
th,
.t-name,
.device-name {
    overflow-wrap: break-word;
    word-break: break-word;
}


/* ---------------------------------------------------------------------------
   8. Images never exceed their column

   Bootstrap 2 sets this for `img` but not for the QR and logo wrappers the app
   adds, and a stray full-size upload would otherwise widen the page.
   --------------------------------------------------------------------------- */
img,
canvas,
svg,
video,
iframe {
    max-width: 100%;
    height: auto;
}


/* ---------------------------------------------------------------------------
   9. Print — the receipt and report print views

   Nothing in the app constrained print width, so a report printed from a wide
   desktop clipped at the paper edge.
   --------------------------------------------------------------------------- */
@media print {
    #notification-container,
    #mobileNavDrawer,
    .navbar,
    .mobile-dock,
    .no-print {
        display: none !important;
    }

    .box-content,
    .box.all_details,
    .dataTables_wrapper {
        overflow: visible !important;
        max-width: none !important;
    }

    body,
    .container-fluid,
    #content {
        width: auto !important;
        margin: 0 !important;
        padding: 0 !important;
    }
}


/* ---------------------------------------------------------------------------
   10. Box model — day report +238, month +115, report details +238

   `.box.all_details` had width 378px with max-width 100% correctly applied, yet
   rendered at 424px: Bootstrap 2 is content-box, so its 23px of padding each
   side sits outside the declared width. max-width cannot contain a box whose
   padding is added on top of it. These containers need border-box.
   --------------------------------------------------------------------------- */
.box,
.box-content,
.box.all_details,
.well,
.reports-hospitality-inner,
.dataTables_wrapper {
    box-sizing: border-box;
}


/* ---------------------------------------------------------------------------
   11. Flex items must be allowed to shrink — login / device board / kitchen
       (+12, +13, +3)

   `a.well.span33.top-block` is a flex item inside `.devices-grid-cell`. With the
   default `min-width: auto` it refused to shrink below its 394px min-content
   width while the cell itself was 358px, so each card sat a few pixels over and
   the page picked up 12-13px of overflow.

   This is the same trap as rule 1, so the rule is written broadly: anything
   laid out as a flex or grid child in this app should be shrinkable. A card
   that genuinely needs a floor sets its own min-width.
   --------------------------------------------------------------------------- */
.devices-grid,
.devices-grid-cell,
.devices-grid-cell > *,
.span33,
.top-block,
.top-blockk {
    min-width: 0;
}

/* NOTE: .devices-grid is a CSS grid (repeat(auto-fill, minmax(260px, 1fr))),
   not flex, so flex-wrap does nothing here. It already reflows correctly. */

/* ---------------------------------------------------------------------------
   12. The report date filter — day report +238, month +115, details +238

   reports_details.php and its siblings carry this in a page <style> block:

       .form_control { display: flex; gap: 15px; justify-content: center; }
       span { width: 108px !important; }

   Two problems. The flex row has no flex-wrap, so the day/month/year controls
   and the submit button are pinned in a single line. And that `span` selector
   is unscoped and !important, so it forces *every* span on the page to 108px -
   five of them plus gaps is the 598px document this measured at a 360px
   viewport.

   Fixed here rather than in the pages because the same block is duplicated
   across reports_details, reports_day, reports_month,
   reports_casheer_all_details and reports_shift_all_details. `.form_control
   span` outranks a bare `span`, so this wins over the inline !important
   regardless of load order.
   --------------------------------------------------------------------------- */
.form_control {
    flex-wrap: wrap;
    max-width: 100%;
}

.form_control > * {
    min-width: 0;
}

@media (max-width: 991px) {
    .form_control span,
    .form_control select,
    .form_control input,
    .form_control label {
        width: auto !important;
        max-width: 100%;
    }

    /* Bootstrap 2 gives inputs fixed pixel widths by span class
       (input.span2 = 140px, input.span11 = 850px). None of those fit a phone. */
    input[class*="span"],
    select[class*="span"],
    textarea[class*="span"] {
        width: 100% !important;
        max-width: 100%;
    }
}

/* ---------------------------------------------------------------------------
   13. Hover growth on touch screens — device board / kitchen (+3 to +14)

   The device and kitchen cards grow on hover:

       .top-block:hover    { transform: scale(1.1) !important; }
       .wellspan3top:hover { transform: scale(1.1); }

   A 358px card becomes 394px, which is 36px wider than its track, and a
   transform does count toward the scrollable area. On a desktop that is a
   deliberate affordance and harmless. On a phone or a till tablet there is no
   pointer to hover with, yet a tap still latches :hover in mobile browsers -
   so the card stays enlarged and the board scrolls sideways until the user
   taps elsewhere.

   Restrict the growth to devices that actually have a hovering pointer. The
   cards keep a press affordance on touch via :active instead.
   --------------------------------------------------------------------------- */
/* The scale lives in a page-level <style> in devices.php, which the browser
   places after every external sheet - so an equal-specificity rule here would
   lose even with !important. The html prefix raises specificity enough to win
   without resorting to a longer selector chain. */
/* A narrow window with a mouse is the remaining case: neither (hover: none)
   nor (pointer: coarse) is true on a desktop browser resized to phone width,
   so the guard below would not fire and hovering a card still pushed the
   board 12px sideways. Below 768px the zoom has nowhere to grow into and no
   longer reads as an affordance, so drop it for every pointer type. */
@media (max-width: 767px) {
    html body .top-block:hover,
    html body .top-blockk:hover,
    html body .wellspan3top:hover,
    html body .devices-off-card:hover,
    html body .span33:hover {
        transform: none !important;
    }
}

@media (hover: none), (pointer: coarse) {
    html body .top-block:hover,
    html body .top-blockk:hover,
    html body .wellspan3top:hover,
    html body .devices-off-card:hover,
    html body .span33:hover {
        transform: none !important;
    }

    /* a tap should still feel like it registered */
    .top-block:active,
    .wellspan3top:active,
    .devices-off-card:active {
        transform: scale(0.98);
        transition: transform 0.08s ease;
    }
}

/* ---------------------------------------------------------------------------
   14. Real touch targets

   The sweep found 147 controls under 44px at phone width. Most of that count
   is checkboxes and radios at their natural 18px, and inline text links at
   26px inside table cells and bill lines - forcing either to 44px would look
   broken, so they are deliberately left alone. What is worth raising is the
   controls a thumb aims at: selects (39px), buttons (40px), submit inputs and
   the device cards (33-40px).
   --------------------------------------------------------------------------- */
@media (hover: none), (pointer: coarse), (max-width: 991px) {
    /* modern-override.css lines 968 and 1058 pin these at
       `min-height: 38px !important`, so matching that needs both !important
       and a selector that outranks `select.spanN`. */
    html body select,
    html body select[class*="span"],
    html body button,
    html body input[type="submit"],
    html body input[type="button"],
    html body input[type="reset"],
    html body .btn,
    html body a.btn,
    html body .devices-start-submit {
        min-height: 44px !important;
    }

    /* text inputs and date pickers at 16px or 18px are unusable on a phone:
       iOS also zooms the page when a focused input is under 16px of type */
    html body input[type="text"],
    html body input[type="number"],
    html body input[type="tel"],
    html body input[type="password"],
    html body input[type="search"],
    html body input[type="date"],
    html body input.hasDatepicker,
    html body textarea {
        min-height: 44px !important;
        font-size: max(16px, 1rem) !important;
    }

    /* a checkbox stays 18px, but its label becomes the hit area */
    label:has(> input[type="checkbox"]),
    label:has(> input[type="radio"]) {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        min-height: 44px;
    }

    /* the device cards are the primary target on the busiest screen */
    a.well.span33,
    .devices-off-card {
        min-height: 44px;
    }
}


/* ---------------------------------------------------------------------------
   15. The last 11px text

   css/modern-override.css line 948 sets `font-size: 11px !important` inside its
   own max-width:576px block, and control_settings.php / attendance.php repeat
   11px in inline styles. On a tablet mounted behind a counter that is not
   readable. 12px is the floor; the selectors below are specific enough to beat
   both the !important and the inline rules.
   --------------------------------------------------------------------------- */
@media (max-width: 991px) {
    /* modern-override.css:948 — the navbar's round shortcut buttons */
    html body .navbar .navbar-inner .container-fluid .circles .dropbtn,
    html body .navbar .navbar-inner .container-fluid .nav_circles .dropbtn {
        font-size: 12px !important;
    }

    /* control_settings.php and attendance.php repeat 11px in inline styles */
    html body [style*="font-size:11px"],
    html body [style*="font-size: 11px"] {
        font-size: 12px !important;
    }
}

/* ===========================================================================
   16. PS_TABLEFIX — the Bootstrap data tables at every size

   Measured: the report and admin tables run 7 to 10 columns and render
   838-1120px wide. Six are affected — reports_day, reports_details,
   reports_month, control_customers (7 each), control_users (9) and
   devices_halls (10). On a 390px phone that is up to three screens across.

   Two behaviours, because two different problems:

     up to 6 columns   scroll sideways inside the table's own box, with the
                       header pinned so you keep track of the column
     7 or more         each row becomes a card with its own labels, read top
                       to bottom like a receipt — you cannot hold ten columns
                       in your head while dragging a scrollbar

   js/ps-tables.js adds the .ps-t / .ps-t-stack classes and stamps each cell
   with data-label from the table's own <thead>. Colours come from the system's
   own tokens (--panel-bg, --panel-border, --text-main) so this looks native
   rather than bolted on.
   =========================================================================== */

/* the scroll container the script wraps every data table in */
.ps-t-wrap {
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border-radius: var(--border-radius-sm, 6px);
}

/* a thin scrollbar that matches the panel, rather than the OS default */
.ps-t-wrap::-webkit-scrollbar { height: 8px; }
.ps-t-wrap::-webkit-scrollbar-track {
    background: var(--bg-color, #12181b);
    border-radius: 999px;
}
.ps-t-wrap::-webkit-scrollbar-thumb {
    background: var(--panel-border, #2c3840);
    border-radius: 999px;
}
.ps-t-wrap::-webkit-scrollbar-thumb:hover {
    background: var(--text-muted, #8c9fae);
}
.ps-t-wrap { scrollbar-width: thin; scrollbar-color: var(--panel-border, #2c3840) transparent; }

/* ----------------------------------------------- up to 6 columns: scroll -- */
@media (max-width: 1100px) {
    /* keep the header visible while the body scrolls under it */
    .ps-t:not(.ps-t-stack) thead th {
        position: sticky;
        top: 0;
        z-index: 2;
        background: var(--panel-bg, #1e262c) !important;
    }

    /* the first column is usually the name or the date: pin it so a row stays
       identifiable once you have scrolled right */
    .ps-t:not(.ps-t-stack) tbody td:first-child,
    .ps-t:not(.ps-t-stack) thead th:first-child {
        position: sticky;
        inset-inline-start: 0;
        z-index: 1;
        background: var(--panel-bg, #1e262c) !important;
    }
    .ps-t:not(.ps-t-stack) thead th:first-child { z-index: 3; }

    /* a hairline so the pinned column reads as pinned */
    .ps-t:not(.ps-t-stack) tbody td:first-child::after,
    .ps-t:not(.ps-t-stack) thead th:first-child::after {
        content: '';
        position: absolute;
        inset-block: 0;
        inset-inline-end: 0;
        width: 1px;
        background: var(--panel-border, #2c3840);
    }
}

/* ------------------------------------------ 7+ columns: one card per row -- */
@media (max-width: 767px) {
    .ps-t-stack {
        display: block;
        width: 100% !important;
        min-width: 0 !important;
        border: 0 !important;
        background: transparent !important;
    }

    /* the header is replaced by the per-cell labels */
    .ps-t-stack thead { display: none; }

    .ps-t-stack tbody,
    .ps-t-stack tfoot { display: block; width: 100%; }

    .ps-t-stack tbody tr {
        display: block;
        margin: 0 0 10px;
        padding: 4px 0;
        background: var(--panel-bg, #1e262c) !important;
        border: 1px solid var(--panel-border, #2c3840);
        border-radius: var(--border-radius-lg, 12px);
        overflow: hidden;
    }

    /* striping would fight the card edges */
    .ps-t-stack tbody tr:nth-child(odd),
    .ps-t-stack tbody tr:nth-child(even),
    .ps-t-stack tbody tr:hover {
        background: var(--panel-bg, #1e262c) !important;
    }

    .ps-t-stack tbody td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 14px;
        width: auto !important;
        padding: 9px 14px !important;
        border: 0 !important;
        border-bottom: 1px solid rgba(255, 255, 255, 0.05) !important;
        text-align: start !important;
        white-space: normal !important;
        overflow-wrap: break-word;
    }

    .ps-t-stack tbody td:last-child {
        border-bottom: 0 !important;
    }

    /* the label, taken from the table's own <thead> by js/ps-tables.js */
    .ps-t-stack tbody td[data-label]::before {
        content: attr(data-label);
        flex: 0 0 40%;
        max-width: 45%;
        color: var(--text-muted, #8c9fae);
        font-size: 12px;
        font-weight: 600;
        text-align: start;
    }

    /* a cell holding only buttons reads better full width, under its label */
    .ps-t-stack tbody td:has(> .btn),
    .ps-t-stack tbody td:has(> form),
    .ps-t-stack tbody td:has(> a.btn) {
        flex-wrap: wrap;
        gap: 8px;
    }

    /* numbers line up on the end edge, as they do in the table view */
    .ps-t-stack tbody td.num,
    .ps-t-stack tbody td[align='right'] {
        font-variant-numeric: tabular-nums;
    }

    /* the totals row is a summary, not another record */
    .ps-t-stack tfoot tr {
        display: block;
        margin-top: 4px;
        background: var(--bg-color, #12181b) !important;
        border: 1px solid var(--panel-border, #2c3840);
        border-radius: var(--border-radius-lg, 12px);
    }
    .ps-t-stack tfoot td {
        display: flex;
        justify-content: space-between;
        gap: 14px;
        padding: 10px 14px !important;
        border: 0 !important;
        font-weight: 700;
    }
    .ps-t-stack tfoot td[data-label]::before {
        content: attr(data-label);
        color: var(--text-muted, #8c9fae);
        font-size: 12px;
        font-weight: 600;
    }

    /* an empty DataTables message should not pretend to be a card */
    .ps-t-stack tbody td.dataTables_empty {
        display: block;
        text-align: center !important;
    }
    .ps-t-stack tbody td.dataTables_empty::before { content: none; }
}

/* a stacked table needs no scroll container */
@media (max-width: 767px) {
    .ps-t-wrap:has(> .ps-t-stack) { overflow-x: visible; }
}

/* --------------------------------------------- a grid-bearing wrapper -- */
/* js/ps-tables.js hands a table's Bootstrap grid classes to the wrapper it
   creates, so the wrapper keeps the table's place in the layout rather than
   taking the whole row. devices_ps.php and devices_cafe.php both float a
   span6 bill table beside a span6 control panel, and a plain wrapper pushed
   that panel underneath.

   Below 768px there are no columns to hold: the wrapper must give up the
   grid width and span the screen, the same way the pages already override
   their own tables at this width. */
@media (max-width: 767px) {
    .ps-t-wrap[class*="span"] {
        float: none !important;
        width: 100% !important;
        margin-left: 0 !important;
    }
}
