/* ============================================================================
   Kappino — responsive layer
   ----------------------------------------------------------------------------
   Loaded AFTER every other stylesheet, so plain (non-!important) rules here win
   on equal specificity. Nothing in this file changes behaviour, copy, colour or
   information architecture — it only makes existing layouts survive narrow and
   short viewports.

   House rules for this file:
     • Desktop-first guard: anything that could alter the ≥1025px rendering is
       wrapped in a max-width media query. Rules outside a query must be
       genuinely size-neutral (e.g. `min-width: 0`, which only matters once a
       track is too small to fit its content anyway).
     • Fix causes, not symptoms. A blanket `overflow-x: auto` hides a broken
       grid instead of repairing it, so it is used only where horizontal
       scrolling is the deliberate, documented strategy for a wide table.

   Breakpoints used here (matching the ranges the audit measures):
     1180px  desktop → narrow desktop        768px  tablet portrait
     1024px  laptop / tablet landscape       560px  large phone
      760px  tablet portrait / phone         380px  small phone
   ============================================================================ */


/* ============================================================================
   1. FOUNDATION — shrinkable flex/grid children
   ----------------------------------------------------------------------------
   A grid/flex item defaults to `min-width: auto`, which means "never shrink
   below my min-content width". One long word, one nowrap row or one fixed-width
   strip inside such an item therefore stretches the whole track: on the
   dashboard, `.dash-grid`'s single `1fr` column resolved to 1592px inside a
   358px viewport, pushing every card off-screen.

   `min-width: 0` lets the track shrink to the space actually available. It is
   inert whenever the content already fits, so it is safe at every width — which
   is why this block is not behind a media query.
   ============================================================================ */
/* Every multi-column layout grid in the platform, enumerated from the
   stylesheets rather than guessed at, so the whole class of bug is closed at
   once instead of one module at a time.

   Wrapped in :where() deliberately — that gives the rule ZERO specificity, so
   any component that sets its own min-width (badge counters, fixed-width
   avatars, sizing tokens) still wins. This only supplies a floor where the
   value was the CSS-initial `auto`, which is exactly the case that breaks. */
:where(
  #mBody:has(#mi-name) .mi-customization-row,
  .ai-act,
  .ai-cards,
  .ai-hero-metrics,
  .ai-settings-stats,
  .aiv-kv-grid,
  .app-shell,
  .app-shell.sb-collapsed,
  .bc-stats,
  .bl-usage-grid,
  .branch-grid,
  .branch-list li,
  .camp-list li,
  .cash-quick,
  .cat-add-row,
  .channel-grid,
  .danger-row,
  .dd-list.branches li,
  .dest-grid,
  .device-row,
  .dlv-proof-filled,
  .donut-legend li,
  .donut-wrap,
  .eo-line,
  .exp-form-grid,
  .field-row,
  .grid-2,
  .grid-2-wide,
  .grid-3,
  .grid-4,
  .held-row,
  .hm-head,
  .hm-row,
  .hr-dayrow,
  .hr-salary-opt,
  .hr-salary-toggle,
  .hr-tr-flow,
  .inbox-body,
  .integ-grid,
  .inv-d-grid,
  .k-row2,
  .k-tables,
  .kanban,
  .pino-confirm-rows,
  .kds-picker,
  .korders-row,
  .korders-thead,
  .kpi-grid,
  .kpi-grid.five,
  .kpi-grid.four,
  .kpi-grid.six,
  .krsv-d-grid,
  .krsv-kpis,
  .ktbl-chip-grid.c3,
  .ktbl-chip-grid.c4,
  .ktbl-edit-stats,
  .ktbl-server-grid,
  .ktbl-stat-grid,
  .loy-metrics,
  .matrix-grid,
  .menu-grid,
  .mi-customization-row,
  .mk-grid.cols-2,
  .mk-grid.cols-3,
  .mk-grid.cols-4,
  .ml-picker,
  .ml-row,
  .ml-thead,
  .ml-times,
  .ml-toggle,
  .mm-grid,
  .notif-item,
  .ops-stats,
  .ord-d-grid,
  .ord-d-items li,
  .ord-d-summary,
  .ot-strip,
  .otf-row,
  .pay-actions,
  .pay-methods,
  .pcm-fields .otf-row,
  .pf-meta-grid,
  .pm-card,
  .pmt-grid,
  .pos-portions-body,
  .pos-right-foot .pay-actions,
  .pos-wrap,
  /* NB: `.price-list li` is deliberately NOT in this list. Giving its children
     a zero floor let the 1.6fr name track hand 24px to the 1.2fr price track at
     1280px, so the item name got narrower on desktop for no benefit — there was
     no overflow to fix at that width. It is handled at <=560px in section 18
     instead, where the row genuinely cannot fit. */
  .pr-menu-quadrants,
  .pr-scn-kpis,
  .ptp-grid,
  .pwd-strength,
  .qpay-methods,
  .rbac-roles,
  .rbac-users,
  .rdr-creds-row,
  .rdr-track-stats,
  .receipt-grid,
  .ref-stats,
  .rewards li,
  .rewards li.reward-row,
  .rp-viewer-identity,
  .rp-viewer-kpi-grid,
  .rule-item,
  .rv-confirm-card,
  .rv-dt-grid,
  .rv-waste-item,
  .set-extra-row,
  .set-extras-head,
  .settings-wrap,
  .shift-item,
  .shifts-foot,
  .tb-grid,
  .tip-grid,
  .top-items li,
  .view[data-view="ai-inventory"] .aiv-grid.cols-2,
  .view[data-view="ai-inventory"] .aiv-grid.cols-4,
  .view[data-view="dashboard"] .dash-grid,
  .view[data-view="dashboard"] .occ-legend,
  .view[data-view="expenses"] .exp-breakdown,
  .view[data-view="expenses"] .exp-kpis,
  .view[data-view="expenses"] .exp-two-col,
  .view[data-view="pos"] .ticket-head,
  .view[data-view="whatsapp"] .wa-chat-wrap,
  .view[data-view="whatsapp"] .wa-grid.cols-2,
  .view[data-view="whatsapp"] .wa-grid.cols-3,
  .view[data-view="whatsapp"] .wa-grid.cols-4,
  .view[data-view="workforce"] .shr-heatmap,
  .view[data-view="workforce"] .shr-kpis,
  .view[data-view="workforce"] .shr-week,
  .waste-grid,
  .wf-stats,
  .workflow,
  .xfer-route,
  body.rp-printing .grid-2,
  li.res-empty
) > * { min-width: 0; }

