/* Global mobile responsiveness safety net.
   Layer 1: stop horizontal scroll / left-right jitter that exists when any
   element renders wider than the viewport on a phone.
   Layer 2: targeted fixes for known overflow culprits (top-bar nav-actions,
   product card grids) so content stays reachable, not just clipped. */

/* ---------- Layer 1: universal ---------- */
html,
body {
  overflow-x: hidden;
  /* `hidden` on both html and body makes <body> a scroll container that never
     scrolls, so every `position: sticky` header stuck to <body> and scrolled
     away with the page (NEW-01, measured 2026-09-14: the home nav sat at
     -846px after a 900px scroll). `clip` cuts the overflow the same way
     without creating a scroll container. Browsers without `clip` (iOS < 16)
     drop this line and keep `hidden`: no sticky header, but no sideways
     scroll either. */
  overflow-x: clip;
  max-width: 100vw;
}
html {
  overscroll-behavior-x: none;
}
body {
  position: relative;
  /* iOS Safari respects touch-action even when overflow-x: hidden alone
     does not fully prevent the elastic horizontal-pan gesture. Locking
     to pan-y + pinch-zoom kills horizontal swipe site-wide while still
     allowing vertical scroll and pinch-zoom. Horizontal carousels (if
     any are added later) can override with touch-action: pan-x on the
     specific scroll container. */
  touch-action: pan-y pinch-zoom;
}

/* touch-action is NOT inherited, so position: fixed overlays (modal
   backdrops, off-canvas drawers, dropdowns) default to touch-action:
   auto and reintroduce horizontal-pan on iOS even when body locks it.
   Repeat the lock for every known floating layer. New overlays should
   either match one of these selectors or set touch-action explicitly. */
.bauth-modal,
.bauth-card,
.cart-drawer,
.ssc-drawer,
.ssa-menu,
.pdm-card,
.ss-search-pop,
[role="dialog"],
[aria-modal="true"] {
  touch-action: pan-y pinch-zoom;
}

/* ---------- Layer 2: targeted at <=480px (phones) ---------- */
@media (max-width: 480px) {
  /* Topbar: tighten so logo + search + actions fit a 375px viewport.
     Scoped to the storefront nav: Founding Shopper has its own <nav>. */
  nav:has(> .nav-inner) {
    padding-left: 3% !important;
    padding-right: 3% !important;
  }
  .nav-inner {
    gap: 8px !important;
  }
  .nav-actions {
    gap: 2px !important;
  }
  .nav-actions .btn-signin,
  .nav-actions .btn-start {
    padding: 6px 10px !important;
    font-size: 12px !important;
  }
  .nav-btn {
    padding: 6px 8px !important;
  }
  /* Hide divider on phones to free a few pixels */
  .nav-divider {
    display: none !important;
  }
  /* Search button compact */
  .search-btn {
    padding: 0 12px !important;
    font-size: 13px !important;
  }
  /* Product card grid on phones.
   *
   * This used to force `grid-template-columns: 1fr !important`, which beat the
   * `repeat(2, 1fr)` that index.html declares for this exact breakpoint — so
   * phones showed ONE product per row and roughly 1.5 products per screen.
   * Overflow was the original worry, but the arithmetic is comfortable: at
   * 390px the .wrap padding below leaves (390 - 28 - 10) / 2 = 176px per
   * column, wider than the 180px cards the horizontal rails already render.
   * Only the gap is clamped here now; the column count belongs to the page. */
  .g5,
  .g6 {
    gap: 10px !important;
  }
  /* Wrap container: tighter horizontal padding */
  .wrap {
    padding-left: 14px !important;
    padding-right: 14px !important;
  }
}

/* ---------- Layer 3: the storefront header, on EVERY page ---------- */
/* These rules used to live in index.html's own <style>, so the homepage was
   the only page that collapsed its header on a phone. Every other page that
   renders <nav><div class="nav-inner"> — all-categories, category, supplier,
   suppliers, suppliers-all — shipped the full desktop header to a 390px
   screen: 785px of content in a 367px row, with Sign in and Sign up pushed
   clean off the right edge. Layer 1's overflow-x:hidden then CLIPPED them
   rather than letting anyone scroll across, so on a phone those two buttons
   did not exist. Header rules belong here, not in a page. */

