/* ============================================================
   Pushpmilan — Flowers, Cakes & Gifts
   ============================================================ */

:root {
  /* Phase 6: deeper botanical green, more controlled floral rose, and a
     restrained champagne (still --gold, value only changed) replacing the
     brighter gold -- see qa/reports/phase6/VISUAL-DESIGN-PROPOSAL.md.
     Variable NAMES are unchanged so every existing rule (including cart/
     checkout, never redesigned in this pass) picks up the refined palette
     automatically instead of drifting from it. */
  --green-dark: #12281b;
  --green: #1f6b45;
  --green-light: #e9f3ec;
  --rose: #a63a56;
  --rose-dark: #7e2740;
  --rose-light: #fbe9ee;
  --gold: #b08d4f;
  --clay: #b9673a;        /* secondary accent -- festive/seasonal warmth, used sparingly (never a large-area background) */
  --clay-light: #f6e6da;  /* soft clay badge/hover background, sibling to --rose-light */
  --cream: #fbf5e9;
  /* New: a second, deeper warm tone for section-to-section banding
     (Phase 6 §"Section backgrounds create rhythm") -- additive, nothing
     existing referenced a second cream tone before this. */
  --cream-deep: #f1e5cd;
  /* UI observations batch: approved section-background tones for the
     Flowers and Occasions sections/pages specifically -- distinct from
     each other and from --cream, so the two currently look visually
     interchangeable no longer do. Subtle/premium by design (not meant to
     be visually loud), applied as full section backgrounds. */
  --flowers-bg: #f7f1e7;
  --occasions-bg: #fbf2f4;
  --paper: #fffdfb;
  --ink: #26221d;
  --muted: #6f6a62;
  --border: #ece4d8;
  --white: #ffffff;
  --shadow-sm: 0 2px 10px rgba(38, 34, 29, 0.06);
  --shadow-md: 0 12px 30px rgba(22, 61, 41, 0.12);
  --shadow-lg: 0 22px 50px rgba(22, 61, 41, 0.18);
  --radius: 14px;
  --radius-sm: 10px;
  /* 8pt spacing scale (Phase 5 DESIGN-SYSTEM.md §4), 4px half-step */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 16px;
  --space-4: 24px;
  --space-5: 32px;
  --space-6: 48px;
  --space-7: 64px;
  --space-8: 96px;
  /* Floating-control tokens (§8) -- consumed by the safe-area-aware
     .fab-wa/.fab-top positioning rules below. */
  --fab-offset: 20px;
  --fab-gap: 14px;
  --fab-size-primary: 56px;
  --fab-size-secondary: 44px;
  /* Typography hierarchy (§3): Fraunces for the hero h1 only, Playfair
     Display narrowed to section-level headings (h2/h3) only, Poppins
     (final branding pass; was Work Sans, kept as fallback) for
     card/component titles and all body/UI text. */
  --ff-hero: "Fraunces", "Playfair Display", Georgia, "Times New Roman", serif;
  --ff-heading: "Playfair Display", Georgia, "Times New Roman", serif;
  --ff-body: "Poppins", "Work Sans", Arial, Helvetica, sans-serif;

  /* Premium redesign: supporting blush/neutral tones + a fluid section
     rhythm -- additive only, every existing variable above keeps its name
     and value so nothing already using them (cart/checkout/admin-adjacent
     rules) drifts. */
  --blush: #f4e3e6;
  --neutral-warm: #efe8dc;
  --space-9: 128px;
  --section-y: clamp(56px, 8vw, 112px);
  --header-h: 76px;
  --header-nav-h: 48px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

/* The `hidden` attribute must always win over any component's own `display`
   rule, regardless of selector specificity elsewhere in this file -- same
   fix already applied globally in css/admin.css. Without this, any element
   that both carries a `display: flex/grid/block/...` class rule AND gets
   toggled via `.hidden = true` in JS renders visible anyway, because an
   author-stylesheet class selector beats the browser's own default
   `[hidden] { display: none }` UA rule at equal specificity. This exact bug
   was hit and patched piecemeal, in at least four separate places in this
   file already (search for "browser's built-in [hidden]" / "browser's own
   [hidden]" in this file's own comments) -- most recently the Flowers/
   Occasions/Decoration hierarchy sections' Filter/Sort toolbar
   (.filter-toolbar-standalone, js/category-hierarchy-nav.js) staying
   visible after navigating Back from a leaf category to the category-cards
   view, even though that JS correctly sets toolbarEl.hidden = true. One
   global rule here removes this entire recurring bug class instead of
   requiring a new narrow override every time it resurfaces on a new
   component. */
[hidden] { display: none !important; }

/* Private Preview banner (js/preview-mode.js) -- UX indicator only, not
   the access-control mechanism. Sticky, unobtrusive, matches brand
   palette; deliberately not styled as an alert/warning to stay calm and
   professional rather than alarming. */
.preview-mode-banner {
  position: sticky;
  top: 0;
  z-index: 200;
  background: var(--ink);
  color: var(--cream);
  font-family: var(--ff-body);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-align: center;
  padding: 7px 12px;
}
@media (max-width: 480px) {
  .preview-mode-banner { font-size: 12px; padding: 6px 10px; }
}

body {
  margin: 0;
  font-family: var(--ff-body);
  color: var(--ink);
  background: var(--cream);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; margin: 0; padding: 0; }
button { font-family: inherit; cursor: pointer; }

.container {
  width: 100%;
  /* Phase 6: was 1240px everywhere with no exception, the audit's #1
     finding -- at 1920px that left 340px of dead margin per side for the
     entire page. Widened moderately (not blown out to full-bleed, which
     would hurt text readability -- VISUAL-DESIGN-PROPOSAL.md keeps a
     "sensible maximum text width"); full-bleed *color* treatment for
     specific bands (Shop by Occasion, the story section) comes from
     backgrounds on the <section> itself, not from this container. */
  max-width: 1320px;
  margin: 0 auto;
  padding: 0 var(--space-4);
}

section { padding: var(--section-y) 0; scroll-margin-top: calc(var(--header-h) + 8px); }
/* Scoped to this one section only -- the body's --cream background stays
   the site-wide default everywhere else; this just softens the tone
   directly behind the Flowers listing per design feedback. */
#flowers-section { background: var(--paper); }
#home { scroll-margin-top: 0; }

.section-tag {
  display: inline-block;
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rose);
  font-weight: 600;
  line-height: 1.4;
  margin-bottom: 12px;
}
/* Sparing --clay accent (DESIGN-SYSTEM.md §2): festive/occasion tags, e.g.
   the Ganpati Decoration block below -- one instance, not a general
   replacement for the rose section-tag color. */
.section-tag-clay { color: var(--clay); }

/* Phase 6: was text-align:center + margin:0 auto on every section on the
   page (8+ identical centered-serif headings in a row, the single largest
   "generic/template" finding in VISUAL-GAP-AUDIT.md §2) -- left-aligned
   here instead, restrained rather than removed. Still the same class on
   every section, so this one change alone fixes the repetition site-wide.
   .head-row (below) adds an optional trailing link for sections that want
   one, without requiring every section to use it. */
.section-head {
  max-width: 640px;
  margin: 0 0 48px;
}
@media (min-width: 981px) {
  .section-head, .head-row { margin-bottom: var(--space-7); }
}

.section-head h2 {
  font-family: var(--ff-heading);
  font-weight: 600;
  font-size: clamp(30px, 3.2vw, 44px);
  line-height: 1.2;
  letter-spacing: -0.01em;
  margin: 0 0 14px;
  color: var(--green-dark);
}

.section-head p { color: var(--muted); margin: 0; font-size: 16.5px; line-height: 1.6; max-width: 56ch; }

/* Optional pairing: a section-head beside a trailing link/CTA (e.g. "Shop
   by Category" -> "View All Categories"), asymmetric instead of a lone
   centered block. Opt-in per section -- .section-head alone (no wrapping
   .head-row) still works exactly as above. */
.head-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
  margin-bottom: 48px;
}
.head-row .section-head { margin-bottom: 0; }
.head-row-link {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--rose);
  white-space: nowrap;
  padding-bottom: 4px;
}
.head-row-link:hover { color: var(--rose-dark); }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 28px;
  border-radius: 100px;
  font-weight: 600;
  font-size: 15px;
  border: 2px solid transparent;
  transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease, color 0.2s ease;
  white-space: nowrap;
}
.btn:active { transform: scale(0.97); }

.btn-rose {
  background: var(--rose);
  color: var(--white);
  box-shadow: var(--shadow-sm);
}
.btn-rose:hover { background: var(--rose-dark); box-shadow: var(--shadow-md); }

.btn-outline {
  background: transparent;
  border-color: rgba(255,255,255,0.7);
  color: var(--white);
}
.btn-outline:hover { background: rgba(255,255,255,0.15); }

.btn-outline-dark {
  background: transparent;
  border-color: var(--green);
  color: var(--green);
}
.btn-outline-dark:hover { background: var(--green); color: var(--white); }

/* Tertiary/quiet action (brief: "secondary action should be visually
   quieter" -- e.g. hero's Call, decor's "ask about a custom setup"). */
.btn-ghost {
  background: transparent; border-color: transparent;
  color: var(--white); padding-left: 8px; padding-right: 8px;
  text-decoration: underline; text-underline-offset: 5px;
  text-decoration-color: rgba(255,255,255,0.45);
}
.btn-ghost:hover { color: var(--gold); text-decoration-color: var(--gold); }

.btn-block { width: 100%; justify-content: center; }
.btn-sm { padding: 10px 18px; font-size: 13.5px; }

.btn-disabled {
  background: var(--border);
  border-color: var(--border);
  color: var(--muted);
  cursor: not-allowed;
  justify-content: center;
}
.btn-disabled:hover, .btn-disabled:active { background: var(--border); color: var(--muted); transform: none; }

/* ---------- Top bar ---------- */
.topbar {
  background: var(--green-dark);
  color: rgba(255,255,255,0.88);
  font-size: 13px;
}
.topbar .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 6px;
  padding-bottom: 6px;
  gap: 12px;
  flex-wrap: wrap;
}
.topbar a { display: inline-flex; align-items: center; gap: 6px; }
.topbar a:hover { color: var(--gold); }
.topbar-links { display: flex; gap: 20px; flex-wrap: wrap; }

/* ---------- Header ----------
   Two-tier commerce header: row 1 is brand + search + account actions, row
   2 is a dedicated category nav strip on a contrasting ground -- the
   structure FNP-class sites use so search is always in reach and category
   browsing reads as navigation, not a row of text links squeezed beside a
   logo. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 500;
  background: var(--white);
  box-shadow: none;
}
.header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-h);
  gap: 20px;
}

.brand { display: flex; align-items: center; gap: 6px; flex: none; }
.brand-mark { width: 22px; height: 22px; color: var(--gold); flex: none; }
.brand-word {
  font-family: "Caveat", cursive;
  font-weight: 700;
  font-size: 34px;
  line-height: 1;
  color: var(--green);
  letter-spacing: 0.01em;
  white-space: nowrap;
}

/* Header wordmark image (customer redesign, final branding pass): fixed
   height so .site-header never grows, width auto-derives from the asset's
   own aspect ratio so it can never distort. */
.brand-logo-img { height: 68px; width: auto; display: block; }
@media (max-width: 480px) { .brand-logo-img { height: 52px; } }
/* Header horizontal-overflow fix (390px/430px): the fixed-size content in
   .header-row (logo ~159px at height:52px, five 40px icon-buttons + their
   6px gaps = 224px, plus .header-row's own 20px gap) totals ~403px --
   .container's own math (viewport - 48px side padding) only clears that
   at a viewport of ~451px or wider, so BOTH 390px and 430px genuinely
   overflow with the full-size header, not just 390px -- confirmed by
   measuring the actual CSS values, not assumed from the bug report's one
   named width. Fixed at max-width:460px (comfortably covers both
   required test widths with margin to spare) via a smaller logo, tighter
   gaps, and the icon-buttons trimmed from 40px to 36px -- still
   comfortably above common ~32px minimum tap-target guidance, and every
   icon/label/structure stays exactly as-is, nothing removed or hidden.
   Every wider viewport (768px, desktop) is completely unaffected. */
@media (max-width: 460px) {
  .brand-logo-img { height: 40px; }
  .header-row { gap: 8px; }
  .header-cta { gap: 2px; }
  .header-cta .icon-btn { width: 36px; height: 36px; }
}

/* Header search: the primary product-discovery entry point lives in the
   header itself (row 1, between brand and account actions) instead of
   below the fold -- #productSearchInput/#productSearchClear are the exact
   same elements js/products.js already wires up, only relocated. */
.header-search {
  flex: 1 1 auto;
  max-width: 480px;
  display: none;
}
@media (min-width: 981px) { .header-search { display: block; } }

.nav { display: flex; align-items: center; gap: 22px; }
.nav a {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.005em;
  white-space: nowrap;
  color: rgba(255,255,255,0.86);
  position: relative;
  padding: 4px 0;
}
.nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 2px;
  background: var(--gold);
  transform: scaleX(0);
  transition: transform 0.2s ease;
}
.nav a:hover::after, .nav a.active::after { transform: scaleX(1); }
.nav a:hover, .nav a.active { color: var(--white); }

/* Row 2: category nav strip -- deep forest ground so it reads as a
   distinct navigation band under the white brand/search row, not a plain
   continuation of it. */
.header-nav-strip {
  background: var(--green-dark);
  border-top: 1px solid rgba(255,255,255,0.08);
}
.header-nav-strip .container { display: flex; align-items: center; justify-content: space-between; height: var(--header-nav-h); gap: 20px; }

.header-cta { display: flex; align-items: center; gap: 6px; }

/* ---------- Customer account drawer ----------
   Mirrors admin-dashboard.html's #profileMenu structure/behaviour
   (js/admin.js: click toggles hidden + aria-expanded, outside-click and
   Escape close it) adapted to customer tokens/content. Only ever shown
   for a signed-in customer -- js/account-nav.js swaps #accountNavBtn
   between a plain "Sign In" link (signed out, unchanged behaviour) and
   this drawer's trigger (signed in), so a signed-out visitor never sees
   an empty/broken drawer. z-index above the sticky header (500) so it
   never renders underneath it. */
.account-nav-wrap { position: relative; display: flex; }
.account-drawer {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 230px;
  background: var(--white);
  color: var(--ink);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  box-shadow: 0 12px 28px rgba(0,0,0,0.14);
  padding: 6px;
  z-index: 520;
}
.account-drawer-head { padding: 10px 12px 8px; border-bottom: 1px solid var(--border); margin-bottom: 6px; }
.account-drawer-head strong { display: block; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-drawer-head span { display: block; font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-drawer-item {
  display: block; width: 100%; text-align: left;
  padding: 9px 12px; border-radius: var(--radius-sm);
  border: none; background: none;
  color: var(--ink); font-family: inherit;
  font-size: 13.5px; font-weight: 600;
  text-decoration: none; cursor: pointer;
}
.account-drawer-item:hover { background: var(--cream); }
.account-drawer-divider { height: 1px; background: var(--border); margin: 6px 4px; }
.account-drawer-item-danger { color: var(--rose); }
@media (max-width: 480px) {
  .account-drawer { width: 210px; right: -8px; }
}
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--green-dark);
  transition: background 0.2s ease, color 0.2s ease;
  position: relative;
}
.icon-btn:hover { background: var(--green-light); }
.icon-btn svg { width: 21px; height: 21px; }
.icon-btn-label { display: none; }
@media (min-width: 1200px) {
  .icon-btn { width: auto; height: auto; border-radius: 0; padding: 6px 4px; gap: 6px; flex-direction: column; }
  .icon-btn:hover { background: transparent; color: var(--rose); }
  .icon-btn-label { display: block; font-size: 10.5px; font-weight: 600; letter-spacing: 0.03em; }
}

.nav-toggle {
  display: none;
  width: 40px;
  height: 40px;
  border: none;
  background: transparent;
  border-radius: 10px;
  align-items: center;
  justify-content: center;
}
.nav-toggle svg { width: 22px; height: 22px; color: var(--green-dark); }

/* Row-2 category strip links */
.nav-strip-links { display: flex; align-items: center; gap: 26px; overflow-x: auto; scrollbar-width: none; }
.nav-strip-links::-webkit-scrollbar { display: none; }
.nav-strip-links a {
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  color: rgba(255,255,255,0.82);
  position: relative;
  padding: 4px 0;
}
.nav-strip-links a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 2px;
  background: var(--gold);
  transform: scaleX(0);
  transition: transform 0.2s ease;
}
.nav-strip-links a:hover::after, .nav-strip-links a.active::after { transform: scaleX(1); }
.nav-strip-links a:hover, .nav-strip-links a.active { color: var(--white); }
.nav-strip-cta {
  flex: none;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--gold);
  white-space: nowrap;
  letter-spacing: 0.01em;
}
.nav-strip-cta:hover { color: var(--white); }

/* Mobile-only search trigger in the header-cta row: expands the same
   #productSearchInput used by .header-search on desktop, so there is still
   exactly one search input/id on the page, just relocated into an
   expandable bar below the header row on narrow screens. */
