/* ══════════════════════════════════════════════════════════════════════════════
   ViyaBook — mobile fixes
   ────────────────────────────────────────────────────────────────────────────
   Cross-cutting mobile corrections that must apply to EVERY page, including the
   standalone ones (login, customer portal, admin) that do not load viyabook.css.
   Injected into every page by tools/add-mobile-fixes.js; keep it last in <head>
   so it wins ties against page stylesheets.

   Deliberately narrow. It contains only corrections that are provably wrong
   without one, not visual preference - anything needing a real device to judge
   is left alone.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── 1. iOS focus-zoom ────────────────────────────────────────────────────────
   Safari on iOS auto-zooms the whole page when a focused text field computes to
   a font-size below 16px, and it does NOT zoom back out on blur. The user is
   stranded on a magnified, horizontally-scrolling page after tapping any field.
   This is deterministic below the 16px threshold, not a rendering quirk.

   140 declarations across 43 files were below it - search boxes, filter rows,
   modal fields, and inline style="font-size:13px" on <select>. Fixing each site
   would mean touching all 43 files and would regress the next time a 14px field
   is added, so the floor is enforced once here.

   !important is required rather than stylistic: several offenders are inline
   styles, which no external rule can beat otherwise.

   UNCONDITIONAL - no media query. iPadOS Safari zooms on sub-16px focus exactly
   as iPhone does, so an earlier <=767px wrapper left every tablet width broken:
   measured at 768px AND 1024px, 24 controls per pass still computed 12-14.5px
   (invoices 7, checkout 5, customers 5, products 5, trips 2). The comment that
   used to sit here said tablets were left alone "so the tablet filter bar is
   untouched" - that was the bug, not a trade-off. Desktop pointer users never
   focus-zoom, so a 16px floor there costs nothing and keeps one rule instead of
   a width-split pair. The excluded input types render as controls rather than
   text, so they never trigger the zoom.

   SPECIFICITY IS LOAD-BEARING HERE - do not "simplify" the selector.
   The first version of this rule was written as three bare selectors:

     input:not(...)x9,  textarea,  select   { font-size:16px !important }

   which gives `input` (0,9,1) but leaves `textarea` and `select` at (0,0,1) -
   weaker than almost any real rule. !important only wins ties; it does not
   outrank a more specific !important. So 32 declarations still beat it and
   those fields kept on zooming: 24 select, 6 textarea, 2 input, in page <style>
   blocks (which load after this file) and in design-system.css / viyabook.css /
   responsive-fix.css. Examples: `.input-box select` (0,1,1),
   `html body .filter-bar select` (0,1,3), `.field textarea` (0,1,1),
   `body.trips-page .page-head .head-actions .select` (0,4,1).

   Grouping the three element types behind :is() lets all of them carry the
   `html body` prefix and the full :not() chain, so every one is (0,9,3). That
   clears all 32. :is() takes the specificity of its most specific argument -
   here a bare element, (0,0,1) - so the chain, not the group, does the work.

   Two ID-level rules still outrank this and are fixed at their own site
   (an ID beats any number of classes, so no shared rule can reach them):
     pages/trip-detail.html  #tripExpenseModal input/select/textarea  (1,0,1)
   One more input rule is stronger still and is handled just below.

   Not a risk: components/layout.js sets 14.5px on the global topbar search
   input, but hides that input outright under 767px, so it can never be
   focused on a phone - but it IS focusable from 768px up, which is why it shows
   in the tablet measurements above and is now covered. */
html body :is(input, select, textarea):not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):not([type="submit"]):not([type="button"]):not([type="image"]):not([type="reset"]) {
  font-size: 16px !important;
}