@media (max-width: 1024px) {
  /* The icon-button labels ("Alerts", "Wishlist", "Cart (0)") are what blow
     the row out. Drop to icon-only; the buttons stay tappable. */
  nav .nav-actions .nav-btn span:not(.ico):not(.badge) { display: none; }
  nav .nav-actions .nav-btn { padding: 8px 9px; gap: 0; }
  nav .nav-actions .btn-signin { padding: 7px 14px; font-size: 12px; }
  nav .nav-actions .btn-start { padding: 8px 14px; font-size: 12px; }
  nav .nav-actions .btn-sell { padding: 8px 10px; font-size: 12px; }
}

@media (max-width: 768px) {
  /* live-search.js appends a <style> to <head> containing
     `.ssls-anchor{...display:flex}`. Being appended it lands after every
     stylesheet, so it beat the plain `.search-wrap-anchor{display:none}`
     that index.html declares at this breakpoint: the desktop search stayed
     in the flex row at zero width with its 42px submit button sticking out
     over the Sign in / Sign up area. Outrank it. The dedicated
     .nav-mobile-search second row is the phone search and is unaffected. */
  nav .search-wrap-anchor { display: none !important; }
}

@media (max-width: 600px) {
  /* Sellers still reach the portal from the footer Sell column; on a phone
     the buyer's Sign in / Sign up are what the row is for. */
  nav .nav-actions .btn-sell { display: none; }
}

@media (max-width: 480px) {
  /* Below 480 the 145px horizontal wordmark cannot coexist with the six
     action controls — 145 + 279 + gaps wants 432px in a 367px row. Swap it
     for the square cart mark (the same artwork the favicon uses): 34px,
     which fits 390 and 360 with room to spare and drops no affordance.
     .logo-img stays in the DOM merely hidden, because logo-auto.js walks
     the logos on the page and swaps their src for the light/dark variant —
     removing it would be a second thing to keep in sync.
     Scoped to the storefront nav: a bare `nav .logo` also caught Founding
     Shopper's text logo and crushed "ShopSmart" into a 34px box. */
  nav > .nav-inner > .logo {
    width: 34px !important;
    height: 34px !important;
    flex: 0 0 34px !important;
    background: url("/assets/favicon.svg") center / contain no-repeat;
  }
  nav > .nav-inner > .logo .logo-img { display: none !important; }

  nav .nav-actions { gap: 2px !important; }
  nav .nav-actions .nav-btn { padding: 6px 7px !important; }
  nav .nav-actions .btn-signin,
  nav .nav-actions .btn-start { padding: 6px 10px !important; font-size: 12px !important; }
}

@media (max-width: 360px) {
  /* 320px (SE, older Androids) needs roughly another 20px out of the row. */
  nav .nav-actions .nav-btn { padding: 6px 5px !important; }
  nav .nav-actions .btn-signin,
  nav .nav-actions .btn-start { padding: 6px 8px !important; }
}

/* ---------- Layer 3b: grid tracks that refuse to shrink ---------- */
/* A `1fr` track is `minmax(auto, 1fr)`, and `auto` as a minimum is the item's
   min-content width — so a single-column grid holding anything wide (here a
   .section-wrap of .pcard-h rows, min-content 480px) sits at its content
   width and shoves the page past the viewport. index.html carries the real
   fix on its own .two-col rule; this is the net for any page that grows one
   later. `.wrap .two-col` outranks a page's bare `.two-col`, which matters
   because page <style> blocks are parsed after this file. */
@media (max-width: 1024px) {
  .wrap .two-col { grid-template-columns: minmax(0, 1fr); }
  .wrap .two-col > * { min-width: 0; }
}

/* ---------- Layer 4: components that were never given a rule ---------- */
/* marketplace.js renders `<div class="cat-count">18 products</div>` inside
   every .cat-item tile, and no stylesheet in landing/ has ever matched it.
   It therefore inherited the 14px body size and text-align:start, making the
   count LARGER than the 11px .cat-name above it and left-aligned inside a
   centred 74px tile — so on a phone it wrapped to "18" / "products" and
   spilled across its neighbours. Same font scale and colour family as
   .cat-name, one step quieter. Fallbacks are required: the box tree has no
   design-tokens sheet, so var(--text3) alone resolves to nothing there. */