/* Flex rows that carry a text child which must be allowed to ellipsis rather
   than push its siblings out of the row. */
.card-head,
.card-head > *,
.toolbar,
.page-head,
.page-head > *,
.list-row,
.list-row > * { min-width: 0; }


/* ============================================================================
   2. FOUNDATION — real viewport height on phones/tablets
   ----------------------------------------------------------------------------
   `--screen-h: 100vh` sizes the shell, but on mobile Safari/Chrome 100vh is the
   *largest* viewport (URL bar hidden), so bottom controls sit under the browser
   chrome until the user scrolls. `100dvh` tracks the visible viewport instead.

   Scoped to ≤1024px on purpose: on desktop the two are identical, and dvh
   reflows while the mobile URL bar animates, so there is no reason to take that
   cost on a screen that cannot benefit.
   ============================================================================ */
@media (max-width: 1024px) {
  @supports (height: 100dvh) {
    :root { --screen-h: 100dvh; }
  }
}


/* ============================================================================
   3. FOUNDATION — toolbars, filter bars and action groups wrap
   ----------------------------------------------------------------------------
   `.toolbar` was `display:flex` with no `flex-wrap`, so filter chips, search and
   action buttons were pushed off the right edge with no way to reach them
   (measured on riders, staff, inventory and suppliers).
   ============================================================================ */
.toolbar,
.page-head,
.card-head,
.filters,
.btn-group,
.actions { flex-wrap: wrap; }

/* The search field was a hard 220px, which cannot shrink on a 320px screen. */
.search-sm { max-width: 100%; }

@media (max-width: 760px) {
  /* Give search the full row once the toolbar has wrapped — a 220px box next to
     a wrapped button group leaves an awkward dead gap. */
  .toolbar > .search-sm,
  .page-head > .search-sm { width: 100%; flex: 1 1 100%; }
}


/* ============================================================================
   4. FOUNDATION — touch targets
   ----------------------------------------------------------------------------
   The segmented control renders ~31px tall (6px padding + 12.5px text). That is
   under the comfortable minimum for a finger and it is the single most reused
   control in the platform (dashboard, POS, menu, forecast, reports, riders).
   Raised only at ≤1024px so the desktop density the design specifies is kept.
   ============================================================================ */
@media (max-width: 1024px) {
  .segmented { flex-wrap: wrap; max-width: 100%; }
  .segmented .seg { min-height: 36px; padding: 8px 14px; display: inline-flex; align-items: center; }
  .segmented.small .seg { min-height: 34px; padding: 7px 12px; font-size: 12px; }

  /* Icon-only buttons are the other common sub-target control. */
  .icon-btn, .mini-btn { min-width: 36px; min-height: 36px; }
}


/* ============================================================================
   5. FOUNDATION — legible small text on small screens
   ----------------------------------------------------------------------------
   Status chips and meta labels are 10.5–11px by design. That reads fine on a
   desktop monitor at arm's length but is the first thing to fail on a phone, and
   the brief is explicit that responsiveness must not be bought with smaller
   text. Raised only at ≤768px, so the desktop type scale is untouched.
   ============================================================================ */
@media (max-width: 1024px) {
  .chip.sm { font-size: 11px; padding: 3px 8px; }
  .chip { font-size: 11.5px; }
  .table thead th { font-size: 11.5px; }
}


/* ============================================================================
   6. FOUNDATION — modals fit the viewport
   ----------------------------------------------------------------------------
   The base `.modal-box` is already sound (max-width: 92vw, max-height: 100%, a
   scrolling body). What fails is the padding: `calc(var(--screen-h) * 0.06)`
   top plus 0.04 bottom eats 10% of a short landscape phone before the dialog
   starts, and individual modules override `width` with fixed pixel values.
   ============================================================================ */
@media (max-width: 760px) {
  .modal { padding: 16px 12px; align-items: flex-start; }
  .modal-box { width: 100%; max-width: 100%; max-height: calc(var(--screen-h) - 32px); }
  .modal-head { padding: 14px 16px; }
  .modal-body { padding: 16px; }
}