.search-toggle { display: none; }
.mobile-search-bar { display: none; }
@media (max-width: 980px) {
  .search-toggle { display: inline-flex; }
  .mobile-search-bar {
    display: block;
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.22s ease;
    background: var(--white);
    border-top: 1px solid var(--border);
  }
  .mobile-search-bar.open { max-height: 72px; }
  .mobile-search-bar .container { padding-top: 10px; padding-bottom: 10px; }
  .mobile-search-bar .product-search { max-width: none; margin: 0; }
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  height: auto;
  min-height: 0;
  overflow: hidden;
  color: var(--white);
}
.hero-slide {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  opacity: 0;
  transition: opacity 1.2s ease;
}
.hero-slide.active { opacity: 1; }
.hero-slide::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Two-layer gradient (bottom wash + a left-side wash where the text
     sits) instead of one flat overlay -- keeps the photo's centre-right
     open and readable rather than uniformly darkened. */
  background:
    linear-gradient(180deg, rgba(18,40,27,0.30) 0%, rgba(18,40,27,0.18) 40%, rgba(18,40,27,0.72) 100%),
    linear-gradient(100deg, rgba(18,40,27,0.62) 0%, rgba(18,40,27,0.18) 62%, rgba(18,40,27,0) 100%);
}
.hero-content {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  max-width: 620px;
  padding-top: clamp(72px, 14vh, 140px);
  padding-bottom: clamp(72px, 14vh, 140px);
}
@media (max-width: 760px) { .hero { min-height: 62vh; } .hero-content { padding-top: 56px; padding-bottom: 64px; } }
.hero-content .section-tag { color: var(--gold); }
.hero-content h1 {
  font-family: var(--ff-heading);
  font-optical-sizing: auto;
  font-size: clamp(29px, 4.6vw, 54px);
  line-height: 1.14;
  margin: 8px 0 16px;
  font-weight: 600;
  max-width: 15ch;
  text-shadow: 0 4px 20px rgba(0,0,0,0.25);
}
.hero-content p { font-size: 16.5px; color: rgba(255,255,255,0.92); margin: 0 0 26px; max-width: 46ch; }
.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }

/* Compact hero variant for standalone pages (flowers/gifts/decoration):
   same photo-led composition, shorter minimum height than the homepage. */
.hero-compact .hero-content { padding-top: clamp(56px, 9vh, 88px); padding-bottom: clamp(56px, 9vh, 88px); }
.hero-compact { min-height: 0; }
@media (max-width: 760px) { .hero-compact { min-height: 46vh; } }

.hero-dots {
  position: absolute;
  z-index: 3;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 10px;
}
.hero-dots button {
  width: 10px; height: 10px;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.8);
  background: transparent;
  padding: 0;
}
.hero-dots button.active { background: var(--white); }

/* ---------- Trust / value proposition ----------
   Second pass (UI observations batch): the first non-overlapping 2x2 pass
   fixed the floating-card overlap bug, but real-user feedback was that
   the dense, horizontally-divided shared-card treatment still read as a
   lesser "compact card" next to .why-strip's stronger, more premium
   treatment further down this same page -- centered column, a larger
   circular icon with its own shadow (not a shared card + dividers),
   generous grid gap. Mirrors .why-item's structural approach (see that
   class below) while keeping this section's own rose-tinted icon accent
   and copy -- a deliberately distinct class (.trust-item-v2), not a
   reuse of .why-item, since the two sections intentionally keep
   different icon color treatments (this one rose, why-item white+shadow)
   as separate, non-duplicate visual moments on the same page. */
.trust {
  /* Fix (real-device feedback): --cream (the page's own default
     background, which this section sat on with no background of its
     own) and the new --flowers-bg tint on the section directly below
     are close enough in value that the two read as one seamless,
     undifferentiated block with no visible section break -- confirmed
     on a real device screenshot. A plain white background gives Trust
     its own clear identity again, distinct from both --cream (used
     elsewhere on the page) and --flowers-bg (Flowers section below). */
  background: var(--white);
  max-width: 1180px;
  margin: 0 auto;
  padding: var(--space-7) var(--space-4);
}
.trust-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 30px;
  max-width: none;
}
.trust-item-v2 { text-align: center; }
.trust-item-v2 .ic {
  width: 58px; height: 58px;
  border-radius: 50%;
  background: var(--rose-light);
  color: var(--rose);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 16px;
  box-shadow: var(--shadow-sm);
}
.trust-item-v2 .ic svg { width: 26px; height: 26px; }
.trust-item-v2 h4 { margin: 0 0 8px; font-family: var(--ff-body); font-weight: 600; font-size: 18px; color: var(--green-dark); }
.trust-item-v2 p { margin: 0; font-size: 13.5px; color: var(--muted); }

/* ---------- Categories ---------- */
.categories-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
}
@media (min-width: 981px) { .categories-grid { gap: var(--space-4); } }
/* Phase 6: the first category tile becomes a featured 2x2 block instead of
   every tile being an identical small square (VISUAL-GAP-AUDIT.md §4) --
   grid-auto-flow: dense re-packs the remaining tiles around it
   automatically, the same technique already proven on .decor-gallery
   above, so no manual row/column bookkeeping is needed as categories are
   added/removed in the Admin. Desktop only -- the existing 980px/520px
   breakpoints already collapse this to 2 columns, where every tile stays
   equal-sized (a 2x2 featured tile would consume the whole grid on a
   narrow screen). */
@media (min-width: 981px) {
  .categories-grid { grid-auto-flow: dense; }
  .categories-grid .cat-card:first-child {
    grid-column: span 2;
    grid-row: span 2;
  }
}
.cat-card {
  position: relative;
  border-radius: var(--radius-sm);
  overflow: hidden;
  aspect-ratio: 4/5;
  border: 1px solid var(--border);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.cat-card:hover { border-color: transparent; box-shadow: var(--shadow-md); }
.cat-card img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; }
.cat-card:hover img { transform: scale(1.05); }
.cat-card::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0) 34%, rgba(15,30,20,0.78) 100%);
}
.cat-card .label {
  position: absolute; left: 0; right: 0; bottom: 0;
  z-index: 2;
  padding: 22px 20px;
  color: var(--white);
}
.cat-card .label span {
  display: block; font-size: 12px; color: var(--gold); letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 4px;
}
.cat-card .label h3 { margin: 0; font-family: var(--ff-heading); font-weight: 600; font-size: 21px; line-height: 1.2; }
@media (min-width: 981px) { .categories-grid .cat-card:first-child .label h3 { font-size: 30px; } }
@media (max-width: 520px) {
  .cat-card { aspect-ratio: 1/1; }
  .cat-card .label { padding: 14px 13px; }
  .cat-card .label h3 { font-size: 16px; }
}

/* ---------- Premium Collections (editorial band) ---------- */
.collections-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
}
.collection-card {
  position: relative;
  border-radius: var(--radius-sm);
  overflow: hidden;
  aspect-ratio: 4/5;
}
.collection-card img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; }
.collection-card:hover img { transform: scale(1.05); }
.collection-card::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0) 40%, rgba(10,20,14,0.82) 100%);
}
.collection-label { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 20px 18px; color: var(--white); }
.collection-label span { display: block; font-size: 11px; color: var(--gold); letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 4px; }
.collection-label h3 { margin: 0; font-family: var(--ff-heading); font-weight: 600; font-size: 19px; }
@media (max-width: 980px) { .collections-grid { grid-template-columns: repeat(2, 1fr); } }

/* ---------- Filter chips ---------- */
.filters {
  display: flex;
  justify-content: flex-start;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: var(--space-6);
}
.chip {
  border: 1.5px solid var(--border);
  background: var(--white);
  padding: 10px 22px;
  border-radius: 100px;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  transition: all 0.2s ease;
}
.chip.active, .chip:hover { background: var(--green); border-color: var(--green); color: var(--white); }

/* ---------- Listing toolbar: product count / Filter button / Sort ---------- */
.listing-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-bottom: var(--space-5);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
}
.listing-toolbar-left { display: flex; align-items: center; gap: 16px; }
.listing-count { font-size: 13.5px; color: var(--muted); }
.filter-open-btn {
  display: none;
  align-items: center;
  gap: 8px;
  border: 1.5px solid var(--border);
  background: var(--white);
  padding: 9px 16px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
/* Product listing filters/sort stage: the homepage's per-section leaf-
   category listings use a compact TOOLBAR-ONLY layout (Filter button +
   Sort select, per the approved mockup) rather than flowers.html's
   persistent desktop sidebar column -- there is no second, always-open
   filter column to make the Filter button redundant on desktop here, so
   it must stay visible at every width. .filter-toolbar-standalone is
   opt-in (added only by js/category-hierarchy-nav.js's generated
   toolbar) so flowers.html's own #filterOpenBtn (inside its two-column
   .listing-layout) keeps its existing mobile-only behaviour unchanged. */
.filter-toolbar-standalone .filter-open-btn { display: inline-flex; }
/* Standalone drawer: on DESKTOP flowers.html's own .filter-sidebar rule
   (above) has no hidden/toggle state at all -- it relies on being
   permanently visible as a sticky grid column, which is exactly what
   this toolbar-only layout does NOT have. Made into a real open/close
   panel at every width: hidden by default, shown as a fixed overlay
   panel (reusing the same bottom-sheet transform/backdrop already
   defined for <=980px) once .open is added — mirrors this file's own
   existing mobile drawer behaviour, just no longer gated to a max-width. */
.filter-sidebar.filter-sidebar-standalone {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  top: auto;
  max-height: 84vh; /* dvh fallback -- see .cart-drawer's own comment on why (Safari's dynamic toolbar) */
  max-height: 84dvh;
  border-radius: 20px 20px 0 0;
  border: none;
  box-shadow: var(--shadow-lg);
  padding: 0 20px 20px;
  transform: translateY(100%);
  transition: transform 0.28s cubic-bezier(.4,0,.2,1);
  z-index: 800;
  overflow-y: auto;
}
.filter-sidebar-standalone.open { transform: translateY(0); }
.filter-sidebar-standalone .filter-sidebar-head { display: flex; }
.filter-sidebar-standalone .filter-sidebar-actions { display: flex; }
@media (min-width: 520px) {
  /* Wider viewports get a centred, max-width panel instead of a
     full-bleed sheet -- still an overlay (not inline content), just not
     stretched edge-to-edge once there is room to spare. */
  .filter-sidebar.filter-sidebar-standalone {
    left: 50%; right: auto; bottom: 24px;
    width: min(420px, calc(100vw - 48px));
    transform: translate(-50%, 24px);
    border-radius: var(--radius);
    border: 1px solid var(--border);
    opacity: 0;
    pointer-events: none;
    transition: transform 0.22s ease, opacity 0.22s ease;
  }
  .filter-sidebar-standalone.open {
    transform: translate(-50%, 0);
    opacity: 1;
    pointer-events: auto;
  }
}
.filter-backdrop-standalone {
  position: fixed; inset: 0; background: rgba(18,40,27,0.45);
  z-index: 790; opacity: 0; pointer-events: none; transition: opacity 0.25s ease;
}
.filter-backdrop-standalone.open { opacity: 1; pointer-events: auto; }
.filter-open-btn svg { width: 17px; height: 17px; }
.filter-open-btn:hover { border-color: var(--green); }
.filter-count-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 100px;
  background: var(--rose);
  color: var(--white);
  font-size: 11px;
  font-weight: 700;
}
.filter-count-badge[hidden] { display: none; }

.sort-row { display: flex; align-items: center; gap: 10px; flex-shrink: 0; min-width: 0; }
.sort-row label { font-size: 13.5px; color: var(--muted); font-weight: 500; flex: none; }
.sort-row select {
  border: 1.5px solid var(--border);
  background: var(--white);
  padding: 9px 16px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  min-width: 0;
}
.sort-row select:hover, .sort-row select:focus { border-color: var(--green); outline: none; }

/* Below ~460px the count + Filter button + Sort dropdown no longer fit on
   one row (min-content widths alone exceed the viewport) -- wrap onto a
   second line and let Sort's <select> shrink instead of forcing
   horizontal scroll. */
@media (max-width: 480px) {
  .listing-toolbar { flex-wrap: wrap; row-gap: 12px; }
  .sort-row { width: 100%; }
  .sort-row select { flex: 1 1 auto; padding-left: 10px; padding-right: 10px; }
}

/* ---------- Listing layout: filter sidebar + product grid ---------- */
.listing-layout { display: grid; grid-template-columns: 260px 1fr; gap: var(--space-5); align-items: start; }
/* The product grid's own base rule is a flat 4-column repeat, correct for
   a full-width section (homepage grids) but visibly cramped once the
   260px sidebar eats into the same row -- 3 columns breathes properly in
   that remaining width until there's enough room (~1300px+) for a real
   4th column beside the sidebar. */
@media (min-width: 981px) and (max-width: 1300px) {
  .listing-layout .product-grid { grid-template-columns: repeat(3, 1fr); }
}
.filter-sidebar {
  position: sticky;
  top: calc(var(--header-h) + var(--header-nav-h) + 20px);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
}
.filter-sidebar-head { display: none; }
.filter-group { padding: 16px 0; border-bottom: 1px solid var(--border); }
.filter-group:first-of-type { padding-top: 0; }
.filter-group:last-of-type { border-bottom: none; padding-bottom: 0; }
.filter-group h4 { margin: 0 0 12px; font-size: 12.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--green-dark); }
.filter-check {
  display: flex; align-items: center; gap: 9px;
  font-size: 14px; color: var(--ink);
  padding: 6px 0;
  cursor: pointer;
}
.filter-check input { width: 16px; height: 16px; accent-color: var(--green); flex: none; cursor: pointer; }
.filter-sidebar-actions { display: none; }
.filter-backdrop { display: none; }

@media (max-width: 980px) {
  .listing-layout { grid-template-columns: 1fr; }
  .filter-open-btn { display: inline-flex; }

  /* Bottom-sheet filter drawer */
  .filter-sidebar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    top: auto;
    max-height: 84vh; /* dvh fallback -- see .cart-drawer's own comment on why (Safari's dynamic toolbar) */
  max-height: 84dvh;
    border-radius: 20px 20px 0 0;
    border: none;
    box-shadow: var(--shadow-lg);
    padding: 0 20px 20px;
    transform: translateY(100%);
    transition: transform 0.28s cubic-bezier(.4,0,.2,1);
    z-index: 800;
    overflow-y: auto;
  }
  .filter-sidebar.open { transform: translateY(0); }
  .filter-sidebar-head {
    display: flex; align-items: center; justify-content: space-between;
    position: sticky; top: 0; background: var(--white);
    padding: 18px 0 14px; margin-bottom: 4px;
    border-bottom: 1px solid var(--border);
  }
  .filter-sidebar-head h3 { margin: 0; font-family: var(--ff-heading); font-size: 19px; color: var(--green-dark); }
  .filter-close-btn { width: 34px; height: 34px; border: none; background: var(--green-light); border-radius: 50%; display: flex; align-items: center; justify-content: center; }
  .filter-close-btn svg { width: 16px; height: 16px; color: var(--green-dark); }
  .filter-sidebar-actions { display: flex; gap: 10px; position: sticky; bottom: 0; background: var(--white); padding-top: 14px; margin-top: 4px; }
  .filter-sidebar-actions .btn { margin-top: 0; }
  .filter-backdrop {
    display: block;
    position: fixed; inset: 0; background: rgba(18,40,27,0.45);
    z-index: 790; opacity: 0; pointer-events: none; transition: opacity 0.25s ease;
  }
  .filter-backdrop.open { opacity: 1; pointer-events: auto; }
}

/* Horizontal chip scroller on narrow screens: 5 chips (Flowers page)
   don't comfortably fit at 390px, so they scroll instead of wrapping to
   several cramped rows. */
@media (max-width: 760px) {
  .filters {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    margin-left: calc(var(--space-4) * -1);
    margin-right: calc(var(--space-4) * -1);
    padding: 2px var(--space-4) 4px;
  }
  .filters::-webkit-scrollbar { display: none; }
  .chip { flex: none; }
}

/* ---------- Product search ---------- */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Now holds only #productSearchMessage -- the input itself lives in
   .header-search (row 1 of the header). Collapses to nothing when the
   message is hidden, so it no longer reserves vertical space on the page. */