/* ── 1b. The one input rule section 1 cannot outrank ──────────────────────────
   styles/design-system.css sets the canonical filter-search box to 14px with an
   eleven-long :not() chain, which lands at (0,13,1) - higher than section 1's
   (0,9,3) because b is compared before c. Matching its selector exactly and
   adding the `html body` prefix makes this (0,13,3), which wins on c.

   Its height:44px and horizontal padding are left alone: 16px text clears a
   44px control, so the box does not move.

   Unwrapped along with section 1 - design-system.css sets that 14px at every
   width, so this counter-rule has to run at every width too. This is what
   floors #customerSearch / #searchInput (.vb-filter-search) on customers and
   products, measured at 14px on tablet before the change. */
html body .main-content .vb-search input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]):not([type=color]):not(.mini-input):not(.row-input):not(.row-desc):not(.qty-val):not(.addr-phone-input):not(.editable-price),
html body .main-content input.vb-filter-search:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]):not([type=color]):not(.mini-input):not(.row-input):not(.row-desc):not(.qty-val):not(.addr-phone-input):not(.editable-price) {
  font-size: 16px !important;
}

/* ── 1c. The OTHER design-system rule section 1 cannot outrank ────────────────
   Section 1b fixed the `.vb-search` / `.vb-filter-search` boxes. The rule that
   sizes every OTHER text input inside the app shell was still winning:

     styles/design-system.css:503
     .main-content input:not([type=checkbox]):not([type=radio]):not([type=range])
       :not([type=file]):not([type=color]):not(.mini-input):not(.row-input)
       :not(.row-desc):not(.qty-val):not(.addr-phone-input):not(.editable-price)
       { font-size: var(--ds-fs-input) !important }   -- which is 14px

   Eleven :not() arguments plus `.main-content` is (0,12,1) against section 1's
   (0,9,3), and b is compared before c, so it won on every page that uses the
   standard shell. Measured on Gas Collection: twelve controls computing 14px
   with section 1 loaded and doing nothing — the Quick Add search among them.

   Same remedy as 1b: match the selector exactly and prefix `html body`, which
   is (0,12,3) and wins on c without an arms race. The rule's sibling `select`
   branch is only (0,3,1) and section 1 already beats it, so it is not repeated
   here — and the six :not() classes design-system exempts (table/inline
   controls) keep falling through to section 1, which floors them at 16px too.

   Height is deliberately untouched: these controls compute to 44px, and 16px
   text clears 44px, so nothing moves.

   Unwrapped along with section 1, for the same reason as 1b. */
html body .main-content input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]):not([type=color]):not(.mini-input):not(.row-input):not(.row-desc):not(.qty-val):not(.addr-phone-input):not(.editable-price) {
  font-size: 16px !important;
}

/* ── 2. Media overflow floor ──────────────────────────────────────────────────
   A safety net, not a fix for a known break: the page layouts audited clean for
   horizontal overflow. Images and embeds arriving from user data (uploaded
   logos, scanned bills, pasted content) carry their own intrinsic width and are
   the one thing that can push a correct layout wider than the viewport. Capping
   them costs nothing when they already fit. */
@media (max-width: 768px) {
  img, svg, video, canvas, iframe, embed, object {
    max-width: 100%;
    height: auto;
  }
}


/* ── 3. Dashboard "Recent invoices" table overflows the phone ─────────────────
   On a 390px screen the AMOUNT column is clipped mid-number and STATUS is off
   screen entirely. The cause is not the width percentages - those are fine and
   have been re-tuned many times - it is the min-width floors on the base rule:

     .col-invoice  min-width:120px
     .col-customer min-width:220px
     .col-date     min-width:100px
     .col-amount   min-width:140px
     .col-status   min-width:100px      = 680px minimum

   The phone rules that follow override `width` and hide td.col-date, but none of
   them resets min-width, and the floors apply to <th> as well as <td>. With
   table-layout:fixed the header row's floors set the column widths, so the table
   insists on ~580px inside a ~340px container and overflows. .vbd-table-wrap has
   overflow-x:auto, which turns that into a sideways scroll the user never finds.

   Dropping the floors on phones lets the existing percentages do their job. The
   inner amount cell has its own floors (.invoice-amount-currency 36px +
   .invoice-amount-value 100px = 136px), so those are released too - otherwise the
   column still cannot shrink. Nothing here changes >=768px. */