/* Short viewports — phone landscape, or a phone with the keyboard open. The
   dialog must not keep 6%/4% vertical padding it cannot afford. */
@media (max-height: 560px) {
  .modal { padding: 8px 12px; }
  .modal-box { max-height: calc(var(--screen-h) - 16px); }
}


/* ============================================================================
   7. ORDER MANAGEMENT — master/detail, then cards
   ----------------------------------------------------------------------------
   `.korders-row` is a six-track grid (104px 1.5fr 1fr 92px 96px 116px) whose
   fixed tracks alone need 478px. Beside the 384px detail pane it runs out of
   room long before the phone breakpoint: at 1280px the item-preview column was
   measured at 85px and at 390px it collapsed to 16px — the order contents, the
   single most useful column, were effectively invisible.

   Two stages, because one breakpoint cannot serve both:
     ≤1180px  stack the detail pane under the list so the row gets the full
              width back (the split already stacked, but only at ≤760px).
     ≤760px   drop the grid entirely and stack each order into a card. The
              previous attempt (min-width:560px + overflow-x:auto) could not
              work: min-width forces the element to 560px, so its own
              overflow-x never engages, and the header scrolled independently
              of its rows.
   ============================================================================ */
@media (max-width: 1180px) {
  .korders { flex-direction: column; height: auto; min-height: 0; }
  .korders-list { flex: 0 0 auto; }
  .korders-rows { overflow-y: visible; }
  .korders-detail {
    width: 100%; flex: 1 1 auto; flex-basis: auto;
    border-left: none; border-top: 1px solid var(--line);
  }
}

@media (max-width: 760px) {
  /* Undo the horizontal-scroll attempt this replaces. */
  .korders-thead, .korders-row { min-width: 0; }
  .korders-rows, .korders-thead { overflow-x: visible; }

  /* Column headings mean nothing once the row is a card. */
  .korders-thead { display: none; }

  .korders-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "id    total"
      "items items"
      "where st";
    gap: 6px 12px;
    padding: 12px;
    margin-bottom: 8px;
    border: 1px solid var(--line);
    border-radius: 12px;
    align-items: start;
  }
  .kor-id    { grid-area: id; }
  .kor-total { grid-area: total; text-align: right; }
  .kor-items { grid-area: items; }
  .kor-where { grid-area: where; }
  .kor-st    { grid-area: st; justify-self: end; align-self: center; }
  /* The elapsed-time column is folded into the status line rather than
     dropped — it drives kitchen urgency and must stay visible. */
  .kor-time  { grid-area: where; justify-self: end; align-self: center; }

  /* Show the whole order instead of an ellipsis, capped so one large order
     cannot push the next card off the screen. */
  .kor-items {
    white-space: normal;
    overflow: hidden;
    text-overflow: clip;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-height: 1.35;
  }
}


/* ============================================================================
   8. WORKFORCE — the week grid scrolls instead of clipping
   ----------------------------------------------------------------------------
   `.shr-week` is `110px repeat(7, minmax(120px, 1fr))`, so it cannot render
   narrower than 950px. The container had `overflow: hidden` (for its rounded
   corners), which meant the last days of the week were clipped away with no
   way to reach them — measured escaping by 26px even at 960px wide, and the
   staff-name column collapsed to 68px.

   A seven-day roster genuinely needs that width, so horizontal scrolling is
   the right strategy here rather than reflowing the calendar.
   ============================================================================ */
@media (max-width: 1400px) {
  .view[data-view="workforce"] .shr-week {
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
  }
  /* The row-label column stays put while the days scroll under it, so you can
     always tell whose shift you are looking at. */
  .view[data-view="workforce"] .shr-week-cell.row-label {
    position: sticky;
    left: 0;
    z-index: 2;
  }

}


/* ============================================================================
   9. SETTINGS — the address block can shrink
   ----------------------------------------------------------------------------
   `.addr-field` carries `min-width: 320px`. The existing ≤760px rule sets
   width/max-width to 100% but never clears the minimum, so on a 320px phone the
   field (and the search input and textarea inside it) hung 39px off the right
   edge with nothing able to scroll to reach them.
   ============================================================================ */
@media (max-width: 760px) {
  .addr-field { min-width: 0; max-width: 100%; }
}


/* ============================================================================
   10. TABLES / FLOOR PLAN — keep the plan legible instead of overlapping
   ----------------------------------------------------------------------------
   Table positions are percentages of the canvas while their sizes are pixels,
   so a narrowing canvas makes tables converge and collide. tables-floor.js
   already scales the shapes with the canvas (applyCanvasScale), but the scale
   is clamped at 0.55 for label legibility — below roughly 650px of canvas the
   shapes stop shrinking while the gaps keep closing, and tables overlap again.

   Rather than shrink the plan past readability, give the canvas a usable
   minimum and let it scroll in both directions, which is what a floor plan on a
   phone should do. The JS scale keeps working unchanged.
   ============================================================================ */