.product-search-wrap:not(:has(#productSearchMessage:not([hidden]))) { padding: 0; }
.product-search-wrap { padding: 0; scroll-margin-top: calc(var(--header-h) + var(--header-nav-h) + 12px); }
.product-search-wrap:has(#productSearchMessage:not([hidden])) { padding: var(--space-4) 0 0; }
.product-search {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 480px;
  margin: 0 auto;
  background: var(--cream);
  border: 1.5px solid var(--border);
  border-radius: 10px;
  padding: 9px 16px;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.product-search:focus-within { background: var(--white); }
.product-search:focus-within { border-color: var(--rose); }
.product-search-icon { width: 18px; height: 18px; color: var(--muted); flex: none; }
.product-search input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: inherit;
  font-size: 14.5px;
  color: var(--ink);
}
.product-search input::placeholder { color: var(--muted); }
/* Hide the native browser search-input decorations so our own Clear button
   is the only clear affordance, consistent across browsers. */
.product-search input[type="search"]::-webkit-search-cancel-button,
.product-search input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }
.product-search-clear {
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: none;
  background: var(--border);
  color: var(--ink);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.product-search-clear:hover { background: var(--rose); color: var(--white); }
.product-search-clear[hidden] { display: none; }
.product-search-message {
  text-align: center;
  color: var(--muted);
  font-size: 14.5px;
  margin: 16px 0 0;
}

@media (max-width: 520px) {
  .product-search-wrap { padding: 0 0 26px; }
  .product-search { max-width: 100%; padding: 9px 14px; }
}

/* ---------- Product grid ---------- */
/* ---------- Product catalogue ----------
   One system, applied uniformly: fixed image ratio, identical spacing/
   typography scale on every card, wishlist as a corner overlay on the
   image (not a floating page-level control), price hierarchy handled by
   font-weight/color rather than a decorative badge shape. No card gets a
   special/asymmetric treatment -- consistency over a single "hero" card. */
.product-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4) var(--space-3);
  /* Fix: applying a Flowers-page filter (js/products.js's
     applyProductFilters()) hides most cards via display:none nearly
     instantly, collapsing this grid's height a lot with no transition.
     No JS anywhere in that path calls scrollIntoView()/focus()/changes
     location.hash -- confirmed by direct investigation -- so the
     reported "jump toward the footer" is the BROWSER's own default
     scroll-anchoring trying to compensate for that layout shift, not
     application code. Opting this grid out is the correct, minimal fix:
     it only disables the browser's compensating adjustment for this one
     frequently-resizing container, and has no effect on any other
     scrolling/anchor behavior on the page. */
  overflow-anchor: none;
}
.product-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
  display: flex;
  flex-direction: column;
}
.product-card:hover { box-shadow: var(--shadow-md); border-color: var(--border); }
.product-media { position: relative; aspect-ratio: 3/4; overflow: hidden; background: var(--green-light); }
.product-media a { display: block; width: 100%; height: 100%; }
.product-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; }
.product-card:hover .product-media img { transform: scale(1.04); }

.wishlist-btn {
  position: absolute;
  top: 10px; right: 10px;
  z-index: 2;
  width: 32px; height: 32px;
  border: none;
  border-radius: 50%;
  background: rgba(255,253,251,0.92);
  color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s ease, color 0.15s ease;
}
.wishlist-btn svg { width: 16px; height: 16px; }
.wishlist-btn:hover { background: var(--white); }
.wishlist-btn.active { color: var(--rose); }
.wishlist-btn.active svg { fill: var(--rose); }

.product-eyebrow {
  font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted);
  line-height: 1.3;
}
.product-body { padding: 14px 14px 16px; display: flex; flex-direction: column; gap: 5px; flex: 1; cursor: pointer; }
.product-body h3 { margin: 0; font-size: 14.5px; line-height: 1.35; font-family: var(--ff-body); font-weight: 600; color: var(--ink); }
.product-price { color: var(--ink); font-weight: 700; font-family: var(--ff-body); font-size: 15.5px; display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px; margin-top: 1px; }
.product-price small { color: var(--muted); font-weight: 400; font-size: 11.5px; }
.product-body .btn { margin-top: auto; cursor: pointer; }

/* ---------- Discount display ---------- */
.price-final { color: var(--ink); font-weight: 700; }
.price-original { color: var(--muted); font-weight: 400; font-size: 12.5px; text-decoration: line-through; }
.price-badge {
  background: var(--rose-light);
  color: var(--rose-dark);
  font-size: 10.5px;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 4px;
  white-space: nowrap;
}

/* Redesign: a real, pre-existing data gap (some live products have no
   published image) gets a deliberate botanical placeholder instead of a
   blank box -- no lightbox is wired for these (buildCardHtml() never
   emits .lightbox-trigger here), so tapping the image is intentionally a
   no-op rather than opening an empty lightbox. */
.product-media-empty {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background:
    repeating-linear-gradient(135deg,
      rgba(31,107,69,0.045) 0 10px,
      rgba(31,107,69,0)     10px 20px),
    var(--green-light);
  color: rgba(31,107,69,0.34);
}
.product-media-empty svg { width: 38%; max-width: 72px; height: auto; }
.product-media-empty-wrap { cursor: default; }

/* ---------- Out of stock ---------- */
.product-media-outofstock img { filter: grayscale(35%); opacity: 0.75; }
.product-outofstock-bar {
  position: absolute; left: auto; right: 10px; bottom: 10px; z-index: 2;
  background: rgba(38, 34, 29, 0.62);
  backdrop-filter: blur(2px);
  color: var(--white);
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 5px 10px; border-radius: 100px;
}
.pdetail-outofstock { color: var(--ink); }

/* ---------- Product detail view ---------- */
.pdetail-overlay {
  position: fixed; inset: 0; z-index: 900;
  background: rgba(15, 20, 17, 0.6);
  display: none;
  align-items: flex-end;
  justify-content: center;
}
.pdetail-overlay:not([hidden]) { display: flex; }
/* Applied to #cartOverlay only, only while Checkout is open on top of it
   (js/cart.js openCheckout()/closeCheckout()) -- visibility:hidden, not
   display:none/[hidden], so Cart's own scroll position/DOM/state are
   completely undisturbed and reappear instantly the moment Checkout
   closes. See openCheckout()'s own comment for why this exists: without
   it, two stacked bottom-anchored drawers left Cart's heading/close
   button visibly peeking out whenever Checkout's own content was shorter
   than Cart's, confirmed on a real iPhone recording. */
.cart-overlay-stacked-below { visibility: hidden; }
.pdetail {
  background: var(--white);
  width: 100%;
  max-width: 100%;
  max-height: 94vh; /* dvh fallback below -- see .cart-drawer's own comment on why */
  max-height: 94dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: 20px 20px 0 0;
  position: relative;
  box-shadow: var(--shadow-lg);
}
/* Pre-release UX pass (item 3/5): 40px was below the 44x44px touch-target
   guideline -- the exact same gap already found and fixed on the Admin
   side (css/admin.css's .admin-modal-close, .admin-btn-sm). This is the
   ONE shared base class every close button (product detail, cart,
   checkout, My Orders order detail) inherits from, so fixing it here
   covers all of them consistently rather than four separate overrides. */
.pdetail-close {
  position: absolute; top: 14px; right: 14px; z-index: 3;
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255,255,255,0.92);
  color: var(--ink);
  border: none;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-sm);
}
.pdetail-close svg { width: 18px; height: 18px; }
.pdetail-gallery { position: relative; }
.pdetail-image-link { display: block; }
.pdetail-image { width: 100%; max-height: 320px; object-fit: cover; background: var(--green-light); display: block; }

.pdetail-gallery-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 36px; height: 36px;
  border-radius: 50%;
  border: none;
  background: rgba(255,255,255,0.9);
  color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-sm);
}
.pdetail-gallery-arrow svg { width: 18px; height: 18px; }
.pdetail-gallery-arrow-prev { left: 10px; }
.pdetail-gallery-arrow-next { right: 10px; }
.pdetail-gallery-arrow:hover { background: var(--white); }

.pdetail-gallery-dots {
  position: absolute;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  gap: 6px;
  padding: 5px 8px;
  border-radius: 100px;
  background: rgba(15, 20, 17, 0.28);
}
.pdetail-gallery-dot {
  width: 6px; height: 6px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255,255,255,0.6);
}
.pdetail-gallery-dot-active { background: var(--white); width: 16px; border-radius: 100px; }

.pdetail-thumbs {
  display: flex;
  gap: 8px;
  padding: 10px 22px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.pdetail-thumb {
  flex: 0 0 auto;
  width: 56px; height: 56px;
  padding: 0;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 2px solid transparent;
  background: var(--green-light);
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.15s ease, border-color 0.15s ease;
}
.pdetail-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pdetail-thumb:hover { opacity: 1; }
.pdetail-thumb-active { border-color: var(--rose); opacity: 1; }
.pdetail-body { padding: 26px 22px 30px; }
.pdetail-category {
  display: inline-block;
  font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--rose); margin-bottom: 8px;
}
.pdetail-body h2 { font-family: var(--ff-body); font-weight: 600; font-size: 26px; margin: 0 0 12px; color: var(--green-dark); }
.pdetail-price { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; font-size: 20px; margin-bottom: 16px; }
.pdetail-price .price-final { font-size: 22px; }
.pdetail-price .price-original { font-size: 15px; }
.pdetail-description { color: var(--muted); font-size: 15px; line-height: 1.7; margin: 0 0 18px; overflow-wrap: break-word; }
.pdetail-availability { color: var(--green); font-weight: 600; font-size: 13.5px; margin: 0 0 20px; }

@media (min-width: 640px) {
  .pdetail-overlay { align-items: center; padding: 24px; }
  .pdetail { max-width: 640px; max-height: 88vh; max-height: 88dvh; border-radius: var(--radius); }
  .pdetail-image { max-height: 380px; }
}

/* Reachability fix, scoped to #productDetailOverlay -- same mechanism as
   .cart-drawer (Admin's own .admin-modal pattern: outer box stops
   scrolling, one designated child becomes the scroll region, close
   button is a pinned non-scrolling flex item). Originally mobile-only
   ("Desktop is untouched"), which left product detail as the one
   remaining customer-side modal where the close button could still
   scroll away on a long description -- made unconditional here for the
   same reason .cart-drawer and Admin's own modal were both generalized
   past their original mobile-only scope (customer UI architecture pass:
   one consistent modal behavior across every overlay, not a per-modal
   exception). */
#productDetailOverlay .pdetail {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
#productDetailOverlay .pdetail-close {
  position: static;
  align-self: flex-end;
  flex: none;
  margin: 10px 10px 0 0;
}
#productDetailBody {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  min-height: 0;
}

.section-more { text-align: center; margin-top: 44px; }

/* credential-band: removed in Phase 6 -- its "40+ years" statement now
   opens the About/story band's own copy (index.html), and its role as a
   floating stat moment is now the .trust floating card after the hero.
   Two separate small stat strips folded into those two, richer moments
   instead of remaining a third, thin, on-its-own dark band. */

/* UI observations batch: the homepage's own Flowers/Occasions sections
   (distinct from gifts.html's #gifts section just below, which keeps its
   own deliberate dark full-bleed "rhythm break" background unchanged --
   out of scope here) previously had no background of their own (Flowers)
   or a plain white one (Occasions), making the two sections look
   visually interchangeable. Approved subtle tones give each its own
   identity without competing with product-card imagery. */
#flowers-section, #flowersAll { background: var(--flowers-bg); }
#occasions-section { background: var(--occasions-bg); }

/* ---------- Occasions / gifts grid ----------
   Phase 6: #gifts (this section's id) gets a full-bleed dark background
   below -- the section rhythm break VISUAL-GAP-AUDIT.md §"Background/
   color problems" found missing (only flat white/cream swaps existed
   before). Just a background-color on the <section> itself -- the grid
   markup/JS hooks (occasion-card, data-wa-order) are untouched. */
#gifts { background: var(--green-dark); color: var(--white); }
/* Fix: .section-head h2's base rule sets color: var(--green-dark) (for
   the light-background sections everywhere else) -- on this section's own
   dark-green background that made the heading invisible. Same white used
   for every other dark-background heading on the page (e.g. .about-copy
   h2). */
#gifts .section-head h2 { color: var(--white); }
#gifts .section-head p { color: rgba(255,255,255,0.72); }
.occasion-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-3);
}
@media (max-width: 1120px) and (min-width: 761px) {
  .occasion-grid { grid-template-columns: repeat(3, 1fr); }
}
/* Homepage restructure: Shop by Flowers/Decoration only have 2 real cards
   (unlike Occasion's 5), so the shared 5-column default above left 3
   empty grid tracks on desktop -- looked unfinished. This modifier caps
   the row at 2 real-width columns instead of stretching 2 cards across 5
   reserved slots; Occasion's own .occasion-grid (no modifier class) is
   completely unaffected. */
.occasion-grid-compact {
  grid-template-columns: repeat(2, minmax(0, 280px));
}
@media (max-width: 760px) {
  .occasion-grid-compact { grid-template-columns: repeat(2, 1fr); }
}
.occasion-card {
  position: relative;
  border-radius: var(--radius-sm);
  overflow: hidden;
  aspect-ratio: 3/4;
  border: 1px solid var(--border);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.occasion-card:hover { border-color: transparent; box-shadow: var(--shadow-md); }
.occasion-card img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; }
.occasion-card:hover img { transform: scale(1.05); }
.occasion-card::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,0) 42%, rgba(15,30,20,0.82) 100%); }
.occasion-card .label {
  position: absolute; left: 16px; right: 16px; bottom: 16px; z-index: 2;
  color: var(--white); text-align: left; font-family: var(--ff-heading); font-weight: 600; font-size: 17px;
}

/* Shop by Occasion (0032_shop_by_occasion.sql): new, additive rules only --
   no existing .occasion-card/.occasion-grid rule above is modified. Marks
   whichever occasion card js/products.js last expanded, and styles the
   results panel it renders into (real product-grid cards, or a graceful
   empty-state message + WhatsApp fallback). */
.occasion-card[data-occasion-slug] { cursor: pointer; }
.occasion-card-active { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold); }

/* Stage 5: Category Hierarchy nav — cards reuse .occasion-card's exact
   visual language (border/aspect-ratio/hover/gradient/label) but render
   as <button> (no href, pure JS navigation) rather than <a>, so a plain
   button reset is needed on top. Crumb/back button are new, compact,
   single-line controls matching the existing .section-tag/.head-row-link
   type scale rather than introducing a new visual style. */
.category-hierarchy-card {
  display: block; width: 100%; padding: 0; margin: 0;
  background: none; font: inherit; text-align: left; cursor: pointer;
  appearance: none; -webkit-appearance: none;
}
/* Production audit M2 fix: immediate tap feedback (applied synchronously
   in js/category-hierarchy-nav.js's click handler, before the hashchange
   round-trip even starts) — reuses .occasion-card-active's exact gold
   ring language rather than inventing a second "selected" visual style.
   Never explicitly removed: the card carrying this class is always about
   to be replaced by the next render's fresh innerHTML anyway. */
.category-hierarchy-card-active { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold); }

/* Section-level fade for the category-cards <-> product-grid swap
   (js/category-hierarchy-nav.js beginNavigationTransition/
   endNavigationTransition). 180ms, plain opacity only — no transform/
   movement, per the "small, premium, no layout jumps" requirement.
   Only ever applied for a card-tap-driven navigation, never for a plain
   hashchange (browser Back/Forward/deep-link/reload keep resolving their
   content immediately, unfaded) — see bindCardClicks/renderWithSearchGuard
   for exactly where this class is added and removed. */
.hierarchy-nav-transitioning { opacity: 0; transition: opacity 0.18s ease; pointer-events: none; }
/* Production audit M1 fix: sticky while a drilled-down level (products or
   a grandchild category grid) is showing, so the way back stays reachable
   on a long product list without the customer scrolling all the way up.
   Only ever rendered non-empty when crumbTrail.length > 0 (see
   js/category-hierarchy-nav.js renderCrumb()) — i.e. exactly the drilled-
   down levels, never the root category-cards view, so this never pins
   anything on a short/empty page. top: var(--sticky-crumb-top) tucks it
   directly under the sticky site header at every width -- that variable
   is set on :root by js/category-hierarchy-nav.js's
   updateStickyCrumbOffset() from #siteHeader's OWN measured
   getBoundingClientRect().height (recomputed on resize), not a static
   --header-h/--header-nav-h calc: the real header is taller than those
   two vars alone suggest (a third, non-strip row exists in the header at
   desktop widths), so measuring is more robust than a third hand-tuned
   magic number that could silently drift again the next time the header
   markup changes. The calc() fallback only matters in the narrow window
   before that JS has run on first paint. z-index 450 — under the header
   (500) so it tucks beneath it, but above ordinary section content; far
   below the filter drawer/backdrop (790/900), mobile nav drawer (700) and
   lightbox/product-detail overlays (1000) so it never competes with any
   of those. An opaque background is required (not optional) once sticky —
   without one, product cards scrolling underneath would show through. */
.category-hierarchy-crumb {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 14px;
}
.category-hierarchy-crumb:not([hidden]) {
  position: sticky;
  top: var(--sticky-crumb-top, calc(var(--header-h) + var(--header-nav-h)));
  z-index: 450;
  background: var(--cream);
  padding: 10px 0;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.category-hierarchy-back {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 1px solid var(--border); border-radius: 999px;
  padding: 6px 14px; font-size: 14px; font-weight: 600; color: var(--ink);
  cursor: pointer; transition: border-color 0.2s ease, background 0.2s ease;
}
.category-hierarchy-back:hover { border-color: var(--gold); background: var(--cream); }
.category-hierarchy-crumb-trail { font-size: 14px; color: var(--muted); }
.occasion-results {
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: 1px solid rgba(255,255,255,0.16);
  /* Deliberate transition into results (brief: "should feel like a
     deliberate transition rather than a sudden injected grid"). */
  animation: occasionReveal 0.34s ease both;
}
@keyframes occasionReveal {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .occasion-results { animation: none; } }
.occasion-results-head h3 {
  color: var(--white);
  font-family: var(--ff-heading);
  font-size: 27px;
  margin: 0 0 var(--space-4);
}
/* Product cards rendered inside the dark #gifts band need a visible edge
   since .product-card's resting border color assumes a light background. */
#gifts .occasion-results .product-card { border-color: rgba(255,255,255,0.14); }

/* "More Gift Ideas" (gifts.html): the three generic WhatsApp-only cards
   reuse .occasion-card's visual language but sit in their own grid class
   so they read as visually distinct from the real, database-backed
   .occasion-grid above -- never presented as occasions. */
.idea-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); }
.idea-grid .occasion-card { aspect-ratio: 16/10; }
@media (max-width: 760px) { .idea-grid { grid-template-columns: 1fr 1fr; } }
.occasion-results-empty {
  color: rgba(255,255,255,0.75);
  margin: 0 0 16px;
  max-width: 520px;
}