@media (max-width: 767px) {
  body.page-dashboard .recent-invoices-table th,
  body.page-dashboard .recent-invoices-table td {
    min-width: 0 !important;
  }
  /* td.col-date is already hidden on phones; hide the matching header cell too so
     the header and body keep the same column count. */
  body.page-dashboard .recent-invoices-table th.col-date,
  body.page-dashboard .recent-invoices-table td.col-date {
    display: none !important;
  }
  body.page-dashboard .recent-invoices-table .invoice-amount-currency {
    flex: 0 0 auto !important;
    width: auto !important;
    min-width: 0 !important;
  }
  body.page-dashboard .recent-invoices-table .invoice-amount-value {
    flex: 0 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
  }
  body.page-dashboard .recent-invoices-table th.col-amount,
  body.page-dashboard .recent-invoices-table td.col-amount {
    padding-right: 10px !important;
  }
  /* Belt and braces: if any column still refuses to shrink, scroll rather than
     letting the panel push the whole page sideways. */
  body.page-dashboard .recent-invoices-table-wrapper {
    max-width: 100% !important;
    overflow-x: auto !important;
  }
}

/* ── 4. Mobile card language ──────────────────────────────────────────────────
   One visual language for every phone screen, matching the approved mockup:
   hairline border instead of a heavy shadow, no coloured top bar, icon ABOVE a
   centred label, compact amount, small type scale.

   The KPI card is not a dashboard component - .vb-kpi-* appears on 106 elements
   across roughly 25 pages (trips, invoices, customers, reports, projects), so
   styling it here reaches all of them at once instead of page by page.

   Specificity note: dashboard.css ships a competing phone layout under
   `html body.dash-mobile-v5 .vbd-cards > .vbd-card` (0,2,2 + html). A plain
   `.vb-kpi-card` rule loses to it, which is exactly why an earlier attempt at
   this had no visible effect. The selectors below carry `html body` so the
   dashboard picks up the same language as everywhere else rather than keeping
   its own. Remove this file's <link> to revert every page in one step. */
@media (max-width: 767px) {

  html body .vb-kpi-card,
  html body .vbd-cards > .vbd-card {
    background: #fff !important;
    border: 0.5px solid #E4EDF8 !important;
    border-radius: 14px !important;
    box-shadow: none !important;
    padding: 11px 5px !important;
    min-height: 0 !important;
    align-items: center !important;
    text-align: center !important;
    overflow: hidden !important;
  }
  /* The coloured strip along the card top is desktop chrome; it reads as noise
     at 84px wide. */
  html body .vb-kpi-card::before,
  html body .vbd-cards > .vbd-card::before { display: none !important; }

  /* Markup order is label then icon, so column-reverse lifts the icon on top
     without touching any HTML. */
  html body .vb-kpi-top {
    flex-direction: column-reverse !important;
    align-items: center !important;
    justify-content: center !important;
    gap: 7px !important;
    width: 100% !important;
  }
  html body .vb-kpi-icon {
    flex: 0 0 30px !important;
    width: 30px !important;
    height: 30px !important;
    border-radius: 9px !important;
    box-shadow: none !important;
  }
  html body .vb-kpi-icon i,
  html body .vb-kpi-icon svg {
    width: 16px !important; height: 16px !important; font-size: 16px !important;
  }
  html body .vb-kpi-label {
    font-size: 9px !important;
    letter-spacing: .05em !important;
    line-height: 1.2 !important;
    text-align: center !important;
    color: #64748B !important;
    white-space: normal !important;
  }
  html body .vb-kpi-value {
    font-size: 15px !important;
    line-height: 1.15 !important;
    margin-top: 3px !important;
    text-align: center !important;
    white-space: nowrap !important;
  }
  /* dashboard.js already renders a compact "16.46K" alongside the full amount
     and labels it "for the compact 4-in-a-row phone KPI cards"; it has been
     hidden at every width since. Show it - the full number does not fit. */
  html body .vbd-amt .vamt-full,
  html body .vb-kpi-value .vamt-full { display: none !important; }
  html body .vbd-amt .vamt-min,
  html body .vb-kpi-value .vamt-min { display: inline !important; }
  html body .vb-kpi-value .kpi-cur {
    font-size: 9px !important;
    margin-inline-end: 2px !important;
    opacity: .85 !important;
  }
  /* Sub-labels ("Collected & due", "Across 15 invoices") cannot fit at this
     width. Untouched from 768px up. */
  html body .vb-kpi-footer { display: none !important; }

  /* Panels, cards and tab strips get the same hairline treatment. */
  html body .vb-cardify,
  html body .vbd-panel,
  html body .vb-filter-bar {
    border: 0.5px solid #E4EDF8 !important;
    border-radius: 16px !important;
    box-shadow: none !important;
  }
  html body .vb-tab {
    font-size: 12px !important;
    border-radius: 10px !important;
  }
}