@media (max-width: 760px) {
  .ktbl-canvas {
    /* 650px is where applyCanvasScale()'s 0.55 clamp starts biting. */
    min-width: 650px;
  }
  /* The canvas is the positioning context, so the scroller has to be its
     parent — a min-width on a scroll container just overflows its own parent. */
  .ktbl-main {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}


/* ============================================================================
   11. FORMS — inputs use the width they have, dropdowns stay on screen
   ============================================================================ */
@media (max-width: 760px) {
  /* Multi-column form rows stack rather than crushing every field. */
  .field-row, .otf-row, .exp-form-grid, .form-grid { grid-template-columns: 1fr; }

  /* A select/input narrower than ~16px of text is unusable; let them fill. */
  .modal-body .input,
  .modal-body input,
  .modal-body select,
  .modal-body textarea { width: 100%; max-width: 100%; }

  /* iOS zooms the page when a focused control's text is under 16px. That zoom
     is what leaves users stranded sideways on a form, and it never restores on
     blur — so the fields that receive focus get a 16px floor on touch widths. */
  input, select, textarea, .input { font-size: max(16px, 1em); }
}

/* Popovers/dropdowns must not render off the right edge. */
@media (max-width: 760px) {
  .dropdown, .dd-menu, .popover, .menu-pop, .search-dropdown {
    max-width: calc(100vw - 24px);
  }
}


/* ============================================================================
   12. POS — the cart stays reachable without eating the product grid
   ----------------------------------------------------------------------------
   `.pos-wrap` collapsed to a single column at ≤1280px, which stacked the cart
   *underneath* the product grid inside a fixed-height container. On a tablet
   that gave roughly half the screen to each: too few products to serve from,
   and a cart you scroll the page to reach. The brief calls this out directly —
   the cart must stay accessible without permanently covering product selection.

   Tablets keep the two-column layout with a narrower cart; only phones switch
   to a different pattern.
   ============================================================================ */
@media (min-width: 761px) and (max-width: 1280px) {
  .pos-wrap { grid-template-columns: minmax(0, 1fr) 320px; }
}
@media (min-width: 761px) and (max-width: 900px) {
  /* Below ~900px the sidebar is already off-canvas, so the whole width is
     available — but the cart still needs to give ground to the menu grid. */
  .pos-wrap { grid-template-columns: minmax(0, 1fr) 292px; }
}
@media (min-width: 561px) and (max-width: 760px) {
  /* Landscape phones land here (740x360). They are short, not narrow, so the
     bottom sheet — which would eat over half of a 360px-tall screen — is the
     wrong pattern; two columns with a compact cart is the right one. */
  .pos-wrap { grid-template-columns: minmax(0, 1fr) 268px; }
}

/* --- phones: the cart becomes a bottom sheet -------------------------------
   Collapsed, it shows the ticket header (order title + running item count) and
   the pinned footer (totals + Charge), so the two things a cashier needs at a
   glance stay on screen and the primary action is always one tap away. Expanded
   (tap the header — see responsive.js), it reveals the line items and the
   order-type fields. The product grid keeps the full width either way.

   Nothing about the ticket, totals or payment logic changes; this only moves
   where the existing panel is painted. */
@media (max-width: 560px) {
  /* Let the POS page scroll normally instead of trying to fit two fixed-height
     columns into the viewport. Measured at 360x740, `.pos-wrap` started 249px
     down the page and was still given `calc(100vh - 210px)`, so it ended 39px
     BELOW the fold and the product grid's own scroller sat partly behind the
     cart sheet — cards rendered in that band could be seen but not tapped.
     With the page as the scroller, every card can be brought above the sheet. */
  .pos-wrap {
    grid-template-columns: 1fr;
    height: auto;
    min-height: 0;
  }
  .pos-left {
    height: auto;
    min-height: 0;
    overflow: visible;
  }
  .pos-left .menu-grid { flex: none; overflow: visible; }
  /* Room at the end of the page for the collapsed sheet to rest over. */
  .view[data-view="pos"] { padding-bottom: 216px; }

  .pos-right {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 60;
    max-height: 88vh;
    max-height: 88dvh;
    background: #fff;
    border-top: 1px solid var(--line);
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -10px 34px rgba(20, 24, 31, .18);
    display: flex;
    flex-direction: column;
  }

  /* Collapsed: header + totals + Charge. The line items and order-type fields
     fold away, and so do the secondary actions — with all four buttons the
     collapsed sheet measured 350px tall, which is half a phone screen. Hold,
     Clear and Send-to-kitchen come back as soon as the sheet is expanded. */
  .pos-right .pos-right-body { display: none; }
  .pos-right:not(.is-open) .pay-actions .btn.ghost { display: none; }
  .pos-right.is-open .pos-right-body { display: flex; flex: 1 1 auto; min-height: 0; overflow-y: auto; }

  /* A grab handle so the sheet reads as draggable/expandable. */
  .pos-right .ticket-head {
    position: relative;
    cursor: pointer;
    padding-top: 18px;
  }
  .pos-right .ticket-head::before {
    content: "";
    position: absolute;
    top: 7px; left: 50%;
    transform: translateX(-50%);
    width: 38px; height: 4px;
    border-radius: 2px;
    background: var(--line-strong);
  }

  /* Four actions cannot sit on one phone row; let them wrap and give Charge
     the full width so the primary action is unmissable. */
  .pos-right-foot .pay-actions {
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }
  .pos-right-foot .pay-actions .btn.primary { grid-column: 1 / -1; }

}


/* ============================================================================
   13. NAVIGATION — drawer scroll lock
   ----------------------------------------------------------------------------
   While the off-canvas drawer is open the page behind it must stay put. The
   scrim is painted by `.main::before`, which is a child of the scrolling
   element, so a drag that starts on the scrim still scrolls `.main` underneath.
   `responsive.js` mirrors `.sidebar.open` onto `body.nav-open` so this can be
   expressed in CSS.
   ============================================================================ */
@media (max-width: 1024px) {
  body.nav-open .main { overflow: hidden; }
  /* Long menus must still reach their last item on a short landscape phone. */
  .sidebar > .nav { overscroll-behavior: contain; }
}


/* ============================================================================
   14. RESERVATIONS — the row wraps before the guest name disappears
   ----------------------------------------------------------------------------
   `.krsv-row` is one flex line: avatar (46px) + name/sub + note icon + status
   pill + time, all `flex: 0 0 auto` except the name block. Below ~430px the
   fixed parts consume the row and the guest name — the one thing you scan the
   list for — was measured at 64px, i.e. two characters and an ellipsis.

   Letting the row wrap keeps every element at a readable size and moves the
   status pill onto its own line instead of starving the name.
   ============================================================================ */
@media (max-width: 430px) {
  .krsv-row { flex-wrap: wrap; row-gap: 8px; }
  /* A real floor for the name; when it cannot be met the pill wraps below. */
  .krsv-row-main { flex: 1 1 auto; min-width: 150px; }
  .krsv-pill { margin-left: auto; }
}


/* ============================================================================
   15. TYPOGRAPHY — a legibility floor on phones
   ----------------------------------------------------------------------------
   Eyebrow labels, status pills and meta text sit at 9–10.5px. That is a
   deliberate desktop density choice and is left alone there; on a phone or
   tablet held at arm's length it is the first thing that fails, and the brief
   is explicit that responsiveness must not be paid for with smaller type.

   The ceiling is 1024px so tablet landscape (960px) is covered; the change is
   0.5-2px per token, which is imperceptible as layout but is the difference
   between legible and not on a touch screen.
   ============================================================================ */
@media (max-width: 1024px) {
  .kds-eyebrow,
  .krsv-eyebrow,
  .krsv-ov-eyebrow,
  .mm-eyebrow,
  .q-label,
  .resv-day,
  .hm-lbl,
  .hr-salary-period,
  .kc-mixed,
  .mm-vat-note,
  .rw-status,
  .kor-srv,
  .krsv-pill { font-size: 11px; }

  /* 9px is the smallest type in the platform. */
  .ampm { font-size: 10.5px; }
}


/* ============================================================================
   16. AUTO-FILL GRIDS — a track minimum must never exceed the container
   ----------------------------------------------------------------------------
   `repeat(auto-fill, minmax(320px, 1fr))` reads as "at least 320px per card",
   including when the container is only 288px wide — so on a 320px phone the
   branch cards hung 32px off the edge. `min(320px, 100%)` keeps the intended
   card width everywhere it fits and yields to the container when it does not.
   Identical rendering above the breakpoint; only the impossible case changes.
   ============================================================================ */
.branch-grid  { grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); }
.mm-grid      { grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); }
.rbac-users   { grid-template-columns: repeat(auto-fill, minmax(min(258px, 100%), 1fr)); }
.pay-methods  { grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr)); }
.rbac-roles   { grid-template-columns: repeat(auto-fill, minmax(min(230px, 100%), 1fr)); }
.integ-grid   { grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); }
.dest-grid    { grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); }


