/* ═══════════════════════════════════════════════════════════
   SORTLIST WHOLESALE — COMPLETE SITE CSS
   All styles for every component on the site.
   PHP 7.1 · cPanel · LiteSpeed
═══════════════════════════════════════════════════════════ */

/* ── Reset & base ── */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{font-size:16px;scroll-behavior:smooth}
body{font-family:'Archivo',Arial,sans-serif;color:#1a1a1a;background:#fff;line-height:1.5;-webkit-font-smoothing:antialiased}
a{color:inherit;text-decoration:none}
button,input,textarea,select{font-family:inherit}
img{max-width:100%;display:block}

/* ── Global overflow hardening (2026-10-28) ──
   Whatever width the browser actually is, nothing on the page should be
   able to force a wider horizontal extent than that. Two different root
   causes create the "content cut off on one side" complaint, and these
   two rules cover both of them everywhere at once, instead of needing a
   one-off fix per component every time it comes up again:
   1) Flex/grid items default to min-width:auto, which means a flex child
      containing unbreakable content (long text, a wide fixed-size child)
      refuses to shrink below its own content's width — even when every
      *explicit* width/gap/wrap rule on the row looks perfectly
      responsive, that one child silently pushes the whole row past its
      container. Resetting min-width to 0 lets the flex-wrap/flex-shrink/
      gap rules that already exist throughout this file actually do their
      job. (This is the same fix already applied by hand to just
      .search input further down — generalising it here.)
   2) An un-truncated long string (a long email address, a long product
      name/SKU, an unexpected value from the API) with no text-wrap rule
      widens its container instead of wrapping. overflow-wrap is the
      catch-all for any text this wasn't specifically anticipated for.
   Neither rule changes anything for content that already fits — they
   only ever kick in once something would otherwise overflow, and an
   explicit min-width set directly on a class (higher specificity than
   this universal selector) still wins where a component genuinely needs
   a hard floor. Deliberately-wide horizontal scrollers (.ticker, any
   overflow-x:auto row/table) are unaffected either way: both rules only
   let content shrink/wrap, never force it to. */
*,*::before,*::after{ min-width:0; overflow-wrap:break-word; }
html{ max-width:100%; overflow-x:hidden; }
body{ max-width:100%; }
svg,video,iframe,canvas,table{ max-width:100%; }

/* ── CSS custom properties ── */
:root{
  --navy:#002147;
  --green:#1a7a45;
  --green-light:#d5f5e3;
  --gold:#F6D079;
  --red:#ef4444;
  --purple:#7c3aed;
  --slate:#4B5563;
  --slate-lt:#9CA3AF;
  --border:#E5E7EB;
  --bg:#F8F9FA;
  --white:#FFFFFF;
  --radius:10px;
  --radius-lg:14px;
  --shadow-card:0 1px 3px rgba(0,0,0,.06),0 4px 16px rgba(0,0,0,.06);
  --shadow-hover:0 4px 12px rgba(0,0,0,.10),0 12px 32px rgba(0,0,0,.08);
  --tx:.18s cubic-bezier(.4,0,.2,1);
  --site-width:1320px;
  --site-pad:28px;
}

/* ════════════════════════════════════════════════════════
   SPLASH / FLASH SCREEN — markup + dismiss script live in includes/
   header.php (first thing in <body>, so position:fixed puts it above
   everything regardless of source order). Only ever present in the DOM
   when an admin has configured an active section='splash' banner row
   (see the PHP query in header.php) — otherwise this markup doesn't
   render at all, so these rules simply never match anything.
════════════════════════════════════════════════════════ */
html.splash-lock,html.splash-lock body{overflow:hidden}
/* Dimmed backdrop, NOT a solid colour block — a solid background behind
   an object-fit:contain image always letterboxes (shows as bars/a colour
   band) whenever the image's own aspect ratio doesn't exactly match the
   screen's. A translucent dark backdrop has the same problem in reverse:
   it never shows as a hard colour band, just the page dimmed around the
   card, regardless of the image's shape. */
.splash-screen{
  position:fixed;inset:0;z-index:99999;
  background:rgba(0,17,34,.72);
  display:flex;align-items:center;justify-content:center;
  padding:24px;
  opacity:1;transition:opacity .35s ease;
}
/* The popup card itself — image sits inside this white rounded card
   instead of filling the whole screen, which is what makes it read as a
   "popup" rather than a full-bleed splash. */
.splash-screen-card{
  position:relative;
  background:#fff;
  border-radius:var(--radius-lg);
  box-shadow:0 20px 60px rgba(0,0,0,.35);
  padding:12px;
  max-width:min(480px,92vw);
  max-height:88vh;
  cursor:default; /* the card's own padding isn't clickable — only the backdrop, the × button, and the image itself (below) are */
}
.splash-screen-img{
  display:block;width:100%;height:auto;
  max-height:calc(88vh - 24px);object-fit:contain;
  border-radius:calc(var(--radius-lg) - 6px);
  cursor:pointer; /* "Banner Config" click-through — see the inline splash script in header.php */
}
.splash-screen-close{
  position:absolute;top:-14px;right:-14px;
  width:32px;height:32px;border-radius:50%;
  background:#fff;border:1.5px solid var(--border);
  display:flex;align-items:center;justify-content:center;
  cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,.18);
  font-size:20px;line-height:1;color:var(--navy);padding:0;
}
.splash-screen-close:hover{background:var(--bg)}
.splash-screen-hide{opacity:0;pointer-events:none}
@media(max-width:480px){
  .splash-screen{padding:16px}
  .splash-screen-card{max-width:100%}
}