@media (max-width: 359px) {
  html body .vb-kpi-card,
  html body .vbd-cards > .vbd-card { padding: 9px 3px !important; }
  html body .vb-kpi-icon { flex: 0 0 26px !important; width: 26px !important; height: 26px !important; }
  html body .vb-kpi-icon i,
  html body .vb-kpi-icon svg { width: 14px !important; height: 14px !important; font-size: 14px !important; }
  html body .vb-kpi-label { font-size: 8px !important; }
  html body .vb-kpi-value { font-size: 13px !important; }
}

/* ── 5. KPI cards: ALWAYS one row, every page, every breakpoint ───────────────
   Card counts vary widely - 2 on admin business detail, 4 on most pages, 5 on
   subscription, 7 on sales and gas collection, 8 on reports, 10 on staffs, 11 on
   daily sales report - and the grids were all fixed at repeat(4), so anything
   above four wrapped onto extra rows.

   `grid-auto-flow: column` puts every card in ONE row without needing to know
   how many there are, so no per-page column counts to maintain. grid-auto-columns
   gives each card an equal share of the width (1fr) but never lets it fall below
   a legible floor.

   The floor is what makes this honest. Eleven cards inside 340px of phone is
   29px per card - narrower than the 30px icon - so "fit them all on screen" is
   not physically available. minmax(96px, 1fr) keeps every card readable and lets
   the ROW scroll sideways instead, which is still one row and is how iOS and
   Android present overflowing card strips. Four cards on a phone still divide the
   full width and never scroll; only the crowded pages do.

   Specificity: this has to outrank `body.page-sales .vb-kpi-grid` (0,2,1) in
   sales.html and `html body.dash-mobile-v5 .vbd-cards` (0,2,2) in dashboard.css.
   `html body[class]` scores (0,2,2) and this file loads last, so it wins the tie
   against the dashboard and beats the sales rule outright. */
/* SCOPED to genuinely crowded strips (5+ cards).
   This used to convert EVERY .vb-kpi-grid into a single-row horizontal scroller.
   For a 4-card row that produced four ~82px cards on a 390px phone - and a money
   figure does not fit in 82px. Measured on pages/reports.html: .vb-kpi-value held
   "MVR 0.00" needing 117px inside 56px, and .vb-kpi-number rendered at 9px in a
   25px box. The row did not scroll (as intended) but every figure in it was cut.

   Rows of four or fewer now fall through to the shared 2-up mobile grid in
   vb-kpi.css (`repeat(2, minmax(0,1fr))` at <=767px), which is the documented
   default and gives ~160px per card - enough for the amount it carries. Only rows
   of five or more become the scroll strip, which is the case the strip was written
   for and the case that cannot fit on a phone any other way. */