/* ============================================================================
   17. ORDER MANAGEMENT — the 1181-1400px band
   ----------------------------------------------------------------------------
   Between the point where the master/detail split is still worth keeping and
   the point where there is genuinely room for it, the 384px detail pane leaves
   the list too little: the item-preview column measured 41px at 1280px. Rather
   than stack (which would change the desktop layout the design calls for),
   narrow the detail pane and tighten the fixed tracks so the two flexible
   columns get real width back — 181px and 113px at 1280px.
   ============================================================================ */
@media (min-width: 1181px) and (max-width: 1400px) {
  .korders-detail { width: 300px; flex: 0 0 300px; }
  .korders-thead,
  .korders-row {
    grid-template-columns: 88px 1.6fr 1fr 74px 70px 116px;
    gap: 10px;
  }
}


/* ============================================================================
   18. AI, LOYALTY — action groups wrap instead of crushing their labels
   ============================================================================ */
@media (max-width: 560px) {
  /* Recommendation rows are `.price-list li`, a four-track GRID
     (1.6fr 1.2fr 60px auto) — not a flex row. On a 320px screen the two
     flexible tracks resolve to 49px and 37px, so the item name rendered in
     46px. Two tracks over two rows gives every field a readable width. */
  .price-list li {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: 6px;
  }

  /* Reward rows: 44px | name | chip | actions — the last two collide. */
  .rewards li.reward-row {
    grid-template-columns: 44px minmax(0, 1fr);
    row-gap: 8px;
  }
  .rewards li.reward-row > .rw-status { grid-column: 2; justify-self: start; }
  .rewards li.reward-row > .rw-actions { grid-column: 1 / -1; justify-content: flex-end; }
  .rw-actions { flex-wrap: wrap; }
}