/* ---------- Decoration services ----------
   Redesign: gallery tiles now share the exact same card language as
   .product-card/.collection-card elsewhere on the site -- fixed aspect
   ratio (was relying on each image's own intrinsic size, which is why a
   500x500 source photo next to two other-ratio photos produced visibly
   uneven, oddly-tall tiles), a plain border instead of a heavy box-shadow
   ring on hover, and the same subtle image-scale interaction used on
   every other card/tile on the site -- one interaction language, not a
   second one invented for this page. */
.decor-block {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: var(--space-6);
  align-items: center;
}
.decor-block + .decor-block { margin-top: var(--space-8); }
.decor-block.reverse .decor-copy { order: 2; }
.decor-block.reverse .decor-gallery { order: 1; }
.decor-copy .section-tag { display: block; }
.decor-copy h3 { font-family: var(--ff-heading); font-size: clamp(26px, 2.6vw, 36px); line-height: 1.18; margin: 8px 0 16px; color: var(--green-dark); }
.decor-copy p { color: var(--muted); font-size: 16px; line-height: 1.7; margin: 0 0 var(--space-4); max-width: 48ch; }
.decor-gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}
.decor-gallery a {
  display: block;
  aspect-ratio: 3/4;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--border);
  transition: border-color 0.2s ease;
}
.decor-gallery a:hover { border-color: transparent; box-shadow: var(--shadow-md); }
.decor-gallery img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; display: block; }
.decor-gallery a:hover img { transform: scale(1.05); }
@media (max-width: 620px) { .decor-gallery { grid-template-columns: repeat(2, 1fr); } .decor-gallery a:first-child { grid-column: span 2; } }

/* ---------- About / editorial story band ----------
   Phase 6: full-bleed background-photo treatment (was a centered 2-column
   photo+text grid using images/about/owners.jpg only) -- puts
   images/hero/hero-02.jpg (the atmospheric florist-at-work shot, unused
   anywhere in the page flow before this) to work as real visual
   storytelling, per VISUAL-DESIGN-PROPOSAL.md. #about keeps its id
   (js/products.js anchors dynamically-created category sections relative
   to it) and .about-person's real owner-name content is unchanged, just
   restyled. */
#about {
  position: relative;
  min-height: 620px;
  display: flex;
  align-items: center;
  background: url('../images/hero/hero-02.jpg') center/cover no-repeat;
  color: var(--white);
  padding: var(--space-8) 0;
}
#about::before {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(100deg, rgba(18,40,27,.94) 0%, rgba(18,40,27,.8) 38%, rgba(18,40,27,.2) 72%, rgba(18,40,27,0) 100%);
}
.about-wrap { position: relative; z-index: 2; display: grid; grid-template-columns: minmax(240px, 340px) 1fr; gap: var(--space-6); align-items: center; }
.about-owners { display: flex; flex-direction: column; gap: 18px; }
.owner-card { display: flex; align-items: center; gap: 14px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.18); border-radius: var(--radius-sm); padding: 14px 16px; backdrop-filter: blur(2px); }
.owner-photo { width: 64px; height: 64px; border-radius: 18px; overflow: hidden; flex: none; border: 2px solid rgba(255,255,255,0.55); }
.owner-photo img { width: 100%; height: 100%; object-fit: cover; }
.owner-info { display: flex; flex-direction: column; min-width: 0; }
.owner-name { display: block; font-family: var(--ff-heading); font-weight: 600; font-size: 16px; color: var(--white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.owner-phone { display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; font-size: 13px; color: rgba(255,255,255,0.72); white-space: nowrap; }
.owner-phone svg { width: 14px; height: 14px; flex: none; }
.owner-phone:hover { color: var(--gold); }
.about-copy { max-width: 560px; }
.about-copy .section-tag { display: block; color: var(--gold); }
.about-copy h2 { font-family: var(--ff-heading); font-weight: 600; font-size: clamp(28px,3vw,40px); margin: 6px 0 18px; color: var(--white); line-height: 1.15; }
.about-copy p { color: rgba(255,255,255,0.82); margin: 0 0 16px; line-height: 1.7; }
.about-list { display: flex; flex-wrap: wrap; gap: 10px 28px; margin: 24px 0 0; }
.about-list li { display: flex; align-items: center; gap: 9px; font-size: 14px; color: var(--white); }
.about-list li svg { width: 17px; height: 17px; color: var(--gold); flex: none; }
@media (max-width: 900px) {
  .about-wrap { grid-template-columns: 1fr; }
  .about-owners { flex-direction: row; flex-wrap: wrap; }
  .owner-card { flex: 1 1 240px; }
}
@media (max-width: 480px) {
  /* Owner cards previously stretched to the full container width once
     stacked (flex: 1 1 240px with no max-width, in a column flex parent,
     grows to fill the cross-axis) -- reads as a huge, sparse panel rather
     than a compact contact card. Capped to a comfortable profile-card
     width and centered instead; padding/photo/font-size trimmed slightly
     so the card reads as a compact unit, not shrunk so far the phone
     number risks wrapping again. */
  .about-owners { flex-direction: column; align-items: center; }
  .owner-card { flex: 0 1 auto; width: 100%; max-width: 300px; padding: 12px 14px; gap: 12px; }
  .owner-photo { width: 52px; height: 52px; border-radius: 14px; }
  .owner-name { font-size: 15px; }
  .owner-phone { font-size: 12.5px; }
}

/* ---------- Gallery ----------
   Was a CSS multi-column layout (fills column 1 completely before column
   2), which produced visibly uneven column heights whenever the item
   count didn't divide evenly -- the reported lopsided-columns bug (also
   seen on the homepage Cakes showcase, which reuses this class). A grid
   with a fixed aspect-ratio per tile means every row is uniform height by
   construction, and object-fit:cover still guarantees no distortion. */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 18px;
}
.gallery-grid a {
  display: block;
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  position: relative;
  aspect-ratio: 4/5;
}
.gallery-grid img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s ease; }
.gallery-grid a:hover img { transform: scale(1.06); }
/* Editorial accent: first tile reads as a wide feature on desktop. */
@media (min-width: 981px) {
  .gallery-grid a:first-child { grid-column: span 2; aspect-ratio: 8/5; }
}

.lightbox {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(15, 20, 17, 0.92);
  display: none;
  align-items: center;
  justify-content: center;
  padding: 40px;
}
.lightbox.open { display: flex; }
.lightbox img { max-width: 90vw; max-height: 86vh; border-radius: 10px; box-shadow: var(--shadow-lg); }
.lightbox-close {
  position: absolute; top: 24px; right: 28px;
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255,255,255,0.12); color: var(--white); border: none;
  display: flex; align-items: center; justify-content: center;
}
.lightbox-close svg { width: 22px; height: 22px; }

/* ---------- Why choose us ----------
   Phase 6: lightened to --cream-deep (was the same --green-dark as the
   About band directly above it -- two consecutive dark sections read as
   one long stretch rather than the section-rhythm alternation the rest of
   the page now has). */
.why-strip {
  background: var(--cream-deep);
  color: var(--ink);
}
.why-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 30px;
}
.why-item { text-align: center; }
.why-item .ic {
  width: 58px; height: 58px; border-radius: 50%;
  background: var(--white);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 16px;
  color: var(--rose);
  box-shadow: var(--shadow-sm);
}
.why-item .ic svg { width: 26px; height: 26px; }
.why-item h4 { margin: 0 0 8px; font-family: var(--ff-body); font-weight: 600; font-size: 18px; color: var(--green-dark); }
.why-item p { margin: 0; font-size: 13.5px; color: var(--muted); }

.section-more { margin-top: var(--space-5); }

/* ---------- WhatsApp CTA band (homepage, between Gallery and Contact) ---------- */
.wa-band { background: var(--green-dark); color: var(--white); text-align: center; }
.wa-band .section-tag { color: var(--gold); }
.wa-band h2 {
  font-family: var(--ff-heading); font-weight: 600;
  font-size: clamp(28px, 3vw, 42px); line-height: 1.18;
  margin: 8px auto 14px; max-width: 18ch;
}
.wa-band p { color: rgba(255,255,255,0.76); max-width: 52ch; margin: 0 auto var(--space-5); }
.wa-band-actions { display: flex; gap: 16px; justify-content: center; align-items: center; flex-wrap: wrap; }

/* ---------- Contact ---------- */
.rating-badge-standalone {
  display: flex;
  width: fit-content;
  margin: 0 auto 40px;
  background: var(--rose-light);
  color: var(--rose-dark);
}
.rating-badge-standalone:hover { background: var(--rose-light); color: var(--rose-dark); }

.contact-tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  margin-bottom: 40px;
}
.contact-tile {
  background: var(--white);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 26px 20px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
a.contact-tile:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.contact-tile .ic {
  width: 46px; height: 46px; border-radius: 50%;
  background: var(--rose-light);
  color: var(--rose);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 10px;
}
.contact-tile .ic svg { width: 20px; height: 20px; }
.contact-tile .label {
  font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}
/* word-break: break-word let the browser split anywhere, including
   mid-word ("pushpmilan@g" / "mail.com") -- confirmed on a real iPhone
   recording. overflow-wrap: break-word only forces a break when a word
   truly can't fit at all; combined with word-break: normal (overriding
   any inherited break-word) the browser prefers wrapping at the existing
   "@"/"." boundaries first, so the email now wraps as a whole word per
   line instead of splitting a word in half. */
.contact-tile .value { font-size: 14.5px; font-weight: 600; color: var(--ink); word-break: normal; overflow-wrap: break-word; }

.contact-wrap {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: 40px;
}
.find-us-card > p { font-size: 15px; line-height: 1.6; color: rgba(255,255,255,0.9); }
.find-us-owners { font-size: 13.5px; color: rgba(255,255,255,0.65); margin: -8px 0 22px; }
.contact-card {
  background: var(--green-dark);
  color: var(--white);
  border-radius: var(--radius);
  padding: 40px 34px;
}
.contact-card h3 { font-family: var(--ff-body); font-weight: 600; font-size: 24px; margin: 0 0 10px; }
.contact-card > p { color: rgba(255,255,255,0.75); margin: 0 0 18px; font-size: 14.5px; }
.rating-badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: rgba(255,255,255,0.1);
  color: var(--gold);
  font-size: 13px;
  font-weight: 600;
  padding: 7px 14px;
  border-radius: 100px;
  margin-bottom: 26px;
}
.rating-badge svg { width: 15px; height: 15px; }
.rating-badge:hover { background: rgba(255,255,255,0.16); color: var(--gold); }
.contact-row { display: flex; gap: 14px; margin-bottom: 22px; align-items: flex-start; }
.contact-row .ic {
  flex: none; width: 42px; height: 42px; border-radius: 50%;
  background: rgba(255,255,255,0.1);
  display: flex; align-items: center; justify-content: center;
  color: var(--gold);
}
.contact-row .ic svg { width: 19px; height: 19px; }
.contact-row strong { display: block; font-size: 14px; margin-bottom: 3px; }
.contact-row span, .contact-row a { color: rgba(255,255,255,0.82); font-size: 14px; }
.contact-row a:hover { color: var(--gold); }
.contact-note {
  margin-top: 26px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.14);
  font-size: 12.5px; color: rgba(255,255,255,0.6);
}
.contact-card .btn-outline { margin-top: 4px; }
.map-embed {
  margin-top: 22px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid rgba(255,255,255,0.14);
  aspect-ratio: 16/10;
}
.map-embed iframe { width: 100%; height: 100%; border: 0; display: block; filter: grayscale(15%); }

.contact-form {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 40px 36px;
}
.contact-form h3 { font-family: var(--ff-body); font-weight: 600; font-size: 24px; margin: 0 0 8px; color: var(--green-dark); }
.contact-form > p { color: var(--muted); margin: 0 0 26px; font-size: 14.5px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.field { margin-bottom: 18px; }
.field label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 7px; color: var(--ink); }
.field input, .field select, .field textarea {
  width: 100%;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  font-family: inherit;
  font-size: 14.5px;
  background: var(--paper);
  transition: border-color 0.2s ease;
}
/* iOS Safari's native input[type="date"] has its own intrinsic internal
   layout that renders TALLER than a plain text input at the identical
   padding -- confirmed on a real device: "Preferred Delivery Date" sat
   visibly taller than the PIN Code field directly above it, both using
   the exact same .field input padding. line-height doesn't affect the
   date-field's own internal chrome the way it does normal text, so an
   explicit height (not just padding) is what actually keeps every .field
   input the same box height regardless of type -- textarea/select are
   excluded (a textarea's height is intentionally its own multi-line
   min-height; select already gets its own correct native height). */
.field input:not([type="checkbox"]) { height: 46px; }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--rose); }
/* iOS Safari's native <select> reserves its own dropdown-arrow glyph
   inside the box's own padding box, not as a separate overlay -- with the
   same symmetric padding as a text input, a long selected value (e.g. the
   "Deliver To" saved-address select) can visually run right up against
   that arrow before iOS's own ellipsis kicks in. Extra right padding only
   (native appearance/arrow itself untouched -- no -webkit-appearance
   override) gives the arrow its own clear space regardless of value
   length. text-overflow only matters on engines that don't already
   ellipsis the closed select natively; harmless where iOS does it itself. */
.field select { padding-right: 34px; text-overflow: ellipsis; }
/* iOS Safari centers a native <input type="date">'s selected value by
   default (its own internal date-field rendering, not something this
   stylesheet ever set) -- confirmed on a real iPhone recording, where
   "Preferred Delivery Date" reads centered while every sibling .field
   input is left-aligned. text-align on the <input> itself (the first
   attempt here) does NOT work on iOS Safari -- re-tested on a second real
   iPhone recording, still centered -- because the visible date text
   lives inside Safari's own ::-webkit-date-and-time-value pseudo-element,
   not the input's own text content; only styling that pseudo-element
   directly actually moves it. Desktop Chromium was never a reliable way
   to verify this specific fix (it doesn't expose the same pseudo-element
   the same way), which is why the first attempt looked fine locally but
   didn't hold on the real device. Native picker UI, min-date, and
   validation are all untouched -- this only repositions the already-
   rendered value text within the box. */
.field input[type="date"] { text-align: left; }
.field input[type="date"]::-webkit-date-and-time-value { text-align: left; }
.field textarea { resize: vertical; min-height: 100px; }
/* Batch 8: contextual, field-level validation message -- matches the
   pattern already established in Admin (css/admin.css's
   .admin-field-error/.admin-field-invalid), same error color as this
   file's own .checkout-notice-error (#7a2020) for consistency. */