html body[class] .vb-kpi-grid:has(> *:nth-child(5)),
html body[class] .vbd-cards:has(> *:nth-child(5)) {
  display: grid !important;
  grid-auto-flow: column !important;
  grid-template-columns: none !important;
  grid-auto-columns: minmax(88px, 1fr) !important;
  overflow-x: auto !important;
  overflow-y: visible !important;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}
html body[class] .vb-kpi-grid > *,
html body[class] .vbd-cards > * {
  scroll-snap-align: start;
  min-width: 0 !important;
}
/* The strip scrolls; the scrollbar itself is chrome nobody needs on a phone. */
html body[class] .vb-kpi-grid::-webkit-scrollbar,
html body[class] .vbd-cards::-webkit-scrollbar { height: 0; display: none; }
html body[class] .vb-kpi-grid,
html body[class] .vbd-cards { scrollbar-width: none; }

/* The floor is per-breakpoint so the 14 pages that have exactly four cards keep
   dividing the full width and never scroll, while the crowded pages do:
     390px phone: 4 cards = 79px each -> above the 76px floor, fits
                  7 cards = 42px each -> forced to 76px, row scrolls
     320px SE   : 4 cards = 61px each -> above the 58px floor, fits
     768px tab  : 7 cards = 93px each -> above the 88px floor, fits
   Picking one global floor would have made four-card phones scroll for no
   reason, which is worse than what they do today. */
@media (max-width: 767px) {
  html body[class] .vb-kpi-grid:has(> *:nth-child(5)),
  html body[class] .vbd-cards:has(> *:nth-child(5)) { grid-auto-columns: minmax(76px, 1fr) !important; }

  /* MONEY DOES NOT FIT IN 76px, and it was being cut without an ellipsis.
     Measured on pages/sales.html at 390px: .vb-kpi-value held "MVR 0.00" with
     scrollWidth 117px inside clientWidth 62px, overflow:hidden and
     text-overflow:CLIP. Real data is worse - "MVR 25,300.00" rendered as
     "MVR 25,3". A truncated amount that shows no ellipsis does not look broken,
     it looks like a smaller number, which is the one failure an accounting screen
     must never have. pages/reports.html was the same at 9px font in a 25px box.

     The 76px floor above is right for the 4-card pages it was written for, and the
     note above explains why a single global floor would make them scroll for no
     reason. So this raises the floor ONLY for a strip that is genuinely crowded -
     five or more cards, which is exactly the set that scrolls anyway. Those pages
     already scroll sideways; a wider card costs a little more scrolling and buys a
     figure the user can actually read. Four-card pages are untouched and still
     divide the full width.

     :has() is supported by the Chromium and WebKit versions Capacitor ships and by
     every browser this app targets; where it is unsupported the selector is simply
     dropped and behaviour falls back to the 76px floor above - no worse than today. */
  html body[class] .vb-kpi-grid:has(> *:nth-child(5)),
  html body[class] .vbd-cards:has(> *:nth-child(5)) {
    grid-auto-columns: minmax(148px, 1fr) !important;
  }
}
@media (max-width: 359px) {
  html body[class] .vb-kpi-grid:has(> *:nth-child(5)),
  html body[class] .vbd-cards:has(> *:nth-child(5)) { grid-auto-columns: minmax(58px, 1fr) !important; }
  html body[class] .vb-kpi-grid:has(> *:nth-child(5)),
  html body[class] .vbd-cards:has(> *:nth-child(5)) {
    grid-auto-columns: minmax(132px, 1fr) !important;
  }
}