/* ============================================================================
   19. TOUCH TARGETS — search fields and table checkboxes
   ----------------------------------------------------------------------------
   `.search-sm input` had no height of its own, rendering an 18px-tall hit area,
   and the row-select checkboxes in the data tables are the browser default
   13x13. Both are well under a fingertip on the tablet the floor staff use.
   ============================================================================ */
@media (max-width: 1024px) {
  .search-sm { padding: 8px 12px; }
  .search-sm input { min-height: 32px; font-size: 13px; }

  /* Row-level actions and header pills were 25-28px tall. */
  .row-btn, .mini-btn, .th-held, #posHeldBtn { min-height: 32px; }

  /* 24px rather than a full 32px square: a 32px checkbox visually overwhelms a
     dense inventory table, and the surrounding cell padding adds forgiving
     space around it. This is a deliberate compromise, not an oversight. */
  .table input[type="checkbox"],
  .table input[type="radio"],
  thead input[type="checkbox"],
  tbody input[type="checkbox"] { width: 24px; height: 24px; }
}


/* ============================================================================
   20. POS — short viewports (tablet landscape)
   ----------------------------------------------------------------------------
   `.pos-wrap` is `height: calc(var(--screen-h) - 188px)` with `min-height:
   520px`. On a 600px-tall landscape tablet the minimum wins, so the panel is
   taller than the space left under the topbar and the pay actions are pushed
   below the fold: the Charge button was measured at bottom=648 in a 600px
   viewport — present, correct-looking, and impossible to press.

   The two POS columns already scroll internally, so the wrap does not need a
   floor on a screen that cannot honour it.
   ============================================================================ */
@media (max-height: 720px) and (min-width: 561px) {
  .pos-wrap { min-height: 0; height: calc(var(--screen-h) - 172px); }
}

/* Below ~480px of height there is no arrangement that fits: at 740x360 the
   cart column gets 188px while its header (80px) plus totals and the four
   order actions (212px) need nearly 300px, so the footer was clipped away
   entirely by `.pos-right { overflow: hidden }`. Rather than hide the Hold /
   Clear / Send-to-kitchen actions to make it fit — which would remove
   functionality with no way to get it back — let the POS page scroll. Nothing
   is clipped and every control is reachable; you just scroll to it. */
@media (max-height: 480px) {
  .pos-wrap { height: auto; min-height: 0; }
  .pos-left { height: auto; min-height: 0; overflow: visible; }
  .pos-left .menu-grid { flex: none; overflow: visible; }
  .pos-right { overflow: visible; }
  .view[data-view="pos"] { padding-bottom: 24px; }
}

/* Phone landscape is shorter still, and there the cart is a bottom sheet, so
   the product column is what has to give. */
@media (max-height: 560px) and (max-width: 560px) {
  .pos-right { max-height: 92vh; max-height: 92dvh; }
}


/* ============================================================================
   21. POS — product cards on short (landscape phone) viewports
   ----------------------------------------------------------------------------
   The card is ~260px tall (118px image + name + price + padding). In landscape
   on a phone the product column is only ~210px, so the first row of cards was
   taller than the space it had and its centre fell behind the collapsed cart
   sheet — the grid scrolled, but the card you were aiming at could not be
   tapped. Shrinking the tile makes a full row fit the column again.
   ============================================================================ */
@media (max-height: 480px) {
  .view[data-view="pos"] .menu-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(132px, 100%), 1fr));
    gap: 8px;
  }
  .view[data-view="pos"] .menu-item { padding: 7px; }
  .view[data-view="pos"] .mi-img { height: 58px; font-size: 26px; }
}

/* Also guard the very narrow phone: 150px tiles do not fit twice in 288px of
   content box, so the grid drops to one very wide card per row. */
@media (max-width: 380px) {
  .view[data-view="pos"] .menu-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(132px, 100%), 1fr));
  }
}


/* ============================================================================
   22. TOASTS — do not land on the POS cart sheet
   ----------------------------------------------------------------------------
   `.toast-container` is `position: fixed; bottom: 24px; z-index: 70`, and the
   phone cart sheet sits at z-index 60 — so the confirmation toast fired when an
   item is added covered the Charge button underneath it. Measured at 360x740:
   the hit test at the button's centre returned the toast, not the button.
   ============================================================================ */
@media (max-width: 560px) {
  body:has(.view[data-view="pos"].active) .toast-container { bottom: 248px; }
}


/* ============================================================================
   23. WORKFORCE — the staff-name column is not viewport-dependent
   ----------------------------------------------------------------------------
   `.shr-week`'s first track is a flat 110px at every width, and the cell also
   carries a remove button, so the name itself was measured at 68px — "Giovanni
   Battista Della Rovere-Montefeltro" renders as about two characters and an
   ellipsis on a 1920px monitor. Widening the label track costs the seven day
   columns 30px between them (they are minmax(120px, 1fr)) and buys ~50% more
   name.
   ============================================================================ */
.view[data-view="workforce"] .shr-week {
  grid-template-columns: 140px repeat(7, minmax(120px, 1fr));
}