.checkout-field-error { color: #7a2020; font-size: 12.5px; margin: 6px 0 0; }
.field-invalid { border-color: #7a2020 !important; box-shadow: 0 0 0 3px rgba(122, 32, 32, 0.12); }
.form-note { font-size: 12.5px; color: var(--muted); margin-top: 10px; text-align: center; }
.hp-field { position: absolute; left: -9999px; }

/* ---------- Footer (Phase 6) ----------
   Was a flat 4-column link list on a plain dark block
   (VISUAL-GAP-AUDIT.md §6/"Footer problems"). Same .site-footer class
   (js/script.js does document.querySelector(".site-footer") for the
   floating-controls fade -- untouched) and same .footer-grid/.footer-col
   link structure, now a 5th "Same-Day Delivery" CTA card breaks the flat
   list, plus a faint brand-mark watermark. */
.site-footer { background: var(--green-dark); color: rgba(255,255,255,0.78); position: relative; overflow: hidden; }
.footer-mark {
  position: absolute; right: -50px; top: -30px;
  width: 300px; height: 300px;
  color: var(--white);
  opacity: 0.04;
  pointer-events: none;
}
/* A3 fix (Phase 7): this element also carries the .container class (see
   index.html), which supplies the intended horizontal gutter via
   `padding: 0 var(--space-4)`. The 3-value `padding: 80px 0 48px`
   shorthand previously used here set left/right to 0 as part of the same
   declaration -- same specificity as .container's rule, later in source
   order, so it silently zeroed the container's horizontal padding at
   EVERY viewport (masked at desktop widths by the max-width:1320px
   auto-margin, which happens to leave outer whitespace anyway; fully
   exposed at mobile widths where the whole footer sits flush against the
   viewport edges). padding-top/padding-bottom longhands only touch the
   vertical rhythm this rule actually owns, leaving .container's
   horizontal padding intact. */
.footer-top { padding-top: var(--space-8); padding-bottom: var(--space-6); position: relative; }
.footer-grid {
  display: grid;
  grid-template-columns: 1.5fr 0.85fr 0.85fr 0.85fr;
  gap: var(--space-5) var(--space-4);
}
/* Footer wordmark: same full-color logo asset/treatment as the header
   (previously shown as a reversed white silhouette instead, so it no
   longer matched the header) -- the logo's dark-green text is otherwise
   unreadable directly against this dark-green footer, so a small, tightly-
   fitted ivory plate sized just to the logo (not a full-width box) keeps
   it legible while still reading as an intentional badge, not a sticker. */
.footer-logo-plate {
  display: inline-flex;
  background: var(--cream);
  border-radius: var(--radius-sm);
  padding: 10px 16px;
}
.footer-brand .brand-logo-img {
  height: 52px; width: auto;
}
.footer-brand p { font-size: 14px; color: rgba(255,255,255,0.6); margin: var(--space-3) 0 var(--space-4); max-width: 300px; line-height: 1.7; }
.social-row { display: flex; gap: 10px; }
.social-row a {
  width: 38px; height: 38px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.25);
  display: flex; align-items: center; justify-content: center;
}
.social-row a:hover { background: var(--rose); border-color: var(--rose); }
.social-row svg { width: 16px; height: 16px; }
.footer-col h4 {
  color: var(--gold);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 0 0 var(--space-3);
  font-family: var(--ff-body);
  font-weight: 600;
}
.footer-col li { margin-bottom: 10px; font-size: 14px; }
.footer-col a { color: rgba(255,255,255,0.74); }
.footer-col a:hover { color: var(--white); }
/* Customer UI architecture pass: padding tightened from
   (--space-4 --space-5) = 24px/32px to a flat --space-3 (16px) -- this
   promo previously "consumed a disproportionate amount of the page"
   (confirmed at 390px: 211px tall for a two-line message + one button).
   Compact CTA card, not a hero moment -- readability/touch target
   (44px button height) preserved, only the surrounding whitespace cut. */
.footer-newsletter {
  grid-column: 1 / -1;
  background: rgba(255,255,255,0.07);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  margin-top: var(--space-3);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.footer-newsletter > div { flex: 1 1 320px; }
.footer-newsletter h4 {
  color: var(--white);
  font-family: var(--ff-heading);
  font-weight: 600;
  font-size: 17px;
  margin: 0 0 8px;
  text-transform: none;
  letter-spacing: 0;
}
/* margin-bottom: 0 -- the flex container's own gap (--space-3) already
   separates this <div> from the button below it (on mobile, where they
   stack); this paragraph's own bottom margin was doubling that space. */
.footer-newsletter p { font-size: 13px; color: rgba(255,255,255,0.62); margin: 0; line-height: 1.6; }
.footer-newsletter .btn { padding: 11px 18px; font-size: 13px; background: var(--gold); color: var(--green-dark); box-shadow: none; }
.footer-newsletter .btn:hover { background: #967a44; color: var(--white); }
/* .footer-delivery removed -- index.html now uses .footer-newsletter,
   the same Same-Day Delivery promo component every other customer page
   already used, consolidating what was two divergent implementations of
   identical content into one reused component. */
.footer-bottom {
  position: relative;
  border-top: 1px solid rgba(255,255,255,0.14);
  /* A3 fix (Phase 7): same .container-padding-zeroing bug as .footer-top
     above (2-value `padding: 22px 0` shorthand also sets left/right: 0).
     Longhand keeps .container's horizontal padding intact. */
  padding-top: 22px;
  padding-bottom: 22px;
  font-size: 13px;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  color: rgba(255,255,255,0.55);
}
.footer-bottom a:hover { color: var(--gold); }

/* ---------- Floating buttons ----------
   Phase 5 §8: both fabs share one positioning formula (--fab-offset/
   --fab-gap/--fab-size-primary/--fab-size-secondary, defined in :root by
   Agent B) instead of hardcoded right/bottom values, with
   env(safe-area-inset-*) folded into the calc() so they never sit under
   an iPhone home-indicator/rounded-corner safe area. Requires
   viewport-fit=cover on index.html's viewport meta tag (added alongside
   this) -- without it env(safe-area-inset-*) always resolves to 0 and
   this is a no-op, same as #checkoutFooter's existing safe-area rule. */
.fab-wa {
  position: fixed;
  right: calc(var(--fab-offset) + env(safe-area-inset-right, 0px));
  bottom: calc(var(--fab-offset) + env(safe-area-inset-bottom, 0px));
  z-index: 400;
  width: var(--fab-size-primary); height: var(--fab-size-primary);
  border-radius: 50%;
  background: #25D366;
  display: flex; align-items: center; justify-content: center;
  color: var(--white);
  box-shadow: 0 10px 24px rgba(37, 211, 102, 0.45);
  transition: transform 0.2s ease, opacity 0.25s ease;
}
.fab-wa:hover { transform: scale(1.08); }
.fab-wa svg { width: 28px; height: 28px; }
/* Behavioural requirement (§8): fade/hide once .site-footer enters view
   (js/script.js IntersectionObserver, mirrors the .fab-top.show pattern
   below rather than inventing a new one). */
.fab-wa.fab-hidden { opacity: 0; pointer-events: none; }

.fab-top {
  position: fixed;
  right: calc(var(--fab-offset) + env(safe-area-inset-right, 0px));
  bottom: calc(var(--fab-offset) + var(--fab-size-primary) + var(--fab-gap) + env(safe-area-inset-bottom, 0px));
  z-index: 400;
  width: var(--fab-size-secondary); height: var(--fab-size-secondary);
  border-radius: 50%;
  background: var(--white);
  border: 1.5px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  color: var(--green-dark);
  box-shadow: var(--shadow-sm);
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition: all 0.25s ease;
}
.fab-top.show { opacity: 1; pointer-events: auto; transform: translateY(0); }
/* Source-order matters: this must win over .fab-top.show (same
   specificity) when the footer is in view AND the user has scrolled past
   the show threshold, so the footer-visibility fade always takes
   priority. */
.fab-top.fab-hidden { opacity: 0; pointer-events: none; }
.fab-top svg { width: 20px; height: 20px; }

/* Systemic fix for the fab-overlaps-content class of bug, replacing the
   previous per-instance approach (each affected element -- .trust-item,
   .find-us-card -- got its own bespoke, hand-measured padding-bottom
   value found only after a real device recording caught the specific
   overlap). Opt any scrollable content container that ends near the
   bottom of the viewport into this ONE utility instead: reserves the
   fab's actual footprint (offset + size + a little clearance) so nothing
   inside can ever render under its fixed corner, and it stays correct
   automatically if --fab-offset/--fab-size-primary ever change, since it
   shares those tokens rather than hardcoding a pixel value. Hidden pages
   (this is customer-only chrome) or pages without the WhatsApp fab don't
   need this class at all -- it's opt-in, not global. */
.fab-safe-bottom { padding-bottom: calc(var(--fab-offset) + var(--fab-size-primary) + 20px + env(safe-area-inset-bottom, 0px)); }

/* ---------- PWA install banner (Batch 2 / 2.1) ----------
   Compact, dismissible bottom card -- intentionally NOT a circular fab
   (would collide with .fab-wa/.fab-top) and NOT a full-width top banner
   (blocks header/search on mobile). Sits above the fabs' column using the
   same --fab-offset/--fab-size-primary/--fab-gap tokens so spacing stays
   consistent with the rest of the fixed-position UI.

   Batch 2.1: .is-fallback is a deliberately distinct visual treatment
   (muted border/background, no filled action button) so the "Chrome
   hasn't offered install yet" message can never be mistaken for the real
   install CTA below. */
.pwa-install-banner {
  position: fixed;
  left: 14px;
  right: 14px;
  bottom: calc(var(--fab-offset) + var(--fab-size-primary) + var(--fab-gap) + env(safe-area-inset-bottom, 0px));
  z-index: 600;
  max-width: 420px;
  margin: 0 auto;
  background: var(--white);
  border: 1.5px solid var(--green-dark);
  border-radius: 14px;
  box-shadow: var(--shadow-md, 0 10px 24px rgba(0,0,0,0.14));
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.pwa-install-banner.is-fallback {
  border: 1.5px dashed var(--border);
  box-shadow: var(--shadow-sm);
  background: #faf6ee;
}
.pwa-install-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.pwa-install-heading {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--ink, #1f2a22);
}
.is-fallback .pwa-install-heading {
  color: rgba(31,42,34,0.75);
  font-weight: 600;
}
.pwa-install-text {
  font-size: 12.5px;
  line-height: 1.4;
  color: rgba(31,42,34,0.8);
}
.pwa-install-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.pwa-install-btn {
  background: var(--green-dark);
  color: var(--white);
  border: none;
  border-radius: 999px;
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.pwa-install-btn:disabled { opacity: 0.6; cursor: default; }
.pwa-install-close {
  background: transparent;
  border: none;
  color: rgba(31,42,34,0.5);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  padding: 4px;
}
@media (max-width: 480px) {
  .pwa-install-banner { left: 10px; right: 10px; padding: 10px 12px; }
  .pwa-install-text { font-size: 12px; }
}

/* Grouped nav (SHOP / CELEBRATE / DISCOVER): one markup, two behaviours.
   Desktop: groups dissolve into a single flat row via display:contents, so
   .nav's existing flex layout is unaffected -- zero desktop regression.
   Mobile: groups become stacked, labeled sections in the slide-in panel. */
.nav-group { display: contents; }
.nav-group-title { display: none; }
.nav-wa-cta { display: none; }

/* ---------- Responsive ---------- */
@media (max-width: 1120px) {
  /* Below this width the row-2 category strip disappears entirely -- its
     links move into the slide-in drawer instead, so there is exactly one
     navigation surface on mobile, not two. */
  .header-nav-strip { display: none; }
  .nav { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: var(--paper);
    flex-direction: column; align-items: stretch; gap: 0; padding: 8px 20px 32px; overflow-y: auto;
    transform: translateX(-100%); transition: transform 0.28s cubic-bezier(.4,0,.2,1); z-index: 700; }
  .nav.open { transform: translateX(0); }
  /* Applied only for the instant before a genuine cross-page drawer link
     navigates away (js/script.js) -- removes the slide-closed transition
     entirely so the drawer disappears instantly instead of animating out
     while the browser is simultaneously unloading the page, which was
     reading as menu items fading/disappearing on a real device. */
  .nav.nav-no-transition { transition: none; }
  .nav-drawer-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 16px 6px 20px; margin-bottom: 4px; border-bottom: 1px solid var(--border);
  }
  .nav-drawer-head .brand-logo-img { height: 32px; }
  .nav-drawer-close {
    width: 38px; height: 38px; border: none; background: var(--green-light);
    border-radius: 50%; display: flex; align-items: center; justify-content: center;
  }
  .nav-drawer-close svg { width: 18px; height: 18px; color: var(--green-dark); }
  .nav > a { color: var(--ink); }
  .nav-group { display: block; margin-bottom: var(--space-4); }
  .nav-group a { color: var(--ink); }
  /* #navDrawerSignOut (js/account-nav.js) is a real <button>, not an <a>
     -- it calls signOut() directly rather than navigating, so it can't be
     a link. Shares the exact same visual recipe as every other drawer
     item so it reads as one consistent list, not a visually distinct
     "different kind of control" -- the earlier My Orders/Account &
     Addresses tab fix already established that Sign Out should be
     unambiguous on its own merits (a full-width standalone button there);
     here in the drawer it's simply the last item in the Account group,
     styled identically to its siblings. */
  .nav-group button { color: var(--rose-dark); background: none; border: none; text-align: left; cursor: pointer; font-family: inherit; }
  /* The drawer sits on a light (--paper) background, unlike the dark
     desktop header strip this .active state was designed for -- without
     this override the shared ".nav a.active { color: var(--white) }" rule
     (used to make the current link stand out on the dark strip) renders
     the current section's label as invisible white-on-cream text here. */
  .nav > a.active, .nav-group a.active { color: var(--muted); }
  .nav-group-title {
    display: block;
    font-family: var(--ff-body);
    font-size: 11.5px; font-weight: 600;
    letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--gold);
    padding: 18px 6px 6px;
  }
  .nav a, .nav-group button { display: block; width: 100%; padding: 13px 6px; font-size: 16px; white-space: normal; }
  /* Same fix already established elsewhere in this file (e.g.
     .my-orders-auth-signout[hidden]): ".nav a"/".nav-group button"'s own
     "display: block" (needed for the shared full-width row look) has
     higher cascade priority than the browser's built-in [hidden]
     UA-stylesheet rule, so #navDrawerMyOrders/#navDrawerAccountAddresses/
     #navDrawerSignOut all rendered visible despite their `hidden`
     attribute being correctly set by js/account-nav.js -- confirmed live,
     all four Account-group items showed simultaneously for a signed-out
     visitor. Restores the native hidden behavior for just these toggled
     elements without touching the shared "display: block" rule itself. */
  .nav-group a[hidden], .nav-group button[hidden] { display: none; }
  .nav-group a + a, .nav-group a + button, .nav-group button + a { border-top: 1px solid var(--border); }
  .nav a.nav-wa-cta {
    display: inline-flex; align-items: center; justify-content: center;
    width: 100%; margin-top: var(--space-3); padding: 15px 24px; font-size: 15px;
  }
  /* Section 4 (post-production UX batch): js/account-nav.js toggles this
     CTA's [hidden] attribute for a signed-in customer -- same override
     needed as .nav-group a[hidden] above, since the unconditional
     "display: inline-flex" immediately above this would otherwise beat
     the browser's own [hidden] default. */
  .nav a.nav-wa-cta[hidden] { display: none; }
  .nav-toggle { display: flex; }
  .nav-open-overlay {
    position: fixed; inset: 0; background: rgba(18,40,27,0.45); z-index: 650;
    opacity: 0; pointer-events: none; transition: opacity 0.25s ease;
  }
  .nav-open-overlay.open { opacity: 1; pointer-events: auto; }
}
@media (min-width: 1121px) {
  .nav-drawer-head, .nav-open-overlay { display: none; }
  /* .nav/#siteNav is drawer-only markup (its `.nav a` text color is
     near-white, meant to sit on the drawer's dark mobile background) --
     .header-nav-strip is the real, separately-styled desktop nav. .nav was
     never actually given a desktop display:none, so it silently rendered
     inline here all along with invisible (near-white-on-white) text; the
     new Account-group Sign Out <button> was simply the first unstyled,
     default-black-text element in it to ever become visible there. */
  .nav { display: none; }
}

@media (max-width: 980px) {
  .categories-grid, .product-grid, .occasion-grid, .contact-tiles { grid-template-columns: repeat(2, 1fr); }
  .trust-grid { grid-template-columns: repeat(2, 1fr); gap: 24px; }
  .why-grid { grid-template-columns: repeat(2, 1fr); }
  .about-wrap, .contact-wrap, .decor-block, .decor-block.reverse { grid-template-columns: 1fr; }
  .decor-block.reverse .decor-copy, .decor-block.reverse .decor-gallery { order: initial; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  /* Brand intro takes a full row; the 3 link columns wrap 2+1 beneath it.
     .footer-newsletter already spans the full grid (base rule above). */
  .footer-brand { grid-column: span 2; }
  .gallery-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
  .head-row { flex-direction: column; align-items: flex-start; gap: 14px; }
}

@media (max-width: 760px) {
  /* Phase 5 §4/§6: mobile section padding maps to --space-6 (48px), not
     the old arbitrary 60px -- part of the section-rhythm frequency fix
     (tighter vertical rhythm so rich/accent bands read closer together
     on a full-width mobile scroll). */
  section { padding: var(--space-6) 0; }
  /* Mobile header contact info: previously hidden entirely below 760px
     (display:none), leaving phone/email with no mobile presence at all --
     reusing the existing topbar rather than duplicating contact info
     elsewhere. Compact typography + tighter gaps instead of hiding, with
     nowrap on each link so "+91 86001 46402" can never wrap mid-number;
     overflow-x:auto is the documented last resort if content still doesn't
     fit content at very narrow widths, scoped to this one bar so it can
     never cause page-level horizontal overflow. */
  .topbar { font-size: 11.5px; }
  .topbar .container {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-top: 5px;
    padding-bottom: 5px;
  }
  .topbar .container::-webkit-scrollbar { display: none; }
  .topbar-links { flex-wrap: nowrap; gap: 12px; flex: none; }
  .topbar a { white-space: nowrap; }
  .header-cta .icon-btn.call-only { display: none; }
  .categories-grid, .product-grid, .occasion-grid { grid-template-columns: 1fr 1fr; }
  .form-row { grid-template-columns: 1fr; }
  .gallery-grid { grid-template-columns: 1fr 1fr; gap: 12px; }

  /* .trust-item-v2 is already a centered column at every width (see base
     rule) -- unlike the old side-by-side .trust-item, no mobile
     flex-direction override is needed here. Just trims icon/type sizes
     down a step to match .why-item's own mobile scale-down, kept
     consistent between the two sibling sections. */
  .trust-item-v2 .ic { width: 48px; height: 48px; }
  .trust-item-v2 .ic svg { width: 22px; height: 22px; }
  .trust-item-v2 h4 { font-size: 15px; }
  .trust-item-v2 p { font-size: 12px; }
  #about { min-height: 0; padding: var(--space-7) 0; }
  .footer-mark { width: 200px; height: 200px; right: -40px; top: -20px; }

  /* Phase 5 §8: the last section before .site-footer (#contact) gets
     bottom padding of at least fab-size-primary + fab-gap + fab-offset
     (~90px) so nothing at the bottom of that section sits directly under
     the fixed WhatsApp fab, regardless of the footer-visibility fade
     behaviour wired up in js/script.js. */
  #contact {
    padding-bottom: calc(var(--fab-size-primary) + var(--fab-gap) + var(--fab-offset));
  }

  /* Compact product cards so more fit in the viewport when browsing a
     category on a phone. Aspect ratio stays fixed (object-fit: cover
     handles the crop), so images never distort — they're just shorter. */
  .product-grid { gap: 12px; }
  .product-media { aspect-ratio: 1/1; }
  .product-eyebrow { font-size: 9.5px; letter-spacing: 0.09em; }
  /* Phase 5 §4 mapping: 10px 12px 12px -> --space-3 (16px), still compact
     but grid-aligned rather than arbitrary -- direct answer to the "cards
     need more breathing room" complaint at mobile widths. */
  .product-body { padding: 12px; gap: 5px; }
  .product-body h3 { font-size: 13.5px; line-height: 1.3; }
  .product-price { font-size: 14.5px; }
  .product-price small { font-size: 10.5px; }
  .product-actions .btn { padding: 10px 10px; font-size: 12px; }

  /* .product-actions .btn's min-width:0/white-space:normal fix for this
     exact overflow (the "Order on WhatsApp" button refusing to shrink
     below its text's min-content width) is now a base rule, not repeated
     here -- see the comment above .product-actions .btn's declaration. */
  /* Same min-width:auto grid blowout, found in the Contact section: at
     760px .contact-wrap collapses to a single 1fr column (existing
     980px-scoped rule), but its two children's content-based minimum
     width still forced the track wider than the container. */
  .contact-wrap > * { min-width: 0; }
  /* The floating WhatsApp button (.fab-wa) is fixed at bottom-right and
     .find-us-card spans full width at this breakpoint (.contact-wrap is a
     single column here) -- confirmed on a real iPhone recording, the fab
     sat directly over this card's own address text near its bottom edge.
     Same reserved-clearance pattern already used for .trust-item and
     .checkout-drawer's own bottom-fixed controls: bottom padding widens
     the card so nothing the card cares about renders in the fab's corner,
     without changing the fab's own fixed position/behavior (js/script.js). */
  .find-us-card { padding-bottom: 76px; }

  /* ---------- Footer restructure (Phase 5 §7): true single column at
     this breakpoint (was a cramped 1fr 1fr step here before), replacing
     grid `gap` with explicit margins so brand-to-Shop spacing (--space-6)
     can differ from the spacing between Shop/Services/Contact
     (--space-5), exactly as specified. ---------- */
  /* A3 fix (Phase 7): longhand for the same reason as the base rule above
     -- `padding: var(--space-7) 0` was zeroing .container's horizontal
     padding at this breakpoint too. */
  .footer-top { padding-top: var(--space-6); padding-bottom: var(--space-5); }
  .footer-grid { grid-template-columns: 1fr; gap: 0; }
  /* Reset the 980px breakpoint's grid-column: span 2 (needed there for a
     2-column grid) -- left un-reset here, a lone item requesting a 2-wide
     span inside this breakpoint's 1-column explicit grid forces the grid
     engine to add an implicit second column just to satisfy the span, and
     every auto-placed item after it (the SHOP/CELEBRATE/HELP columns)
     falls into that 2-column implicit flow instead of stacking -- exactly
     the "SHOP and CELEBRATE sitting side by side, cramped" bug this line
     fixes. */
  .footer-brand { grid-column: auto; margin-bottom: var(--space-5); }
  /* Redesign: link columns collapse into accordions on mobile instead of
     three fully-expanded stacked lists -- the single largest source of
     the "excessive empty vertical space" the spec called out (three
     unconditionally-open columns of 2-3 short links each, with a full
     --space-5 gap after every one, made the mobile footer run very tall
     for how little content it holds). Closed by default; opening one
     doesn't close the others -- these are short lists, not an exclusive
     accordion. Structure/links themselves are unchanged, only how they're
     revealed. */
  .footer-col { margin-bottom: 0; border-top: 1px solid rgba(255,255,255,0.12); }
  .footer-col:last-of-type { border-bottom: 1px solid rgba(255,255,255,0.12); }
  .footer-col h4 {
    display: flex; align-items: center; justify-content: space-between;
    margin: 0; padding: 16px 2px; cursor: pointer;
  }
  .footer-col h4::after {
    content: "+";
    font-size: 18px; font-weight: 400; color: rgba(255,255,255,0.5);
    transition: transform 0.2s ease;
  }
  .footer-col.open h4::after { transform: rotate(45deg); }
  .footer-col ul {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.25s ease;
  }
  .footer-col.open ul { max-height: 220px; }
  .footer-col li:first-child { margin-top: 2px; }
  .footer-col li:last-child { margin-bottom: 16px; }
  /* Phase 6: .footer-newsletter is now the true last item (was
     .footer-col:last-child before the delivery CTA card was added) --
     explicit margin-top instead of relying on child order. */
  /* Bug fix: .footer-newsletter > div's 320px flex-basis (base rule above)
     is read along the vertical main axis once flex-direction switches to
     column here, forcing that inner div to a 320px minimum height on
     mobile regardless of its actual (much shorter) text content -- a
     large empty gap between the copy and the "Order on WhatsApp" button
     on flowers/gifts/decoration's shared footer. Overriding the flex
     shorthand to 0 1 auto on mobile only lets the div size to its content
     instead; .footer-newsletter's own column/align-items/margin rules
     here are unchanged. */
  .footer-newsletter { margin-top: var(--space-3); margin-bottom: 0; flex-direction: column; align-items: flex-start; }
  .footer-newsletter > div { flex: 0 1 auto; }
}
@media (min-width: 981px) {
  .footer-col h4::after { display: none; }
  .footer-col ul { max-height: none !important; }
}

@media (max-width: 520px) {
  .categories-grid, .product-grid, .occasion-grid { grid-template-columns: 1fr 1fr; }
  .hero-content { align-items: stretch; text-align: left; }
  .hero-content h1 { max-width: none; }
  .hero-actions { flex-direction: column; align-items: stretch; }
  .hero-actions .btn-rose { justify-content: center; }
  /* Secondary action (Call) is a filled quiet pill -- still visually
     lighter than the primary rose CTA, but readable as a real button
     rather than plain muted text. */
  .hero-actions .btn-outline {
    background: rgba(255,255,255,0.14);
    border-color: rgba(255,255,255,0.3);
    padding: 12px 20px; font-size: 14.5px;
  }

  /* The floating WhatsApp button (.fab-wa, js/script.js) is fixed at
     bottom-right and only hides once the footer scrolls into view -- on
     the compact-hero pages (flowers/gifts/decoration) the very first
     section heading (right after the hero+breadcrumb) sits at exactly
     that screen position on load, so its full-width text box was
     rendering underneath the button. Reserving clearance on the right
     edge of just that first heading lets the fab's corner sit in
     genuinely empty space instead of on top of text -- CSS-only,
     scoped by id to each page's known first section so headings
     further down the page (long since scrolled past the fab) keep
     their normal full-width measure. */
  #flowersAll .section-head h2, #flowersAll .section-head p,
  #gifts .section-head h2, #gifts .section-head p,
  #decoration .section-head h2, #decoration .section-head p {
    max-width: calc(100% - 88px);
  }
}

/* Second mobile canvas (430px): between the 390px and 760px rules above --
   a little more breathing room than the tightest phone width. */
@media (min-width: 400px) and (max-width: 520px) {
  .product-body h3 { font-size: 14px; }
  .cat-card .label h3 { font-size: 17px; }
}

/* Desktop is not a stretched mobile layout: stronger whitespace and a
   wider content container at larger viewports, capped so text/cards never
   spread uncomfortably wide. */
@media (min-width: 1440px) {
  .container { max-width: 1400px; }
  .product-grid, .categories-grid { gap: var(--space-5); }
  .section-head h2 { font-size: clamp(36px, 2.6vw, 46px); }
}
@media (min-width: 1800px) {
  .container { max-width: 1520px; }
  .section-head p { font-size: 17.5px; }
}

/* ==================== Stage 9: Cart Foundation ====================
   Reuses existing tokens/patterns throughout: --green/--rose/--border/etc,
   .icon-btn (header cart icon), .pdetail-overlay/.pdetail/.pdetail-close
   (the cart drawer IS that same overlay family, via the added .cart-*
   modifier classes below -- see index.html), .btn/.btn-rose/
   .btn-outline-dark, .price-final/.price-original. Nothing here
   introduces a second visual language. */

/* ---------- Header cart icon + count badge ---------- */
.cart-open-btn { position: relative; }
.cart-count-badge {
  position: absolute;
  top: -4px; right: -4px;
  min-width: 19px;
  height: 19px;
  padding: 0 4px;
  border-radius: 100px;
  background: var(--rose);
  color: var(--white);
  font-size: 11px;
  font-weight: 700;
  line-height: 19px;
  text-align: center;
  box-shadow: 0 0 0 2px var(--white);
}

/* ---------- Product card: WhatsApp (primary) + quick-add (secondary) sit
   side by side ---------- */
.product-actions { display: flex; align-items: center; gap: 8px; margin-top: var(--space-3); }
/* min-width:0 + white-space:normal as a BASE rule, not mobile-only: a flex
   item with nowrap text and no min-width override refuses to shrink below
   its text's min-content width (default min-width:auto) -- harmless in a
   wide single-column card, but the same overflow this previously fixed
   only below 520px recurs at ANY width once the card itself gets narrow
   enough (e.g. a 4-column grid squeezed by the 1024px sidebar+filters
   layout, where "Order on WhatsApp" was clipping to "rder on WhatsApp").
   Letting the label wrap to two lines instead of clipping is correct at
   every width, so this is no longer conditional on viewport. */
.product-actions .btn { margin-top: 0; flex: 1 1 auto; box-shadow: none; min-width: 0; white-space: normal; line-height: 1.2; text-align: center; }
.cart-quickadd-btn {
  flex: none;
  width: 42px; height: 42px;
  background: var(--white);
  border: 1.5px solid var(--border);
  color: var(--green-dark);
}
.cart-quickadd-btn:hover { background: var(--green-light); border-color: var(--green-light); color: var(--green-dark); }

/* ---------- Cart drawer (reuses .pdetail-overlay/.pdetail as a base) ---------- */
.cart-drawer-head { padding: 22px 22px 0; }
.cart-drawer-head h2 { font-family: var(--ff-body); font-weight: 600; font-size: 22px; margin: 0; color: var(--green-dark); }
/* Stage 11D-F fix: #checkoutBody reuses this same .cart-drawer shell
   (#checkoutDrawer carries the .cart-drawer class -- see index.html) but
   had never actually been given this padding rule, unlike #cartBody --
   an oversight from when Stage 10 built checkout on top of the cart
   drawer shell. Fixed here rather than duplicated, since the two should
   look consistent anyway. */
#cartBody, #checkoutBody, #addressFormBody, #orderDetailBody { padding: 16px 22px 26px; }

.cart-empty { text-align: center; padding: 40px 10px; color: var(--muted); }
.cart-empty p { margin: 0 0 18px; font-size: 15px; }

.cart-lines { display: flex; flex-direction: column; gap: 16px; margin-bottom: 18px; }
.cart-line { display: flex; gap: 12px; padding-bottom: 16px; border-bottom: 1px solid var(--border); }
.cart-line:last-child { border-bottom: none; padding-bottom: 0; }
.cart-line-media { flex: none; width: 64px; height: 64px; border-radius: var(--radius-sm); overflow: hidden; background: var(--green-light); }
.cart-line-media img { width: 100%; height: 100%; object-fit: cover; }
.cart-line-media-placeholder { width: 100%; height: 100%; background: var(--green-light); }
.cart-line-info { flex: 1; min-width: 0; }
.cart-line-info h4 { margin: 0 0 6px; font-size: 14.5px; font-weight: 600; color: var(--ink); overflow-wrap: break-word; }
.cart-line-price { font-size: 14px; margin-bottom: 10px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cart-line-unavailable { color: var(--rose-dark); font-size: 12.5px; font-weight: 600; margin: 0 0 8px; }
.cart-line-unavailable-row { opacity: 0.6; }

/* Quantity stepper -- reused identically by both the cart drawer lines
   and the product-detail Add-to-Cart section below. Sized for comfortable
   thumb use (44px total row height), not a tiny inline control. */
.cart-line-qty { display: inline-flex; align-items: center; gap: 0; border: 1.5px solid var(--border); border-radius: 100px; overflow: hidden; }
.cart-qty-btn {
  width: 40px; height: 40px;
  border: none;
  background: var(--white);
  color: var(--green-dark);
  font-size: 18px;
  font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.cart-qty-btn:hover:not(:disabled) { background: var(--green-light); }
.cart-qty-btn:disabled { color: var(--border); cursor: not-allowed; }
.cart-qty-value { min-width: 28px; text-align: center; font-size: 14.5px; font-weight: 600; }
/* Stage 11E: Remove now sits beside the quantity stepper as its own row
   (justify-content: space-between keeps qty at the start and Remove at
   the end, with breathing room between them via gap -- not crammed
   against .cart-line-qty's pill border, not stranded at the item's far
   edge with no visual relationship to its own controls). */
.cart-line-controls { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.cart-line-remove {
  border: none;
  background: none;
  color: var(--muted);
  font-size: 12.5px;
  text-decoration: underline;
  padding: 8px 0;
}
.cart-line-remove:hover { color: var(--rose-dark); }

.cart-unavailable-note { font-size: 12.5px; color: var(--muted); margin: -6px 0 16px; }

.cart-summary { border-top: 1px solid var(--border); padding-top: 18px; display: flex; flex-direction: column; gap: 12px; }
.cart-summary-row { display: flex; justify-content: space-between; align-items: center; font-size: 15px; }
/* Stage 11E: was font-family: var(--ff-heading) (a decorative serif
   display font, elsewhere only used for section headings) + green -- an
   inconsistent, "overly creative" treatment for a price figure compared
   to .price-final (rose-dark, plain body font) used everywhere else a
   price appears -- product cards, product detail, and each individual
   cart-line price via .cart-line-price above. Aligned here to the same
   restrained rose-dark/body-font treatment; only size/weight still set
   it apart, for legitimate emphasis as the one grand-total figure. No
   price/calculation logic changed -- CSS only. */
.cart-summary-total { font-size: 18px; font-weight: 700; color: var(--rose-dark); }
.cart-clear-btn {
  align-self: center;
  border: none;
  background: none;
  color: var(--muted);
  font-size: 13px;
  text-decoration: underline;
  padding: 6px;
  margin-top: 4px;
}
.cart-clear-btn:hover { color: var(--rose-dark); }

/* ---------- Product-detail: variant selector + quantity + Add to Cart ---------- */
.pdetail-cart-section { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); }
.pdetail-variant-select { margin-bottom: 14px; }
.pdetail-variant-select label { display: block; font-size: 12.5px; font-weight: 600; color: var(--muted); margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.04em; }
.pdetail-variant-select select {
  width: 100%;
  min-height: 46px;
  padding: 10px 12px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 15px;
  background: var(--white);
  color: var(--ink);
}
.pdetail-qty-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* Stage 11E: Add to Cart + Buy Now are grouped in their own flex row
   (.pdetail-cart-actions) rather than being direct children of
   .pdetail-qty-row -- this keeps the two buttons side by side, equal
   width, equal height as a single unit at every viewport. On a wide
   viewport that unit sits inline next to the quantity control (both are
   .pdetail-qty-row flex items); when there isn't room, flex-wrap on the
   parent wraps the whole button pair onto its own row below the quantity
   control, instead of the three items (qty / Add to Cart / Buy Now)
   wrapping independently and landing unevenly. */
.pdetail-cart-actions { display: flex; gap: 10px; flex: 1 1 220px; }
.pdetail-cart-actions .btn { flex: 1 1 0; min-width: 0; }

/* ---------- Toast ---------- */
.cart-toast {
  position: fixed;
  left: 50%;
  /* Phase 5 §6 safe-area audit: same fixed-bottom-offset situation as the
     fabs, so gets the same env(safe-area-inset-bottom) treatment. */
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%) translateY(10px);
  z-index: 950;
  background: var(--green-dark);
  color: var(--white);
  padding: 13px 22px;
  border-radius: 100px;
  font-size: 13.5px;
  font-weight: 500;
  box-shadow: var(--shadow-md);
  opacity: 0;
  transition: opacity 0.2s ease, transform 0.2s ease;
  max-width: 90vw;
  text-align: center;
}
.cart-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Reachability fix, exactly mirroring Admin's own .admin-modal pattern
   (css/admin.css: ".admin-modal { display:flex; flex-direction:column;
   overflow:hidden }" + ".admin-modal > *:not(.admin-modal-headerbar) {
   overflow-y:auto; flex:1 1 auto; min-height:0 }" -- the outer box never
   scrolls, only a designated child does, and the close button/header
   become plain non-scrolling flex items). Originally scoped to a mobile-
   only media query and only to .cart-drawer; per a direct comparison
   against the Admin modal this was made UNCONDITIONAL (Admin's own
   "Stage 14" already did the same generalization: "previously mobile
   only... now unconditional, since the same problem happens identically
   on desktop"). #orderDetailBody added here alongside #cartBody/
   #checkoutBody/#addressFormBody so My Orders' order-detail modal
   (#orderDetailOverlay's .pdetail, now carrying .cart-drawer -- see
   my-orders.html) gets the SAME fixed-header/scrolling-body mechanism as
   Checkout/Cart/Admin, instead of the older whole-box-scrolls behavior
   that let its close button scroll away with the content. NOT applied to
   the shared bare .pdetail/.pdetail-close base rules themselves -- the
   plain product-detail view (no .cart-drawer class) is deliberately left
   on its original behavior, unchanged. */
/* ROOT CAUSE of "close button not reliably visible/tappable at normal
   iPhone zoom" (real-device report, not reproducible in headless
   Chromium -- devtools mobile emulation doesn't model Safari's dynamic
   toolbar at all): `vh` on iOS Safari is calculated against the LARGEST
   possible viewport (as if the address bar were fully collapsed), not
   the actual currently-visible area. When a customer taps a field
   inside Checkout, the keyboard opens and Safari's own chrome can
   re-expand at the same time -- 94vh stayed pinned to the old, larger
   measurement, so the modal could render taller than the real visible
   screen, pushing its own top edge (and the close button living there)
   above the top of what's actually on screen. `dvh` (dynamic viewport
   height) is the standard fix -- it tracks the ACTUAL visible viewport
   as Safari's chrome expands/collapses, recomputing automatically.
   Falls back to the old 94vh in any browser that doesn't support dvh
   (the vh line stays first deliberately; a browser that doesn't
   recognize `max-height: 94dvh` simply keeps using it, since CSS
   ignores an unparseable declaration and does not overwrite a working
   prior one with `unset`). */
.cart-drawer {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  max-height: 94vh;
  max-height: 94dvh;
}
.cart-drawer .pdetail-close {
  position: static;
  align-self: flex-end;
  flex: none;
}
.cart-drawer-head {
  flex: none;
  padding-top: 14px;
}
#cartBody, #checkoutBody, #addressFormBody, #orderDetailBody {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  min-height: 0;
}

@media (max-width: 639.98px) {
  .cart-line-media { width: 56px; height: 56px; }
  /* Add/Edit Address on phone: the shared .field spacing (18px bottom
     margin, 46px input height, 12px/14px padding) is tuned for short
     forms like Checkout's -- stacked across this form's 6 fields it read
     as bulky/heavy on a real phone screen. Scoped to #addressFormBody
     only (not .field globally) so Cart/Checkout/every other .field
     consumer keeps its existing sizing untouched. Still keeps a real
     44px+ touch target on every input. */
  #addressFormBody .field { margin-bottom: 12px; }
  #addressFormBody .field label { margin-bottom: 4px; font-size: 12.5px; }
  #addressFormBody .field input,
  #addressFormBody .field select,
  #addressFormBody .field textarea {
    padding: 10px 12px;
    font-size: 14px;
  }
  #addressFormBody .field input:not([type="checkbox"]) { height: 44px; }
  #addressFormBody .field textarea { min-height: 72px; }
}

@media (min-width: 640px) {
  .cart-drawer { max-width: 420px; }
  /* Order Details modal (#orderDetailOverlay, my-orders.html) reuses
     .cart-drawer purely for its mobile reachability/scroll-lock mechanism
     (pinned close button, internal scroll body -- see that class's own
     comment) -- it was never meant to also inherit Cart/Checkout/Saved-
     Addresses' deliberately narrow 420px cap, which is far too tight for
     an order's status timeline + item list + delivery details on a real
     desktop screen. Scoped by id so Cart/Checkout/Addresses (every other
     .cart-drawer consumer) keep their existing 420px unchanged -- same
     scoped-override technique already used for Admin's own wider modals
     (css/admin.css's #userDetailModalOverlay .admin-modal). */
  #orderDetailOverlay .cart-drawer { max-width: 760px; }
  /* Add/Edit Address (My Orders' Saved Addresses, my-orders.html): same
     scoped-override technique as Order Details above. The base 420px cap
     exists for Cart/Checkout's short line-item lists, but this form has 6
     stacked fields (Label, Recipient Name, Phone, Address, Landmark, PIN)
     -- at 420px on a real desktop screen it forced avoidable internal
     scrolling and looked cramped relative to the large amount of unused
     viewport around it. Widened so the form can lay out with more
     breathing room and needs less vertical scroll; still well short of
     the 640px .pdetail fallback so it doesn't look like a full product
     page. Scoped by id so Cart/Checkout keep their existing 420px. */
  #addressFormOverlay .cart-drawer { max-width: 520px; }
}

/* ==================== Stage 10: Checkout ====================
   Reuses .pdetail-overlay/.pdetail/.cart-drawer for the modal shell itself
   (including the mobile reachability fix and desktop width cap already
   defined above -- #checkoutDrawer carries the .cart-drawer class, so it
   inherits both for free) and .field/.btn/.cart-summary/.cart-line-remove/
   .cart-line-unavailable/.price-final/.price-original from elsewhere on
   the site. Only the checkout-specific content below is new. */

.checkout-section-title {
  font-family: var(--ff-body);
  font-weight: 600;
  font-size: 16px;
  color: var(--green-dark);
  margin: 22px 0 12px;
}
.checkout-section-title:first-child { margin-top: 0; }

.checkout-summary-lines { display: flex; flex-direction: column; gap: 12px; margin-bottom: 4px; }
.checkout-summary-line { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; padding-bottom: 12px; border-bottom: 1px solid var(--border); }
.checkout-summary-line:last-child { border-bottom: none; padding-bottom: 0; }
.checkout-summary-line-info h4 { margin: 0 0 4px; font-size: 14px; font-weight: 600; color: var(--ink); }
.checkout-summary-line-meta { margin: 0; font-size: 12.5px; color: var(--muted); }
.checkout-summary-line-total { font-size: 14px; font-weight: 600; color: var(--green-dark); white-space: nowrap; }
.checkout-summary-line-unavailable { align-items: center; }

.checkout-loading { text-align: center; padding: 40px 10px; color: var(--muted); }

.checkout-notice {
  border-radius: var(--radius-sm);
  padding: 16px;
  margin-bottom: 18px;
  font-size: 13.5px;
}
.checkout-notice p { margin: 0 0 12px; }
.checkout-notice p:last-child { margin-bottom: 0; }
.checkout-notice-warning { background: #fdf3e3; border: 1px solid #f0d9a8; color: #6b4c14; }
.checkout-notice-error { background: #fdeeee; border: 1px solid #f3c6c6; color: #7a2020; }
.checkout-price-changes { list-style: none; margin: 0 0 14px; padding: 0; font-size: 13px; }
.checkout-price-changes li { padding: 6px 0; }
.checkout-price-was { text-decoration: line-through; color: var(--muted); }
.checkout-price-now { font-weight: 700; color: var(--rose-dark); }

.checkout-confirmation { text-align: center; padding: 8px 4px 4px; }
.checkout-confirmation h3 { font-family: var(--ff-body); font-weight: 600; font-size: 22px; color: var(--green-dark); margin: 0 0 10px; }
.checkout-confirmation > p { color: var(--muted); font-size: 14px; margin: 0 0 22px; }
.checkout-confirmation-detail {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 0; border-bottom: 1px solid var(--border);
  font-size: 14px; text-align: left;
}
.checkout-confirmation-detail:last-of-type { border-bottom: none; margin-bottom: 22px; }
.checkout-confirmation-detail strong { color: var(--green-dark); font-size: 16px; }
.checkout-confirmation .btn { margin-top: 10px; }

/* ==================== Stage 11: Payment (Razorpay) ====================
   Reuses .field/.checkout-notice/.checkout-loading/.checkout-confirmation/
   .btn* from Stage 10 directly -- only the payment-choice radio group is
   genuinely new markup. Razorpay's own Checkout popup is a third-party
   overlay outside this site's CSS control (Section 23), so nothing here
   attempts to style it. */
.checkout-payment-choice { margin-bottom: 18px; }
.checkout-payment-option {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  margin-bottom: 10px;
  font-size: 14.5px;
  cursor: pointer;
  transition: border-color 0.15s ease;
}
.checkout-payment-option:has(input:checked) { border-color: var(--rose); background: var(--paper); }
/* Stage 11D-F: explicit classes, kept in sync by a JS change listener
   (js/cart.js), so the "selected" highlight does not depend solely on
   :has() support -- reported symptom was "clicking options produces no
   meaningful feedback", and :has() is the ONLY thing that previously
   produced a visible highlight beyond the native radio dot itself. */
.checkout-payment-option-selected { border-color: var(--rose); background: var(--paper); }
.checkout-payment-option input[type="radio"] { width: 20px; height: 20px; flex: none; accent-color: var(--rose); }

/* #checkoutDrawer's own flex-column/pinned-header/scrolling-body behavior
   now comes entirely from the generalized, unconditional .cart-drawer
   rule above (it already carries that class, index.html) -- the
   #checkoutDrawer-scoped duplicate of the same properties was removed.
   #checkoutFooter below is Checkout's own genuinely distinct piece: a
   sibling of #checkoutBody, OUTSIDE the scrolling area, only ever
   containing the Place Order button when the order form itself is
   showing (js/cart.js clears it in every other checkout state). */
#checkoutFooter:not(:empty) {
  flex: none;
  padding: 14px 22px calc(14px + env(safe-area-inset-bottom, 0px));
  background: var(--white);
  border-top: 1px solid var(--border);
  box-shadow: 0 -10px 14px -8px rgba(15, 20, 17, 0.1);
}

@media (min-width: 640px) {
  /* Stage 11D-F: checkout specifically widened (was 480px, set in the
     original Stage 10 rule further up this file) -- reported as "too
     narrow" even after the previous round's fix. Cart drawer itself
     (.cart-drawer's own 420px cap, set earlier in this file) is
     unaffected -- this ID-scoped rule only overrides it for checkout. */
  #checkoutDrawer.cart-drawer { max-width: 560px; }
}

/* Manage-addresses sub-view (edit/delete/set-default) -- reuses .field/
   .checkout-notice/.checkout-section-title/.btn/.btn-sm conventions
   already established for the rest of checkout rather than a new system. */
.checkout-manage-addresses-link {
  display: inline-block;
  margin-top: 6px;
  background: none;
  border: none;
  padding: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--rose-dark);
  text-decoration: underline;
  cursor: pointer;
}
.checkout-address-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  align-items: flex-start;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  margin-bottom: 12px;
}
.checkout-address-row-confirm-delete { border-color: #f3c6c6; background: #fdeeee; }
.checkout-address-row-main { flex: 1 1 220px; min-width: 0; }
.checkout-address-row-label { font-weight: 700; font-size: 14px; margin-bottom: 4px; }
.checkout-address-default-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  color: var(--rose-dark);
  border: 1px solid currentColor;
  border-radius: 100px;
  padding: 1px 8px;
  margin-left: 6px;
  vertical-align: middle;
}
.checkout-address-row-text { font-size: 13.5px; color: var(--ink); word-break: break-word; }
.checkout-address-row-subtext { font-size: 12.5px; opacity: 0.8; }
.checkout-address-row-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.checkout-address-confirm-text { font-size: 13px; font-weight: 600; color: #7a2020; margin-right: 4px; }

/* ==================== Delivery Eligibility (customer UI) ====================
   New, scoped classes only -- reuses .field input styling's look-and-feel
   (border/radius/padding) and .btn/.checkout-notice/.chip conventions
   already established above rather than inventing a parallel system.
   Used by js/delivery-ui.js (PIN checker, date selector, product-card
   badge, product-detail delivery section) and js/cart.js (cart delivery
   validation notices). */

.delivery-pin-checker {
  background: var(--paper);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
}
.hero-delivery-checker { max-width: 360px; margin-top: 24px; }
.hero-delivery-checker:empty { margin-top: 0; }
.delivery-pin-label,
.delivery-date-selector-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 6px;
}
.delivery-pin-row { display: flex; gap: 8px; }
.delivery-pin-input {
  flex: 1 1 auto;
  min-width: 0;
  border: 1.5px solid var(--border);
  border-radius: 100px;
  padding: 9px 16px;
  font-family: inherit;
  font-size: 13.5px;
  background: var(--white);
  letter-spacing: 0.04em;
}
.delivery-pin-input:focus { outline: none; border-color: var(--rose); }
.delivery-pin-row .btn { flex: none; padding: 9px 18px; font-size: 13.5px; }