/* ── 6. 44x44 touch targets without changing any layout ───────────────────────
   104 interactive controls across 29 files declare a height or width below 44px
   (worst: checkout-mobile.css 12, admin/users.html 7), missing the WCAG 2.5.5 and
   Apple HIG minimum.

   Forcing min-height/min-width:44px would change the SIZE of every one of them,
   and many sit in table rows and toolbars laid out around their current
   dimensions. Doing that blind, with no way to render the result, trades a
   tap-accuracy problem for broken layouts on pages I cannot see. So the HIT AREA
   is extended instead of the box: a transparent pseudo-element centred on the
   control claims 44x44 of touch space while the control keeps its painted size
   and its place in the flow. Nothing moves.

   Scoped narrowly on purpose. .btn, .icon-btn, .close-btn, .modal-close and
   several others ALREADY define ::after in this project (ripples, carets,
   decorations); claiming that pseudo-element would silently destroy them. Those
   classes are excluded, so this applies only to bare <button> and [role=button]
   elements that own no ::after of their own. Narrower coverage, zero breakage -
   the right trade when the result cannot be inspected.

   pointer:coarse only - a mouse does not need the help. */
@media (max-width: 767px) and (pointer: coarse) {
  button:not([class*="btn"]):not([class*="close"]):not([class*="-x"]):not(.viya-bottom-nav *):not(.vism-row):not(.viya-more-sheet-item),
  [role="button"]:not([class*="btn"]):not([class*="close"]) {
    position: relative;
  }
  button:not([class*="btn"]):not([class*="close"]):not([class*="-x"]):not(.viya-bottom-nav *):not(.vism-row):not(.viya-more-sheet-item)::after,
  [role="button"]:not([class*="btn"]):not([class*="close"])::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
    z-index: 0;
    background: transparent;
  }
}

/* ── 7. Checkout cart: restore the item list as the scroll container ──────────
   Symptom: products added to the cart cannot all be reached on mobile - the last
   rows sit under the payment bar and the first rows scroll away.

   Root cause is not a missing rule, it is ~30 CONFLICTING ones. .cart-items-wrap
   is declared flex:1 1 auto / 1 1 0 / 0 1 auto / 0 0 auto in different places,
   with min-height 0,40,48,64,96,100,120,180px and max-height none,260,360,420px,
   36vh,38dvh, spread across checkout.html inline <style> blocks and
   checkout-mobile.css. The pair that decides the mobile outcome is:

     checkout-mobile.css  .cart-items-wrap { overflow: visible !important;
                                             height: auto !important; }
     checkout-mobile.css  .cart-panel      { overflow-y: auto !important; }
     checkout-mobile.css  .cart-actions    { position: fixed !important; bottom: … }

   That deletes the item list's own scroll area, makes the WHOLE panel scroll, and
   floats the footer above it - so the last products are covered by the payment
   bar and there is nothing to scroll them out from under it.

   The fix restores the intended hierarchy: the panel is a flex column that does
   not scroll, the item list is the single scroll container with min-height:0 (a
   flex child without it refuses to shrink below its content and the scrollbar
   never appears), and the footer returns to normal flow so it occupies real
   layout space instead of overlaying rows.

   Scoped to <=767px. Desktop and tablet POS layouts are untouched. */
@media (max-width: 767px) {
  html body .cart-panel {
    display: flex !important;
    flex-direction: column !important;
    min-height: 0 !important;
    overflow: hidden !important;      /* the PANEL must not scroll - the list does */
    padding-bottom: 0 !important;
  }
  html body .cart-header,
  html body .cart-table-head,
  html body .cart-totals {
    flex: 0 0 auto !important;
  }
  html body .cart-items-wrap {
    flex: 1 1 auto !important;
    min-height: 0 !important;         /* without this the list never scrolls */
    height: auto !important;
    max-height: none !important;
    overflow-y: auto !important;
    overflow-x: hidden !important;
    -webkit-overflow-scrolling: touch !important;
    overscroll-behavior: contain !important;
    scroll-behavior: smooth;
  }
  /* Back into the flow. As position:fixed it reserved no space, so the rows
     beneath it were unreachable no matter how far the list scrolled. */
  html body .cart-actions {
    position: static !important;
    flex: 0 0 auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;
    padding-bottom: calc(10px + env(safe-area-inset-bottom)) !important;
  }
}