/* Below 1400px the grid scrolls horizontally (section 8), so a wider label
   column costs only scroll distance — and it has to be wider there, because the
   32px touch-sized remove button plus gap and padding takes 56px of the cell
   and left the name at 86px. Both rules live here rather than next to the
   scroll rule: media queries add no specificity, so whichever
   `.view[data-view="workforce"] .shr-week` rule comes last in this file wins,
   and keeping them adjacent is what makes that safe to edit. */
@media (max-width: 1400px) {
  .view[data-view="workforce"] .shr-week {
    grid-template-columns: 176px repeat(7, minmax(120px, 1fr));
  }
}


/* ============================================================================
   24. SIGN-IN / SIGN-UP / RESET — touch targets on the first screen
   ----------------------------------------------------------------------------
   These pages are outside the SPA and carry their own stylesheets, so none of
   the rules above reached them. Measured on login.html at 390px: the password
   reveal toggle was 24x24, the "remember me" checkbox 20x20 and the "Forgot
   password?" link 16px tall — three of the four things anyone touches on that
   screen. (The `.ax-blob` gradients that bleed off the edge are deliberate and
   are left alone; the page itself does not scroll sideways.)
   ============================================================================ */
@media (max-width: 1024px) {
  .auth-card .pwd-toggle,
  .lf-input .pwd-toggle { min-width: 40px; min-height: 40px; display: inline-flex; align-items: center; justify-content: center; }

  .auth-card .checkbox input[type="checkbox"],
  .form-row .checkbox input[type="checkbox"] { width: 22px; height: 22px; }

  /* A 16px-tall text link is a mis-tap waiting to happen next to a checkbox. */
  .auth-card .link-btn,
  .form-row .link-btn { min-height: 36px; display: inline-flex; align-items: center; }

  /* Keep the row readable once those grow. */
  .auth-card .form-row { flex-wrap: wrap; row-gap: 6px; align-items: center; }
}


/* ============================================================================
   25. REMAINING SUB-11px TEXT ON TOUCH SCREENS
   ----------------------------------------------------------------------------
   Two tokens survived the pass in section 15 because they are set inside
   component rules rather than on the shared chip/eyebrow classes: the per-item
   VAT note on POS product tiles (9px — the smallest text in the product grid)
   and the forecast chart's axis ticks (10px). Both are read at arm's length on
   a tablet on the floor.
   ============================================================================ */
@media (max-width: 1024px) {
  .mi-vat-note { font-size: 11px; }
  .fc-chart-wrap .chart-axis .fc-tick { font-size: 11px; }
}


/* ============================================================================
   26. AUTH FOOTER — legal links wrap, and can be tapped
   ----------------------------------------------------------------------------
   auth-redesign.css (login / signup / reset) already wraps its footer nav;
   auth.css (delete-account) does not, so the row of policy links ran 54px past
   the right edge at 1024px with nothing able to scroll to it. The links are
   also 15px tall, which is the smallest tap target on any of these pages.
   ============================================================================ */
.auth-footer nav { flex-wrap: wrap; }

@media (max-width: 1024px) {
  .auth-footer nav { row-gap: 4px; justify-content: center; }
  .auth-footer nav a,
  .auth-card .auth-alt a,
  .auth-foot a,
  .auth-switch a { min-height: 32px; display: inline-flex; align-items: center; }
}


/* ============================================================================
   27. TYPOGRAPHY — tokens that needed a matching selector
   ----------------------------------------------------------------------------
   Section 15 set an 11px floor using single-class selectors, which lost to the
   more specific per-component rules that actually set these sizes
   (`.hr-salary-pill .hr-salary-period`, `.ktbl-resv-time .ampm`, and so on).
   Repeating the original selector here wins on source order without needing
   !important, and keeps the relationship obvious to the next reader.

   Deliberately NOT raised: `.avatar.sm` (10px). That is two initials inside a
   22px circle — a glyph, not text to read — and enlarging it would break the
   avatar rather than help anyone.
   ============================================================================ */
@media (max-width: 1024px) {
  .hr-salary-pill .hr-salary-period { font-size: 11px; }
  .pr-ai-card .ai-pill-sm           { font-size: 11px; }
  .view[data-view="dashboard"] .resv-day { font-size: 11px; }
  .krsv-up-top .krsv-pill           { font-size: 11px; }
  .krsv-pill                        { font-size: 11px; }
  .ktbl-resv-time .ampm             { font-size: 10.5px; }
  .ktbl-rel                         { font-size: 11px; }
  .mi-tag                           { font-size: 11px; }
}


/* ============================================================================
   28. TOUCH TARGETS — a floor for buttons and text inputs
   ----------------------------------------------------------------------------
   A long tail of controls sit at 23-30px: the floor-plan date/time pickers, the
   reports range Apply button, the pricing AI actions, the Waste and Customers
   tab strips, the menu availability toggle. Each is only a few pixels short, so
   rather than chase them one class at a time this sets a single floor for
   buttons and text-entry controls on touch-sized viewports.

   Checkboxes and radios are excluded — they are sized in section 19, where a
   24px square is a deliberate compromise for dense tables.
   ============================================================================ */
@media (max-width: 1024px) {
  button,
  .btn,
  [role="button"],
  input:not([type="checkbox"]):not([type="radio"]),
  select,
  textarea { min-height: 32px; }

  /* Keep icon-only buttons square rather than letting the floor stretch them. */
  .icon-btn, .mini-btn, .cat-icon-btn { min-width: 32px; }
}