/* ════════════════════════════════════════════════════════
   TICKER
════════════════════════════════════════════════════════ */
.ticker{background:var(--navy);color:rgba(255,255,255,.78);font-size:11.5px;font-weight:500;letter-spacing:.04em;overflow:hidden;white-space:nowrap;height:34px;display:flex;align-items:center;position:sticky;top:0;z-index:400}
.ticker-track{display:inline-flex;gap:60px;animation:ticker 35s linear infinite}
.ticker-track span{flex-shrink:0}
@keyframes ticker{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* ════════════════════════════════════════════════════════
   HEADER / NAV
════════════════════════════════════════════════════════ */
.head-main{background:#fff;border-bottom:1px solid var(--border);padding:0 var(--site-pad);display:flex;align-items:center;height:64px;position:sticky;top:34px;z-index:300;box-shadow:0 1px 8px rgba(0,0,0,.06)}
.head-inner{max-width:var(--site-width);width:100%;margin:0 auto;display:flex;align-items:center;gap:20px}
.logo{display:flex;align-items:center;gap:10px;flex-shrink:0;text-decoration:none}
.logo-img{height:38px;width:auto}
.logo-div{width:1px;height:22px;background:var(--border)}
.logo-tag{font-family:'Bricolage Grotesque',sans-serif;font-size:13px;font-weight:800;color:var(--navy);line-height:1.2;letter-spacing:-.01em}
.logo-tag span{display:block;font-size:9px;font-weight:600;color:var(--slate-lt);letter-spacing:.06em;text-transform:uppercase}

.search{flex:1;max-width:480px;display:flex;gap:0;border:1.5px solid var(--border);border-radius:8px;overflow:hidden;transition:border-color var(--tx)}
.search:focus-within{border-color:var(--navy)}
.search input{flex:1;border:none;padding:9px 14px;font-size:13px;color:#111;background:#fff;outline:none}
.search button{background:var(--navy);color:#fff;border:none;padding:0 16px;font-size:13px;font-weight:700;cursor:pointer;transition:background var(--tx)}
.search button:hover{background:#1a3a60}

.head-actions{display:flex;align-items:center;gap:6px;margin-left:auto;flex-shrink:0}
.head-btn{display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 10px;border:none;background:none;cursor:pointer;border-radius:8px;transition:background var(--tx);font-size:10px;font-weight:600;color:var(--slate);min-width:48px;position:relative}
.head-btn:hover{background:var(--bg)}
.head-btn svg{width:20px;height:20px;stroke:var(--navy);fill:none}
.cart-btn{position:relative}
.cart-count{position:absolute;top:2px;right:6px;background:var(--red);color:#fff;font-size:9px;font-weight:800;min-width:16px;height:16px;border-radius:99px;display:flex;align-items:center;justify-content:center;padding:0 3px;line-height:1}
.mem-join-btn{border-radius:8px;padding:8px 14px;font-size:12px;font-weight:700;flex-direction:row;min-width:auto;gap:5px}
.mem-join-btn svg{width:14px;height:14px;stroke:#fff}

/* ════════════════════════════════════════════════════════
   CATEGORY NAV — sticky pill bar
════════════════════════════════════════════════════════ */
.cats{background:var(--navy);border-top:1px solid rgba(255,255,255,.08);position:sticky;top:98px;z-index:200}
.cats-inner{display:flex;gap:0;overflow-x:auto;padding:0 20px;scrollbar-width:none;-ms-overflow-style:none}
.cats-inner::-webkit-scrollbar{display:none}
.cat-link{display:inline-flex;align-items:center;white-space:nowrap;padding:11px 18px;font-family:'Archivo',sans-serif;font-size:12px;font-weight:600;letter-spacing:.03em;color:rgba(255,255,255,.60);text-decoration:none;border-bottom:2.5px solid transparent;transition:color var(--tx),border-color var(--tx);cursor:pointer;user-select:none}
.cat-link:hover{color:rgba(255,255,255,.88)}
.cat-link.active{color:#fff;border-bottom-color:var(--gold);font-weight:700}

/* ════════════════════════════════════════════════════════
   HERO
════════════════════════════════════════════════════════ */
.hero{padding:0;background:#fff}

/* ── Hero slider — FULL WIDTH, single merged banner (blue + gold blend),
   dynamic/DB-driven (see includes/banners.php). Each slide supports an
   optional background image (image_url); the scrim gradient keeps text
   readable over any image and is what "merges" navy + gold into one
   continuous banner instead of two separate colour blocks. ── */
.hero-slider{position:relative;width:100%;max-width:var(--site-width);margin:0 auto;overflow:hidden}
.hero-slider-track{display:flex;transition:transform .5s cubic-bezier(.4,0,.2,1)}
.hero-slide{
  flex:0 0 100%;width:100%;min-height:460px;box-sizing:border-box;
  position:relative;display:flex;align-items:center;
  /* No-image fallback: an opaque navy→gold brand gradient (clean colour, no image needed) */
  background:linear-gradient(115deg,var(--navy) 0%,#0a2d5a 50%,#8a6a24 82%,var(--gold) 100%);
  background-size:cover;background-position:center;
}
/* .is-linked = a DB-driven slide from renderHeroSlides() (main.js), which
   always wires up a "Banner Config" click-through — see
   bannerGoToLinkedItems() in main.js. The static no-JS/API-unavailable
   fallback slide in header.php never gets this class, so its own nested
   "Browse the catalogue" / "Open a free trade account" links keep working
   exactly as before, untouched. */
.hero-slide.is-linked{cursor:pointer}
/* When a slide has a photo (image_url), the photo replaces the gradient as the
   background-image (inline style, added by PHP/JS) and this scrim — invisible
   by default — becomes the navy→gold brand wash on top of it for legibility. */
.hero-slide-scrim{position:absolute;inset:0;opacity:0}
.hero-slide.has-image{background-color:var(--navy)}
/* Real <img> element for admin-uploaded banner photos (added alongside the
   existing background-image approach). Hidden on desktop, where the hero
   keeps its original fixed-height background-image/cover layout — only
   mobile switches to sizing the slide off the image's own natural
   proportions (see @media(max-width:768px) below), which is what removes
   the leftover navy/blue letterbox band a shared fixed height caused. */
.hero-slide-img{display:none}
.hero-slide-content{position:relative;z-index:2;width:100%;padding:64px 52px;color:#fff}
.hero-tag{font-family:'Space Mono',monospace;font-size:11px;color:rgba(255,255,255,.55);letter-spacing:.12em;text-transform:uppercase;margin-bottom:20px;display:flex;align-items:center;gap:8px}
.hero-tag::before{content:'';display:inline-block;width:28px;height:1px;background:rgba(255,255,255,.35)}
.hero h1{font-family:'Bricolage Grotesque',sans-serif;font-size:clamp(2.4rem,4vw,3.4rem);font-weight:800;color:#fff;line-height:1.08;margin-bottom:20px;max-width:640px}
.hero h1 em{font-style:italic;color:var(--gold)}
.hero p{font-size:15px;color:rgba(255,255,255,.72);max-width:460px;line-height:1.75;margin-bottom:32px}
.hero-cta{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
.btn-primary{background:var(--gold);color:var(--navy);border-color:var(--gold)}
.btn-primary:hover{background:#e8c057;border-color:#e8c057}
.btn-ghost{background:transparent;color:rgba(255,255,255,.85);border-color:rgba(255,255,255,.35)}
.btn-ghost:hover{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.7);color:#fff}
.btn{display:inline-flex;align-items:center;gap:8px;padding:12px 22px;border-radius:8px;font-size:14px;font-weight:700;cursor:pointer;border:2px solid transparent;transition:all var(--tx);text-decoration:none}
.btn-primary{background:var(--navy);color:#fff;border-color:var(--navy)}
.btn-primary:hover{background:#1a3a60;border-color:#1a3a60}
.btn-ghost{background:transparent;color:var(--navy);border-color:var(--navy)}
.btn-ghost:hover{background:var(--navy);color:#fff}

/* Arrows sit in a bottom control strip (NOT overlaid mid-text) so they never
   collide with slide copy, no matter how long the admin's headline/paragraph
   text runs or how narrow the viewport is. */
.hero-arrow{
  position:absolute;bottom:12px;
  width:34px;height:34px;border-radius:50%;
  border:1.5px solid rgba(255,255,255,.35);
  background:rgba(255,255,255,.12);
  backdrop-filter:blur(4px);
  cursor:pointer;display:flex;align-items:center;justify-content:center;
  transition:all var(--tx);z-index:3;
}
.hero-arrow:hover{background:rgba(255,255,255,.28);border-color:rgba(255,255,255,.7)}
.hero-arrow svg{width:15px;height:15px;stroke:#fff}
.hero-prev{left:20px}
.hero-next{right:20px}
.hero-dots{position:absolute;left:0;right:0;bottom:20px;display:flex;justify-content:center;gap:7px;z-index:3}
.hero-dots .dot,.offers-dots .dot{
  width:7px;height:7px;border-radius:50%;padding:0;
  border:1.5px solid rgba(255,255,255,.6);background:transparent;
  cursor:pointer;transition:all var(--tx);
}
.hero-dots .dot.active{background:var(--gold);border-color:var(--gold);width:18px;border-radius:4px}
.offers-dots{display:flex;justify-content:center;gap:7px;padding:4px 0 0}
.offers-dots .dot{border-color:var(--border);background:#fff}
.offers-dots .dot.active{background:var(--navy);border-color:var(--navy);width:18px;border-radius:4px}

/* ════════════════════════════════════════════════════════
   TRUST BAR
════════════════════════════════════════════════════════ */
.trust{padding:32px var(--site-pad);border-top:1px solid var(--border);border-bottom:1px solid var(--border);background:#fafafa}
.trust-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}
.trust-item{display:flex;align-items:flex-start;gap:12px}
.trust-item svg{width:24px;height:24px;stroke:var(--green);flex-shrink:0;margin-top:2px;fill:none}
.trust-item b{display:block;font-size:13px;font-weight:700;color:var(--navy);margin-bottom:2px}
.trust-item p{font-size:12px;color:var(--slate);line-height:1.45;margin:0}

/* Official FSA Food Hygiene Rating badge (2026-08-14) — links out to
   ratings.food.gov.uk so the claim is independently verifiable. */
.trust-fhrs{display:flex;justify-content:center;margin-top:24px}
.fhrs-badge{display:inline-flex;align-items:center;gap:12px;background:#fff;border:1.5px solid var(--border);border-radius:var(--radius-lg,14px);padding:9px 18px 9px 9px;text-decoration:none;transition:border-color var(--tx),box-shadow var(--tx),transform var(--tx)}
.fhrs-badge:hover{border-color:var(--green);box-shadow:0 4px 14px rgba(26,122,69,.15);transform:translateY(-1px)}
.fhrs-badge-score{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:50%;background:var(--green);color:#fff;font-family:'Space Mono',monospace;font-weight:800;font-size:17px;flex-shrink:0}
.fhrs-badge-copy{display:flex;flex-direction:column;gap:2px}
.fhrs-badge-copy b{font-family:'Bricolage Grotesque',sans-serif;font-size:13px;color:var(--navy);font-weight:800}
.fhrs-badge-copy span{font-family:'Archivo',sans-serif;font-size:11.5px;color:var(--slate-lt)}
@media(max-width:480px){
  .fhrs-badge{flex-direction:column;text-align:center;gap:8px;padding:14px}
}

/* SUPPLY ROWS section removed — "Who we supply" no longer shown on the homepage. */

/* ════════════════════════════════════════════════════════
   CATEGORY GRID (audit F-009 — homepage routing into the catalogue)
════════════════════════════════════════════════════════ */
.cat-grid-section{padding:44px 0;background:#fff}
.cat-grid-inner{max-width:var(--site-width);margin:0 auto}
.cat-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:14px;padding:0 var(--site-pad)}
.cat-tile{appearance:none;border:1.5px solid var(--border);background:#fff;border-radius:var(--radius-lg);padding:20px 16px;display:flex;flex-direction:column;align-items:flex-start;gap:8px;cursor:pointer;text-align:left;transition:transform var(--tx),box-shadow var(--tx),border-color var(--tx)}
.cat-tile:hover{transform:translateY(-3px);box-shadow:var(--shadow-hover);border-color:var(--navy)}
.cat-tile-icon{width:34px;height:34px;border-radius:9px;background:var(--gold-tint,#FDF3D2);display:flex;align-items:center;justify-content:center;color:var(--navy)}
.cat-tile-icon svg{width:19px;height:19px}
/* Admin-uploaded DB icon (CAT_ICON_URLS, see _categoryIconHTML() in
   main.js) — sized to match the hardcoded SVG it can replace, object-fit
   so a non-square source image doesn't distort. */
.cat-tile-icon img{width:20px;height:20px;object-fit:contain}
.cat-tile-name{font-family:'Bricolage Grotesque',sans-serif;font-size:14px;font-weight:800;color:var(--navy);line-height:1.25}
.cat-tile-count{font-family:'Space Mono',monospace;font-size:10.5px;color:var(--slate-lt);letter-spacing:.03em}

@media(max-width:768px){
  .cat-grid-section{padding:32px 0}
  .cat-grid{grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:10px;padding:0 16px}
  .cat-tile{padding:14px 12px}
}
@media(max-width:480px){
  .cat-grid{grid-template-columns:1fr 1fr}
}

/* ════════════════════════════════════════════════════════
   CATALOG SECTION
════════════════════════════════════════════════════════ */
.catalog{padding:48px 0 64px;background:#fff}
.catalog-inner{max-width:var(--site-width);margin:0 auto}
.sec-head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;padding:0 var(--site-pad) 28px;flex-wrap:wrap;max-width:var(--site-width);margin:0 auto}
.sec-head .sub{font-family:'Space Mono',monospace;font-size:11px;color:var(--slate-lt);letter-spacing:.12em;text-transform:uppercase;display:block;margin-bottom:6px}
.sec-head h2{font-family:'Bricolage Grotesque',sans-serif;font-size:clamp(1.4rem,3vw,2rem);font-weight:800;color:var(--navy);margin:0;line-height:1.15}
.spacer{flex:1}

/* ── Filter chips ── */
.filters{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}
.filter{appearance:none;border:1.5px solid var(--border);background:#fff;color:var(--slate);font-family:'Archivo',sans-serif;font-size:11px;font-weight:600;letter-spacing:.03em;padding:6px 14px;border-radius:99px;cursor:pointer;transition:all var(--tx);white-space:nowrap}

/* ── Brand filter chips — own full-width row under the category chips
   (both live inside .sec-head, a flex-wrap container; flex-basis:100%
   forces this row to wrap onto its own line below the category row and
   the title, regardless of viewport width). Reuses the .filters class so
   it automatically inherits the SAME layout as the category chips at every
   breakpoint (flex row on desktop, the equal-width grid on mobile — see
   the .filters rules in the @media(max-width:768px) blocks below; nothing
   here needs to duplicate that) — only display:none/hidden is special-
   cased here, via :not(.show), so brand-filters starts hidden and JS
   (renderBrandFilters() in main.js) reveals it only once BRANDS actually
   has at least one real brand, so sites with no sp_brand_name data never
   show an empty/pointless row. The :not(.show) selector's specificity
   (three classes) is deliberately higher than the plain .filters{display:
   ...} rules at either breakpoint, so this hidden state always wins
   without needing to fight over what display value "shown" should be per
   breakpoint. A thin top border + small label separate it visually from
   the category chips directly above it. ── */
.filters.brand-filters:not(.show){display:none !important}
.brand-filters{
  flex:0 0 100%;
  width:100%;
  box-sizing:border-box;
  margin-top:10px;
  padding-top:10px;
  border-top:1px solid var(--border);
}
.brand-filters-label{
  font-family:'Space Mono',monospace;font-size:10px;color:var(--slate-lt);
  letter-spacing:.08em;text-transform:uppercase;margin-right:auto;white-space:nowrap;
}
.brand-filters .filter.active{
  background:var(--gold);border-color:var(--gold);color:var(--navy);
  box-shadow:0 2px 8px rgba(0,33,71,.14);
}
.filter:hover{border-color:var(--navy);color:var(--navy);background:#f0f4ff}
.filter.active{background:var(--navy);border-color:var(--navy);color:#fff;box-shadow:0 2px 8px rgba(0,33,71,.22)}

/* ── "Active Offers" filter — own full-width row under the brand chips
   (same .sec-head flex-wrap container; flex-basis:100% wraps it onto its
   own line, same trick as .brand-filters above). Hidden via :not(.show)
   until renderNav() (main.js) confirms at least one product currently has
   an offer_price — see _hasActiveOffer()/toggleOffersOnly(). Warm red/
   orange theme (matches the 🏷 offer badge colour on product cards)
   deliberately distinct from the navy category chips and gold brand chips,
   so it reads as "deals", not just another filter. ── */
.filters.offers-only-row:not(.show){display:none !important}
.offers-only-row{
  flex:0 0 100%;
  width:100%;
  box-sizing:border-box;
  margin-top:10px;
  padding-top:10px;
  border-top:1px solid var(--border);
  justify-content:flex-start;
}
.offers-only-btn{
  border-color:#fca5a5;
  color:#b91c1c;
  background:#fff5f5;
  font-weight:700;
}
.offers-only-btn:hover{border-color:#ef4444;color:#ef4444;background:#fef2f2}
.offers-only-btn.active{
  background:#ef4444;border-color:#ef4444;color:#fff;
  box-shadow:0 2px 8px rgba(239,68,68,.28);
}
.offers-only-count{
  display:inline-block;margin-left:4px;padding:1px 6px;border-radius:99px;
  font-family:'Space Mono',monospace;font-size:10px;
  background:rgba(0,0,0,.08);
}
.offers-only-btn.active .offers-only-count{background:rgba(255,255,255,.25)}

/* ── Individual promo pills — one per distinct offer_label (see
   _offerLabelGroups()/setOfferLabel() in main.js), sitting right after the
   master "Active Offers" button inside the same .offers-only-row so they
   read as "here's the breakdown of what that button includes", not a
   separate filter group. Same warm red family as the master button but
   lighter/smaller (outline style) so the master toggle still reads as the
   primary control and these as refinements of it; each pill goes solid red
   when it's the one currently selected, same visual language as
   .offers-only-btn.active. ── */
.offer-label-pills{display:flex;flex-wrap:wrap;gap:6px}
.offer-label-btn{
  border-color:#fecaca;
  color:#b91c1c;
  background:#fff;
  font-weight:600;
  font-size:12px;
  padding:6px 10px;
}
.offer-label-btn:hover{border-color:#ef4444;color:#ef4444;background:#fef2f2}
.offer-label-btn.active{
  background:#ef4444;border-color:#ef4444;color:#fff;
  box-shadow:0 2px 8px rgba(239,68,68,.28);
}
.offer-label-count{
  display:inline-block;margin-left:4px;padding:1px 6px;border-radius:99px;
  font-family:'Space Mono',monospace;font-size:10px;
  background:rgba(0,0,0,.08);
}
.offer-label-btn.active .offer-label-count{background:rgba(255,255,255,.25)}

/* ── Member note ── */
.member-note{margin:0 auto 20px;padding:10px var(--site-pad);border-radius:var(--radius);font-size:13px;font-weight:600;display:none;max-width:var(--site-width);width:100%}
.member-note.on{display:block;background:var(--green-light);border:1px solid #86efac;color:#14532d}

/* ── Product grid ── */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:20px;padding:0 var(--site-pad);max-width:var(--site-width);margin:0 auto;width:100%}

/* ── Category rows (the "All Products" overview) & the category-header strip
   shown above a single category's full list ── both are injected as direct
   children of #grid, which is itself a `.grid`; force each one to span the
   full row so it stacks as a section instead of being laid out as a card
   column. (A single category's product cards are also direct children of
   #grid and rely on #grid's own grid-template-columns — no extra wrapper.)
   .cat-row-line (the packed-category-row wrapper, see below) is the direct
   child now instead of .cat-row itself — .cat-row sections live inside it. ── */
#grid > .cat-row-line,
#grid > .cat-full-head,
#grid > .breadcrumbs{grid-column:1/-1}

/* ── Breadcrumbs (audit F-011) ── */
.breadcrumbs{display:flex;align-items:center;gap:8px;font-family:'Archivo',sans-serif;font-size:12px;color:var(--slate-lt);margin-bottom:14px}
.breadcrumbs .bc-home{color:var(--slate);text-decoration:none;font-weight:600;cursor:pointer}
.breadcrumbs .bc-home:hover{color:var(--navy);text-decoration:underline}
.breadcrumbs .bc-sep{color:var(--slate-lt)}
.breadcrumbs .bc-current{color:var(--navy);font-weight:700}

.cat-full-head{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding-bottom:12px;margin-bottom:4px;border-bottom:2px solid var(--border)}
.cat-full-back{appearance:none;border:1.5px solid var(--border);background:#fff;color:var(--slate);font-family:'Archivo',sans-serif;font-size:11px;font-weight:700;letter-spacing:.03em;padding:6px 12px;border-radius:99px;cursor:pointer;display:inline-flex;align-items:center;gap:4px;transition:all var(--tx);white-space:nowrap}
.cat-full-back:hover{border-color:var(--navy);color:var(--navy);background:#f0f4ff}
.cat-full-back svg{width:13px;height:13px}
.cat-full-title{font-family:'Bricolage Grotesque',sans-serif;font-size:clamp(1.2rem,2.6vw,1.6rem);font-weight:800;color:var(--navy);margin:0}
.cat-full-count{font-family:'Space Mono',monospace;font-size:11px;color:var(--slate-lt);margin-left:auto}

/* Packs several small-category .cat-row blocks into one visual row instead
   of each getting a full-width row (2026-08-12) — see _packCategoryRows()/
   CATEGORY_ROW_FILL in main.js. A 4-column planning grid purely for
   allocating width to category blocks; each block's own .cat-row-grid
   below still lays its cards out with its own auto-fill sizing, so card
   width stays consistent whether a block is alone in a row or sharing one. */
.cat-row-line{display:grid;grid-template-columns:repeat(4,1fr);gap:24px 20px;margin-bottom:8px}
.cat-row-line > .cat-row{grid-column:span var(--cat-span,4);min-width:0}

.cat-row{display:flex;flex-direction:column;gap:14px;padding-bottom:8px}
.cat-row-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.cat-row-title{font-family:'Bricolage Grotesque',sans-serif;font-size:1.1rem;font-weight:800;color:var(--navy);margin:0;cursor:pointer;transition:color var(--tx)}
.cat-row-title:hover{color:var(--gold,#B8860B)}
.cat-row-count{font-family:'Space Mono',monospace;font-size:11px;color:var(--slate-lt);letter-spacing:.04em}
.cat-row-viewall{appearance:none;border:1.5px solid var(--border);background:#fff;color:var(--navy);font-family:'Archivo',sans-serif;font-size:11px;font-weight:700;letter-spacing:.03em;padding:6px 12px;border-radius:99px;cursor:pointer;display:inline-flex;align-items:center;gap:4px;transition:all var(--tx);margin-left:auto;white-space:nowrap}
.cat-row-viewall:hover{border-color:var(--navy);background:#f0f4ff}
.cat-row-viewall svg{width:13px;height:13px}
.cat-row-grid{padding:0;margin:0;grid-template-columns:repeat(auto-fill,minmax(220px,1fr))}

@media(max-width:768px){
  /* Below tablet width, packing several categories per row leaves each
     one too narrow to browse comfortably — revert to one full-width
     category block per row, same as before this feature. */
  .cat-row-line{grid-template-columns:1fr;gap:20px}
  .cat-row-line > .cat-row{grid-column:1/-1}
  .cat-row-grid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
  .cat-row-title{font-size:1rem}
  .cat-row-viewall{padding:5px 10px;font-size:10px}
}
@media(max-width:480px){
  .cat-row-grid{grid-template-columns:1fr 1fr;gap:8px}
}

/* ════════════════════════════════════════════════════════
   PRODUCT CARD
════════════════════════════════════════════════════════ */
.card{background:var(--white);border:1.5px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow-card);display:flex;flex-direction:column;overflow:hidden;transition:transform var(--tx),box-shadow var(--tx),border-color var(--tx);position:relative}
.card:hover{transform:translateY(-3px);box-shadow:var(--shadow-hover);border-color:var(--border-hover,#D1D5DB)}
/* reveal animation disabled — all cards show immediately */
.card.reveal{opacity:1;transform:none}
.card.reveal.in{opacity:1;transform:none}

/* Brief highlight when a "This Week's Deals" banner deep-links to this card
   (see goToProduct() in main.js) — draws the eye without changing layout. */
.card.card-deal-highlight{
  border-color:var(--gold);
  box-shadow:0 0 0 3px rgba(246,208,121,.55),var(--shadow-hover);
  transition:box-shadow .25s ease,border-color .25s ease,transform var(--tx);
}

/* ── Image: fixed 164px, contain, no overflow ── */
.card-img{position:relative;height:164px;overflow:hidden;background:#f8f9fa;border-bottom:1px solid var(--border);flex-shrink:0}
.card-photo{width:100%;height:100%;object-fit:contain;display:block;background:#fff;padding:8px;box-sizing:border-box;transition:transform .3s ease}
.card:hover .card-photo{transform:scale(1.04)}
.card-sku{position:absolute;top:8px;right:8px;background:rgba(0,0,0,.45);color:#fff;font-family:'Space Mono',monospace;font-size:9px;letter-spacing:.06em;padding:2px 6px;border-radius:4px}
.card-badge{position:absolute;top:8px;left:8px;background:var(--navy);color:var(--gold);font-size:9px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;padding:3px 8px;border-radius:4px}
.card-badge.deal{background:var(--gold);color:var(--navy)}

/* ── Card body ── */
.card-body{padding:14px 16px 10px;flex:1;display:flex;flex-direction:column;gap:4px}
.card-cat{font-family:'Space Mono',monospace;font-size:9px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--slate-lt);display:flex;align-items:center;justify-content:space-between;gap:6px}
.stock-badge{font-family:'Archivo',sans-serif;font-size:9px;font-weight:800;letter-spacing:.02em;text-transform:none;padding:2px 7px;border-radius:99px;white-space:nowrap}
.stock-badge.low{background:#FEE2E2;color:#B91C1C}
.stock-badge.ok{background:#F3F4F6;color:#6B7280}
.card-body h3{font-family:'Bricolage Grotesque',sans-serif;font-size:14px;font-weight:700;color:var(--navy);margin:0;line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.price-block{margin-top:4px}
.price-now{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap}
.price-now b{font-family:'Space Mono',monospace;font-size:18px;font-weight:700;color:var(--navy);letter-spacing:-.02em}
.price-now small{font-size:10px;color:var(--slate-lt);font-weight:500}
.price-unit{font-size:11px;color:var(--slate-lt);margin-top:2px;line-height:1.5}

/* ── Tiers ── */
.tiers{margin-top:8px;border:1px solid var(--border);border-radius:6px;overflow:hidden;font-size:11px}
.tiers-label{background:var(--navy);color:var(--gold);font-size:9px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;padding:4px 10px}
.tier-row{display:flex;justify-content:space-between;align-items:center;padding:5px 10px;border-top:1px solid var(--border);color:var(--slate)}
.tier-row:first-of-type{border-top:none}
.tier-row b{font-family:'Space Mono',monospace;color:var(--green);font-size:12px}
.tier-row.active-tier{background:#f0fdf4}
.tiers.locked{position:relative;margin-top:8px;display:none;}
.tier-blur{filter:blur(3px);user-select:none;pointer-events:none;opacity:.5}
.tier-lock{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;background:rgba(0,33,71,.85);color:#fff;border:none;cursor:pointer;font-size:11px;font-weight:600;text-align:center;padding:8px 12px;transition:background var(--tx);width:100%}
.tier-lock:hover{background:rgba(0,33,71,.95)}
.tier-lock svg{width:14px;height:14px;flex-shrink:0}
.tier-lock span{line-height:1.4}
.tier-lock small{font-size:10px;opacity:.75;display:block;margin-top:2px}

/* ── Card footer ── */
.card-foot{padding:10px 16px 14px;border-top:1px solid var(--border);display:flex;flex-direction:column;gap:8px}
.moq{font-size:10px;color:var(--slate-lt);font-weight:600;letter-spacing:.02em}
.qty-add{display:flex;gap:7px;align-items:stretch}
.stepper{display:flex;align-items:stretch;border:1.5px solid var(--border);border-radius:6px;overflow:hidden;flex:0 0 auto}
.stepper button{width:30px;background:#f8f9fa;border:none;cursor:pointer;font-size:16px;line-height:1;color:var(--navy);font-weight:700;transition:background var(--tx);display:flex;align-items:center;justify-content:center}
.stepper button:hover{background:#e5e7eb}
.stepper input{width:42px;border:none;border-left:1.5px solid var(--border);border-right:1.5px solid var(--border);text-align:center;font-family:'Space Mono',monospace;font-size:13px;font-weight:700;color:var(--navy);padding:0}
.stepper input:focus{outline:none;background:#f0f4ff}
.stepper input[type=number]{-moz-appearance:textfield}
.stepper input[type=number]::-webkit-inner-spin-button,.stepper input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;appearance:none}
.add-btn{flex:1;background:var(--navy);color:#fff;border:none;border-radius:6px;font-family:'Archivo',sans-serif;font-size:13px;font-weight:700;cursor:pointer;transition:background var(--tx),transform .1s;display:flex;align-items:center;justify-content:center;gap:5px;padding:0 14px;min-height:38px}
.add-btn:hover{background:#1a3a60}
.add-btn:active{transform:scale(.97)}
.add-btn.added{background:var(--green)}
.signin-order{width:100%;padding:10px 14px;background:var(--navy);color:#fff;border:none;border-radius:6px;font-family:'Archivo',sans-serif;font-size:12px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px;transition:background var(--tx);text-align:center;line-height:1.4}
.signin-order:hover{opacity:.9}
.signin-order svg{width:13px;height:13px;flex-shrink:0}

/* ════════════════════════════════════════════════════════
   BULK CTA
════════════════════════════════════════════════════════ */
.bulk{background:var(--navy);padding:56px 28px}
.bulk-inner{display:flex;gap:48px;max-width:var(--site-width);margin:0 auto;align-items:center;flex-wrap:wrap}
.bulk-l{flex:1;min-width:260px}
.bulk-l h2{font-family:'Bricolage Grotesque',sans-serif;font-size:clamp(1.6rem,3vw,2.2rem);font-weight:800;color:#fff;margin-bottom:14px;line-height:1.15}
.bulk-l h2 em{font-style:italic;color:var(--gold)}
.bulk-l p{font-size:14px;color:rgba(255,255,255,.75);line-height:1.7;margin-bottom:24px;max-width:460px}
.bulk-r{flex:1;min-width:260px;display:flex;flex-direction:column;gap:16px}
.bulk-feat{display:flex;align-items:flex-start;gap:12px}
.bulk-feat svg{width:18px;height:18px;stroke:var(--gold);flex-shrink:0;margin-top:2px;fill:none}
.bulk-feat b{display:block;font-size:13px;font-weight:700;color:#fff;margin-bottom:2px}
.bulk-feat span{font-size:12px;color:rgba(255,255,255,.65)}

/* ════════════════════════════════════════════════════════
   FOOTER
════════════════════════════════════════════════════════ */
footer{background:#0d1a29;color:rgba(255,255,255,.7);padding:0}
.foot-top{display:grid;grid-template-columns:1.5fr repeat(3,1fr);gap:32px;padding:48px var(--site-pad);max-width:var(--site-width);margin:0 auto}
.foot-brand{}
.foot-brand .logo-tag{color:#fff;font-size:15px;margin-bottom:10px;display:block}
.foot-brand p{font-size:12px;color:rgba(255,255,255,.55);line-height:1.7;max-width:220px;margin-top:8px}
.foot-col h4{font-size:11px;font-weight:800;color:#fff;letter-spacing:.1em;text-transform:uppercase;margin-bottom:14px}
.foot-col a{display:block;font-size:13px;color:rgba(255,255,255,.6);margin-bottom:8px;transition:color var(--tx)}
.foot-col a:hover{color:#fff}
.foot-bot{border-top:1px solid rgba(255,255,255,.08);padding:14px var(--site-pad) 10px}
.foot-bot-wrap{max-width:var(--site-width);margin:0 auto;display:flex;justify-content:space-between;align-items:center;font-size:11px;color:rgba(255,255,255,.4);flex-wrap:wrap;gap:8px}

/* ════════════════════════════════════════════════════════
   CART DRAWER
════════════════════════════════════════════════════════ */
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:400;opacity:0;pointer-events:none;transition:opacity var(--tx)}
.overlay.open{opacity:1;pointer-events:auto}
.drawer{position:fixed;right:-420px;top:0;width:min(420px,100vw);height:100vh;background:#fff;z-index:500;display:flex;flex-direction:column;box-shadow:-4px 0 30px rgba(0,0,0,.15);transition:right var(--tx)}
.drawer.open{right:0}
.drawer-head{padding:18px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
.drawer-head h3{font-family:'Bricolage Grotesque',sans-serif;font-size:16px;font-weight:800;color:var(--navy);display:flex;align-items:center;gap:8px}
.drawer-head h3 svg{stroke:var(--navy);fill:none}
.cnt{background:var(--navy);color:#fff;font-size:10px;font-weight:800;min-width:18px;height:18px;border-radius:99px;display:inline-flex;align-items:center;justify-content:center;padding:0 4px}
.drawer-close{background:none;border:none;cursor:pointer;font-size:22px;color:var(--slate);width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:6px;transition:background var(--tx)}
.drawer-close:hover{background:var(--bg)}
.drawer-body{flex:1;overflow-y:auto;padding:12px 20px}
.drawer-foot{padding:14px 20px;border-top:1px solid var(--border);flex-shrink:0}

/* Cart lines */
.line{display:flex;gap:12px;padding:12px 0;border-bottom:1px solid var(--border)}
.line:last-child{border-bottom:none}
.line-img{width:52px;height:52px;border-radius:6px;overflow:hidden;flex-shrink:0;background:#f3f4f6;border:1px solid var(--border)}
.line-info{flex:1;min-width:0}
.line-info h4{font-size:13px;font-weight:700;color:var(--navy);margin:0 0 2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.line-sku{font-size:10px;color:var(--slate-lt)}
.line-tier{font-size:10px;color:var(--green);font-weight:600}
.line-controls{display:flex;align-items:center;gap:8px;margin-top:6px}
.mini-step{display:inline-flex;align-items:center;border:1.5px solid var(--border);border-radius:6px;overflow:hidden}
.mini-step button{width:24px;height:28px;background:#f8f9fa;border:none;font-size:15px;font-weight:700;cursor:pointer;color:var(--navy);display:flex;align-items:center;justify-content:center}
.mini-step button:hover{background:var(--border)}
.mini-step span{width:32px;text-align:center;font-family:'Space Mono',monospace;font-size:12px;font-weight:700;color:var(--navy);border-left:1.5px solid var(--border);border-right:1.5px solid var(--border);padding:4px 0}
.line-price{font-family:'Space Mono',monospace;font-size:13px;font-weight:700;color:var(--navy);margin-left:auto}
.line-rm{font-size:10px;color:var(--slate-lt);cursor:pointer;transition:color var(--tx)}
.line-rm:hover{color:var(--red)}

.cart-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 20px;color:var(--slate-lt);text-align:center;gap:14px}
.cart-empty svg{width:48px;height:48px;stroke:var(--slate-lt);stroke-width:1.4;fill:none}
.cart-empty p{font-size:13px;line-height:1.6;margin:0}
.sum-row{display:flex;justify-content:space-between;align-items:center;font-size:13px;color:var(--slate);padding:4px 0}
.sum-row.save{color:var(--green);font-weight:600}
.sum-row.total{font-weight:800;font-size:15px;color:var(--navy);border-top:2px solid var(--border);padding-top:10px;margin-top:4px}
.ship-bar{height:4px;background:var(--border);border-radius:99px;margin:14px 0 6px;overflow:hidden}
.ship-bar i{display:block;height:100%;background:linear-gradient(90deg,var(--green),#22c55e);border-radius:99px;transition:width .4s ease}
.free-ship{font-size:11px;color:var(--slate-lt);margin-bottom:14px;text-align:center}
.free-ship b{color:var(--green)}
.checkout-btn{width:100%;padding:13px 20px;background:var(--green);color:#fff;border:none;border-radius:var(--radius);font-family:'Archivo',sans-serif;font-size:14px;font-weight:800;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;letter-spacing:.01em;transition:background var(--tx),transform .1s;box-shadow:0 4px 14px rgba(26,122,69,.3)}
.checkout-btn:hover{background:#166038}
.checkout-btn:active{transform:scale(.98)}

/* ════════════════════════════════════════════════════════
   STICKY MOBILE MINI-CART BAR
   Desktop already has a persistent cart icon + badge in the header, so
   this is mobile-only (see the max-width:768px block further down) and
   off by default here regardless of viewport — updateStickyCartBar() in
   main.js adds/removes .show once the cart has items. Kept as a totally
   separate element from .drawer-foot/.checkout-btn: that one only exists
   while the drawer is open; this one is meant to always be reachable
   while browsing, which is the actual "always footer only cart button"
   ask — the drawer's own footer being prominent wasn't enough on its own
   since it's invisible until the drawer is opened.
════════════════════════════════════════════════════════ */
.sticky-cart-bar{
  display:none;
  position:fixed;
  left:0; right:0; bottom:60px;
  z-index:300;
  background:var(--green);
  color:#fff;
  align-items:center;
  gap:10px;
  padding:14px 18px;
  cursor:pointer;
  box-shadow:0 -6px 20px rgba(0,0,0,.22);
  font-family:'Archivo',sans-serif;
}
/* .show is only ever added on mobile by main.js, but gate it here too so
   nothing changes if that check is ever loosened — desktop keeps relying
   on the header cart icon + badge instead. */
@media(max-width:768px){
  .sticky-cart-bar.show{ display:flex; }
  /* Reserve space at the bottom of the page so the bar never sits on top
     of the last row of products or the footer links. Toggled alongside
     .show by main.js.
     2026-08-14: bumped from 60px to 130px to match .sticky-cart-bar's new
     bottom:60px position (it now stacks above the mobile tab bar instead
     of sitting flush at bottom:0) — 60px reserved for the tab bar's own
     unconditional `body{padding-bottom:66px}` rule further down plus
     ~65-70px for the cart bar itself sitting above it. */
  body.has-sticky-cart{ padding-bottom:130px; }
}
.scb-count{
  background:rgba(255,255,255,.22);
  border-radius:99px;
  min-width:22px; height:22px;
  padding:0 6px;
  display:flex; align-items:center; justify-content:center;
  font-size:12px; font-weight:800; flex-shrink:0;
}
.scb-label{ font-size:12px; opacity:.85; flex-shrink:0 }
.scb-total{ font-size:15px; font-weight:800; margin-left:auto }
.scb-cta{
  display:flex; align-items:center; gap:6px;
  background:rgba(255,255,255,.16);
  border-radius:8px;
  padding:9px 14px;
  font-size:13px; font-weight:800;
  white-space:nowrap;
  flex-shrink:0;
}

/* ════════════════════════════════════════════════════════
   TOAST
════════════════════════════════════════════════════════ */
.toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:var(--navy);color:#fff;padding:12px 20px;border-radius:8px;font-size:13px;font-weight:600;display:flex;align-items:center;gap:8px;opacity:0;transition:opacity .25s;pointer-events:none;z-index:9999;white-space:nowrap;box-shadow:0 4px 20px rgba(0,0,0,.2)}
.toast.show{opacity:1}
.toast svg{width:16px;height:16px;stroke:var(--gold);flex-shrink:0;fill:none}

/* ════════════════════════════════════════════════════════
   ACCOUNT MODAL (sign-in / sign-up)
════════════════════════════════════════════════════════ */
.acct-overlay{position:fixed;inset:0;z-index:800;background:rgba(8,16,26,.64);backdrop-filter:blur(3px);display:flex;align-items:center;justify-content:center;padding:24px;opacity:0;visibility:hidden;transition:.25s;overflow-y:auto}
.acct-overlay.open{opacity:1;visibility:visible}
.acct-modal{width:min(1040px,100%);max-height:92vh;background:#fff;border-radius:22px;overflow:hidden;display:grid;grid-template-columns:1.02fr 1.12fr;box-shadow:0 40px 100px rgba(8,16,24,.5);position:relative;transform:translateY(18px) scale(.985);transition:.34s cubic-bezier(.4,0,.1,1)}
.acct-overlay.open .acct-modal{transform:none}
.acct-x{position:absolute;top:18px;right:18px;width:40px;height:40px;border:1.5px solid rgba(20,22,25,.18);border-radius:50%;display:grid;place-items:center;font-size:22px;line-height:1;color:var(--ink,#141619);background:#fff;z-index:4;transition:.15s}
.acct-x:hover{background:var(--ink,#141619);color:#fff;border-color:var(--ink,#141619);transform:rotate(90deg)}
/* left brand panel */
.acct-left{background:linear-gradient(157deg,#123a52 0%,#0c2436 54%,#081420 100%);color:#fff;padding:44px 40px;display:flex;flex-direction:column;position:relative;overflow:hidden}
.acct-left::after{content:"";position:absolute;right:-90px;bottom:-90px;width:280px;height:280px;border-radius:50%;background:radial-gradient(circle,rgba(0,152,224,.24),transparent 70%);pointer-events:none}
.acct-brand{display:flex;align-items:center;gap:14px;margin-bottom:40px;position:relative;z-index:1}
.acct-logo-box{width:58px;height:58px;background:#fff;border-radius:14px;display:grid;place-items:center;padding:8px;flex-shrink:0;box-shadow:0 8px 22px rgba(0,0,0,.28)}
.acct-logo-box img{width:100%;height:auto;display:block}
.acct-brand-txt b{font-family:'Fraunces',Georgia,serif;font-weight:700;font-size:27px;line-height:1;display:block;letter-spacing:-.01em}
.acct-brand-txt small{font-family:'Space Mono',monospace;font-size:10px;letter-spacing:.2em;color:rgba(255,255,255,.55);text-transform:uppercase}
.acct-h{font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:clamp(34px,4vw,44px);line-height:1.04;letter-spacing:-.02em;margin-bottom:20px;position:relative;z-index:1}
.acct-h em{font-style:italic;color:#E8A93A}
.acct-lead{font-size:16px;line-height:1.55;color:rgba(255,255,255,.78);max-width:360px;margin-bottom:32px;position:relative;z-index:1}
.acct-list{list-style:none;display:flex;flex-direction:column;gap:17px;position:relative;z-index:1}
.acct-list li{display:flex;gap:13px;align-items:flex-start;font-size:14.5px;color:rgba(255,255,255,.9)}
.acct-list li::before{content:"";width:18px;height:18px;flex-shrink:0;margin-top:1px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23E8A93A' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E");background-size:contain;background-repeat:no-repeat}
.acct-foot{margin-top:auto;padding-top:32px;position:relative;z-index:1}
.acct-divider{height:1px;background:rgba(255,255,255,.16);margin-bottom:22px}
.acct-foot b{font-size:15px;display:block;margin-bottom:7px}
.acct-foot p{font-size:13px;color:rgba(255,255,255,.6);line-height:1.5}
/* right form panel */
.acct-right{padding:42px 46px;overflow-y:auto;display:flex;flex-direction:column}
.acct-tabs{display:flex;gap:30px;border-bottom:1.5px solid rgba(20,22,25,.12);margin-bottom:30px}
.acct-tab{font-family:'Archivo',sans-serif;font-size:18px;font-weight:600;color:#565B63;padding:0 2px 14px;position:relative;transition:.15s;cursor:pointer;background:none;border:none}
.acct-tab:hover{color:#141619}
.acct-tab.active{color:#141619}
.acct-tab.active::after{content:"";position:absolute;left:0;right:0;bottom:-1.5px;height:3px;background:#0098E0;border-radius:3px}
.acct-pane{}
.acct-pane[hidden]{display:none}
.acct-h3{font-family:'Fraunces',Georgia,serif;font-weight:700;font-size:clamp(27px,3.2vw,34px);letter-spacing:-.02em;line-height:1.05;margin-bottom:10px;color:#141619}
.acct-sub{font-size:15px;line-height:1.5;color:#565B63;margin-bottom:26px;max-width:440px}
.acct-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.fld{display:flex;flex-direction:column;margin-bottom:18px}
.acct-grid .fld{margin-bottom:0}
.fld label{font-family:'Space Mono',monospace;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:#565B63;margin-bottom:8px}
.fld input,.fld select,.fld textarea{font-family:'Archivo',sans-serif;font-size:15px;padding:14px 15px;border:1.5px solid #DDE3EA;border-radius:11px;background:#EEF1F5;color:#141619;outline:none;transition:.15s;width:100%}
.fld input::placeholder{color:#9AA2AC}
.fld input:focus,.fld select:focus,.fld textarea:focus{border-color:#0098E0;background:#fff;box-shadow:0 0 0 3px rgba(0,152,224,.14)}
.fld select{appearance:none;-webkit-appearance:none;cursor:pointer;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23565B63' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 15px center;background-size:18px}
.acct-check{display:flex;gap:11px;align-items:flex-start;margin:8px 0 22px;font-size:13.5px;line-height:1.5;color:#565B63;cursor:pointer}
.acct-check.sm{margin:0;font-size:13.5px}
.acct-check input{width:19px;height:19px;margin-top:2px;accent-color:#0098E0;flex-shrink:0;cursor:pointer}
.acct-check a{color:#0A6FA0;font-weight:600}
.acct-check a:hover{text-decoration:underline}
.acct-row{display:flex;justify-content:space-between;align-items:center;margin:2px 0 22px;gap:14px;flex-wrap:wrap}
.acct-link{font-size:13.5px;color:#0A6FA0;font-weight:600;cursor:pointer}
.acct-link:hover{text-decoration:underline}
.acct-submit{display:flex;align-items:center;justify-content:center;gap:12px;width:100%;background:#0098E0;color:#fff;border-radius:40px;padding:17px;font-family:'Archivo',sans-serif;font-size:16px;font-weight:600;transition:.18s;box-shadow:0 10px 24px rgba(0,152,224,.32);border:none;cursor:pointer}
.acct-submit:hover{background:#0A6FA0;transform:translateY(-1px);box-shadow:0 14px 30px rgba(0,152,224,.42)}
.acct-submit svg{width:18px;height:18px}
.acct-switch{text-align:center;font-size:14px;color:#565B63;margin-top:18px}
.acct-switch a{color:#0A6FA0;font-weight:600;cursor:pointer}
.acct-switch a:hover{text-decoration:underline}
.fee-callout{display:flex;gap:13px;align-items:flex-start;background:#FFF7E8;border:1.5px solid #E8C98A;border-radius:12px;padding:14px 16px;margin:4px 0 20px}
.fee-callout svg{width:22px;height:22px;stroke:#B5811E;flex-shrink:0;margin-top:1px;fill:none}
.fee-callout b{display:block;font-size:14.5px;color:#141619;margin-bottom:3px}

/* ════════════════════════════════════════════════════════
   COMPACT FORM FIELDS ON LARGER SCREENS (2026-10-28)
   The 14-15px padding / 16px font on .fld input and the generous
   22-30px gaps between rows are sized for touch — fine on a phone,
   but on a mouse+keyboard desktop screen they add up to real wasted
   height for no benefit, which is exactly what pushes a form like
   Sign Up (name row + email + mobile + password + terms + button)
   past the modal's internal scroll threshold on an ordinary laptop
   window. None of this touches anything ≤768px — mobile keeps its
   current touch-friendly sizing (including the 16px input font-size
   that prevents iOS zoom-on-focus) completely as-is. This only
   tightens things once there's a mouse pointer and more vertical
   competition from browser chrome/taskbar instead of a full-height
   phone screen. Covers every flow that has form fields: Sign In,
   Sign Up, the Checkout delivery-address form, and the Payment
   ("final page") step. Cart has no text fields of its own (just the
   qty steppers, already compact), so there's nothing to shrink there.
════════════════════════════════════════════════════════ */
@media(min-width:769px){
  /* ── Sign in / Sign up (.acct-*) ── */
  .acct-right{ padding:34px 40px; }
  .acct-tabs{ margin-bottom:18px; }
  .acct-h3{ margin-bottom:6px; }
  .acct-sub{ margin-bottom:14px; }
  .acct-grid{ gap:12px; }
  .fld{ margin-bottom:12px; }
  .fld input,.fld select,.fld textarea{ padding:11px 14px; font-size:14px; }
  .acct-check{ margin:6px 0 14px; }
  .acct-row{ margin:2px 0 14px; }
  .acct-submit{ padding:14px; }
  .acct-switch{ margin-top:12px; }
  .fee-callout{ padding:11px 14px; margin:4px 0 12px; }

  /* ── Checkout: delivery address form + payment ("final") step ── */
  .co-inp{ padding:7px 10px !important; }
  .co-next-btn{ padding:11px 20px; }
  .co-pay-content{ padding:10px 14px; }
  #coPane3{ padding:16px 20px; }
}
.fee-callout b .fee-amt{color:#B5811E}
.fee-callout span{font-size:12.5px;color:#565B63;line-height:1.45}
.req{color:#C0506A}
@media(max-width:860px){
  .acct-modal{grid-template-columns:1fr;max-height:94vh}
  .acct-left{padding:32px 28px}
  .acct-brand{margin-bottom:22px}
  .acct-h{margin-bottom:12px}
  .acct-list,.acct-foot{display:none}
  .acct-lead{margin-bottom:4px;max-width:none}
  .acct-right{padding:32px 28px}
  .acct-grid{grid-template-columns:1fr}
}
@media(max-width:480px){
  .acct-overlay{padding:0}
  .acct-modal{border-radius:0;max-height:100vh;height:100%}
}

/* ════════════════════════════════════════════════════════
   MEMBERSHIP MODAL
════════════════════════════════════════════════════════ */
.mem-modal{width:min(75vw,900px) !important;max-width:75vw !important;max-height:92vh !important;grid-template-columns:1fr !important;display:block !important;overflow:hidden}
.mem-modal-body{padding:24px 28px;overflow-y:auto;max-height:90vh;display:block}
.mem-fee-span{font-family:'Space Mono',monospace}

/* ════════════════════════════════════════════════════════
   MEMBER-GATED PRICING
════════════════════════════════════════════════════════ */
.tiers.locked{position:relative}
.tier-blur{filter:blur(4.5px);opacity:.5;pointer-events:none;user-select:none}
.tier-lock{position:absolute;left:0;right:0;top:24px;bottom:0;display:flex;align-items:center;justify-content:center;gap:9px;background:linear-gradient(180deg,rgba(243,245,248,.55),rgba(243,245,248,.94));cursor:pointer;text-align:left;border:none;width:100%}
.tier-lock svg{width:18px;height:18px;stroke:#0A6FA0;flex-shrink:0;fill:none}
.tier-lock span{font-family:'Space Mono',monospace;font-size:11px;font-weight:700;letter-spacing:.03em;color:#0A6FA0;line-height:1.3}
.tier-lock span small{display:block;font-weight:400;color:#565B63;letter-spacing:.02em;text-transform:none;margin-top:2px}
.tier-lock:hover span{text-decoration:underline}
.signin-order{display:flex;align-items:center;justify-content:center;gap:9px;width:100%;border:2px solid var(--navy);background:var(--navy);color:#fff;font-family:'Space Mono',monospace;font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:13px;transition:.15s;cursor:pointer}
.signin-order svg{width:15px;height:15px;fill:none}
.signin-order:hover{background:#0A6FA0;border-color:#0A6FA0}
.member-note{display:flex;align-items:center;gap:10px;padding:13px 18px;border:2px solid var(--navy);margin:0 auto 22px;font-size:13.5px;line-height:1.45;max-width:var(--site-width);width:100%}
.member-note svg{width:18px;height:18px;flex-shrink:0;fill:none}
.member-note.off{background:#FFF7E8;border-color:#E8A93A}
.member-note.off svg{stroke:#B5811E}
.member-note.off a{color:#0A6FA0;font-weight:700;text-decoration:underline;cursor:pointer;margin-left:4px}
.member-note.on{background:#E9F7F0;border-color:#1E8E5A}
.member-note.on svg{stroke:#1E8E5A}
.head-btn.is-member{background:#1E8E5A;color:#fff;border-color:#1E8E5A}
.head-btn.is-member:hover{background:var(--navy);border-color:var(--navy)}

/* ════════════════════════════════════════════════════════
   ACCOUNT DASHBOARD — superseded by the "DASHBOARD — Left/Right
   Panel Layout" section further down (search .dash-overlay).
   Old bordered/brutalist dashboard rules removed 2026-07-18 —
   they were duplicate selectors with equal-or-higher specificity
   than the redesigned rules and were leaking a stray 2px black
   border + wrong dash-ttl colour through onto the live modal.
   .mbadge is still in active use (JS renders it) and now lives
   in the newer section below, restyled to match the flat design.
════════════════════════════════════════════════════════ */

/* ════════════════════════════════════════════════════════
   RESPONSIVE
════════════════════════════════════════════════════════ */
@media(max-width:1024px){
  .trust-grid{grid-template-columns:1fr 1fr}
  .foot-top{grid-template-columns:1fr 1fr}
  .bulk-inner{gap:32px}
  /* hero-slide-content padding at this width: see the dedicated HERO
     responsive section further down — kept in one place to avoid the
     two blocks drifting out of sync with each other. */
}
@media(max-width:768px){
  .head-main{padding:0 16px;gap:12px;top:34px}
  /* hero rules: see the dedicated HERO responsive section below */
  .sec-head{flex-direction:column;align-items:flex-start;padding:0 16px 20px}
  /* Category chip grid (2026-07-26 v2) — superseded by the #filters,
     #brandFilters, #offersOnlyRow{display:none !important} rule in the
     "CATALOG / PRODUCT GRID" @media block further down (same breakpoint,
     later in source, so that one wins the cascade and these rows are
     actually hidden on mobile, replaced by the #pickerOverlay modal). Left
     as historical record of the equal-width grid layout these rows used
     back when they were still shown inline on mobile. */
  .filters{
    display:grid;
    width:100%;
    box-sizing:border-box;
    grid-template-columns:repeat(auto-fill,minmax(128px,1fr));
    gap:8px;
    justify-content:stretch;
  }
  .grid{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;padding:0 16px}
  .card-img{height:130px}
  .card-body{padding:10px 12px 8px}
  .card-body h3{font-size:12px}
  .price-now b{font-size:15px}
  .card-foot{padding:8px 12px 12px}
  .bulk{padding:40px 16px}
  .foot-top{grid-template-columns:1fr 1fr;padding:32px 16px;max-width:100%}
  .trust-grid{grid-template-columns:1fr 1fr;gap:16px}
  .trust{padding:24px 16px}
  .acct-modal{grid-template-columns:1fr}
  .acct-left{display:none}
  .mem-modal{max-width:calc(100% - 24px) !important;width:calc(100% - 24px) !important}
  .catbar{top:98px}
}
@media(max-width:480px){
  .grid{grid-template-columns:1fr 1fr}
  .card-img{height:110px}
  .foot-top{grid-template-columns:1fr}
  /* hero rules: see the dedicated HERO responsive section below */
}

/* ═══════════════════════════════════════════════════════════
   OFFER BANNER SLIDER
═══════════════════════════════════════════════════════════ */
.offers-section{
  padding:48px 0 40px;
  background:linear-gradient(180deg,#f8f9fa 0%,#fff 100%);
  overflow:hidden;
}
.offers-header{
  padding:0 var(--site-pad) 24px;max-width:var(--site-width);margin:0 auto;
  display:flex;
  flex-direction:column;
  gap:6px;
}
.offers-label{
  font-family:'Space Mono',monospace;
  font-size:11px;
  color:var(--slate-lt);
  letter-spacing:.12em;
  text-transform:uppercase;
}
.offers-title{
  font-family:'Bricolage Grotesque',sans-serif;
  font-size:clamp(1.3rem,2.5vw,1.8rem);
  font-weight:800;
  color:var(--navy);
  margin:0;
}

/* ── Track wrapper with prev/next ── */
.offers-track-wrap{
  position:relative;
  display:flex;
  align-items:center;
  gap:0;
  padding:0 var(--site-pad);max-width:var(--site-width);margin:0 auto;
}
.offers-track{
  display:flex;
  gap:16px;
  overflow-x:auto;
  scroll-behavior:smooth;
  scrollbar-width:none;
  -ms-overflow-style:none;
  /* "proximity" (not "mandatory") so a manual finger-drag on mobile can
     freely land anywhere and only gently snaps once you let go near a card
     edge — "mandatory" was fighting short/slow swipes on touch devices
     (common iOS Safari behaviour: a partial drag on a mandatory-snap track
     springs straight back to the start instead of moving), which read as
     "can't manually slide the deals, only the arrow buttons work". touch-
     action:pan-x + -webkit-overflow-scrolling:touch make sure this element
     claims horizontal touch gestures for itself (rather than the page's
     vertical scroll swallowing them) and keeps the drag smooth on iOS. */
  scroll-snap-type:x proximity;
  touch-action:pan-x;
  -webkit-overflow-scrolling:touch;
  padding:8px 4px 16px;
  flex:1;
  cursor:grab;
}
.offers-track:active{cursor:grabbing}
.offers-track::-webkit-scrollbar{display:none}

/* ── Arrow buttons ── */
.offers-arrow{
  flex-shrink:0;
  width:40px;height:40px;
  border-radius:50%;
  border:1.5px solid var(--border);
  background:#fff;
  cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 2px 8px rgba(0,0,0,.08);
  transition:all var(--tx);
  z-index:2;
}
.offers-arrow:hover{background:var(--navy);border-color:var(--navy)}
.offers-arrow:hover svg{stroke:#fff}
.offers-arrow svg{width:18px;height:18px;stroke:var(--navy);transition:stroke var(--tx)}
.offers-prev{margin-right:12px}
.offers-next{margin-left:12px}

/* ── Individual offer card ── */
.offer-card{
  flex:0 0 280px;
  scroll-snap-align:start;
  border-radius:16px;
  padding:22px 22px 18px;
  display:flex;
  flex-direction:column;
  gap:8px;
  position:relative;
  overflow:hidden;
  transition:transform var(--tx),box-shadow var(--tx);
  box-shadow:0 2px 12px rgba(0,0,0,.08);
}
.offer-card:hover{transform:translateY(-4px);box-shadow:0 8px 28px rgba(0,0,0,.14)}

/* card colour themes */
.offer-card--hot{
  background:linear-gradient(135deg,var(--navy) 0%,#1a3a60 100%);
  color:#fff;
}
.offer-card--new{
  background:linear-gradient(135deg,var(--green) 0%,#2da05c 100%);
  color:#fff;
}
.offer-card--bulk{
  background:linear-gradient(135deg,#7c3aed 0%,#9d65f0 100%);
  color:#fff;
}

/* decorative circle */
.offer-card::after{
  content:'';
  position:absolute;
  right:-30px;bottom:-30px;
  width:130px;height:130px;
  border-radius:50%;
  background:rgba(255,255,255,.07);
  pointer-events:none;
}

.offer-badge{
  font-size:10px;font-weight:800;letter-spacing:.06em;
  background:rgba(255,255,255,.18);
  color:#fff;
  border-radius:4px;
  padding:3px 8px;
  display:inline-block;
  width:fit-content;
  backdrop-filter:blur(4px);
}
.offer-cat{
  font-family:'Space Mono',monospace;
  font-size:9px;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:rgba(255,255,255,.65);
  margin-top:4px;
}
.offer-name{
  font-family:'Bricolage Grotesque',sans-serif;
  font-size:17px;
  font-weight:800;
  color:#fff;
  line-height:1.25;
}
.offer-name span{
  font-size:12px;
  font-weight:500;
  color:rgba(255,255,255,.75);
  font-family:'Archivo',sans-serif;
  display:block;
  margin-top:2px;
}
.offer-prices{
  display:flex;
  align-items:baseline;
  gap:8px;
  flex-wrap:wrap;
  margin-top:4px;
}
.offer-was{
  font-family:'Space Mono',monospace;
  font-size:12px;
  color:rgba(255,255,255,.5);
  text-decoration:line-through;
}
.offer-now{
  font-family:'Space Mono',monospace;
  font-size:26px;
  font-weight:700;
  color:var(--gold);
  letter-spacing:-.02em;
  line-height:1;
}
.offer-unit{
  font-size:10px;
  color:rgba(255,255,255,.6);
}
.offer-saving{
  font-size:11px;
  font-weight:700;
  color:rgba(255,255,255,.8);
  background:rgba(255,255,255,.12);
  border-radius:4px;
  padding:4px 8px;
  width:fit-content;
}
.offer-cta{
  margin-top:auto;
  padding-top:14px;
  display:inline-flex;
  align-items:center;
  gap:6px;
  font-family:'Archivo',sans-serif;
  font-size:13px;
  font-weight:700;
  color:#fff;
  text-decoration:none;
  border-top:1px solid rgba(255,255,255,.15);
  transition:gap var(--tx);
}
.offer-cta:hover{gap:10px;color:var(--gold)}

/* ── Deal card background photo (2026-08-14, updated 2026-08-15 — direct
   image view, no dimming) — same convention as the hero slider: an optional
   image_url becomes an inline background-image on the card itself (desktop,
   cover/center), with an optional separate mobile_image_url crop shown via
   a real <img> at the mobile breakpoint (falls back to image_url when no
   dedicated mobile crop is set). Cards without a photo are completely
   unaffected — .offer-card-img is only ever added to the markup
   (renderDealSlides() in main.js) when a slide actually has one.
   The dark scrim gradient that used to sit over the photo has been removed
   per request — these are pre-designed supplier banners (own branding/
   contrast baked in), not plain photos needing a wash for legibility, so
   the image now shows exactly as uploaded. A fixed aspect-ratio box
   (4:5 — matches the reference banners) replaces the old text-driven auto
   height, so the photo always fills the card's full width/height via
   cover with no letterboxing or unpredictable per-card cropping, and a
   text-shadow (not a background tint) keeps the overlaid title/CTA
   readable on top of it without visually darkening the picture. ── */
.offer-card.is-linked{cursor:pointer}
.offer-card.has-image{
  background-color:var(--navy);background-size:cover;background-position:center;
  aspect-ratio:4/5;
}
.offer-card-img{display:none}
/* Title/badge stay pinned to the top and the CTA to the bottom, same layout
   as before (.offer-cta's own margin-top:auto does that) — just no dark
   wash between the text and the photo any more. */
.offer-card.has-image .offer-card-body{
  text-shadow:0 1px 4px rgba(0,0,0,.65),0 1px 12px rgba(0,0,0,.35);
}
.offer-card-body{position:relative;z-index:2;display:flex;flex-direction:column;gap:8px;height:100%}
@media(max-width:768px){
  .offer-card.has-image .offer-card-img{
    display:block;position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  }
}

/* ── Loading / empty / error states for #offersTrack (2026-08-14) — replaces
   the fixed set of made-up example deal cards that used to sit here in
   header.php. See loadBanners()/renderDealSlides()/_offersShowEmpty()/
   _offersShowError() in main.js. ── */
.offers-loading,.offers-empty{
  display:flex;align-items:center;justify-content:center;gap:10px;
  width:100%;min-height:160px;padding:24px;
  font-family:'Archivo',sans-serif;font-size:13.5px;color:var(--slate,#6b7280);
  text-align:center;
}
.offers-spinner{
  width:16px;height:16px;flex-shrink:0;border-radius:50%;
  border:2px solid rgba(0,33,71,.15);border-top-color:var(--navy);
  animation:offersSpin .7s linear infinite;
}
@keyframes offersSpin{to{transform:rotate(360deg)}}

/* ── Dots ── */
.offers-dots{
  display:flex;
  justify-content:center;
  gap:6px;
  margin-top:4px;
}
.offers-dot{
  width:6px;height:6px;
  border-radius:99px;
  background:var(--border);
  cursor:pointer;
  transition:all .25s;
  border:none;
  padding:0;
}
.offers-dot.active{background:var(--navy);width:18px}

/* ── Responsive — superseded by the "OFFERS SLIDER" @media blocks further
   down the file (same breakpoints, later in source, so those actually win
   the cascade); kept in sync with them rather than removed, same reasoning
   as the .filters duplication noted elsewhere in this file. ── */
@media(max-width:768px){
  .offers-track-wrap{padding:0 10px}
  .offers-header{padding:0 16px 12px}
  .offer-card{flex:0 0 200px}
  .offers-prev{margin-right:6px}
  .offers-next{margin-left:6px}
}
@media(max-width:480px){
  .offer-card{flex:0 0 74vw}
  .offer-now{font-size:20px}
}

/* ═══════════════════════════════════════════════════════════
   PROFESSIONAL CATEGORY BAR
   Replaces the old .cats scrollbar nav
═══════════════════════════════════════════════════════════ */

/* hide old nav completely */
.cats { display: none !important; }

/* ── Catbar wrapper ── */
.catbar {
  background: #fff;
  border-bottom: 1.5px solid var(--border);
  position: sticky;
  top: 98px;           /* ticker 34px + head-main 64px */
  z-index: 190;
  box-shadow: 0 2px 12px rgba(0,0,0,.06);
}

.catbar-inner {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 var(--site-pad);
  max-width: var(--site-width);
  margin: 0 auto;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  min-height: 52px;
  width: 100%;
}
.catbar-inner::-webkit-scrollbar { display: none; }

/* ── Pills ── */
.catbar-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  padding: 7px 16px;
  border-radius: 99px;
  border: 1.5px solid var(--border);
  background: #fff;
  color: var(--slate);
  font-family: 'Archivo', sans-serif;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: all var(--tx);
  flex-shrink: 0;
  letter-spacing: .01em;
  line-height: 1;
}
.catbar-pill svg {
  width: 13px;
  height: 13px;
  stroke: currentColor;
  flex-shrink: 0;
}
/* Admin-uploaded DB icon (CAT_ICON_URLS, see _categoryIconHTML() in
   main.js) — same footprint as the SVG it can replace. A real icon image
   carries its own colour, so unlike the SVG above it doesn't switch to
   --gold on .catbar-pill.active — expected, not a bug. */
.catbar-pill img {
  width: 13px;
  height: 13px;
  object-fit: contain;
  flex-shrink: 0;
  border-radius: 2px;
}
.catbar-pill:hover {
  border-color: var(--navy);
  color: var(--navy);
  background: #f0f4ff;
}
.catbar-pill.active {
  background: var(--navy);
  border-color: var(--navy);
  color: #fff;
  box-shadow: 0 2px 10px rgba(0,33,71,.22);
}
.catbar-pill.active svg {
  stroke: var(--gold);
}

/* ── Dynamic pills container ── */
.catbar-pills {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* ── Divider between "All" and categories ── */
.catbar-div {
  width: 1px;
  height: 22px;
  background: var(--border);
  flex-shrink: 0;
  margin: 0 4px;
}

/* ── Count badge (shows "12 items") ── */
.catbar-count {
  margin-left: auto;
  flex-shrink: 0;
  font-family: 'Space Mono', monospace;
  font-size: 10px;
  color: var(--slate-lt);
  letter-spacing: .04em;
  white-space: nowrap;
  padding-left: 12px;
  border-left: 1px solid var(--border);
}

/* ── Category icon colours ── */
.catbar-pill[data-cat="Beverages"]         { --ic: #3b82f6; }
.catbar-pill[data-cat="Canned Goods"]      { --ic: #f59e0b; }
.catbar-pill[data-cat="Dairy"]             { --ic: #60a5fa; }
.catbar-pill[data-cat="Disposables"]       { --ic: #a78bfa; }
.catbar-pill[data-cat="Eggs"]              { --ic: #fbbf24; }
.catbar-pill[data-cat="Flours"]            { --ic: #d1d5db; }
.catbar-pill[data-cat="Fresh Vegetables"]  { --ic: #22c55e; }
.catbar-pill[data-cat="Frozen Food"]       { --ic: #67e8f9; }
.catbar-pill[data-cat="Herbs/Spices"]      { --ic: #f97316; }
.catbar-pill[data-cat="Households"]        { --ic: #14b8a6; }
.catbar-pill[data-cat="Lentils & Pulses"]  { --ic: #d97706; }
.catbar-pill[data-cat="Meat / Poultry / Sea Items"] { --ic: #ef4444; }
.catbar-pill.active[data-cat] svg { stroke: #fff; }
.catbar-pill:not(.active)[data-cat] svg { stroke: var(--ic, var(--slate)); }

/* ── Responsive: tighten on mobile ── */
@media (max-width: 768px) {
  .catbar { top: 58px; }
  .catbar-inner { padding: 0 12px; min-height: 46px; gap: 5px; }
  .catbar-pill  { padding: 6px 12px; font-size: 11.5px; }
  .catbar-count { display: none; }
}
/* ── Saved address cards (checkout left panel) ── */
.co-addr-card{
  border:1.5px solid #e5e7eb;
  border-radius:9px;
  padding:10px 11px;
  cursor:pointer;
  transition:border-color .15s,background .15s,box-shadow .15s;
  background:#fff;
  font-size:12px;
  color:#374151;
  line-height:1.6;
}
.co-addr-card:hover{
  border-color:var(--navy);
  box-shadow:0 2px 8px rgba(0,33,71,.1);
}
.co-addr-card.selected{
  border-color:var(--navy);
  background:#f0f4ff;
  box-shadow:0 2px 10px rgba(0,33,71,.15);
}
.co-addr-card .addr-label{
  font-size:10.5px;
  font-weight:800;
  color:var(--navy);
  text-transform:uppercase;
  letter-spacing:.04em;
  display:block;
}
/* Selected indicator on card */
.co-addr-card.selected::before{
  content:'✓';
  float:right;
  font-size:12px;
  font-weight:800;
  color:var(--navy);
  margin-top:1px;
}
/* Address outside the delivery area (sortlist_delivery_fee has no zone
   for its postcode) — greyed out and not clickable to select; the "This
   Postcode Not Deliverable Area" banner itself is rendered inside the
   card by _renderSavedAddresses() in main.js, this just styles the card
   shell around it. */
.co-addr-card.undeliverable{
  cursor:not-allowed;
  opacity:.72;
  background:#fafafa;
}
.co-addr-card.undeliverable:hover{
  border-color:#e5e7eb;
  box-shadow:none;
}
.co-addr-card.undeliverable .addr-label{
  color:#9ca3af;
}

/* ═══════════════════════════════════════════════════════════
   PRICE MODE TOGGLE  (All / Retail / Wholesale)
═══════════════════════════════════════════════════════════ */
.price-toggle{
  display:flex;
  align-items:center;
  gap:0;
  border:1.5px solid var(--border);
  border-radius:8px;
  overflow:hidden;
  flex-shrink:0;
  background:#fff;
}
.pt-btn{
  padding:6px 13px;
  font-family:'Archivo',sans-serif;
  font-size:12px;
  font-weight:700;
  color:var(--slate);
  background:#fff;
  border:none;
  border-right:1px solid var(--border);
  cursor:pointer;
  transition:background .15s,color .15s;
  white-space:nowrap;
  line-height:1;
}
.pt-btn:last-child{ border-right:none; }
.pt-btn:hover{ background:#f0f4ff; color:var(--navy); }
.pt-btn.active{
  background:var(--navy);
  color:#fff;
}
/* Retail active → amber/gold */
.pt-btn.active#ptRetail{
  background:#1a6fa8;
  color:#fff;
}
/* Wholesale active → green */
.pt-btn.active#ptWholesale{
  background:var(--green);
  color:#fff;
}
@media(max-width:768px){
  .price-toggle{ margin-right:4px; }
  .pt-btn{ padding:5px 8px; font-size:11px; }
}

/* ═══════════════════════════════════════════════════════════
   DASHBOARD — Left/Right Panel Layout
═══════════════════════════════════════════════════════════ */

/* Overlay + outer shell */
.dash-overlay{position:fixed;inset:0;background:rgba(8,14,24,.65);backdrop-filter:blur(3px);display:none;align-items:flex-start;justify-content:center;z-index:1200;padding:32px 20px;overflow:auto}
.dash-overlay.open{display:flex}
.dash{
  width:100%;max-width:1100px;
  background:#fff;
  border-radius:16px;
  overflow:hidden;
  box-shadow:0 24px 80px rgba(0,0,0,.28);
  display:flex;flex-direction:column;
  max-height:calc(100vh - 64px);
  min-height:560px;
}

/* Top bar */
.dash-bar{
  display:flex;align-items:center;gap:12px;
  padding:14px 22px;
  background:var(--navy);
  color:#f3f5f8;
  flex-shrink:0;
}
.dash-ttl{font-family:'Space Mono',monospace;font-size:9px;letter-spacing:.16em;text-transform:uppercase;color:#6b8fb5;margin-bottom:2px}
.dash-bar h2{font-size:17px;font-weight:800;line-height:1.1;color:#fff}
.spacer{flex:1}
.dash-signout{background:transparent;border:1.5px solid rgba(255,255,255,.3);color:#f3f5f8;font-family:'Space Mono',monospace;font-size:10px;letter-spacing:.06em;text-transform:uppercase;padding:7px 13px;cursor:pointer;border-radius:6px;display:flex;align-items:center;gap:6px;transition:.15s}
.dash-signout:hover{background:rgba(255,255,255,.12)}
.dash-x{background:transparent;border:none;color:rgba(255,255,255,.6);cursor:pointer;font-size:24px;line-height:1;padding:2px 4px;margin-left:4px}
.dash-x:hover{color:#fff}

/* Left nav */
.dash-nav{
  width:210px;flex-shrink:0;
  background:#0d1f35;
  display:flex;flex-direction:column;
  overflow-y:auto;
}
.dash-nav-items{display:flex;flex-direction:column;padding:8px 0;flex:1}
.dash-nav-btn{
  display:flex;align-items:center;gap:10px;
  width:100%;padding:11px 18px;
  background:none;border:none;border-left:3px solid transparent;
  color:rgba(255,255,255,.6);font-size:13px;font-weight:600;
  cursor:pointer;text-align:left;transition:.15s;
  font-family:'Archivo',sans-serif;
}
.dash-nav-btn svg{flex-shrink:0;opacity:.7;transition:.15s}
.dash-nav-btn:hover{background:rgba(255,255,255,.07);color:rgba(255,255,255,.9)}
.dash-nav-btn:hover svg{opacity:1}
.dash-nav-btn.active{
  background:rgba(255,255,255,.1);
  color:#fff;border-left-color:var(--gold);
}
.dash-nav-btn.active svg{opacity:1;stroke:var(--gold)}
.dash-nav-badge{
  margin-left:auto;background:rgba(246,208,121,.25);color:var(--gold);
  font-family:'Space Mono',monospace;font-size:9px;font-weight:700;
  padding:1px 6px;border-radius:10px;
  display:none;
}
.dash-nav-badge.on{display:inline-block}

/* Right content panel */
.dash-content{
  flex:1;overflow-y:auto;padding:28px 30px;
  background:#f4f6f9;
  min-width:0;
}

/* Section headings inside content */
.dsec-title{
  display:flex;align-items:center;gap:10px;
  font-family:'Bricolage Grotesque','Space Grotesk',sans-serif;
  font-size:20px;font-weight:800;color:var(--navy);
  margin-bottom:20px;padding-bottom:12px;
  border-bottom:2px solid #e5e7eb;
}
.dsec-title svg{opacity:.5}
.dsec-title .cnt-badge{
  margin-left:auto;background:var(--navy);color:#fff;
  font-family:'Space Mono',monospace;font-size:10px;font-weight:700;
  padding:2px 9px;border-radius:10px;
}

/* Stats grid */
.dash-stats{
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:12px;margin-bottom:22px;
}
@media(max-width:700px){.dash-stats{grid-template-columns:1fr 1fr}}
.dstat{
  background:#fff;border:1.5px solid #e5e7eb;border-radius:10px;
  padding:14px 16px;
}
.dstat .k{font-family:'Space Mono',monospace;font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:#9ca3af;margin-bottom:6px}
.dstat .v{font-family:'Space Mono',monospace;font-size:20px;font-weight:700;color:var(--navy)}
.dstat .v.due{color:#c0392b}
.dstat .sub{font-size:11px;color:#6b7280;margin-top:3px}

/* Tables */
.dtable{width:100%;border-collapse:collapse;background:#fff;border-radius:10px;overflow:hidden;font-size:13px;box-shadow:0 1px 4px rgba(0,0,0,.06)}
.dtable th{background:#f8f9fa;text-align:left;font-family:'Space Mono',monospace;font-size:9.5px;letter-spacing:.07em;text-transform:uppercase;color:#9ca3af;padding:10px 14px;border-bottom:2px solid #e5e7eb}
.dtable td{padding:12px 14px;border-bottom:1px solid #f3f4f6;vertical-align:middle;color:#374151}
.dtable tbody tr:last-child td{border-bottom:none}
.dtable tbody tr:hover{background:#f8f9fb}
.dtable .num{font-family:'Space Mono',monospace;white-space:nowrap}
.dtable tr.row-warn{background:#fffbf0}
.dtable tr.row-warn:hover{background:#fff8e7}

/* Pills */
.pill{display:inline-flex;align-items:center;font-family:'Space Mono',monospace;font-size:9.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:3px 9px;border-radius:20px;border:1.5px solid;white-space:nowrap}
.pill.ok   {color:var(--green);border-color:var(--green);background:rgba(26,122,69,.08)}
.pill.warn {color:#9a6a00;border-color:#d4a017;background:rgba(212,160,23,.1)}
.pill.bad  {color:#c0392b;border-color:#c0392b;background:rgba(192,57,43,.08)}
.pill.info {color:#0a6fa0;border-color:#0a6fa0;background:rgba(0,152,224,.08)}

/* Profile cards */
.dcards{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
@media(max-width:700px){.dcards{grid-template-columns:1fr}}
.dcard{background:#fff;border:1.5px solid #e5e7eb;border-radius:10px;padding:18px 20px}
.dcard h5{font-family:'Space Mono',monospace;font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:#0a6fa0;margin-bottom:10px}
.dcard p{font-size:13px;line-height:1.75;color:#374151}
.dcard .muted{color:#9ca3af}

/* Member welcome card */
.dash-welcome{
  display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
  background:linear-gradient(135deg,var(--navy) 0%,#0a2d5a 100%);
  border-radius:12px;padding:22px 24px;margin-bottom:22px;color:#fff;
}
.dash-welcome h3{font-family:'Bricolage Grotesque',sans-serif;font-size:1.3rem;font-weight:800;margin-bottom:6px}
.dash-welcome p{font-size:13px;color:rgba(255,255,255,.7);line-height:1.5}

/* Retry payment button inside table */
.dpay-retry{
  display:inline-flex;align-items:center;gap:4px;
  padding:5px 10px;background:#635bff;color:#fff;
  border:none;border-radius:5px;font-size:10px;font-weight:700;
  cursor:pointer;transition:.15s;margin-top:4px;
}
.dpay-retry:hover{opacity:.85}
.dpay-retry:disabled{opacity:.5;cursor:not-allowed}

/* Addresses grid */
.dash-addr-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px}
.dash-addr-card{background:#fff;border:1.5px solid #e5e7eb;border-radius:10px;padding:16px}
.dash-addr-card.default{border-color:var(--navy);background:#f0f4ff}
.dash-addr-label{font-family:'Space Mono',monospace;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.1em;color:#0a6fa0;margin-bottom:6px}
.dash-addr-text{font-size:12.5px;color:#374151;line-height:1.65}
.dash-addr-def{display:inline-block;background:var(--navy);color:#fff;font-size:9px;font-weight:700;padding:1px 7px;border-radius:10px;margin-bottom:6px}

/* Membership card */
.mem-status-card{background:#fff;border:1.5px solid #e5e7eb;border-radius:12px;padding:20px 22px;margin-bottom:16px}

/* Trade-member badge — used in nav badge, profile header, membership status card */
.mbadge{display:inline-flex;align-items:center;gap:6px;background:linear-gradient(150deg,var(--gold),#E7A92E);color:#1A1407;font-family:'Space Mono',monospace;font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:5px 11px;border-radius:20px}

/* Reusable responsive grid utilities for dashboard forms/content —
   2 or 3 columns on desktop, collapse to 1 column on mobile
   (see the "MY ACCOUNT DASHBOARD" media query below for the collapse). */
.dash-2col{grid-template-columns:1fr 1fr}
.dash-3col{grid-template-columns:1fr 1fr 1fr}

/* Mobile */
@media(max-width:640px){
  .dash{max-height:100vh;border-radius:0}
  .dash-overlay{padding:0}
  .dash-nav{width:180px}
  .dash-content{padding:18px 16px}
  .dash-stats{grid-template-columns:1fr 1fr}
}

/* ── Order items expand row ── */
#dashOrdersTable tr[id^="ditems-"] td {
  padding: 0;
  border-top: none;
}
#dashOrdersTable tr[id^="ditems-"] > td > div {
  border-top: 2px dashed #c7d7ff;
  border-bottom: 1px solid #e5e7eb;
  animation: slideDown .18s ease;
}
@keyframes slideDown {
  from { opacity:0; transform:translateY(-4px); }
  to   { opacity:1; transform:translateY(0); }
}

/* ── PDF slide-in panel ── */
@keyframes slideInRight {
  from { transform:translateX(100%); opacity:0; }
  to   { transform:translateX(0);    opacity:1; }
}
#dashPdfPanel iframe {
  background:#f4f6f8;
}

/* ── Footer policy links ── */
.footer-policies {
  text-align: center;
  padding: 6px 0 2px;
  font-size: 12px;
  color: rgba(255,255,255,.55);
  max-width: var(--site-width);
  margin: 0 auto;
}
.footer-policies a {
  color: var(--gold);
  text-decoration: none;
  white-space: nowrap;
}
.footer-policies a:hover { text-decoration: underline; }

.footer-invoice-note {
  text-align: center;
  font-size: 11px;
  color: rgba(255,255,255,.4);
  margin: 0;
  padding-bottom: 4px;
  font-family: 'Space Mono', monospace;
  letter-spacing: .04em;
}

/* ════════════════════════════════════════════════════════
   POLICY PAGES  (trade-terms / delivery-returns / privacy)
   Rendered inside the main site header — no duplicate nav
   ════════════════════════════════════════════════════════ */
.policy-page {
  background: #f4f6f9;
  min-height: calc(100vh - 120px);
  padding: 36px var(--site-pad) 60px;
}
.policy-wrap {
  max-width: 820px;
  margin: 0 auto;
  background: #fff;
  border-radius: 14px;
  box-shadow: 0 2px 16px rgba(0,0,0,.07);
  padding: 40px 48px 48px;
}
@media(max-width:600px){
  .policy-wrap { padding: 24px 18px 36px; }
}

/* Breadcrumb row + Home button (2026-08-14) — sits above the H1 on every
   interior/policy page. The Home button is a clear, explicit way back to
   the homepage now that those pages no longer show the hero banner's own
   "Browse the catalogue" CTA (see $hideHomeSections in includes/header.php). */
.policy-top-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.policy-crumbs {
  font-size: 12px;
  color: #9ca3af;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.policy-crumbs a { color: #9ca3af; text-decoration: none; }
.policy-crumbs a:hover { color: var(--navy); }
.policy-crumbs strong { color: #374151; }
.policy-crumb-sep { color: #d1d5db; font-size: 14px; }
.policy-home-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'Archivo', sans-serif;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--navy);
  text-decoration: none;
  padding: 7px 14px;
  border: 1.5px solid var(--border);
  border-radius: 99px;
  transition: border-color .15s, background .15s;
  white-space: nowrap;
}
.policy-home-btn:hover { border-color: var(--navy); background: #f0f4ff; }

/* Page heading */
.policy-h1 {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-size: 1.85rem;
  font-weight: 800;
  color: var(--navy);
  margin: 0 0 6px;
  line-height: 1.2;
}
.policy-meta {
  font-size: .8rem;
  color: #9ca3af;
  margin: 0 0 28px;
  line-height: 1.5;
}
.policy-meta a { color: #9ca3af; }

/* Section headings */
.policy-wrap h2 {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--navy);
  margin: 2em 0 .5em;
  border-left: 3px solid var(--gold);
  padding-left: 10px;
}

/* Body text */
.policy-wrap p, .policy-wrap li {
  line-height: 1.75;
  color: #1f2937;
  font-size: .94rem;
  margin-bottom: .75em;
}
.policy-wrap a { color: var(--navy); font-weight: 600; }
.policy-wrap a:hover { color: var(--gold); }
.policy-wrap code {
  background: #f3f4f6;
  border-radius: 4px;
  padding: 1px 6px;
  font-size: .88rem;
}

/* Invoice policy highlight box */
.policy-kbox {
  background: #fffbef;
  border-left: 4px solid var(--gold);
  padding: 14px 18px;
  border-radius: 0 10px 10px 0;
  margin-bottom: 24px;
  box-shadow: 0 2px 8px rgba(0,0,0,.05);
}
.policy-kbox p { margin: 0; font-size: .94rem; color: #1f2937; }

/* Table (delivery page) */
.policy-table {
  border-collapse: collapse;
  width: 100%;
  margin: 14px 0 20px;
  font-size: .88rem;
}
.policy-table th {
  background: var(--navy);
  color: #fff;
  font-family: 'Space Mono', monospace;
  font-size: .78rem;
  letter-spacing: .04em;
  padding: 10px 14px;
  text-align: left;
}
.policy-table td {
  border: 1px solid #e5e7eb;
  padding: 9px 14px;
  background: #fff;
  vertical-align: top;
}
.policy-table tr:nth-child(even) td { background: #f9fafb; }

/* ── Contact Us page (2026-08-14) ── */
.contact-quick-actions{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:32px}
/* .policy-wrap a{color:var(--navy)} (higher specificity than .btn-primary
   alone) was winning here and making the primary button's text navy-on-navy
   — invisible. Re-assert white text at higher specificity to fix it. */
.contact-quick-actions .btn-primary{color:#fff}
.contact-quick-actions .btn-primary:hover{color:#fff}
.contact-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-bottom:8px}
.contact-card{background:#fff;border:1.5px solid var(--border);border-radius:var(--radius-lg,14px);padding:20px 22px}
.contact-card-icon{width:38px;height:38px;border-radius:10px;background:var(--gold-tint,#FDF3D2);display:flex;align-items:center;justify-content:center;color:var(--navy);margin-bottom:12px}
.contact-card-icon svg{width:19px;height:19px}
.contact-card h3{font-family:'Bricolage Grotesque',sans-serif;font-size:15px;font-weight:800;color:var(--navy);margin:0 0 6px}
.contact-card p{font-size:.88rem;line-height:1.6;color:#4b5563;margin:0 0 12px}
.contact-card .contact-link{display:block;font-size:.88rem;font-weight:700;color:var(--navy);text-decoration:none;margin-bottom:4px}
.contact-card .contact-link:hover{color:var(--gold);text-decoration:underline}
.contact-name{font-size:.92rem;font-weight:800;color:var(--navy);margin:0 0 8px}
.contact-name span{font-weight:600;color:#6b7280;font-size:.85em}
.contact-address{font-style:normal;font-size:.88rem;line-height:1.6;color:#1f2937;margin-bottom:10px}
.contact-kv{font-size:.88rem;line-height:1.7;color:#1f2937;margin:0}
.contact-kv b{color:var(--navy)}
.contact-map-wrap{border-radius:var(--radius-lg,14px);overflow:hidden;border:1.5px solid var(--border);margin-bottom:8px}
.contact-map-wrap iframe{display:block}
@media(max-width:700px){
  .contact-grid{grid-template-columns:1fr}
  .contact-quick-actions{flex-direction:column}
  .contact-quick-actions .btn{width:100%;justify-content:center}
}

/* Related links row at bottom */
.policy-related-links {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 36px;
  padding-top: 24px;
  border-top: 1px solid #e5e7eb;
}
.policy-related-links a {
  font-size: .88rem;
  font-weight: 700;
  color: var(--navy);
  text-decoration: none;
  padding: 8px 16px;
  border: 1.5px solid #e5e7eb;
  border-radius: 8px;
  transition: border-color .15s, color .15s;
}
.policy-related-links a:hover {
  border-color: var(--navy);
  color: var(--navy);
}

/* ════════════════════════════════════════════════════════════════════════════
   COMPLETE MOBILE RESPONSIVE OVERHAUL
   Breakpoints: 768px (tablet), 480px (mobile), 360px (small mobile)
   Covers: Header/Nav, Hero, Catbar, Cards, Cart Drawer,
           Account/Sign-in Modal, My Account Dashboard, Checkout Modal
════════════════════════════════════════════════════════════════════════════ */

/* ── Viewport & base ── */
html { -webkit-text-size-adjust:100%; touch-action:manipulation; }

/* ════════════════════════
   HEADER / NAV
════════════════════════ */
@media(max-width:768px){
  /* Ticker */
  .ticker{ height:28px; font-size:10.5px; top:0; }

  /* Header bar */
  .head-main{
    padding:0 16px;
    height:56px;
    top:28px;           /* below ticker */
    gap:8px;
  }
  .head-inner{ gap:10px; }

  /* Logo — hide text label on mobile */
  .logo-div,.logo-tag span{ display:none; }
  .logo-tag{ font-size:12px; }
  .logo-img{ height:32px; }

  /* Search bar */
  .search{ max-width:none; flex:1; min-width:0; }
  .search input{ padding:7px 10px; font-size:12px; }
  .search button{ padding:0 12px; font-size:12px; }

  /* Action buttons */
  .head-actions{ gap:2px; }
  .head-btn{ min-width:38px; padding:5px 6px; font-size:9px; }
  .head-btn svg{ width:18px; height:18px; }

  /* Join membership — label hidden on mobile */
  .mem-join-btn span{ display:none; }
  .mem-join-btn{ padding:6px 8px; min-width:36px; }

  /* Catbar */
  .catbar{ top:84px; }   /* 28+56 */
  .catbar-inner{ padding:0 10px; min-height:44px; gap:4px; }
  .catbar-pill{ padding:5px 11px; font-size:11px; }
  .catbar-count{ display:none; }
}

@media(max-width:480px){
  .head-main{ gap:6px; padding:0 14px; height:52px; top:28px; }
  .search input{ font-size:11.5px; padding:6px 8px; }
  .search button{ padding:0 10px; font-size:11.5px; }
  .catbar{ top:80px; }
  /* Hide saved/wishlist label on very small screens */
  #wishlistBtn span:not(.cart-count){ display:none; }
  #wishlistBtn{ min-width:34px; padding:5px; }
}

/* ════════════════════════
   HERO — full-width slider, single column below

   Fix (mobile "doesn't fit" bug): .hero-slide only ever had a min-height,
   so it never clipped content — but .hero-arrow/.hero-dots are position:
   absolute against the slider, anchored to the BOTTOM edge regardless of
   how tall the text above them grows. On admin-authored slides with a
   longer title/subtitle (very plausible — banners.php lets an admin type
   anything), the paragraph's last line runs straight into the arrow/dot
   row on narrow screens, since nothing reserved space for them. Two
   changes fix this: (1) arrows are hidden on mobile — swipe already
   covers slide navigation there (see the touch-swipe handler in main.js),
   so there's one less absolutely-positioned element to collide with; (2)
   .hero-slide-content gets real bottom padding sized to clear the dots
   strip that remains, instead of the same padding used top and bottom.
════════════════════════ */
@media(max-width:1024px){
  .hero-slide-content{ padding:48px 36px; }
}
@media(max-width:768px){
  .hero{ padding:0; }
  /* 2026-07-26 v5. History: v1 used cover (cropped image edges) → v2
     switched has-image to contain (fixed cropping, but left a tall navy
     slab since the box still used the 400px height sized for TEXT slides)
     → v3 gave image slides their own short aspect-ratio (fixed the slab,
     but now text and image slides were different heights) → v3-uniform /
     v4 matched heights by growing/clamping both slide types to a SHARED
     fixed min-height (315–320px) — real height parity between text and
     image slides, but any image whose own aspect ratio didn't exactly fill
     that shared box still left a visible navy/blue letterbox band behind
     it → v5 dropped the shared fixed height entirely: every slide sized
     itself off the <img>'s own natural height (zero letterbox), but that
     meant (a) a wide, desktop-shaped photo forced to width:100% on a
     narrow phone rendered very short/"small" instead of filling the
     banner, and (b) the text-only fallback slide (shown before the DB
     banner fetch resolves) had a DIFFERENT height than whatever real
     slide replaced it, so the hero visibly jumped/resized right after
     load.
     v6 (current): back to a shared box, but sized by a fixed ASPECT RATIO
     (700:920 — the recommended upload size for mobile_image_url, see
     banners.php/renderHeroSlides() in main.js) rather than a flat pixel
     value, capped by max-height so it can never grow absurdly tall near
     the 768px top of this breakpoint. Every slide — the text-only
     fallback, an image slide using the new mobile_image_url, and an image
     slide falling back to the desktop image_url when no mobile crop was
     ever set — now renders at the SAME height, so: the fallback and
     whatever the DB fetch swaps in afterwards never jump (fixes the
     load-time height jump), and any hero photo fills a proper tall
     portrait box via object-fit:cover instead of shrinking to its own
     (usually wide) natural ratio (fixes the "image displayed very small"
     complaint) — even for a banner that hasn't been given a dedicated
     mobile_image_url yet, since it still gets auto-cropped to this ratio
     instead of stretched flat. syncHeroSliderHeight() (main.js) still
     resyncs #heroSlider's own outer height to match, now mostly a no-op
     confirmation since every slide already computes to the same height
     via CSS alone. */
  .hero-slider{ transition:height .3s ease; }
  .hero-slider-track{ align-items:flex-start; }
  .hero-slide{
    min-height:0;
    aspect-ratio:700/920;
    max-height:min(70vh,600px);
  }
  .hero-slide-content{
    padding:24px 22px 30px;
  }
  .hero-tag{ margin-bottom:10px; }
  .hero h1{ font-size:clamp(1.5rem,4.5vw,2rem); margin-bottom:8px; }
  .hero p{
    font-size:13.5px; margin-bottom:14px;
    /* Cap the paragraph at 2 lines instead of letting admin-authored (or
       this fallback's own) copy run arbitrarily long — this is what
       makes a short, predictable slide height possible at all. */
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  }
  .hero-cta{ gap:10px; }
  .btn{ padding:10px 18px; font-size:13px; }
  /* Arrows hidden on mobile — touch swipe (main.js) is the nav method;
     removes the taller (34px) of the two overlaid controls entirely. */
  .hero-arrow{ display:none !important; }
  .hero-dots{ bottom:10px; }
  /* Image slides: fill the shared aspect-ratio box (above) via cover, not
     stretch — crops the photo's edges as needed rather than distorting it
     or leaving letterbox. mobile_image_url (a purpose-shot 700×920
     portrait) crops cleanly with little/no loss; a banner still only using
     the wider desktop image_url gets a reasonable auto-crop instead of
     rendering tiny. */
  .hero-slide.has-image{
    display:block;
    background-image:none !important;
  }
  .hero-slide.has-image .hero-slide-img{
    display:block;
    width:100%;
    height:100%;
    object-fit:cover;
  }
  /* Text overlays the image itself (no separate space reserved for it),
     so it's absolutely positioned over the bottom of the photo with a
     scrim gradient underneath for legibility — same tag/h1/p content,
     just laid over the image instead of beside a fixed-height block. */
  .hero-slide.has-image .hero-slide-content{
    position:absolute; inset:auto 0 0 0; width:auto;
    display:flex; flex-direction:column; justify-content:flex-end;
    padding:34px 22px 22px;
  }
  .hero-slide.has-image .hero-slide-scrim{
    opacity:1;
    background:linear-gradient(to top, rgba(0,33,71,.85) 0%, rgba(0,33,71,.45) 50%, rgba(0,33,71,0) 85%);
  }
  .hero-slide.has-image .hero-tag{ margin-bottom:6px; }
  .hero-slide.has-image .hero-slide-img{ display:block; }
}
@media(min-width:769px){
  /* Desktop never uses the <img> tag (background-image/cover, unchanged) —
     explicit hide even though the base rule already covers it, so a future
     change to that base rule can't silently reintroduce it here. */
  .hero-slide-img{ display:none !important; }
}

@media(max-width:480px){
  /* .hero-slide's own height no longer needs a breakpoint-specific value
     here — the 700:920 aspect-ratio box set at 768px (above) is already
     responsive down through every narrower width, small phones included.
     Narrower width wraps the (now 2-line-capped) paragraph the same amount
     regardless — clamped text stops growing past 2 lines — so this block
     only needs to cover the smaller padding/font sizing. */
  .hero-slide-content{ padding:18px 16px 22px; }
  .hero-tag{ margin-bottom:8px; }
  .hero h1{ font-size:1.4rem; margin-bottom:6px; }
  .hero p{ margin-bottom:12px; }
  .hero-cta{ flex-direction:column; align-items:stretch; }
  .hero-cta .btn{ width:100%; justify-content:center; padding:9px 16px; }
}

/* ════════════════════════
   TRUST BAR
════════════════════════ */
@media(max-width:768px){
  .trust-grid{ grid-template-columns:1fr 1fr; gap:14px; }
  .trust{ padding:22px 16px; }
}
@media(max-width:480px){
  .trust-grid{ grid-template-columns:1fr; gap:12px; }
}

/* ════════════════════════
   OFFERS SLIDER
════════════════════════ */
@media(max-width:768px){
  /* Shrunk further (2026-08-05 mobile pass): the deals cards were reading
     as near full-screen slabs on phones — barely any of the next card
     peeked in, which (combined with the old mandatory scroll-snap above)
     made the carousel feel stuck rather than swipeable. Smaller card +
     tighter padding/type both cuts the section's overall height and, by
     leaving more of the next card visible at the edge, signals "there's
     more here, swipe me" the way a carousel should. */
  .offers-section{ padding:22px 0 18px; }
  .offers-header{ padding:0 14px 12px; }
  .offers-track-wrap{ padding:0 8px; }
  .offer-card{ flex:0 0 200px; padding:14px 14px 12px; gap:6px; }
  .offer-badge{ font-size:9px; padding:2px 7px; }
  .offer-name{ font-size:15px; }
  .offer-name span{ font-size:11px; }
  .offer-now{ font-size:20px; }
  .offer-cta{ padding-top:10px; font-size:12px; }
  .offers-prev{ margin-right:4px; }
  .offers-next{ margin-left:4px; }
}
@media(max-width:480px){
  /* Card width as a % of viewport (not a near-full calc(92vw - 20px) like
     before) so roughly a quarter of the next card always peeks in at the
     right edge, regardless of exact phone width. */
  .offer-card{ flex:0 0 74vw; }
}

/* ════════════════════════
   CATALOG / PRODUCT GRID
════════════════════════ */
@media(max-width:768px){
  .sec-head{ flex-direction:column; align-items:flex-start; padding:0 14px 20px; }
  /* Category/Brand/Active-Offers inline chip rows are hidden on mobile as of
     2026-08-05 — the bottom tab bar's Category/Brand/Offers buttons now
     open the #pickerOverlay modal instead (see openPicker() in main.js),
     so these rows would just be redundant, cramped duplicates of it. Still
     rendered/kept up to date by renderNav() under the hood (nothing reads
     their hidden state to decide what to show), just not displayed. Hiding
     by ID rather than the shared .filters class since all three of these
     rows are the only elements that use it. */
  #filters, #brandFilters, #offersOnlyRow{ display:none !important; }
  .grid{
    grid-template-columns:repeat(auto-fill,minmax(150px,1fr));
    gap:10px; padding:0 14px;
  }
  .card-img{ height:120px; }
  .card-body{ padding:10px 11px 8px; gap:3px; }
  .card-body h3{ font-size:12.5px; }
  .price-now b{ font-size:16px; }
  .card-foot{ padding:8px 11px 12px; }
  .stepper input{ width:36px; }
  .catalog{ padding:32px 0 48px; }
}

@media(max-width:480px){
  .grid{ grid-template-columns:1fr 1fr; gap:8px; padding:0 14px; }
  .card-img{ height:110px; }
  .card-body{ padding:9px 10px 7px; }
  .card-body h3{ font-size:12px; -webkit-line-clamp:2; }
  .price-now b{ font-size:15px; }
  .stepper button{ width:26px; }
  .stepper input{ width:32px; font-size:12px; }
  .add-btn{ font-size:12px; padding:0 10px; }
}

@media(max-width:360px){
  .grid{ grid-template-columns:1fr; gap:8px; }
  /* 2026-10-28 fix — this used to be flex-direction:row, which puts
     .card's three direct children (.card-img, .card-body, .card-foot)
     side by side as THREE competing flex items in one row instead of
     the intended "image on the left, everything else stacked on the
     right": .card-foot (stepper + Add button) doesn't have much room
     to give up, so .card-body (category/title/price) was left with
     almost no width — and with nothing to stop it shrinking further,
     its text wrapped one character per line instead of staying
     readable. Switching .card itself to a 2-column grid lets the image
     span both rows in column 1 while .card-body/.card-foot keep
     stacking normally in column 2, which is what this layout always
     intended. */
  .card{
    display:grid;
    grid-template-columns:100px 1fr;
    grid-template-rows:auto 1fr;
    min-height:100px;
  }
  .card-img{
    grid-row:1 / 3; grid-column:1;
    width:100px; height:auto; min-height:100px;
    border-bottom:none; border-right:1px solid var(--border);
  }
  .card-body{ grid-row:1; grid-column:2; min-width:0; }
  .card-foot{ grid-row:2; grid-column:2; min-width:0; }
}

/* ════════════════════════
   BULK CTA
════════════════════════ */
@media(max-width:768px){
  .bulk{ padding:36px 16px; }
  .bulk-inner{ flex-direction:column; gap:24px; align-items:flex-start; }
  .bulk-l h2{ font-size:clamp(1.3rem,4vw,1.8rem); }
  .bulk-l p{ font-size:13px; }
  /* .bulk-l/.bulk-r each carry their own min-width:260px (set for the
     side-by-side desktop layout, so neither column collapses too
     narrow next to the other) — once stacked into a single column
     above, that floor no longer has a sibling to make room for, so on
     any viewport under ~292px (260px + this section's 16px×2 padding)
     it would be the one forcing horizontal overflow instead. Drop it
     back to 0 now that both columns are full-width anyway. */
  .bulk-l, .bulk-r{ min-width:0; }
}

/* ════════════════════════
   FOOTER
════════════════════════ */
@media(max-width:768px){
  .foot-top{
    grid-template-columns:1fr 1fr;
    gap:22px; padding:28px 16px;
  }
}
@media(max-width:480px){
  .foot-top{ grid-template-columns:1fr; gap:20px; padding:24px 16px; }
  .foot-bot-wrap{ flex-direction:column; align-items:flex-start; gap:6px; }
  .footer-policies{ text-align:left; }
}

/* ════════════════════════
   CART DRAWER
════════════════════════ */
@media(max-width:480px){
  .drawer{ width:100vw; right:-100vw; }
  .drawer-head{ padding:14px 16px; }
  .drawer-body{ padding:10px 16px; }
  .drawer-foot{ padding:12px 16px; }
}

/* ════════════════════════
   TOAST
════════════════════════ */
@media(max-width:480px){
  .toast{ left:12px; right:12px; transform:none; white-space:normal; text-align:center; bottom:16px; }
}
/* 2026-07-26: the "1 x Product added" toast sits at a fixed bottom:24px
   (16px below 480px), which is right where the new sticky mini-cart bar
   (.sticky-cart-bar, ~65px tall) lives — the two were rendering on top of
   each other. When the bar is showing, push the toast up clear of it;
   body.has-sticky-cart is toggled by updateStickyCartBar() in main.js in
   lockstep with the bar itself.
   2026-08-14: .sticky-cart-bar moved from bottom:0 to bottom:60px (see
   that rule below) so it stacks above the mobile bottom tab bar instead
   of sitting underneath/behind it — bumped this to clear both bars. */
@media(max-width:768px){
  body.has-sticky-cart .toast{ bottom:130px; }
}

/* ════════════════════════════════════════════════════════
   WHATSAPP CLICK-TO-CHAT FLOATING BUTTON
   Always-reachable floating action button (see #whatsappFab in
   header.php) that opens WhatsApp (wa.me) pre-filled with a message
   naming whichever page the visitor is currently on — built
   client-side from window.location.href at click time (see the
   inline script right after the button's markup), so it stays
   correct even after the catalogue's client-side URL updates
   (_updateUrlState() in main.js) without a full page reload.
   z-index:350 — same tier as .mobile-tabbar: above the sticky
   header/hero/category-rail (z-index 200-400) but still well below
   the cart drawer (400/500), account modal (800) and dashboard
   overlay (1200), so any of those opening correctly covers it with
   no extra show/hide JS needed.
   Positioned bottom-right; pushed up on mobile to clear the bottom
   tab bar's own unconditional padding-bottom, and pushed up further
   via body.has-sticky-cart — the same toggle updateStickyCartBar()
   already flips for .toast above — so it never sits on top of the
   mobile mini-cart bar either. */
.wa-fab{
  position:fixed;
  right:22px; bottom:24px;
  width:58px; height:58px;
  border-radius:50%;
  background:#25D366;
  color:#fff;
  display:flex;
  align-items:center;
  justify-content:center;
  box-shadow:0 4px 16px rgba(0,0,0,.28);
  z-index:350;
  text-decoration:none;
  line-height:0;
  transition:transform .15s ease, box-shadow .15s ease;
}
.wa-fab:hover, .wa-fab:focus{
  transform:scale(1.07);
  box-shadow:0 6px 20px rgba(0,0,0,.32);
  color:#fff;
}
@media(max-width:768px){
  .wa-fab{ right:14px; bottom:86px; width:52px; height:52px; }
  body.has-sticky-cart .wa-fab{ bottom:150px; }
}

/* ════════════════════════════════════════════════════════
   2026-10-28 MOBILE RESPONSIVE FIXES — customer complaints about
   content being "cut off" on phones. Root causes found: several pieces
   of UI were built with fixed pixel widths and no way to shrink, so on
   narrow viewports they silently overflowed and got clipped by the
   site-wide body{overflow-x:hidden} rule (see GENERAL UTILITY above) —
   invisible content rather than a visible layout break, which is why
   it read as random missing text/buttons rather than an obvious bug.
════════════════════════════════════════════════════════ */

/* ── Email verification OTP boxes (see _showVerifyPane() in main.js) ──
   Were 6 inline-styled inputs at a fixed 46px + 8px gap each — 316px
   total with zero flexibility — which doesn't fit the usable width
   inside .acct-right on many phones (328px at ≤480px: 360px viewport
   minus 16px+16px padding) once you account for real-world display
   zoom/accessibility text-size settings, and gave no room at all below
   that. clamp() lets every box shrink continuously with the viewport
   instead of hitting a hard wall. */
.otp-box-row{
  display:flex;
  flex-wrap:nowrap;
  gap:clamp(4px,2vw,8px);
  justify-content:center;
  max-width:100%;
  margin:0 0 22px;
  animation:otpShake .4s ease;
}
.otp-box{
  flex:0 0 auto;
  width:clamp(28px,11vw,46px);
  height:clamp(40px,15vw,54px);
  padding:0;
  text-align:center;
  font-size:clamp(1rem,4.2vw,1.7rem);
  font-weight:800;
  border:2px solid #d1d5db;
  border-radius:10px;
  color:var(--navy);
  background:#fff;
  outline:none;
  font-family:monospace;
  transition:border-color .15s;
}
.otp-box:focus{ border-color:#0098E0; box-shadow:0 0 0 3px rgba(0,152,224,.14); }
@keyframes otpShake{
  0%,100%{ transform:translateX(0); }
  20%,60%{ transform:translateX(-4px); }
  40%,80%{ transform:translateX(4px); }
}

/* ── Dashboard order-history "view items" table (see _renderOrderItems()
   in main.js) — the Unit Price column alone has a 140px min-width, so
   the 5 columns together never fit a phone screen. Rather than cram
   columns that need room to be legible (price, line total, saving),
   scroll the table horizontally within its own box — same pattern
   already used for .policy-table above. */
.dash-table-scroll{
  overflow-x:auto;
  -webkit-overflow-scrolling:touch;
  max-width:100%;
}

/* ════════════════════════
   SIGN-IN / SIGN-UP ACCOUNT MODAL
════════════════════════ */
@media(max-width:768px){
  .acct-overlay{ padding:0; align-items:flex-end; }
  .acct-modal{
    width:100%;
    max-height:95vh;
    border-radius:20px 20px 0 0;
    grid-template-columns:1fr;
    transform:translateY(100%) scale(1);
  }
  .acct-overlay.open .acct-modal{ transform:none; }
  .acct-left{ display:none; }
  .acct-right{ padding:28px 22px 32px; max-height:95vh; overflow-y:auto; }
  .acct-tabs{ gap:20px; margin-bottom:22px; }
  .acct-tab{ font-size:16px; }
  .acct-h3{ font-size:clamp(22px,5vw,28px); }
  .acct-grid{ grid-template-columns:1fr; gap:14px; }
  .fld input,.fld select,.fld textarea{ font-size:14px; padding:12px 13px; }
  .acct-submit{ padding:15px; font-size:15px; }
}

@media(max-width:480px){
  .acct-modal{ max-height:100vh; border-radius:0; }
  .acct-right{ padding:24px 16px 28px; }
  .acct-tabs{ gap:16px; margin-bottom:18px; }
  .acct-tab{ font-size:15px; }
}

/* ════════════════════════
   MEMBERSHIP MODAL
════════════════════════ */
@media(max-width:768px){
  .mem-modal{
    max-width:calc(100% - 16px) !important;
    width:calc(100% - 16px) !important;
    max-height:92vh !important;
  }
  .mem-modal-body{ padding:18px 16px; max-height:88vh; }
}
@media(max-width:480px){
  .mem-modal{
    max-width:100% !important;
    width:100% !important;
    border-radius:0 !important;
    max-height:100vh !important;
  }
  .mem-modal-body{ max-height:96vh; }
}

/* ════════════════════════
   MY ACCOUNT DASHBOARD
════════════════════════ */
@media(max-width:768px){
  .dash-overlay{ padding:0; align-items:flex-start; }
  .dash{
    max-height:100vh;
    border-radius:0;
    flex-direction:column;
  }
  .dash-bar{ padding:12px 16px; gap:10px; }
  .dash-bar h2{ font-size:15px; }
  .dash-signout{ font-size:9.5px; padding:6px 10px; }

  /* FIX 2026-07-18: this wrapper holds .dash-nav + .dash-content side by side
     (display:flex, row by default) with no class to target — so .dash-nav's
     mobile "become a horizontal strip" rules below never actually stacked it
     above the content; it just squeezed into the row and stretched to fill
     the full modal height instead. Force it to stack on mobile. */
  .dash-body-wrap{ flex-direction:column; }

  /* Left nav → horizontal scrolling top bar on mobile */
  .dash-nav{
    width:100%;
    flex-direction:row;
    background:#0d1f35;
    overflow-x:auto;
    overflow-y:visible;
    flex-shrink:0;
    min-height:46px;
    border-bottom:2px solid rgba(255,255,255,.08);
  }
  .dash-nav::-webkit-scrollbar{ display:none; }
  .dash-nav{ scrollbar-width:none; }
  .dash-nav-items{
    flex-direction:row;
    padding:0;
    width:max-content;
  }
  .dash-nav-btn{
    flex-direction:column;
    align-items:center;
    gap:3px;
    padding:8px 14px;
    border-left:none;
    border-bottom:3px solid transparent;
    font-size:10px;
    white-space:nowrap;
  }
  .dash-nav-btn svg{ width:15px; height:15px; }
  .dash-nav-btn.active{ border-left:none; border-bottom-color:var(--gold); }

  /* Content panel */
  .dash-content{ padding:18px 14px; overflow-y:auto; }

  /* Stats */
  .dash-stats{ grid-template-columns:1fr 1fr; gap:10px; margin-bottom:16px; }
  .dstat{ padding:12px 13px; }
  .dstat .v{ font-size:18px; }

  /* Tables — card-style on mobile */
  .dtable{ font-size:12px; }
  .dtable th{ font-size:9px; padding:8px 10px; }
  .dtable td{ padding:10px 10px; }

  /* Profile cards */
  .dcards{ grid-template-columns:1fr; gap:12px; }
  .dcard{ padding:14px 16px; }

  /* Welcome banner */
  .dash-welcome{ flex-direction:column; gap:10px; padding:16px 18px; }
  .dash-welcome h3{ font-size:1.1rem; }

  /* Address grid */
  .dash-addr-grid{ grid-template-columns:1fr; gap:10px; }
}

@media(max-width:480px){
  .dash-bar{ padding:10px 12px; }
  .dash-bar h2{ font-size:14px; }
  .dash-content{ padding:14px 12px; }
  .dash-stats{ grid-template-columns:1fr 1fr; gap:8px; }
  .dstat .v{ font-size:16px; }
  .dstat .k{ font-size:9px; }

  /* Orders table → stacked cards
     NOTE: fixed 2026-07-18 — selectors previously read "#dashOrdersTable .dtable ..."
     but the table element carries BOTH id="dashOrdersTable" and class="dtable" on the
     SAME node, so the descendant combinator never matched anything and this entire
     block was inert (thead hid, but rows never actually stacked into cards). */
  #dashOrdersTable thead{ display:none; }
  #dashOrdersTable{ border:none; box-shadow:none; }
  #dashOrdersTable tbody tr[id^="drow-"]{
    display:block;
    background:#fff;
    border:1.5px solid #e5e7eb;
    border-radius:10px;
    margin-bottom:10px;
    padding:12px 14px;
  }
  #dashOrdersTable tbody tr[id^="drow-"]:hover{ background:#f8f9fb; }
  #dashOrdersTable tbody tr[id^="drow-"] td{
    display:flex;
    justify-content:space-between;
    align-items:center;
    border:none;
    padding:4px 0;
    font-size:12.5px;
  }
  #dashOrdersTable tbody tr[id^="drow-"] td::before{
    content:attr(data-label);
    font-family:'Space Mono',monospace;
    font-size:9.5px;
    text-transform:uppercase;
    letter-spacing:.06em;
    color:#9ca3af;
    flex-shrink:0;
    margin-right:8px;
  }
  #dashOrdersTable tr[id^="ditems-"]{ display:block; margin-bottom:10px; }
  #dashOrdersTable tr[id^="ditems-"] td{ display:block; padding:0; }

  /* Invoices table */
  #dashInvoicesTable thead{ display:none; }
  #dashInvoicesTable tbody tr{
    display:block;
    background:#fff;
    border:1.5px solid #e5e7eb;
    border-radius:10px;
    margin-bottom:10px;
    padding:12px 14px;
  }
  #dashInvoicesTable tbody tr td{
    display:flex;
    justify-content:space-between;
    align-items:center;
    border:none;
    padding:4px 0;
    font-size:12.5px;
  }
  #dashInvoicesTable tbody tr td::before{
    content:attr(data-label);
    font-family:'Space Mono',monospace;
    font-size:9.5px;
    text-transform:uppercase;
    letter-spacing:.06em;
    color:#9ca3af;
    flex-shrink:0;
    margin-right:8px;
  }

  /* Payments table — same stacked-card treatment as Orders/Invoices */
  #dashPaymentsTable thead{ display:none; }
  #dashPaymentsTable tbody tr{
    display:block;
    background:#fff;
    border:1.5px solid #e5e7eb;
    border-radius:10px;
    margin-bottom:10px;
    padding:12px 14px;
  }
  #dashPaymentsTable tbody tr td{
    display:flex;
    justify-content:space-between;
    align-items:center;
    border:none;
    padding:4px 0;
    font-size:12.5px;
  }
  #dashPaymentsTable tbody tr td::before{
    content:attr(data-label);
    font-family:'Space Mono',monospace;
    font-size:9.5px;
    text-transform:uppercase;
    letter-spacing:.06em;
    color:#9ca3af;
    flex-shrink:0;
    margin-right:8px;
  }

  /* Form grids inside the dashboard (profile, membership benefits, address form) */
  .dash-2col, .dash-3col{ grid-template-columns:1fr; }
}

/* ════════════════════════
   CHECKOUT MODAL — mobile rules consolidated 2026-07-26.
   (Previously here: an early, superseded pass at the checkout modal's
   mobile layout. It's been merged into the single consolidated block
   under "3. CHECKOUT — Full-screen, single column" further down, next to
   the CART bottom-sheet rules — see the note there for why.)
════════════════════════ */

/* ════════════════════════
   PDF SLIDE-IN PANEL (invoices)
════════════════════════ */
@media(max-width:768px){
  #dashPdfPanel{
    width:100% !important;
    left:0 !important;
    right:0 !important;
    top:auto !important;
    bottom:0 !important;
    height:85vh !important;
    border-radius:20px 20px 0 0 !important;
  }
  #dashPdfPanel iframe{ height:calc(85vh - 52px) !important; }
}

/* ════════════════════════
   POLICY PAGES
════════════════════════ */
@media(max-width:768px){
  .policy-page{ padding:24px 14px 44px; }
  .policy-wrap{ padding:24px 18px 32px; }
  .policy-h1{ font-size:1.55rem; }
  .policy-table{ font-size:.82rem; }
  .policy-table th,.policy-table td{ padding:8px 10px; }
}
@media(max-width:480px){
  .policy-wrap{ padding:20px 14px 28px; }
  .policy-h1{ font-size:1.35rem; }
  /* Table → scroll on small screens */
  .policy-table{ display:block; overflow-x:auto; white-space:nowrap; }
}

/* ════════════════════════
   GENERAL UTILITY
════════════════════════ */
/* Prevent horizontal overflow on all screens */
body{ overflow-x:hidden; }
img{ max-width:100%; height:auto; }
/* Make all modals/drawers scroll internally, not the page */
.acct-overlay{ -webkit-overflow-scrolling:touch; }
/* Tap highlight removal */
button,a{ -webkit-tap-highlight-color:transparent; }

/* ════════════════════════════════════════════════════════════════════════════
   BUG FIXES v2 — 2026-07-18
   Issues from screenshots:
   1. Saved Items panel stuck on "Loading..." (JS fix in main.js)
   2. Header search bar clips on mobile
   3. Checkout saved addresses show side-by-side on mobile (should stack)
   4. Checkout modal: full clean mobile layout
════════════════════════════════════════════════════════════════════════════ */

/* ── FIX 2: Header — tighter layout so search never clips ── */
@media(max-width:640px){
  .head-inner{ gap:8px; }
  .search{ min-width:0; }
  .search input{ min-width:0; padding:8px 10px; font-size:13px; }
  .search button{ padding:0 12px; font-size:13px; white-space:nowrap; }
  /* Shrink logo text */
  .logo-tag{ font-size:11.5px; }
  .logo-div{ display:none; }
}
@media(max-width:480px){
  /* On very small screens, let logo be icon only */
  .logo-tag{ display:none; }
  .logo-img{ height:30px; }
  .search input{ font-size:12.5px; }
}

/* (FIX 3 + 4 checkout overhaul — superseded, merged into the single
   consolidated checkout block under "3. CHECKOUT — Full-screen, single
   column" below, next to the CART bottom-sheet rules.) */

/* ── Wishlist panel full-width on mobile ── */
@media(max-width:480px){
  #wishlistPanel{
    width: 100vw !important;
    right: 0 !important;
  }
  #wishlistBody{ padding: 10px 14px !important; }
}

/* ── My Account dashboard: ensure nav items visible on mobile ── */
@media(max-width:480px){
  .dash-nav-btn{ padding: 8px 12px; font-size:9.5px; }
  .dash-nav-btn svg{ width:14px; height:14px; }
  .dsec-title{ font-size:16px; }
  .dash-content{ padding: 14px 12px; }
}

/* ── Prevent iOS font zoom on inputs ── */
@media(max-width:768px){
  input, select, textarea{ font-size: 16px !important; }
  .search input{ font-size: 14px !important; }
  .co-inp{ font-size: 16px !important; }
  /* but keep tiny labels small */
  .co-fld label, .af-fld label{ font-size: 10.5px !important; }
}

/* ════════════════════════════════════════════════════════════════════════════
   MOBILE REVAMP v3 — 2026-07-18
   1. Header: 2-row layout (Row 1: logo+icons, Row 2: search)
   2. Catbar: hidden on mobile
   3. Cart: center modal on mobile
   4. Checkout: address-list first, form on demand
════════════════════════════════════════════════════════════════════════════ */

/* ── 1. HEADER 2-ROW MOBILE LAYOUT ── */
@media(max-width:768px){
  /* Header becomes a 2-row container */
  .head-main{
    flex-direction: column !important;
    height: auto !important;
    padding: 0 !important;
    position: sticky;
    top: 28px;
    z-index: 300;
  }
  .head-inner{
    flex-wrap: wrap;
    gap: 0 !important;
    width: 100%;
    padding: 0;
  }

  /* Row 1: logo left + actions right, full width */
  .logo{
    flex: 1;
    padding: 8px 12px 8px;
    min-height: 48px;
    align-items: center;
  }
  .logo-div{ display:none !important; }
  .logo-tag{ font-size:12px; }
  .logo-img{ height:30px; }

  /* Row 1 actions */
  .head-actions{
    padding: 6px 10px 6px 4px;
    gap: 2px !important;
    align-items: center;
    flex-shrink: 0;
  }
  .head-btn{ min-width:36px; padding:5px 6px; font-size:9px; }
  .head-btn svg{ width:18px; height:18px; }

  /* Join Membership — show star icon only */
  .mem-join-btn span{ display:none !important; }
  .mem-join-btn{ padding:5px 7px; min-width:34px; border-radius:8px; }

  /* Row 2: search bar — full width strip */
  .search-mobile-row{
    display: flex !important;
    order: 99; /* forces to new row */
    width: 100%;
    flex: none;
    border-top: 1px solid var(--border);
    border-radius: 0;
    max-width: 100%;
    min-height: 40px;
  }
  .search-mobile-row input{
    flex:1; padding:9px 12px; font-size:14px; border:none; outline:none;
    background:#fff; color:#111;
  }
  .search-mobile-row button{
    background:var(--navy); color:#fff; border:none;
    padding:0 16px; font-size:13px; font-weight:700; cursor:pointer;
    flex-shrink:0; white-space:nowrap;
  }

  /* Hide desktop search on mobile */
  .search-desktop{ display:none !important; }

  /* Catbar DIV: HIDDEN on mobile (header element keeps its own display) */
  div.catbar{ display:none !important; }

  /* Page top offset: ticker(28) + head-row1(48) + search-row(40) = 116px */
  /* Hero just flows naturally under sticky header */
}

@media(min-width:769px){
  /* Desktop: show desktop search, hide mobile search */
  .search-mobile-row{ display:none !important; }
  .search-desktop{ display:flex !important; }
}

/* ── 2. CART — CENTER MODAL on mobile ── */
@media(max-width:768px){
  /* Overlay becomes full screen backdrop */
  .overlay{
    position: fixed !important;
    inset: 0 !important;
    background: rgba(0,0,0,.55) !important;
    z-index: 450 !important;
  }
  /* Cart drawer becomes a center bottom-sheet modal */
  .drawer{
    position: fixed !important;
    right: auto !important;
    left: 0 !important;
    bottom: 0 !important;
    top: auto !important;
    width: 100% !important;
    max-height: 88vh !important;
    height: auto !important;
    border-radius: 20px 20px 0 0 !important;
    box-shadow: 0 -8px 40px rgba(0,0,0,.22) !important;
    transform: translateY(100%);
    transition: transform .3s cubic-bezier(.4,0,.2,1) !important;
    right: unset !important;
  }
  .drawer.open{
    transform: translateY(0) !important;
    right: unset !important;
  }
  .drawer-head{ padding: 16px 18px 14px; }
  .drawer-body{ padding: 10px 16px; }
  /* Foot is already flex-shrink:0 in a flex-column drawer (base CSS), so it
     never scrolls out of view even with a long cart — but on real devices
     it read as just another block of text, not "the footer with the
     checkout button". Pin it visually as its own bar: sticky to the
     bottom, separated by a shadow, with the button itself sized up so
     it's the one unmistakable action on screen. */
  .drawer-foot{
    padding: 12px 16px 20px;
    position: sticky;
    bottom: 0;
    background: #fff;
    box-shadow: 0 -6px 16px rgba(0,0,0,.08);
    z-index: 2;
  }
  .drawer-foot .checkout-btn{
    padding: 16px 20px;
    font-size: 15.5px;
    border-radius: 10px;
  }
  /* Drag handle */
  .drawer-head::before{
    content:'';
    display:block;
    width:40px; height:4px;
    background:#d1d5db;
    border-radius:99px;
    margin: 0 auto 12px;
  }

  /* ── 3. CHECKOUT — Full-screen, single column ──
     CONSOLIDATED 2026-07-26. This used to be THREE separate, partially-
     overlapping @media(max-width:768px) blocks for the checkout modal
     ("CHECKOUT MODAL", "FIX 3 + 4: Checkout modal — complete mobile
     overhaul", and this one), added at different times while chasing the
     same two symptoms (issues #3/#4: cart button prominence, missing
     Continue button). Each later block won the cascade only for the
     specific properties it redefined, so the real rendered result was an
     unpredictable merge of all three — that partial merge is what left
     the Continue button unreachable. All checkout-modal mobile rules now
     live in this single place; the other two blocks have been removed. */
  #checkoutOverlay{
    padding: 0 !important;
    align-items: flex-end !important;
  }
  #checkoutOverlay > div{
    width: 100% !important;
    max-height: 100vh !important;
    border-radius: 0 !important;
    overflow: hidden !important;
  }
  /* Step header */
  #checkoutOverlay > div > div:first-child{
    padding: 12px 16px !important;
  }
  #checkoutOverlay .co-step label{ font-size:10px; }
  #checkoutOverlay .co-step span{ width:20px; height:20px; font-size:9px; }
  #checkoutOverlay .co-step-line{ width:20px; }

  /* Step 1 body: SINGLE COLUMN — addresses fill full width */
  #coPane1{
    flex-direction: column !important;
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
  }
  /* THE ROOT-CAUSE FIX for issue #4 ("continue button missing"): the
     Delivery step's inner "panels row" (#coSavedPanel + #coFormPanel side
     by side) never had its own mobile rule, so it stayed a ROW flex
     container while both children were separately told to be width:100%.
     Two 100%-wide items squeezed into one overflow:hidden row meant the
     form panel — and the Continue button inside it — got squeezed out of
     the visible/clickable area. Stacking this row vertically is what
     actually fixes it; the #coSavedPanel/#coFormPanel width rules below
     were correct all along. */
  #coPanelsRow{
    flex-direction: column !important;
    overflow: visible !important;
    min-height: 0;
  }
  /* Saved addresses: full width, compact header */
  #coSavedPanel{
    width: 100% !important;
    max-height: none !important;
    height: auto !important;
    flex-shrink: 0 !important;
    border-right: none !important;
    border-bottom: 1.5px solid var(--border) !important;
    background: #f8f9fa;
    overflow: visible !important;
  }
  #coSavedAddrList{
    flex-direction: column !important;
    flex-wrap: nowrap !important;
    overflow-x: hidden !important;
    overflow-y: auto !important;
    max-height: 40vh !important;
    padding: 8px 12px 12px !important;
    gap: 8px !important;
    -webkit-overflow-scrolling: touch;
  }
  .co-addr-card{
    min-width: 0 !important;
    width: 100% !important;
    flex-shrink: 0 !important;
    box-sizing: border-box;
  }
  #coAddNewBtn{ width:100%; border-radius:8px !important; font-size:13px; }

  /* Form panel: a normal stacked block now that the row above it stacks
     too. Scrolls internally (address form + schedule + notes + Continue
     button) if content exceeds the remaining height, so the Continue
     button is always reachable by scrolling this panel, never hidden. */
  #coFormPanel{
    flex: 1 !important;
    width: 100% !important;
    min-height: 0;
    padding: 16px 14px !important;
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
  }
  /* Back button visible on mobile */
  #coBackToAddresses{
    position: sticky;
    top: 0;
    background: #fff;
    z-index: 2;
    border-bottom: 1px solid var(--border);
    padding: 10px 0 12px !important;
    margin: 0 -14px 12px !important;
    padding-left: 14px !important;
    width: calc(100% + 28px);
  }
  .co-inp{
    font-size: 16px !important; /* prevents iOS zoom on focus */
    padding: 11px 13px !important;
    border-radius: 8px !important;
  }
  .co-fld label{ font-size:10.5px !important; margin-bottom:5px !important; }
  #coScheduleSlots{ gap:8px !important; }

  /* Review pane (Step 2): fixed head/foot, only #coItemsList's wrapper
     scrolls internally when the order has many items (issue #5) — see
     the flex-column markup restructure in header.php. #coPane2 itself no
     longer needs its own padding; each of its three sub-sections carries
     its own. */
  #coPane2{ padding:0 !important; }
  #coPane3{ padding: 16px 14px !important; }
  .co-next-btn{ font-size:14px !important; padding:13px !important; }
  #coAddrSummary{ padding: 10px 12px !important; }
  /* City+Postcode 2-col */
  #coPane1 .co-fld input, #coPane1 .co-fld textarea, #coPane1 .co-fld select{
    font-size:16px !important;
  }
}

@media(max-width:480px){
  #checkoutOverlay > div{ max-height:100vh !important; border-radius:0 !important; }
  #coFormPanel{ padding:14px 12px !important; }
  #coPane3{ padding:14px 12px !important; }
  .co-inp{ font-size:16px !important; }
}

/* ════════════════════════════════════════════════════════
   MOBILE BOTTOM TAB BAR — Home / Category / Brand / Offers /
   My Account (2026-08-05). App-style fixed nav bar, phone-only —
   markup is in header.php (#mobileTabbar), click handlers
   (mobTabHome()/mobTabCategory()/mobTabBrand()/mobTabOffers()/
   mobTabAccount()) are in main.js and each just reuses an existing
   desktop action (setCat('All'), the brand chip row, the Active
   Offers filter, acctButton()) rather than introducing separate
   state to keep in sync.
   Hidden by default (desktop/tablet); only unhidden inside the
   @media block below, at the same 768px breakpoint used throughout
   the rest of this file's mobile rules. z-index:350 sits above the
   sticky header/hero/category-rail (z-index 200-400) but well below
   the cart drawer (400/500), account modal (800) and dashboard
   overlay (1200) — so any of those opening still correctly covers it,
   no extra show/hide JS needed.
════════════════════════════════════════════════════════ */
.mobile-tabbar{ display:none; }
@media(max-width:768px){
  .mobile-tabbar{
    display:flex;
    position:fixed;
    left:0; right:0; bottom:0;
    z-index:350;
    background:#fff;
    border-top:1px solid var(--border);
    box-shadow:0 -2px 12px rgba(0,0,0,.08);
    padding:6px 4px calc(6px + env(safe-area-inset-bottom,0px));
  }
  .mtab{
    flex:1 1 0;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    gap:2px;
    border:none;
    background:none;
    padding:6px 2px;
    cursor:pointer;
    color:var(--slate-lt,#6b7280);
    font-family:'Archivo',sans-serif;
    font-size:10px;
    font-weight:600;
    line-height:1.1;
    -webkit-tap-highlight-color:transparent;
  }
  .mtab svg{ width:21px; height:21px; margin-bottom:1px; }
  .mtab.active{ color:var(--navy); }
  .mtab.active svg{ stroke:var(--navy); }
  /* Now that every mobile page has a bar docked to the viewport bottom,
     give the page enough bottom padding that the bar never overlaps the
     last bit of real content (footer links, final "Order now" button,
     etc.) — sized a little taller than the bar itself to clear the
     iOS home-indicator safe area too. */
  body{ padding-bottom:66px; }
}

/* ════════════════════════════════════════════════════════
   PICKER MODAL — Category / Brand / Offers (2026-08-05)
   Opened from the bottom tab bar (openPicker() in main.js) now that the
   inline chip rows are hidden on mobile (see the #filters/#brandFilters/
   #offersOnlyRow{display:none} rule in the CATALOG/PRODUCT GRID section
   above) — this bottom-sheet modal is the only way to pick a category,
   brand, or promo on a phone. Shares the same open/close mechanics as
   .acct-overlay (opacity+visibility transition toggled via .open) and
   reuses .acct-x for its close button, but its own shell — a sheet that
   slides up from the bottom (feels native right next to the tab bar that
   opens it) rather than a centered dialog. Tile styling (.picker-tile*)
   mirrors the site's existing .cat-tile "icon + name + count" card
   language (see CAT_ICONS/renderCategoryGrid() in main.js) rather than
   inventing a new look, per the "professional display" ask. Not scoped to
   a mobile-only @media block since the only things that ever call
   openPicker() are the bottom tab bar buttons, which are themselves
   mobile-only — nothing on desktop can trigger this modal in the first
   place. ════════════════════════════════════════════════════════ */
.picker-overlay{
  position:fixed; inset:0; z-index:900;
  background:rgba(8,16,26,.6);
  backdrop-filter:blur(3px);
  display:flex; align-items:flex-end; justify-content:center;
  opacity:0; visibility:hidden;
  transition:opacity .25s, visibility .25s;
}
.picker-overlay.open{ opacity:1; visibility:visible; }
.picker-modal{
  background:#fff;
  width:100%;
  max-width:560px;
  max-height:82vh;
  border-radius:20px 20px 0 0;
  padding:20px 18px calc(18px + env(safe-area-inset-bottom,0px));
  position:relative;
  overflow-y:auto;
  transform:translateY(24px);
  transition:transform .3s cubic-bezier(.4,0,.1,1);
  box-shadow:0 -8px 40px rgba(0,0,0,.2);
}
.picker-overlay.open .picker-modal{ transform:translateY(0); }
.picker-title{
  font-family:'Bricolage Grotesque',sans-serif;
  font-size:19px;
  font-weight:800;
  color:var(--navy);
  margin:0 0 16px;
  padding-right:40px;
}
.picker-x{ position:absolute; top:14px; right:14px; width:36px; height:36px; }
.picker-grid{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(128px,1fr));
  gap:10px;
}
.picker-tile{
  appearance:none;
  border:1.5px solid var(--border);
  background:#fff;
  border-radius:var(--radius-lg,14px);
  padding:16px 14px;
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  gap:6px;
  cursor:pointer;
  text-align:left;
  transition:transform var(--tx),box-shadow var(--tx),border-color var(--tx),background var(--tx);
  -webkit-tap-highlight-color:transparent;
}
.picker-tile:active{ transform:scale(.96); }
.picker-tile.active{ border-color:var(--navy); background:#f0f4ff; }
.picker-tile-icon{
  width:32px; height:32px;
  border-radius:9px;
  background:var(--gold-tint,#FDF3D2);
  display:flex; align-items:center; justify-content:center;
  color:var(--navy);
}
.picker-tile.active .picker-tile-icon{ background:var(--navy); color:#fff; }
.picker-tile-icon svg{ width:18px; height:18px; }
/* Admin-uploaded DB icon (CAT_ICON_URLS, see _categoryIconHTML() in
   main.js) — same footprint as the SVG it can replace. */
.picker-tile-icon img{ width:19px; height:19px; object-fit:contain; }
.picker-tile-name{
  font-family:'Bricolage Grotesque',sans-serif;
  font-size:13px;
  font-weight:800;
  color:var(--navy);
  line-height:1.25;
}
.picker-tile-count{
  font-family:'Space Mono',monospace;
  font-size:10px;
  color:var(--slate-lt);
}
.picker-empty{
  font-family:'Archivo',sans-serif;
  font-size:14px;
  color:var(--slate-lt);
  padding:24px 4px;
  text-align:center;
}

/* ═══════════════════════════════════════════════════════════════
   PRODUCT QUICK-VIEW MODAL — opened by clicking any product card's
   image or name in the catalogue grid (openProductModal() in main.js).
   A centered dialog (unlike the bottom-sheet .picker-overlay above,
   since this is available on desktop too, not just mobile). The body
   reuses the exact .card markup cardHTML() already produces for the
   grid — same pricing/stepper/Add-basket logic, zero duplication —
   with a few !important overrides below to strip the card chrome
   (border/shadow/hover-lift) and enlarge the image, since .card-img's
   height is set inline per-card and would otherwise win over a plain
   class rule. ════════════════════════════════════════════════════ */
.product-modal-overlay{
  position:fixed; inset:0; z-index:950;
  background:rgba(8,16,26,.6);
  backdrop-filter:blur(3px);
  display:flex; align-items:center; justify-content:center;
  padding:24px;
  opacity:0; visibility:hidden;
  transition:opacity .25s, visibility .25s;
}
.product-modal-overlay.open{ opacity:1; visibility:visible; }
.product-modal{
  background:#fff;
  width:100%;
  max-width:640px;
  max-height:88vh;
  border-radius:20px;
  padding:22px;
  position:relative;
  overflow-y:auto;
  transform:scale(.96);
  transition:transform .25s cubic-bezier(.4,0,.1,1);
  box-shadow:0 20px 60px rgba(0,0,0,.28);
}
.product-modal-overlay.open .product-modal{ transform:scale(1); }
.product-modal-x{ position:absolute; top:14px; right:14px; width:36px; height:36px; z-index:5; }
.product-modal-body .card{
  border:none !important;
  box-shadow:none !important;
  border-radius:0 !important;
  transform:none !important;
}
.product-modal-body .card:hover{ transform:none !important; box-shadow:none !important; }
.product-modal-body .card-img{
  height:320px !important;
  border-radius:14px !important;
  border-bottom:none !important;
}
.product-modal-body .card-body{ padding:16px 2px 8px; }
.product-modal-body h3{ font-size:19px; }
.product-modal-desc{
  padding:14px 2px 4px;
  border-top:1px solid var(--border);
  margin-top:6px;
}
.product-modal-desc-label{
  font-family:'Space Mono',monospace;
  font-size:10px;
  font-weight:700;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--slate-lt);
  margin-bottom:6px;
}
.product-modal-desc p{
  font-family:'Archivo',sans-serif;
  font-size:13.5px;
  line-height:1.55;
  color:var(--ink,#141619);
  margin:0;
  white-space:pre-line;
}
.product-modal-actions{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:12px;
  padding:14px 2px 2px;
  border-top:1px solid var(--border);
  margin-top:10px;
}
.product-modal-wish{
  appearance:none;
  border:1.5px solid var(--border);
  background:#fff;
  border-radius:999px;
  padding:8px 16px;
  font-family:'Archivo',sans-serif;
  font-size:12.5px;
  font-weight:700;
  color:var(--slate-lt);
  cursor:pointer;
  transition:.15s;
}
.product-modal-wish:hover{ border-color:#ef4444; color:#ef4444; }
.product-modal-wish.active{ border-color:#ef4444; background:#fef2f2; color:#ef4444; }
.product-modal-viewfull{
  font-family:'Archivo',sans-serif;
  font-size:12.5px;
  font-weight:700;
  color:var(--navy);
  text-decoration:none;
  white-space:nowrap;
}
.product-modal-viewfull:hover{ text-decoration:underline; }
@media(max-width:600px){
  .product-modal-overlay{ padding:0; align-items:flex-end; }
  .product-modal{
    max-width:100%;
    max-height:92vh;
    border-radius:20px 20px 0 0;
    padding:18px 16px calc(16px + env(safe-area-inset-bottom,0px));
  }
  .product-modal-body .card-img{ height:220px !important; }
  .product-modal-actions{ flex-direction:column; align-items:stretch; }
  .product-modal-viewfull{ text-align:center; padding:6px 0; }
}