.cat-count {
  font-size: 10px;
  font-weight: 500;
  line-height: 1.35;
  text-align: center;
  color: var(--text3, #55657C);
  font-family: var(--font-body, "Plus Jakarta Sans", Arial, Helvetica, sans-serif);
}
/* .cat-item is a flex column with gap:8px, which reads as a gap between two
   unrelated lines rather than a caption under a name. Pull the count up. */
.cat-name + .cat-count { margin-top: -5px; }
/* Names run to one or two lines ("Bags" vs "Beans and Legumes"), so the counts
   beneath them landed on different baselines across the row and the whole
   strip read as misaligned. Reserve two lines for every name. line-height is
   pinned here on purpose: inherited it is 1.6, so "two lines" would be 35.2px
   while `min-height: 2.4em` reserves only 26.4px and the rows stay ragged.
   At 320 "Maintenance" alone is wider than a 74px tile, so let long words
   break rather than run out of the tile (HOME-09). */
.cat-name { line-height: 1.25; min-height: 2.5em; overflow-wrap: anywhere; }

/* ================= SS enforcement v2 (2026-09) =================
   From here down is the UI overhaul (docs/ui-audit-2026-09-14). This file is
   unlayered and every buyer page now loads it as its LAST stylesheet, so
   these rules beat page rules. Header rules stay here, never in a page. */

/* SYS-03: iOS Safari zooms the page when a focused field is under 16px. */
@media (max-width: 1023.98px) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]),
  select,
  textarea { font-size: 16px !important; }
}

/* A flex item's min-width defaults to `auto`, its content, so a search form
   holding a <select> and an <input> refuses to shrink and slides over the
   controls beside it (HDR-01 to HDR-03: the search button landing on the
   Alerts bell at 1024-1280). */
.search-wrap,
.search-wrap input,
header.topbar > .ssls-anchor,
header.topbar .search,
header.topbar .search input { min-width: 0; }

/* SYS-10: logo links never shrink, so the logo image is never squashed. */
a.logo { flex: 0 0 auto; }
/* !important because each page sets the logo's height inline (Contact: 34px). */
header.topbar > a.logo img { height: 28px !important; width: auto !important; max-width: none; }
@media (min-width: 768px) {
  header.topbar > a.logo img { height: 32px !important; }
}

/* Elements that still render despite their `hidden` attribute because a page
   rule sets `display` (ACC-04's stray badge, Suppliers A-Z's empty pager). */
.pagination[hidden],
[data-portal-badge][hidden] { display: none !important; }

/* CAT-03: the category bar clipped "Flash Deals" at 320-360 and the last
   department at 1280 behind `overflow: hidden`. Scroll it like a tab row. */