/* ============================================================================
   29. LAST-MILE FIXES AT 320px
   ----------------------------------------------------------------------------
   Two things still did not fit on the narrowest supported phone.

   Settings rows: the existing ≤760px rule stacks `.set-row` into a column, but
   its children keep `min-width: auto`, so a select or input with intrinsic
   width pushed the row 18px past the card.

   Upcoming-reservation rows: `.krsv-up-top` is a flex line holding the guest
   name and the status pill with `justify-content: space-between`. At 320px the
   pill took what it needed and the name was left with 88px. Same fix as the
   main reservation list in section 14 — wrap rather than starve the name.
   ============================================================================ */
@media (max-width: 560px) {
  .set-row > * { min-width: 0; max-width: 100%; }
}

@media (max-width: 430px) {
  .krsv-up-top { flex-wrap: wrap; row-gap: 4px; }
  .krsv-up-name { min-width: 150px; flex: 1 1 auto; }
}


/* ============================================================================
   30. TOUCH TARGETS — in-table links and icon-only row buttons
   ----------------------------------------------------------------------------
   Two shapes the section-28 floor could not reach:
     • A link inside a table cell (supplier email, and the same pattern
       elsewhere) is an inline box, so `min-height` alone does nothing — it
       needs to become inline-flex first. Measured 187x16.
     • `.shr-row-remove` is an icon-only button 20px WIDE; the floor only set a
       height.
   ============================================================================ */
@media (max-width: 1024px) {
  .table a,
  td a,
  .sup-link { min-height: 32px; display: inline-flex; align-items: center; }

  .shr-row-remove,
  .view[data-view="workforce"] .shr-row-remove { min-width: 32px; }
}

/* Table select-all / select-row checkboxes: 28px rather than the 32px button
   floor. A 32px square visibly inflates every row of a dense inventory table,
   and the checkbox sits inside a padded cell that adds forgiving space around
   it. This is a deliberate, bounded exception — see the matching floor in the
   audit probe. */
@media (max-width: 1024px) {
  .table input[type="checkbox"],
  .table input[type="radio"],
  thead input[type="checkbox"],
  tbody input[type="checkbox"] { width: 28px; height: 28px; }
}


/* ============================================================================
   31. POS — order-action buttons grow with their label
   ----------------------------------------------------------------------------
   `.pos-right-foot .pay-actions .btn` is a hard `height: 50px`. Once the cart
   column narrows (tablet, or the phone sheet), "Send to kitchen" wraps to two
   lines inside a box that cannot grow, so the second line rendered outside the
   button. Fixed heights on text that can wrap is the anti-pattern; a minimum
   height keeps the touch target while letting the label fit.
   ============================================================================ */
@media (max-width: 1280px) {
  .pos-right-foot .pay-actions .btn,
  .pos-right-foot .pay-actions .btn.primary.large {
    height: auto;
    min-height: 50px;
    padding: 8px 10px;
    line-height: 1.2;
    text-align: center;
  }
}

/* ============================================================
   Settings tab bar — reachable at every width
   ------------------------------------------------------------
   The bar already had `overflow-x: auto` and a hidden scrollbar, and it
   already scrolled by touch and by a trackpad's sideways gesture. What it
   could not do was scroll for anyone holding a MOUSE: a vertical wheel over a
   horizontally-overflowing element does nothing in any browser, and with the
   scrollbar hidden there was no other way in. Measured at 900px: 868px of bar
   against 2321px of tabs, so roughly five of the sixteen were unreachable —
   including Security, Backup and Legal.

   The wheel translation lives in app.js (it cannot be done in CSS). These are
   the parts that can:
     - a swipe must not turn into browser back-navigation halfway along a bar
       this long, which `overscroll-behavior-x` prevents;
     - the momentum flick on iOS needs saying explicitly on older WebKit.

   `scroll-behavior: smooth` is deliberately NOT set here: it would animate
   every wheel tick, and a queue of overlapping animations reads as lag rather
   than smoothness. The two places that should glide — bringing the active tab
   into view — ask for it per call.
   ============================================================ */
.view[data-view="settings"] .settings-nav {
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  /* The bar scrolls sideways; a vertical drag on it still belongs to the page. */
  touch-action: pan-x pan-y;
}
/* Restated next to the behaviour it belongs to. styles.css already hides the
   bar for WebKit and Firefox; without this the wheel handler would hand the
   user a scrollbar they were promised they would not see. */
.view[data-view="settings"] .settings-nav { scrollbar-width: none; -ms-overflow-style: none; }
.view[data-view="settings"] .settings-nav::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* A tab marked `hidden` must actually be hidden.
   `.settings-nav .sn-item { display: flex }` outranks the `hidden` attribute's
   UA rule, so `dangerNav.hidden = !isAdmin` in app.js — "Staff/managers don't
   have permission to wipe the entire account, so we hide the entry point
   entirely" — set an attribute that changed nothing, and the Danger Zone tab
   rendered for every role. It was easy to miss while it sat off the right-hand
   edge of an unscrollable bar; making the bar scrollable puts it one flick from
   anyone, so it is spelled out here rather than left for later. */
.view[data-view="settings"] .settings-nav .sn-item[hidden],
.settings-nav .sn-item[hidden] { display: none; }