@media (max-width: 380px) {
  .delivery-pin-row { gap: 6px; }
  .delivery-pin-input { padding: 9px 12px; }
  .delivery-pin-row .btn { padding-left: 14px; padding-right: 14px; }
}

/* Specificity note: this card can sit inside .hero-content, whose own
   ".hero-content p" rule (0,1,1) outranks a single tone class like
   ".delivery-pin-message-error" (0,1,0) -- confirmed live, the error text
   was rendering at rgba(255,255,255,0.92) (the hero's near-white paragraph
   color) instead of rose-dark, making a real, correctly-worded error
   message ("We don't currently deliver to this PIN code.") effectively
   invisible against the light .delivery-pin-checker card. Qualifying the
   selector with its own parent class (.delivery-pin-checker .delivery-pin-message,
   0,2,0) always outranks ".hero-content p" regardless of where this
   component is placed, without touching the hero's own paragraph styling. */
.delivery-pin-checker .delivery-pin-message { font-size: 13px; margin: 12px 0 0; color: var(--ink); }
.delivery-pin-checker .delivery-pin-message-error { color: var(--rose-dark); font-weight: 600; }
.delivery-pin-checker .delivery-pin-message-success { color: var(--green-dark); font-weight: 600; }

.delivery-pin-checker-saved { padding: 18px 22px; }
.delivery-pin-saved-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin-bottom: 14px;
}
.delivery-pin-saved-label { font-size: 12.5px; color: var(--muted); }
.delivery-pin-saved-value { font-size: 14.5px; color: var(--green-dark); }
.delivery-change-link {
  border: none;
  background: none;
  padding: 12px 4px;
  margin: -12px -4px -12px auto;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--rose);
  text-decoration: underline;
  cursor: pointer;
}
.delivery-change-link:hover { color: var(--rose-dark); }