/* ── 8. Grid children that cannot shrink ─────────────────────────────────────
   381 multi-column grids across 62 files declare bare `1fr` rather than
   `minmax(0, 1fr)`. A `1fr` track resolves its floor to `min-width: auto`, which
   is the child's min-content size - so one long customer name, island route or
   unbroken amount makes the column wider than its share, and the grid pushes the
   page sideways. It is the single most common overflow cause in this codebase.

   Rewriting 381 declarations blind is the wrong trade: each would need checking
   for intentional min-content sizing, across files I cannot render.
   `dashboard.css` already carries a comment reaching the same conclusion -
   "Fixing the CHILDREN rather than ..." - so this follows that decision.

   `min-width: 0` on a grid child ONLY permits shrinking below min-content. It
   changes nothing for a child that already fits, which is why it is safe to apply
   broadly. Paired with overflow-wrap so long unbroken strings wrap instead of
   forcing width.

   Scoped by class pattern (-grid, -cards, -row) rather than `*` so buttons,
   badges and icons outside grid containers keep their intrinsic sizing. Phones
   and small tablets only. */
@media (max-width: 767px) {
  html body [class*="-grid"] > *,
  html body [class*="grid-"] > *,
  html body [class*="-cards"] > *,
  html body [class*="-row"] > * {
    min-width: 0;
  }
  html body [class*="-grid"] > *,
  html body [class*="-cards"] > * {
    overflow-wrap: anywhere;
  }
}

/* ── 9. width:100vw ───────────────────────────────────────────────────────────
   5 declarations in checkout-mobile.css (3) and responsive-fix.css (2). 100vw is
   the viewport INCLUDING the scrollbar gutter, so on any surface that shows a
   classic scrollbar the element is wider than the content box and the page gains
   a horizontal scroll it can never lose. 100% is the containing block, which is
   what these want. */
@media (max-width: 767px) {
  html body [style*="100vw"],
  html body .cart-panel,
  html body .co-shell {
    max-width: 100% !important;
  }
}


/* ── A clipped amount must never pass for a whole one ──────────────────────────
   Independent of any width fix above: wherever a KPI figure still cannot fit, it
   ends in an ellipsis rather than a clean cut. text-overflow:clip was the default
   on these elements, so truncation was invisible - the number just looked smaller.
   An ellipsis is the difference between "this is cut off" and a wrong figure. */
html body[class] .vb-kpi-value,
html body[class] .vb-kpi-number,
html body[class] .vbd-card-value {
  text-overflow: ellipsis !important;
}

/* A KPI slot holding a DATE RANGE, not an amount. pages/reports.html #periodText
   renders "01 Aug 2026 to 26 Aug 2026" - 26 characters - through .vb-kpi-value,
   which is sized for a figure (24px). It needed 348px inside 148px. No card floor
   fixes a string that long; the fix is to stop typesetting a sentence at
   headline-figure size. Scoped to the element, so no amount is affected. */
@media (max-width: 767px) {
  html body[class] #periodText.vb-kpi-value,
  html body[class] #periodText {
    font-size: 12px !important;
    line-height: 1.35 !important;
    white-space: normal !important;
    overflow: visible !important;
  }
}


/* #periodText is a DATE RANGE in a KPI slot, not an amount. The <=767px rule above
   sized it down; at 768-1024 it reverted to KPI type and clipped again
   ("01 Aug 2026 to 26 Aug 2026" needed 159px in 142px). Same treatment, tablet
   range. Amounts are unaffected - this targets one id. */
@media (min-width: 768px) and (max-width: 1024px) {
  html body[class] #periodText.vb-kpi-value,
  html body[class] #periodText {
    font-size: 12px !important;
    line-height: 1.35 !important;
    white-space: normal !important;
    overflow: visible !important;
  }
}