.megabar-inner {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.megabar-inner::-webkit-scrollbar { display: none; }
.megabar-inner > .featured-depts { flex: 0 0 auto; overflow: visible; }

/* ================= SS header, Phase 1 (2026-09) =================
   Until the shared header partial lands (brief 6.2) the storefront has two
   header systems, and this block gives both the same three layouts:
     under 768     row 1 logo and actions, row 2 a full-width search, and the
                   sticky block at or under 104px
     768 to 1279   the same two rows; labels and the category <select> hidden
     1280 and up   one row, as before
   Pages already said "search goes to row 2", but live-search.js wraps each
   header search in `.ssls-anchor`, so their `order` landed on the wrapped
   element while the wrapper stayed inline and collapsed to an empty pill
   (HDR-01) or ran past the edge (HDR-02). The flex item to move is the anchor
   when present, the search element otherwise. */

/* nav > .nav-inner: home, category, all-categories, supplier, suppliers,
   suppliers-all. */
@media (max-width: 1279.98px) {
  nav > .nav-inner { flex-wrap: wrap; row-gap: 6px; }
  nav > .nav-inner > .ssls-anchor,
  nav > .nav-inner > .search-wrap,
  nav > .nav-inner > .search-wrap-anchor { order: 10; flex: 1 0 100%; min-width: 0; }
  nav > .nav-inner .search-wrap { width: 100%; }
  nav > .nav-inner .search-wrap select { display: none; }
  nav > .nav-inner > .nav-actions { margin-left: auto; flex: 0 0 auto; }
  nav .nav-actions .nav-btn span:not(.ico):not(.badge) { display: none; }
}
@media (max-width: 767.98px) {
  /* Budget, measured: 6 + 34 (mark) + 6 + 47 (search) + 6 + 1 (border) = 100px.
     index.html's separate .nav-mobile-search row lands at 101px. */
  nav > .nav-inner { column-gap: 8px; padding-top: 6px; padding-bottom: 6px; }
  nav > .nav-inner > a.logo img { height: 28px; }
  /* Signed in, buyer-header.js swaps Sign in for a 41px account chip, which set
     row 1's height and put the sticky block at 105px. */
  nav .nav-actions .ssa-id-trigger { padding: 2px 10px 2px 2px; }
  nav .nav-mobile-search { padding-bottom: 6px; }
  nav > .nav-inner .search-wrap,
  nav .nav-mobile-search .search-wrap { box-sizing: border-box; min-height: 44px; }
  nav > .nav-inner .search-wrap input,
  nav .nav-mobile-search .search-wrap input { flex: 1 1 auto; width: auto; }
  nav > .nav-inner .search-wrap .search-btn,
  nav .nav-mobile-search .search-wrap .search-btn { flex: 0 0 44px; width: 44px; min-height: 44px; padding: 0 !important; }
}

/* header.topbar: product, search, gift-finder, contact. */
@media (max-width: 1279.98px) {
  /* Contact's own 14px padding made its sticky bar 133px at 768 (budget 132). */
  header.topbar { flex-wrap: wrap; row-gap: 8px; padding-top: 10px; padding-bottom: 10px; }
  header.topbar > .ssls-anchor,
  header.topbar > .search { order: 10; flex: 1 0 100%; min-width: 0; max-width: none; }
  header.topbar .search { width: 100%; max-width: none; }
  header.topbar .search select { display: none; }
  header.topbar > .nav-icons { margin-left: auto; flex: 0 0 auto; }
}
@media (max-width: 767.98px) {
  /* Budget, measured on Contact: 6 + 34 + 6 + 46 (search) + 6 + 1 = 99px. */
  header.topbar { column-gap: 12px; row-gap: 6px; padding: 6px var(--ss-gutter, 16px); }
  header.topbar .search { box-sizing: border-box; min-height: 44px; padding: 0 0 0 14px; }
  /* stretch: the input was 21px tall inside a 44px pill, so most of the pill
     did not focus it. */
  header.topbar .search input { flex: 1 1 auto; width: auto; padding: 0 8px; align-self: stretch; }
  header.topbar .search .go { flex: 0 0 44px; width: 44px; height: 44px; }
  header.topbar > .nav-icons { gap: 12px; }
  header.topbar .nav-icon { font-size: 0; gap: 0; }
  header.topbar .nav-icon .glyph { font-size: 20px; }
  /* SRCH-06: labels are hidden here, so the icon is 20px wide and the badge's
     page offset put it over the glyph's left shoulder. Pin it top-right. */
  header.topbar .nav-icon .badge { font-size: 12px; top: -8px; right: -10px; }
  /* 13px, not 12: white on the accent pill fails contrast at 12px (axe, Gift Finder). */
  header.topbar .cta { padding: 8px 14px; font-size: 13px; }
  /* HDR-04: Contact's "Contact Us" pill was 40px tall and set the row height. */
  header.topbar .acct { padding: 2px 12px 2px 2px; font-size: 12px; }
  header.topbar .acct .av { width: 28px; height: 28px; font-size: 12px; }
}
@media (max-width: 399.98px) {
  /* HDR-04: below 400 the pill keeps only its avatar; the link has an aria-label. */
  header.topbar .acct { font-size: 0; gap: 0; padding: 2px; }
}