.delivery-date-selector { margin-top: 4px; }
.delivery-date-options { display: flex; flex-wrap: wrap; gap: 10px; }
.delivery-date-option {
  border: 1.5px solid var(--border);
  background: var(--white);
  border-radius: var(--radius-sm);
  padding: 10px 16px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
}
.delivery-date-option:hover { border-color: var(--rose); }
.delivery-date-option-active { border-color: var(--green); color: var(--green-dark); background: var(--clay-light); }
.delivery-date-option-custom { cursor: default; }
.delivery-date-option-custom input,
.delivery-date-option-custom select {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  font-family: inherit;
  font-size: 13px;
  background: var(--paper);
}

/* Product-card badge -- js/products.js renders this hidden by default on
   every card; js/delivery-ui.js is the only file that ever shows/fills it,
   and only once a PIN is saved. */
.delivery-card-badge {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--green-dark);
  background: var(--clay-light);
  border-radius: 20px;
  padding: 4px 10px;
  margin: 6px 0 2px;
}
.delivery-card-badge-sameday { color: var(--rose-dark); background: var(--rose-light); }

/* Product detail modal delivery section */
.delivery-pdetail {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}
.delivery-pdetail-message { font-size: 13.5px; margin: 0 0 14px; }
.delivery-pdetail-available { color: var(--green); font-weight: 600; }
.delivery-pdetail-unavailable { color: var(--rose-dark); }
.delivery-pdetail-checking { font-size: 13.5px; color: var(--muted); }

/* Cart delivery validation (js/cart.js) -- reuses .checkout-notice tones */
.delivery-cart-notice { font-size: 13px; }
.delivery-cart-notice ul { margin: 8px 0 0; padding-left: 18px; }
.delivery-cart-notice li { margin-bottom: 4px; }
.delivery-cart-earliest {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--green-dark);
  margin: 0 0 14px;
}

/* Part D: mandatory-login checkout (js/cart.js) + My Orders (my-orders.html
   / js/my-orders.js). Reuses existing tokens/.field/.checkout-notice/
   .cart-summary/.pdetail-overlay conventions rather than inventing a
   parallel design language. */
.checkout-notice-success { background: var(--green-light); border: 1px solid #bfe0cc; color: var(--green-dark); }
.checkout-auth-subtext { color: var(--muted); font-size: 13.5px; line-height: 1.6; margin: 0 0 16px; }
.field-checkbox label { display: flex; align-items: center; gap: 8px; font-weight: 500; cursor: pointer; }
.field-checkbox input[type="checkbox"] { width: 17px; height: 17px; accent-color: var(--rose); }

/* Phase 1 (js/customer-auth-ui.js): "Continue with Google" button + the
   "or" divider above the existing email-OTP form; shared by Checkout and
   My Orders' sign-in cards. */
/* Now the sole, primary customer sign-in action (email-OTP disabled) --
   sized up slightly from the base .btn (16px vs 15px font, taller
   padding) so it reads as THE action on this card, not one option among
   several. Still built entirely from existing .btn/.btn-outline-dark/
   .btn-block classes -- these rules only add emphasis on top. */
.pmauth-google-btn { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 18px; padding: 15px 28px; font-size: 16px; }
.pmauth-google-btn svg { flex: none; }
.pmauth-divider { display: flex; align-items: center; gap: 12px; margin: 0 0 18px; color: var(--muted); font-size: 12.5px; text-transform: uppercase; letter-spacing: 0.04em; }
.pmauth-divider::before, .pmauth-divider::after { content: ""; flex: 1; height: 1px; background: var(--border, #e5dcd6); }
.checkout-signout-btn { margin-top: 14px; }
.checkout-signout-link { float: right; margin: 0 0 14px; }

.my-orders-page { padding: 40px 0 80px; min-height: 60vh; }
.my-orders-head { margin-bottom: 28px; }
/* This page's heading is a real <h1> (semantically correct -- it's the
   page's own top-level heading, not a subsection like the h2s
   .section-head styles elsewhere), so it never matched .section-head h2's
   margin reset and fell back to the browser's default <h1> UA margin
   (21.44px top AND bottom at this font-size) -- confirmed live, that
   default bottom margin was stacking with .my-orders-head's own 28px,
   producing a visibly oversized gap before "Signed in as..." on a real
   iPhone recording. Same reset .section-head h2 already applies, scoped
   to this page's h1 instead. */
/* This bare <h1> had only a margin reset (no font-family/size/weight),
   so it fell back to the browser's UA-stylesheet default -- the one page
   with a real page-level heading was the one page NOT using the site's
   own heading typography. Matches .hero-content h1's own scale
   (var(--ff-heading), same clamp family) so My Orders reads as the same
   product as every marketing page instead of a plain-HTML fallback. */
.my-orders-head h1 { margin: 0 0 14px; font-family: var(--ff-heading); font-weight: 600; font-size: clamp(28px, 4vw, 40px); line-height: 1.2; letter-spacing: -0.01em; color: var(--green-dark); }
/* .my-orders-content is a <section>, so the sitewide base "section { padding:
   var(--section-y) 0 }" rule (intended for full page sections, ~48-112px)
   was ALSO adding its own top padding here, stacking on top of
   .my-orders-head's own 28px margin-bottom -- confirmed live (28px + 48px
   = 76px measured, matching the oversized gap seen on a real iPhone
   recording before "Signed in as..."). This section sits directly under
   a page head that already provides its own spacing, so it needs none of
   its own; .my-orders-auth (the signed-out equivalent) never had this
   problem only because it happens to be visually similar by coincidence,
   not because it was actually addressed -- reset both for consistency. */
.my-orders-content, .my-orders-auth { padding-top: 0; }
.my-orders-loading-inline { color: var(--muted); }
/* Pre-release UX pass (item 4): the sign-in card is now the customer's
   FIRST and ONLY interaction with authentication (Google-only, per the
   current interim architecture) -- it previously read as a plain, flat
   form box (--shadow-sm, tight 28px/24px padding, no header weight),
   not a deliberate moment. Upgraded using ONLY existing design tokens
   (--shadow-lg already exists for exactly this kind of "elevated,
   this-matters" surface elsewhere in the design system; no new color,
   radius, or shadow value invented) -- more generous padding/max-width
   so the card doesn't feel cramped. */
.my-orders-auth-card {
  max-width: 440px;
  /* Follow-up (deferred from the customer-phone-otp-rpc-catch-bugfix task,
     2026-09-02): the card sat directly under .my-orders-head with only the
     heading's own ~28px bottom margin between them -- confirmed via a
     harness render (no dedicated top spacing existed here at all), making
     the card feel like it was crowding the page title rather than reading
     as its own deliberate moment. --space-5 (32px, already used sitewide
     for this kind of section-level breathing room) is the only change --
     horizontal centering (margin auto) was already mathematically correct
     at every viewport, confirmed via getBoundingClientRect, so left
     untouched. */
  margin: var(--space-5) auto 0;
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 36px 32px;
  box-shadow: var(--shadow-lg);
}
.my-orders-auth-card h2 { margin: 0 0 10px; font-size: 22px; color: var(--green-dark); }
.my-orders-auth-card p { color: var(--muted); font-size: 14px; margin: 0 0 20px; line-height: 1.5; }
/* Follow-up (same review pass as the .my-orders-auth-card spacing fix
   above): max-width + margin:auto alone do not center this element --
   .btn's own `display: inline-flex` (confirmed at line ~221, and already
   flagged as fighting this exact button once before, see the [hidden]
   comment just below) keeps it sized to/anchored at its inline
   formatting context's start edge rather than participating in
   block-level auto-margin centering. `display: block` is enough to let
   the existing max-width/margin:auto actually center it, matching the
   card above; `.btn-block`'s own width:100% (up to the 420px cap) and
   justify-content:center (for the label) are untouched. */
.my-orders-auth-signout { display: block; max-width: 420px; margin: 14px auto 0; }
/* Live-testing finding (customer-account-auth-entrypoint-implementation
   task): .btn's own `display: inline-flex` (line ~194) has higher
   cascade priority than the browser's built-in [hidden] UA-stylesheet
   rule, so a plain `hidden` attribute alone does NOT actually hide an
   element carrying the .btn class -- confirmed live, this button
   rendered fully visible in a genuinely signed-out session despite its
   `hidden` DOM property correctly reading true. This one rule restores
   the expected behavior, scoped only to this specific button (no other
   .btn-classed element in this codebase relies on a static `hidden`
   toggle the way this one does -- Checkout's equivalent sign-out button
   is only ever inserted into the DOM when needed, never toggled via
   `hidden`, which is why it never hit this issue). */
.my-orders-auth-signout[hidden] { display: none; }

.my-orders-empty {
  text-align: center;
  padding: 40px 20px;
  color: var(--muted);
}
.my-orders-empty p { margin: 0 0 16px; }

#myOrdersList { display: flex; flex-direction: column; gap: 14px; }
.my-orders-card {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 16px 18px;
  cursor: pointer;
  font-family: inherit;
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.my-orders-card:hover { box-shadow: var(--shadow-sm); border-color: var(--rose-light); }
.my-orders-card-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.my-orders-card-top strong { color: var(--green-dark); font-size: 15px; }
.my-orders-card-items { margin: 0 0 10px; color: var(--muted); font-size: 13.5px; overflow-wrap: break-word; }
.my-orders-card-bottom { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; font-size: 13px; color: var(--ink); }
.my-orders-card-status { font-weight: 600; color: var(--rose-dark); margin-left: auto; }

.my-orders-payment-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 20px;
  flex: none;
}
.my-orders-payment-badge.is-paid { color: var(--green-dark); background: var(--green-light); }
.my-orders-payment-badge.is-pending { color: var(--gold); background: var(--cream-deep); }
.my-orders-payment-badge.is-failed { color: #7a2020; background: #fdeeee; }

/* Order-detail (D8) -- redesigned to directly match Admin's own order-
   detail card pattern (.admin-detail-section/.admin-detail-grid/
   .admin-detail-item, css/admin.css, used in js/admin-orders.js's
   buildDetailHtml()): each group is its own bordered, soft-background
   card with real internal padding, label/value pairs sit in a proper
   2-column grid (not a full-width space-between row stretched to the
   card's edges), and each card's own heading is a real, bordered
   section title -- not just a thin underline with content still running
   edge-to-edge beneath it. A first attempt at this (group title + plain
   list rows, no card background/padding) was shown to the user and
   confirmed still not good enough ("layout is not good... go look at
   Admin's order detail") -- this version reproduces that exact reference
   structure, translated onto this stylesheet's own tokens
   (--green-dark/--muted/--border/--paper/--radius-sm) rather than
   admin.css's separate --admin-* tokens (same visual pattern, no cross-
   stylesheet dependency). */
.my-orders-detail-meta { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 0 0 4px; color: var(--muted); font-size: 13.5px; }
.my-orders-status-cancelled { color: #7a2020; background: #fdeeee; border: 1px solid #f3c6c6; border-radius: var(--radius-sm); padding: 12px 14px; margin-bottom: 20px; font-weight: 600; }

.my-orders-detail-section {
  margin-top: 18px;
  padding: 18px;
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.my-orders-detail-meta + .my-orders-detail-section { margin-top: 14px; }
.my-orders-detail-section h3 {
  font-size: 12px;
  font-weight: 700;
  margin: 0 0 14px;
  padding-bottom: 10px;
  color: var(--green-dark);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-bottom: 1px solid var(--border);
}
.my-orders-detail-section > *:last-child { margin-bottom: 0 !important; }

.my-orders-detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.my-orders-detail-field { min-width: 0; }
.my-orders-detail-field label {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  margin-bottom: 4px;
  font-weight: 600;
}
.my-orders-detail-field span { font-size: 14px; font-weight: 600; color: var(--ink); overflow-wrap: break-word; }

.my-orders-timeline {
  display: flex;
  flex-direction: column;
  gap: 14px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.my-orders-timeline-step {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--muted);
}
.my-orders-timeline-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--border);
  flex: none;
}
.my-orders-timeline-step.is-done { color: var(--green-dark); }
.my-orders-timeline-step.is-done .my-orders-timeline-dot { background: var(--green); }
.my-orders-timeline-step.is-current { color: var(--rose-dark); }
.my-orders-timeline-step.is-current .my-orders-timeline-dot { background: var(--rose); box-shadow: 0 0 0 3px var(--rose-light); }

.my-orders-detail-items { display: flex; flex-direction: column; gap: 10px; }
.my-orders-detail-item { display: flex; justify-content: space-between; align-items: center; gap: 14px; font-size: 14px; }
.my-orders-detail-item:not(:last-child) { padding-bottom: 10px; border-bottom: 1px solid var(--border); }
.my-orders-detail-delivery { font-size: 13.5px; color: var(--ink); line-height: 1.8; }
.my-orders-detail-delivery p { margin: 0 0 6px; }
.my-orders-detail-delivery p:last-child { margin-bottom: 0; }

.my-orders-summary-total-row { display: flex; justify-content: space-between; align-items: center; padding-top: 12px; margin-top: 4px; border-top: 1px solid var(--border); }
.my-orders-summary-total-row span:first-child { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); }
.my-orders-summary-total-row span:last-child { font-size: 18px; font-weight: 700; color: var(--rose-dark); }

@media (max-width: 480px) {
  .my-orders-detail-grid { grid-template-columns: 1fr; gap: 12px; }
}

/* ---------- My Orders: Saved Addresses section ----------
   Customer-facing entry point for the existing customer_addresses CRUD
   (previously only reachable mid-Checkout, and only once the customer
   already had a saved address). Row/card presentation intentionally
   reuses .checkout-address-row and its children as-is (already generic,
   not #checkoutBody-scoped) so this never becomes a second visual
   language for the same data -- only the section wrapper/heading below
   are new. */
/* margin-top: 0 -- the profile card directly above already provides the
   "Subsection" gap via its own margin-bottom (--space-4); a second
   margin here would double-stack (block margins between siblings don't
   collapse when the elements aren't both plain block boxes in every
   browser the same way, so this was measured as real extra space, not
   assumed to collapse away). */
.my-orders-addresses-section { margin-top: 0; }
.my-orders-addresses-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.my-orders-addresses-title { margin: 0 0 2px; font-family: var(--ff-body); font-weight: 600; font-size: 19px; color: var(--green-dark); }
.my-orders-addresses-subtitle { margin: 0; font-size: 13px; color: var(--muted); }
.my-orders-addresses-empty {
  text-align: center;
  padding: 32px 20px;
  color: var(--muted);
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.my-orders-addresses-empty p { margin: 0 0 16px; }
#myOrdersAddressesList { display: flex; flex-direction: column; gap: 12px; }

/* ---------- My Orders: tabs (Orders / Account & Addresses) ----------
   "Section" rhythm level (--space-5, 32px): the toolbar that used to sit
   between the page head and these tabs is gone (see my-orders.html's own
   comment -- identity/Sign Out now live in exactly one place, the
   profile card below), so this is now the first thing under the page
   head and needs its own top spacing at that rhythm level. */
/* Sticky while scrolling so the customer can switch Orders <-> Account &
   Addresses without scrolling back to the top -- same sticky-under-header
   pattern as .category-hierarchy-crumb (see its own comment for why a
   plain calc() of --header-h/--header-nav-h, not a JS-measured value, is
   safe here: my-orders.html's header has no extra third row those two
   vars don't already account for). z-index 450 matches that same crumb --
   under the site header (500), above ordinary page content. An opaque
   background is required once sticky so order/address cards scrolling
   underneath don't show through. */
.my-orders-continue-shopping {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--ff-body); font-weight: 600; font-size: 14px;
  color: var(--green-dark); text-decoration: none;
  margin-top: var(--space-5);
}
.my-orders-continue-shopping svg { width: 16px; height: 16px; flex: none; }
.my-orders-continue-shopping:hover { color: var(--rose); }
.my-orders-tabs {
  display: flex; gap: 4px; border-bottom: 1.5px solid var(--border);
  margin-top: 14px; margin-bottom: 20px;
  position: sticky;
  /* --sticky-crumb-top: measured by the inline script in my-orders.html
     (mirrors js/category-hierarchy-nav.js's own updateStickyCrumbOffset())
     from #siteHeader's real rendered height -- the static calc() fallback
     below is wrong on its own below the 1120px breakpoint, where
     .header-nav-strip is display:none and --header-nav-h no longer
     applies (see that file's own comment for the full reasoning). */
  top: var(--sticky-crumb-top, calc(var(--header-h) + var(--header-nav-h)));
  z-index: 450;
  background: var(--cream);
}
.my-orders-tab {
  border: none; background: none; cursor: pointer;
  padding: 10px 4px; margin-right: 20px;
  font-family: var(--ff-body); font-weight: 600; font-size: 14.5px;
  color: var(--muted);
  border-bottom: 2px solid transparent;
  min-height: 44px;
}
.my-orders-tab.is-active { color: var(--green-dark); border-bottom-color: var(--rose); }

/* ---------- My Orders: pagination ---------- */
.my-orders-pagination {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 20px; padding-top: 16px;
  border-top: 1px solid var(--border);
}
.my-orders-pagination-status { font-size: 13px; color: var(--muted); }

/* ---------- My Orders: Favourites tab ---------- */
.my-orders-fav-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px;
}
@media (min-width: 640px) {
  .my-orders-fav-grid { grid-template-columns: repeat(3, 1fr); }
}
.my-orders-fav-card {
  border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; background: var(--white);
  display: flex; flex-direction: column;
}
.my-orders-fav-media { display: block; aspect-ratio: 1 / 1; background: var(--cream); }
.my-orders-fav-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.my-orders-fav-noimg { display: block; width: 100%; height: 100%; }
.my-orders-fav-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
.my-orders-fav-name { font-size: 13.5px; font-weight: 600; margin: 0; line-height: 1.3; }
.my-orders-fav-price { font-size: 13px; color: var(--muted); margin: 0; }
.my-orders-fav-actions { display: flex; gap: 8px; margin-top: auto; padding-top: 8px; }
.my-orders-fav-actions .btn { flex: 1; padding: 7px 10px; font-size: 12.5px; }
.my-orders-fav-remove { background: none; border: 1px solid var(--border); color: var(--muted); cursor: pointer; border-radius: var(--radius-sm, 8px); }
.my-orders-fav-remove:hover { border-color: var(--rose); color: var(--rose); }
.my-orders-fav-empty { text-align: center; padding: 40px 20px; }
.my-orders-fav-empty p { color: var(--muted); margin-bottom: 16px; }
.my-orders-fav-loading { color: var(--muted); padding: 20px 0; }

/* ---------- My Account: FNP-style tile dashboard (UI observations batch) ----------
   Replaces the previous header-dropdown account menu (Profile/My Orders/
   Address/Favourite all crammed into one small nested menu) with clear,
   equal-weight primary navigation -- mirrors FNP reference 02's tile
   layout adapted to Pushpmilan's own card/spacing language (same
   border/radius/shadow-on-hover pattern the site already uses for
   .cat-card and .my-orders-fav-card, not a copied FNP visual). */
.my-account-tiles {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
  margin: var(--space-5) 0;
}
@media (min-width: 640px) {
  .my-account-tiles { grid-template-columns: repeat(4, 1fr); }
}
.my-account-tile {
  display: block;
  text-align: center;
  padding: 28px 16px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.my-account-tile:hover { border-color: transparent; box-shadow: var(--shadow-md); }
.my-account-tile-ic {
  display: flex; align-items: center; justify-content: center;
  width: 52px; height: 52px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: var(--rose-light);
  color: var(--rose);
}
.my-account-tile-ic svg { width: 24px; height: 24px; }
.my-account-tile h3 { margin: 0 0 4px; font-family: var(--ff-body); font-weight: 600; font-size: 15px; color: var(--green-dark); }
.my-account-tile p { margin: 0; font-size: 12.5px; color: var(--muted); }
.my-account-signout { margin-top: var(--space-3); }

/* ---------- My Orders: read-only profile summary ----------
   Now the ONE place customer identity is shown (name/phone/email) AND
   the one place Sign Out lives -- previously split across a separate
   toolbar (name/email + Sign Out) and this card (name/phone/email again),
   which duplicated name+email on-screen simultaneously whenever the
   Account tab was open. margin-bottom tightened from 28px to the
   "Subsection" rhythm level (--space-4, 24px) between this card and
   Saved Addresses below it -- both are already visually separated by
   being distinct bordered cards, so the gap between them only needs to
   read as "next section," not a full "Section" (--space-5) break. */
.my-orders-profile-card {
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 18px 20px;
  margin-bottom: var(--space-4);
  display: flex; flex-direction: column; gap: 10px;
}
.my-orders-profile-row { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; }
.my-orders-profile-label { color: var(--muted); font-weight: 600; }
.my-orders-profile-value { color: var(--ink); word-break: break-word; text-align: right; }
/* Sign Out as a standalone, full-width action -- deliberately NOT nested
   inside .my-orders-profile-card (where, as a plain btn-sm, it previously
   read as just another card-level action, easy to confuse with an
   address's own Edit/Delete controls). Its own margin-bottom before
   Saved Addresses matches the same "Subsection" rhythm (--space-4) the
   profile card already uses below it, so the page's vertical spacing
   stays consistent even though this element is no longer inside a card. */
.my-orders-standalone-signout { margin-bottom: var(--space-4); }
