/* Extracted from the original single-file index.html. Shared by every page. */

  /* "Studio" — a clean minimal palette: near-white/near-black neutrals with
     one restrained clay accent, in the spirit of modern storefront design
     systems (Shopify, Dribbble showcase sites) rather than jewel tones. */
  :root, html[data-theme="light"] {
    color-scheme: light;
    --c-ivory: 250 250 248;      --c-porcelain: 255 255 255;
    --c-blush: 244 242 238;      --c-blushdeep: 234 231 224;
    --c-rose: 176 56 44;         --c-espresso: 23 23 21;
    --c-taupe: 96 93 86;      --c-taupelight: 228 224 216;
    --c-gold: 163 82 51;         --c-goldlight: 199 140 108;
    --c-golddark: 122 58 34;     --c-sage: 82 109 86;
    --pattern-line: 23,23,21;    --pattern-op: 0.025;
  }
  /* Same neutral/clay system, inverted for night mode — true dark neutrals
     rather than a tinted black, with the accent lightened to stay legible. */
  html[data-theme="dark"] {
    color-scheme: dark;
    --c-ivory: 14 14 13;         --c-porcelain: 22 22 20;
    --c-blush: 31 31 28;         --c-blushdeep: 40 40 36;
    --c-rose: 224 100 88;        --c-espresso: 240 239 234;
    --c-taupe: 166 162 152;      --c-taupelight: 55 55 50;
    --c-gold: 214 132 98;        --c-goldlight: 232 172 140;
    --c-golddark: 176 100 68;    --c-sage: 138 172 140;
    --pattern-line: 214,132,98;  --pattern-op: 0.04;
  }
  html, body { transition: background-color .4s ease, color .4s ease; }
  * { transition-property: background-color, border-color, color; transition-duration: .25s; }
  * { -webkit-tap-highlight-color: transparent; }
  /* NOTE: intentionally no overscroll-behavior-y:none here anymore — that was
     killing the browser's native pull-to-refresh gesture on the whole page.
     Scroll-chaining out of drawers/modals is still contained separately
     below (`.drawer, .overflow-y-auto { overscroll-behavior: contain; }`),
     so this only restores the top-level pull-to-refresh, nothing else changes. */
  html { scroll-behavior: smooth; overflow-x: clip; max-width: 100%; }
  button, a { touch-action: manipulation; }
  body {
    font-family: 'Manrope', sans-serif;
    color: rgb(var(--c-espresso));
    overflow-x: clip;
    background-color: rgb(var(--c-ivory));
  }
  .font-display { font-family: 'Space Grotesk', sans-serif; letter-spacing: -0.01em; }
  .font-mono { font-family: 'JetBrains Mono', monospace; }
  ::selection { background: rgb(var(--c-gold)); color: rgb(var(--c-porcelain)); }

  /* scrollbar */
  ::-webkit-scrollbar { width: 8px; height: 8px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: linear-gradient(180deg, rgb(var(--c-gold)), rgb(var(--c-goldlight))); border-radius: 10px; }

  /* stitch divider — signature motif: a running embroidery stitch in thread-gold */
  .stitch { height: 1px; background-image: linear-gradient(90deg, rgb(var(--c-goldlight)) 55%, transparent 45%); background-size: 14px 1px; background-repeat: repeat-x; opacity: .6; }
  .stitch-v { width: 1px; background-image: linear-gradient(180deg, rgb(var(--c-goldlight)) 55%, transparent 45%); background-size: 1px 14px; background-repeat: repeat-y; opacity: .45; }

  /* glass */
  .glass { background: rgb(var(--c-porcelain) / 0.94); border: 1px solid rgb(var(--c-taupelight) / 0.6); }

  /* ---- Homepage header floating over the hero ------------------------
     .hero-header is added by JS on the homepage only; .is-transparent is
     toggled on/off by scroll position. Everything here is scoped to those
     two classes so the other 11 pages keep the normal solid header.

     FIXED rather than sticky: sticky keeps the header in normal flow, which
     pushes the hero down instead of letting the photo run up behind it. */
  @media (min-width: 768px) {
    #siteHeader.hero-header {
      position: fixed;
      left: 0; right: 0;
      top: var(--topbar-h, 0px);
    }
  }
  /* The pieces below each carry their own text colour, so the fade has to be
     declared on them too — without this the background eases while the text
     snaps from dark to light. */
  #siteHeader,
  #siteHeader nav,
  #siteHeader .text-espresso,
  #siteHeader .brand-logo-tag {
    transition: color .3s ease, background-color .3s ease, border-color .3s ease;
  }
  #siteHeader.is-transparent {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
  }
  /* A transparent header sits on whatever banner Admin happens to upload, and
     a light one (a bright window, a pale wall) leaves white nav text with
     nothing to read against. This scrim guarantees the contrast without
     bringing back a visible bar: opaque enough at the very top where the text
     is, gone by the bottom edge.

     It lives on a pseudo-element with an opacity transition because gradients
     cannot be interpolated — setting background-image straight on the header
     would make the scrim snap in and out instead of fading with everything
     else. */
  #siteHeader.hero-header::before {
    content: '';
    position: absolute;
    /* Extends BELOW the header so the gradient can stay strong across the
       full height where the text actually is, and do its fading out in the
       48px underneath — where there is nothing to read. Fading within the
       header itself left the nav sitting on the weakest part of the scrim. */
    inset: 0 0 -48px 0;
    z-index: 0;
    pointer-events: none;
    background: linear-gradient(to bottom, rgb(0 0 0 / .6), rgb(0 0 0 / .5) 45%, rgb(0 0 0 / 0));
    opacity: 0;
    transition: opacity .3s ease;
  }
  #siteHeader.hero-header.is-transparent::before { opacity: 1; }
  /* Keeps the nav and icons above the scrim (the header is a stacking context
     of its own, so these two z-indexes only compete with each other). */
  #siteHeader.hero-header > div { position: relative; z-index: 1; }
  /* Belt and braces for a banner that is bright right under the text. */
  #siteHeader.is-transparent { text-shadow: 0 1px 10px rgb(0 0 0 / .45); }
  #siteHeader.is-transparent svg { filter: drop-shadow(0 1px 5px rgb(0 0 0 / .45)); }
  /* Icon buttons carry no colour of their own, so they inherit this. Lucide
     renders its icons with stroke="currentColor", so they follow along. */
  #siteHeader.is-transparent,
  #siteHeader.is-transparent nav,
  #siteHeader.is-transparent .text-espresso { color: rgb(255 255 255 / .95); }
  #siteHeader.is-transparent .brand-logo-tag { color: rgb(255 255 255 / .72); }
  /* The Shop mega-menu is a white panel of its own that happens to live
     inside the header, so it must opt OUT of everything above: white text on
     its white background is invisible, and the glow meant for text on a photo
     just smears it. Anything else in the header that brings its own
     background needs the same treatment. */
  #siteHeader.is-transparent .megamenu,
  #siteHeader.is-transparent .megamenu .text-espresso { color: rgb(var(--c-espresso)); }
  #siteHeader.is-transparent .megamenu,
  #siteHeader.is-transparent .megamenu * { text-shadow: none; }
  #siteHeader.is-transparent .megamenu svg { filter: none; }
  /* Deliberately NOT re-coloured: .brand-logo-gold still reads well on the
     hero, and the cart/wishlist count pills keep their own solid background. */
  .glass-dark { background: rgba(8,10,20,0.72); border: 1px solid rgba(148,128,255,0.16); }
  /* Countdown chips on the Flash Sale banner. The section itself
     (.flash-sale-dark) is now a fixed dark band regardless of theme (see
     below), so this chip just needs a plain light-glass look on dark —
     no theme variables, so it can't flip out of contrast in dark mode. */
  .sale-countdown-box { background: rgba(255,255,255,0.10); border: 1px solid rgba(255,255,255,0.22); }
  .sale-countdown-num { color: #faf6ee; }
  .sale-countdown-label { color: rgba(250,246,238,0.65); }
  /* Flash Sale band: this section (and its .sale-pcard product cards, whose
     text is hardcoded white) is only meant to ever look "dark with light
     text" — it never should have been theme-reactive. It previously used
     bg-espresso/text-ivory, whose tokens invert with the theme; in dark
     mode that flips the whole band to a near-white background while the
     child card text stayed hardcoded white, making it disappear. Fixed
     colors here (matching the light-mode espresso/ivory values) keep the
     exact same look in light mode and fix dark mode too. */
  .flash-sale-dark { background: #171715; color: #faf6ee; }

  /* fade in on scroll */
  .reveal { opacity: 0; transform: translateY(28px); transition: opacity .8s cubic-bezier(.22,1,.36,1), transform .8s cubic-bezier(.22,1,.36,1); }
  .reveal.in { opacity: 1; transform: translateY(0); }
  /* Content-hiding fix (see the early <html class="js"> script in <head>):
     without JS running, these sections stay fully visible instead of
     opacity:0, so crawlers/tools that don't execute JS still see them. */
  html:not(.js) .reveal { opacity: 1; transform: none; }

  /* skeleton */
  @keyframes shimmer { 0% { background-position: -400px 0; } 100% { background-position: 400px 0; } }
  .skeleton { background: linear-gradient(90deg, rgb(var(--c-blush)) 25%, rgb(var(--c-porcelain)) 37%, rgb(var(--c-blush)) 63%); background-size: 400px 100%; animation: shimmer 1.4s infinite linear; }

  /* product card hover */
  .pcard .pc-badge, .pcard .pc-heartwrap, .pcard .pc-viewsbadge, .pcard .quick-actions, .pcard .pc-heartbtn { z-index: 2; }

  /* ---- card photo: push-in on hover + white shutter on the photo swap ----
     The photo eases in while the pointer is over the card, and the moment it
     swaps to the product's second shot a white layer pulses over it, so the
     change reads as a deliberate transition rather than a hard cut.

     The flash is its OWN white layer stacked above the photo — never opacity
     on the <img> itself. Fading the image out exposes whatever sits behind
     it, and that is exactly what used to read as a black flash here. */
  .pcard .pimg { transition: transform .65s cubic-bezier(.22,1,.36,1); }
  .pcard .pc-flash {
    position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background: #fff; opacity: 0;
  }
  /* Sits above the photo but below the badges/heart/Add-to-Bag pill (z-index 2),
     so the chrome stays crisp while the photo behind it blows out to white. */
  .pcard .pc-flash.is-flashing { animation: pcPhotoFlash .42s ease-out; }
  @keyframes pcPhotoFlash {
    0%   { opacity: 0; }
    30%  { opacity: .8; }
    100% { opacity: 0; }
  }
  @media (hover: hover) {
    .pcard:hover .pimg { transform: scale(1.06); }
    /* Extra scale held only across the swap itself; dropping the class lets
       the photo settle back to the 1.06 hover state on its own transition. */
    .pcard .pimg.is-swapping { transform: scale(1.12); }
  }
  @media (prefers-reduced-motion: reduce) {
    .pcard .pimg,
    .pcard:hover .pimg,
    .pcard .pimg.is-swapping { transform: none; transition: none; }
    .pcard .pc-flash.is-flashing { animation: none; }
  }
  .pcard .quick-actions { opacity: 0; transform: translate(-50%, 8px); transition: all .35s ease; pointer-events: none; }
  .pcard:hover .quick-actions { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }
  /* The pill's Tailwind sizing was set when this button only ever appeared on
     the narrow carousel cards. It is the primary call to action on 340px-wide
     shop cards now, where 11px left it smaller than the product name (14px)
     sitting right below it — a CTA should not be the smallest text on a card.
     Mobile is already proportionate (the max-width:767px block further down
     sets its own size) so this is scoped to the wider layout only. */
  @media (min-width: 768px) {
    .pcard .pc-quickbtn { font-size: 13px; padding: .6rem 1.5rem; letter-spacing: .015em; }
  }
  /* Wishlist heart wrapper: right-anchored (not centered like the "Add to
     Bag" pill above), so it only fades/slides vertically — no -50% X shift,
     otherwise it drifts left away from the card's right edge. */
  .pcard .pc-heartwrap { opacity: 0; transform: translateY(8px); transition: all .35s ease; pointer-events: none; }
  .pcard:hover .pc-heartwrap { opacity: 1; transform: translateY(0); pointer-events: auto; }
  /* Views badge (bottom-right corner): same hover-fade treatment as the
     wishlist heart. NOT currently rendered — productCardHTML stopped
     emitting .pc-viewsbadge, so the card hover is just "Add to Bag".
     Kept so re-enabling the badge needs no CSS work. */
  .pcard .pc-viewsbadge { opacity: 0; transform: translateY(8px); transition: all .35s ease; }
  .pcard:hover .pc-viewsbadge { opacity: 1; transform: translateY(0); }
  /* Touch devices have no :hover, so quick-actions (wishlist heart, Add to Bag)
     would otherwise be permanently invisible on mobile — always show them there. */
  @media (hover: none) {
    .pcard .quick-actions { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }
    .pcard .pc-heartwrap { opacity: 1; transform: translateY(0); pointer-events: auto; }
    .pcard .pc-viewsbadge { opacity: 1; transform: translateY(0); }
  }
  .pcard { transition: transform .45s cubic-bezier(.22,1,.36,1); }
  /* Only lift the card on real hover (mouse/trackpad). On touch devices,
     tapping simulates :hover with no corresponding "leave" event, so this
     would otherwise leave the card permanently raised after a tap. */
  @media (hover: hover) {
    .pcard:hover { transform: translateY(-6px); }
  }
  .pcard > div:first-child { transition: box-shadow .45s ease; }
  .pcard:hover > div:first-child { box-shadow: 0 18px 40px -14px rgba(27,51,88,0.35), 0 0 0 1px rgba(180,148,92,0.22); }
  /* Clicking a button left its focus ring sitting there until focus moved
     somewhere else — most obvious on the header's theme toggle, which keeps
     a box around it after every click. The Tailwind build carries a bare
     `:focus { border-color: gold }` base rule, and the browser adds its own
     outline on top; neither distinguishes a mouse click from keyboard
     navigation.

     :focus-visible is that distinction. Keyboard users still get a clear
     ring — it is their only way to know where they are — while a click
     leaves nothing behind. Inputs and selects are deliberately NOT included:
     a text field should keep showing its focus border while being typed in. */
  button:focus:not(:focus-visible),
  a:focus:not(:focus-visible),
  [role="button"]:focus:not(:focus-visible) {
    outline: none;
    box-shadow: none;
    border-color: transparent;
  }
  button:focus-visible,
  a:focus-visible,
  [role="button"]:focus-visible {
    outline: 2px solid rgb(var(--c-gold));
    outline-offset: 2px;
    border-radius: 4px;
  }

  .icon-nudge { transition: transform .3s cubic-bezier(.34,1.56,.64,1); }
  /* Forces the header "Track Order" icon off regardless of the sm:flex
     responsive display it also carries — toggled by JS once a customer is
     signed in, since "My Orders" replaces it at that point. */
  .signed-in-hide { display: none !important; }
  /* Real hover only — on touch, tapping simulates :hover with no "leave"
     event, which would leave header icons (theme toggle, cart) stuck
     nudged-up after a tap. :active still applies on both. */
  @media (hover: hover) {
    .icon-nudge:hover { transform: translateY(-2px) scale(1.08); }
  }
  .icon-nudge:active { transform: scale(0.92); }

  /* cart icon "new item" shake — started on add-to-cart, stopped once the cart is opened */
  @keyframes cartShake {
    0%, 8%, 100% { transform: rotate(0deg); }
    1%, 3%, 5%, 7% { transform: rotate(-15deg); }
    2%, 4%, 6% { transform: rotate(15deg); }
  }
  .cart-shake { animation: cartShake 1.8s ease-in-out infinite; transform-origin: 70% 70%; }

  /* draggable support-chat widget */
  .chat-drag-handle { cursor: grab; touch-action: none; }
  .chat-drag-handle:active { cursor: grabbing; }

  /* whatsapp "dodge" — hops out of the way if the dragged chat bubble touches it */
  #whatsappFab { transition: transform .32s cubic-bezier(.34,1.56,.64,1); }

  /* Chat bubble horizontal centering: the bubble is wider than the WhatsApp
     FAB it sits above (80px vs 56px desktop, 60px vs 44px mobile). Sharing
     the same `right` offset as WhatsApp therefore left-shifts the bubble's
     visual center relative to WhatsApp's. Solving right_child = right_parent
     + (width_parent - width_child)/2 for both breakpoints lands on the same
     12px offset in each case, so a single fixed value centers it at every
     size instead of needing a per-breakpoint override.
     Not !important: this is only the UNDRAGGED default position. The ID
     selector already beats the Tailwind right-5/right-6 utility classes on
     its own, but an !important here would also beat the drag script's
     inline style once the visitor repositions the bubble, permanently
     pinning it back to this edge and killing free dragging. */
  #chatBubble { right: 12px; }

  /* Bot greeting speech bubble — appears above the bot's head once the
     assembly animation finishes. */
  #botGreeting {
    position: fixed;
    bottom: 210px; /* chatBubble bottom(120px) + height(80px) + 10px gap */
    right: 12px;
    z-index: 41;
    max-width: 188px;
    background: rgb(var(--c-porcelain));
    border: 1px solid rgb(var(--c-taupelight) / 0.6);
    border-radius: 14px;
    font-size: 12.5px;
    line-height: 1.35;
    font-weight: 600;
    color: rgb(var(--c-espresso));
    box-shadow: 0 10px 28px rgba(0,0,0,.14);
    opacity: 0;
    transform: translateY(8px) scale(.94);
    transform-origin: bottom right;
    transition: opacity .45s ease, transform .45s cubic-bezier(.34,1.56,.64,1);
    pointer-events: none;
  }
  #botGreeting.show { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
  /* The clickable surface is now a real <button> (see markup) instead of
     an onclick on the outer div, so it carries the bubble's padding/cursor
     instead of the wrapper. Fixes an invalid accessibility-tree structure
     where a role="status" div with a click handler contained a real
     <button> child (nested interactive controls). */
  #botGreeting .botGreeting-open {
    display: block;
    width: 100%;
    padding: 9px 26px 9px 13px;
    background: transparent;
    border: 0;
    border-radius: inherit;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
  }
  #botGreeting::after {
    content: '';
    position: absolute;
    bottom: -6px;
    right: 30px;
    width: 12px; height: 12px;
    background: rgb(var(--c-porcelain));
    border-right: 1px solid rgb(var(--c-taupelight) / 0.6);
    border-bottom: 1px solid rgb(var(--c-taupelight) / 0.6);
    transform: rotate(45deg);
  }
  #botGreeting .botGreeting-close {
    position: absolute; top: 4px; right: 4px;
    width: 18px; height: 18px; border-radius: 999px;
    display: flex; align-items: center; justify-content: center;
    color: rgb(var(--c-taupe));
  }
  #botGreeting .botGreeting-close:hover { color: rgb(var(--c-espresso)); }
  @media (max-width: 767px){
    #botGreeting{
      /* above chat: chat bottom + chat height(60) + small gap */
      bottom: calc(1.5rem + 44px + 16px + 60px + 10px + var(--mobile-nav-h) + env(safe-area-inset-bottom));
      right: 12px;
      max-width: 168px;
      font-size: 12px;
    }
    #botGreeting::after{ right: 22px; }
  }

  /* Chat bubble robot: scattered-pixel assembly on first appearance, then
     the bot icon assembles out of the pixels. No back-circle — just the
     bot face floating free, sized to match the WhatsApp icon beside it. */
  #chatBubble {
    background: none;
    border: none;
    box-shadow: none;
  }
  .chat-bubble-pixels { position:absolute; inset:0; pointer-events:none; }
  .chat-bubble-pixels .px {
    position:absolute; top:50%; left:50%;
    border-radius:1px; opacity:0;
    transition: transform 1.4s cubic-bezier(.16,1,.3,1), opacity 1.1s ease;
    box-shadow: 0 0 6px 0 currentColor;
  }
  .chat-bubble-iconwrap {
    opacity:0; transform:scale(.7);
    transition: opacity .5s ease, transform .5s cubic-bezier(.34,1.56,.64,1);
  }
  .chat-bubble-iconwrap.bot-in { opacity:1; transform:scale(1); }
  .bot-eye { transform-box: fill-box; transform-origin:center; animation: botBlink 2.6s ease-in-out infinite; }
  .bot-eye-r { animation-delay: .08s; }
  @keyframes botBlink { 0%, 90%, 100% { transform:scaleY(1); } 94% { transform:scaleY(.12); } }

  /* Sleep/wake states: after a stretch of no page activity the bot's eyes
     close and a floating "zzz" appears; falling asleep eases in gently,
     waking is instant (see .wake-instant) so it reads as a sudden startle. */
  .bot-open-eye, .bot-eye-glow {
    opacity:1; transition: opacity .5s ease;
  }
  .bot-eyelid {
    opacity:0; transition: opacity .5s ease;
  }
  .bot-zzz {
    opacity:0; transform-box: fill-box; transform-origin:center;
    transform: translateY(6px) scale(.7);
    transition: opacity .5s ease, transform .5s ease;
  }
  #chatBubbleIconWrap.asleep .bot-open-eye,
  #chatBubbleIconWrap.asleep .bot-eye-glow { opacity:0; }
  #chatBubbleIconWrap.asleep .bot-eyelid { opacity:1; }
  #chatBubbleIconWrap.asleep .bot-zzz { opacity:1; transform:translateY(0) scale(1); }
  #chatBubbleIconWrap.asleep .bot-eye { animation-play-state:paused; }
  #chatBubbleIconWrap.asleep .bot-zzz text {
    animation: zzzFloat 2.4s ease-in-out infinite;
    transform-box: fill-box; transform-origin:center;
  }
  @keyframes zzzFloat {
    0%, 100% { transform:translateY(0); opacity:.55; }
    50% { transform:translateY(-4px); opacity:1; }
  }
  /* while this class is present, every sleep-related transition is killed —
     used for exactly one frame on wake so the eyes snap open instantly */
  #chatBubbleIconWrap.wake-instant,
  #chatBubbleIconWrap.wake-instant .bot-open-eye,
  #chatBubbleIconWrap.wake-instant .bot-eye-glow,
  #chatBubbleIconWrap.wake-instant .bot-eyelid,
  #chatBubbleIconWrap.wake-instant .bot-zzz { transition:none !important; }

  /* ===========================================================
     MOBILE: hide the hero overlay text/CTAs (eyebrow, tagline,
     "Shop New Arrivals" / "Browse All") — photo speaks for itself
     on small screens; kept intact on tablet/desktop.
     =========================================================== */
  @media (max-width: 767px){
    .hero-cinematic-content { display: none !important; }
  }

  /* ===========================================================
     MOBILE: locked bottom tab bar (Menu / Home / Account / Search),
     replacing the old floating cart FAB. Every other bottom-anchored
     floating widget is nudged up by --mobile-nav-h so nothing sits
     underneath the bar.
     =========================================================== */
  :root { --mobile-nav-h: 60px; }
  #mobileTabBar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    height: var(--mobile-nav-h);
    padding-bottom: env(safe-area-inset-bottom);
    display: grid; grid-template-columns: repeat(4, 1fr);
    background: rgb(var(--c-porcelain));
    border-top: 1px solid rgb(var(--c-taupelight)/.45);
    box-shadow: 0 -6px 20px -12px rgba(0,0,0,.18);
  }
  #mobileTabBar button {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; color: rgb(var(--c-espresso)/.75); background: none; border: none;
    font-size: 10px; font-weight: 600; letter-spacing: .01em;
  }
  #mobileTabBar button:active { color: rgb(var(--c-gold)); }
  #mobileTabBar svg { width: 19px; height: 19px; }
  @media (min-width: 768px){ #mobileTabBar{ display: none !important; } }

  @media (max-width: 767px){
    body { padding-bottom: calc(var(--mobile-nav-h) + env(safe-area-inset-bottom)); }
    #whatsappFab { bottom: calc(1.5rem + var(--mobile-nav-h) + env(safe-area-inset-bottom)) !important; }
    #backTop { bottom: calc(6rem + var(--mobile-nav-h) + env(safe-area-inset-bottom)) !important; }
    /* Chat sits clearly above WhatsApp: WA height 44px + ~16px gap above
       the WA bottom offset. Not !important so drag can still move it. */
    #chatBubble { bottom: calc(1.5rem + 44px + 16px + var(--mobile-nav-h) + env(safe-area-inset-bottom)); }
    #chatPanel { bottom: calc(1.5rem + 44px + 16px + var(--mobile-nav-h) + env(safe-area-inset-bottom)) !important; }
    #toast { bottom: calc(1.5rem + var(--mobile-nav-h) + env(safe-area-inset-bottom)) !important; }
  }

  /* Pin the floating action buttons to their own compositor layer. Without
     this, mobile browsers recompute their `bottom` offset (which depends on
     env(safe-area-inset-bottom)) on every scroll frame while the address
     bar collapses/expands, which shows up as a small vertical wobble as
     you scroll. Promoting them to a GPU layer lets the compositor pin them
     to the visual viewport directly instead of re-laying them out. */
  #chatBubble, #whatsappFab, #cartFab, #backTop, #mobileTabBar {
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    will-change: transform;
  }

  /* buttons */
  .btn-gold { background: rgb(var(--c-espresso)); color:rgb(var(--c-ivory)); transition: background .25s ease, transform .2s cubic-bezier(.34,1.56,.64,1); }
  .btn-gold:hover { background: rgb(var(--c-gold)); transform: translateY(-1px); }
  .btn-gold:active { transform: translateY(0) scale(0.98); }
  .btn-ghost { border:1px solid rgb(var(--c-espresso) / 0.7); color:rgb(var(--c-espresso)); }
  .btn-ghost:hover { background:rgb(var(--c-espresso)); color:rgb(var(--c-ivory)); }
  .btn-blush { background:rgb(var(--c-blush)); color:rgb(var(--c-espresso)); }
  .btn-blush:hover { background:rgb(var(--c-blushdeep)); }

  /* ============================================================
     SHOP GRID — "Gloebuy" style catalog layout: clean white cards,
     checklist filters, active-filter chips. Scoped under #shopView
     only, so it never touches the rest of the site's look.
  ============================================================ */
  /* Shop body grid: definite row height so filter/product columns don't collapse.
     Desktop-only (md+) — this is what lets the filter column and product column
     sit side-by-side and each scroll internally at full height. On mobile the
     two stack in normal flow instead, so forcing this same fixed-height single
     row there was clipping #shopMainCol's background/border to the viewport
     height while its overflowing product cards kept rendering past it. */
  @media (min-width: 768px) {
    #shopView .shop-body-grid { grid-template-rows: minmax(0, 1fr); }
  }
  /* Mobile scroll container (#shopBodyGrid) uses the browser's default
     scrollbar, which on some platforms (esp. desktop Chrome DevTools mobile
     emulation, and some Android WebViews) renders as a wide, opaque bar
     sitting right at the edge — visually competing with the product grid.
     Thin it out and make it unobtrusive without hiding it entirely (keeping
     it visible/usable matters for accessibility and discoverability). */
  #shopBodyGrid { scrollbar-width: thin; scrollbar-color: rgb(var(--c-taupelight)) transparent; }
  #shopBodyGrid::-webkit-scrollbar { width: 4px; }
  #shopBodyGrid::-webkit-scrollbar-track { background: transparent; }
  #shopBodyGrid::-webkit-scrollbar-thumb { background: rgb(var(--c-taupelight)); border-radius: 999px; }
  #shopView .shop-filter-block label { user-select:none; }
  #shopView .filter-panel-stack > * + * { margin-top: 1rem; }
  #shopView .filter-check-row { display:flex; align-items:center; gap:.6rem; cursor:pointer; }
  #shopView .filter-check-row input { position:absolute; opacity:0; width:1px; height:1px; }
  #shopView .filter-check-box { width:17px; height:17px; border-radius:5px; border:1.5px solid rgb(var(--c-taupelight)); display:flex; align-items:center; justify-content:center; flex-shrink:0; transition: all .18s ease; }
  #shopView .filter-check-row input:checked + .filter-check-box { background:rgb(var(--c-espresso)); border-color:rgb(var(--c-espresso)); }
  #shopView .filter-check-box svg { width:11px; height:11px; opacity:0; transform:scale(.5); transition: all .15s ease; stroke:rgb(var(--c-porcelain)); }
  #shopView .filter-check-row input:checked + .filter-check-box svg { opacity:1; transform:scale(1); }
  /* Rating tiers with zero matches shown disabled. */
  #shopView .filter-check-row.is-disabled { opacity:.4; cursor:not-allowed; }
  #shopView .filter-check-row .facet-count { color:rgb(var(--c-taupe)); font-weight:500; }
  #shopView .filter-color-row { display:flex; align-items:center; gap:.55rem; cursor:pointer; }
  #shopView .filter-color-row .dot { width:14px; height:14px; border-radius:999px; flex-shrink:0; box-shadow: inset 0 0 0 1px rgb(0 0 0 / .12); }
  #shopView .filter-color-row .ring { width:17px; height:17px; border-radius:999px; border:1.5px solid transparent; display:flex; align-items:center; justify-content:center; transition: border-color .18s ease; }
  #shopView .filter-color-row.is-active .ring { border-color:rgb(var(--c-espresso)); }
  #shopView input[type="range"] { -webkit-appearance:none; appearance:none; height:4px; border-radius:99px; background:rgb(var(--c-taupelight)); outline:none; }
  #shopView input[type="range"]::-webkit-slider-thumb { -webkit-appearance:none; width:16px; height:16px; border-radius:50%; background:rgb(var(--c-espresso)); border:3px solid rgb(var(--c-porcelain)); box-shadow:0 0 0 1px rgb(var(--c-taupelight)); cursor:pointer; margin-top:-6px; }
  #shopView input[type="range"]::-webkit-slider-runnable-track { height:4px; border-radius:99px; }
  #shopView input[type="range"]::-moz-range-thumb { width:16px; height:16px; border-radius:50%; background:rgb(var(--c-espresso)); border:3px solid rgb(var(--c-porcelain)); cursor:pointer; }
  #shopView .shop-chip { display:inline-flex; align-items:center; gap:.4rem; padding:.35rem .5rem .35rem .85rem; border-radius:999px; border:1px solid rgb(var(--c-taupelight)); font-size:.72rem; font-weight:600; color:rgb(var(--c-espresso)); background:rgb(var(--c-porcelain)); white-space:nowrap; }
  #shopView .shop-chip button { min-width:24px; min-height:24px; border-radius:999px; background:rgb(var(--c-blush)); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
  #shopView .shop-chip button:hover { background:rgb(var(--c-blushdeep)); }
  #shopView .shop-size-btn { min-width:2.5rem; padding:.4rem .55rem; border-radius:999px; border:1px solid rgb(var(--c-taupelight)); font-size:.72rem; font-weight:600; text-align:center; transition: all .18s ease; }
  #shopView .shop-size-btn.is-active { background:rgb(var(--c-espresso)); color:rgb(var(--c-porcelain)); border-color:rgb(var(--c-espresso)); }
  /* product cards, reworked to sit flush white with a border instead of the
     dark-glass site look — kept scoped to #shopGrid only */
  #shopGrid.shop-grid-clean { background:rgb(var(--c-porcelain)); }
  /* Un-scoped from #shopGrid on purpose — this same white-card style (with
     rating stars + Add to Cart) is now reused for product cards on the main
     site pages too, not just inside the shop/filter view. The class is only
     ever added by JS when a card opts into it (productCardHTML's shop
     param), so widening the selector doesn't affect any other cards. */
  .pcard.shop-pcard { background:rgb(var(--c-porcelain)); border:1px solid rgb(var(--c-taupelight) / .5); border-radius:0; padding:0; overflow:hidden; }
  .pcard.shop-pcard > div:first-child { border-radius:0; }
  .pcard.shop-pcard:hover { transform:none; box-shadow:0 10px 26px -14px rgb(0 0 0 / .18); }
  /* The .shop-addbtn strip used to sit below .pc-body and supplied the
     card's bottom breathing room via its own margin. The call to action is
     the hover "Add to Bag" pill on the image now, so .pc-body is the last
     child and has to carry that spacing itself. */
  .pcard.shop-pcard .pc-body { padding-left:.55rem; padding-right:.55rem; padding-bottom:.7rem; }
  /* NOT currently rendered — productCardHTML no longer emits .shop-addbtn.
     Kept so the under-price button can be restored without redoing its look. */
  .pcard.shop-pcard .shop-addbtn { width:auto; margin:.35rem .55rem .55rem; padding:.4rem 0; border-radius:2px; border:1.5px solid rgb(var(--c-espresso)); font-size:.74rem; font-weight:700; color:rgb(var(--c-espresso)); background:transparent; transition: all .2s ease; }
  .pcard.shop-pcard .shop-addbtn:hover { background:rgb(var(--c-espresso)); color:rgb(var(--c-porcelain)); }
  .pcard.shop-pcard .shop-desc { font-size:.72rem; color:rgb(var(--c-taupe)); line-height:1.3; }
  .pcard.shop-pcard .shop-rating { display:flex; align-items:center; gap:.3rem; }
  .pcard.shop-pcard .shop-rating svg { width:11px; height:11px; }
  .pcard.shop-pcard .shop-rating span { font-size:.68rem; color:rgb(var(--c-taupe)); }

  /* Dark "glass" card variant — used only for the Sale Edit row, whose
     section background is near-black. Overrides the white shop-pcard
     look so cards read as part of the dark scene instead of stark
     white boxes floating on black. */
  .pcard.sale-pcard { background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.16); border-radius:0; }
  .pcard.sale-pcard > div:first-child { border-radius:0; }
  .pcard.sale-pcard:hover { box-shadow:0 14px 34px -16px rgba(0,0,0,0.55); }
  .pcard.sale-pcard .pc-cat { color:rgba(255,255,255,0.55); }
  .pcard.sale-pcard .pc-name { color:#fff; }
  .pcard.sale-pcard .pc-price { color:rgb(var(--c-goldlight)); }
  .pcard.sale-pcard .pc-oldprice { color:rgba(255,255,255,0.4); }
  .pcard.sale-pcard .shop-addbtn { border-color:rgba(255,255,255,0.55); color:#fff; }
  .pcard.sale-pcard .shop-addbtn:hover { background:#fff; color:#0A0A14; }
  .pcard.sale-pcard .shop-rating span { color:rgba(255,255,255,0.55); }

  /* hero-over-photo variants — always light-on-dark regardless of active theme, since the hero always sits on a scrimmed photograph */
  .btn-ghost-hero { border:1px solid rgba(255,255,255,0.55); color:#fff; }
  .btn-ghost-hero:hover { background:#fff; color:#0A0A14; }
  .btn-glass-hero { background:rgba(255,255,255,0.12); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); color:#fff; border:1px solid rgba(255,255,255,0.28); }
  .btn-glass-hero:hover { background:rgba(255,255,255,0.22); }

  /* drawer — default is for RIGHT-anchored panels (cart/wishlist): hide off-screen to the right */
  /* overscroll-behavior: contain stops scroll chaining — once a drawer/modal's own
     scroll hits its top or bottom, further scrolling no longer bleeds through to
     the page behind it (previously the main menu would start scrolling once an
     open panel like the product modal or cart drawer ran out of room to scroll).
     Scoped to vertical containers only — applying it to .overflow-x-auto too
     caught the horizontal product carousels (which sit in normal page flow, not
     inside a drawer) and blocked a vertical swipe from continuing to scroll the
     page once it started on top of a product card in one of those rows. */
  .drawer, .overflow-y-auto, .overflow-x-auto { -webkit-overflow-scrolling: touch; }
  .drawer, .overflow-y-auto { overscroll-behavior: contain; }

  .drawer { transform: translateX(100%); transition: transform .5s cubic-bezier(.22,1,.36,1); }
  .drawer.open { transform: translateX(0); }
  /* mobileNav is LEFT-anchored, so it must hide off-screen to the left instead — otherwise on wide
     viewports translateX(100%) just shifts it sideways while staying on screen. ID selector wins over .drawer. */
  #mobileNav.drawer { transform: translateX(-100%); }
  #mobileNav.drawer.open { transform: translateX(0); }
  .overlay-bg { opacity:0; pointer-events:none; transition: opacity .4s ease; }
  .overlay-bg.open { opacity:1; pointer-events:auto; }

  .modal-pop { opacity:0; transform: scale(.94) translateY(10px); transition: all .4s cubic-bezier(.22,1,.36,1); }
  .modal-pop.open { opacity:1; transform: scale(1) translateY(0); }

  .megamenu { opacity:0; transform: translateY(-8px); pointer-events:none; transition: all .3s cubic-bezier(.22,1,.36,1); max-width: calc(100vw - 32px); }
  .nav-underline { position:relative; }
  .nav-underline::after{ content:''; position:absolute; left:0; bottom:-6px; width:0; height:1px; background:currentColor; transition:width .35s cubic-bezier(.22,1,.36,1); }
  .nav-underline:hover::after{ width:100%; }
  .logo-mark { transition: transform .6s cubic-bezier(.22,1,.36,1); }
  .group\/logo:hover .logo-mark { transform: rotate(12deg); }
  @media (max-width: 767px) {
    header nav.hidden.md\:flex { display: none !important; }
  }
  /* Guard against the header wordmark pushing the cart/wishlist icons past the
     clipped viewport edge on narrow phones — shrink the logo text instead. */
  @media (max-width: 380px) {
    header .font-display.text-xl { font-size: 1rem; }
    header .flex.items-center.gap-1 { gap: 0.1rem; }
  }
  .group:hover .megamenu, .megamenu.pin { opacity:1; transform: translateY(0); pointer-events:auto; }

  input[type=range] { accent-color: #B28C4A; }
  input[type=checkbox] { accent-color: #B28C4A; }

  /* Form fields — force explicit bg/text colors tied to the theme variables so
     inputs never inherit mismatched light-on-light or white-on-white text in
     dark/gold mode. Higher specificity than any bg-porcelain utility class. */
  input[type="text"], input[type="email"], input[type="password"], input[type="tel"],
  input[type="number"], input[type="url"], input[type="search"], input[type="date"],
  input:not([type]), textarea, select {
    background-color: rgb(var(--c-porcelain));
    color: rgb(var(--c-espresso));
  }
  input::placeholder, textarea::placeholder { color: rgb(var(--c-taupe)); opacity: 1; }
  select option { background-color: rgb(var(--c-porcelain)); color: rgb(var(--c-espresso)); }

  .marquee-track { animation: marquee 22s linear infinite; }
  @keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

  /* ============================================================
     HERO — Cinematic full-bleed editorial
     Modern fashion look: full-viewport photography with refined
     overlay typography. Keeps all dynamic IDs used by applyHeroContent /
     renderHeroSlider so Admin banners continue to drive content.
     ============================================================ */
  .hero-cinematic {
    position: relative;
    width: 100%;
    min-height: 88vh;
    max-height: 980px;
    background: rgb(var(--c-espresso));
    overflow: hidden;
  }
  @media (max-width: 767px) {
    .hero-cinematic { min-height: 78vh; max-height: none; }
  }

  /* Full-bleed slider */
  #heroSlider {
    position: absolute;
    inset: 0;
    z-index: 0;
  }
  .hero-slide {
    position: absolute;
    inset: 0;
    background: rgb(var(--c-espresso));
    opacity: 0;
    transition: opacity 1s cubic-bezier(.4,0,.2,1);
    pointer-events: none;
  }
  .hero-slide.active,
  .hero-slide.opacity-100 {
    opacity: 1;
    pointer-events: auto;
    z-index: 1;
  }
  .hero-slide-bg { display: none; }
  .hero-slide picture {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
  }
  .hero-slide-fg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: var(--fx-m, 50%) var(--fy-m, 42%);
    transform: scale(1.02);
    transition: transform 6s ease-out;
  }
  .hero-slide.active .hero-slide-fg,
  .hero-slide.opacity-100 .hero-slide-fg {
    transform: scale(1);
  }
  @media (min-width: 768px) {
    .hero-slide-fg { object-position: var(--fx-d, 50%) var(--fy-d, 42%); }
  }

  /* Layered scrim — strong on left/bottom for text, softer elsewhere */
  .hero-cinematic-scrim {
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    background:
      linear-gradient(90deg, rgba(14,14,13,0.72) 0%, rgba(14,14,13,0.45) 38%, rgba(14,14,13,0.12) 68%, transparent 100%),
      linear-gradient(0deg, rgba(14,14,13,0.55) 0%, transparent 45%);
  }
  @media (max-width: 767px) {
    .hero-cinematic-scrim {
      background:
        linear-gradient(0deg, rgba(14,14,13,0.78) 0%, rgba(14,14,13,0.35) 42%, rgba(14,14,13,0.15) 70%, transparent 100%);
    }
  }

  /* Content overlay */
  .hero-cinematic-content {
    position: relative;
    z-index: 3;
    height: 100%;
    min-height: inherit;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding: 28px 20px 112px;
  }
  @media (min-width: 768px) {
    .hero-cinematic-content {
      /* Bottom-anchored (not vertically centered) so the text block sits in
         a consistent, predictable spot — clear of the model's face/hair no
         matter how a given banner photo is framed or cropped. */
      justify-content: flex-end;
      padding: 0 48px 64px 56px;
      max-width: 580px;
    }
  }
  @media (min-width: 1280px) {
    .hero-cinematic-content {
      padding-left: 80px;
      padding-bottom: 84px;
      max-width: 620px;
    }
  }

  #heroContentInner { color: #fff; }

  .hero-panel-eyebrow {
    font-size: 11px;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: rgba(255,255,255,0.72);
    margin-bottom: 12px;
    font-weight: 500;
  }
  @media (min-width: 768px) {
    .hero-panel-eyebrow { font-size: 12px; margin-bottom: 14px; }
  }

  .hero-panel-tagline {
    font-size: 14.5px;
    color: rgba(255,255,255,0.78);
    margin-top: 14px;
    max-width: 34ch;
    line-height: 1.55;
    font-weight: 400;
  }
  @media (min-width: 768px) {
    .hero-panel-tagline { font-size: 15.5px; margin-top: 16px; }
  }

  /* CTAs */
  .hero-pills-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 20px;
  }
  .hero-pill {
    flex-shrink: 0;
    white-space: nowrap;
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    padding: 11px 20px;
    border-radius: 999px;
    border: 1.5px solid rgba(255,255,255,0.55);
    color: #fff;
    background: transparent;
    transition: all .25s cubic-bezier(.22,1,.36,1);
  }
  .hero-pill:hover {
    background: #fff;
    color: #0A0A14;
    border-color: #fff;
  }
  .hero-pill-accent {
    background: #fff;
    color: #0A0A14;
    border-color: #fff;
  }
  .hero-pill-accent:hover {
    background: rgb(var(--c-goldlight));
    border-color: rgb(var(--c-goldlight));
    color: #0A0A14;
  }
  @media (min-width: 768px) {
    .hero-pill { font-size: 13.5px; padding: 14px 28px; }
  }
  @media (min-width: 768px) {
    /* Slightly tighter rhythm now that the block sits bottom-anchored
       instead of vertically centered — keeps the whole group feeling
       like one composed unit rather than spread out. */
    .hero-pills-row { margin-top: 22px; }
  }

  /* Quick links under CTAs */
  #heroQuickLinks { margin-top: 22px; }
  @media (min-width: 768px) {
    #heroQuickLinks { margin-top: 18px; }
  }
  #heroQuickLinks a {
    color: rgba(255,255,255,0.65);
    font-size: 12.5px;
    letter-spacing: 0.02em;
    transition: color .2s ease;
  }
  #heroQuickLinks a:hover { color: #fff; }
  #heroQuickLinks a.text-rose { color: #f0b4ab; }
  #heroQuickLinks a.text-rose:hover { color: #fff; }

  /* Dots + arrows */
  #heroDots {
    position: absolute;
    z-index: 4;
    bottom: 22px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 8px;
  }
  @media (min-width: 768px) {
    #heroDots {
      left: auto;
      right: 40px;
      bottom: 36px;
      transform: none;
    }
  }
  .hero-arrow {
    width: 44px;
    height: 44px;
    background: rgba(255,255,255,0.12);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255,255,255,0.22);
    color: #fff;
  }
  .hero-arrow:hover { background: rgba(255,255,255,0.22); }
  @media (min-width: 768px) {
    .hero-arrow { width: 48px; height: 48px; }
  }

  /* Keep layout below the new full-bleed hero */
  .home-top-grid { max-width: none; margin: 0; }

  .font-brand { font-family: 'Playfair Display', serif; }

  /* Desktop header wordmark — monogram badge + gradient name + tagline,
     replacing the old plain text so the brand actually catches the eye. */
  .brand-logo-badge { position: relative; width: 38px; height: 38px; border-radius: 9999px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; overflow: hidden; background: linear-gradient(135deg, rgb(var(--c-goldlight)), rgb(var(--c-golddark))); box-shadow: 0 3px 10px rgb(var(--c-espresso) / .28); }
  .brand-logo-badge::after { content: ''; position: absolute; inset: -3px; border-radius: 9999px; border: 1px solid rgb(var(--c-gold) / .35); }
  /* Two-letter "NW" monogram: N stands upright (echoes the roman "Nabeela"),
     W leans italic (echoes the italic gold "Wardrobe") so the badge reads as
     a compressed version of the wordmark rather than a generic single initial. */
  .brand-logo-badge-mono { display: flex; align-items: baseline; line-height: 1; }
  .brand-logo-badge-letter { font-family: 'Playfair Display', serif; font-size: 14px; font-weight: 600; color: rgb(var(--c-porcelain)); line-height: 1; }
  .brand-logo-badge-letter-w { font-style: italic; font-weight: 500; margin-left: 0.5px; color: rgb(var(--c-porcelain) / 0.88); }
  /* Flower + "NW" monogram mark — used in the mobile header row where the
     wordmark used to sit alone with empty space either side. Petals are
     drawn with currentColor so they inherit the badge's porcelain tone. */
  .brand-logo-flower { color: rgb(var(--c-porcelain)); display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; }
  .brand-logo-flower svg { width: 78%; height: 78%; }
  .brand-logo-name { display: block; font-size: 1.05rem; letter-spacing: .01em; }
  @media (min-width: 1024px) { .brand-logo-name { font-size: 1.2rem; } }
  .brand-logo-gold { background-image: linear-gradient(90deg, rgb(var(--c-goldlight)), rgb(var(--c-gold)) 55%, rgb(var(--c-golddark))); -webkit-background-clip: text; background-clip: text; color: transparent; }
  .brand-logo-tag { display: block; font-size: 9px; letter-spacing: .22em; text-transform: uppercase; color: rgb(var(--c-taupe)); margin-top: 1px; white-space: nowrap; }

  /* Mobile header wordmark replacement — a small stitched flourish either
     side of the logo badge, echoing the "House of Lawn & Embroidery" motif
     used elsewhere (see .stitch) instead of spelling the brand name out. */
  .header-stitch-mobile { width: 42px; height: 1px; background-image: linear-gradient(90deg, rgb(var(--c-gold)) 55%, transparent 45%); background-size: 8px 1px; background-repeat: repeat-x; opacity: .55; flex-shrink: 0; }

  /* Horizontal-scroll product carousels (Menswear / Undergarments) */
  /* Card widths mirror the Best Selling grid's own column math (2-up on
     mobile, 3-up on tablet, 5-up on desktop with matching gaps) so cards
     scrolling here are the same size as the grid sections, not an
     unrelated fixed width. */
  .carousel-row { scroll-snap-type: x proximity; }
  .carousel-row > .pcard { flex: 0 0 auto; width: calc((100% - .35rem) / 2); scroll-snap-align: start; }
  @media (min-width: 768px) { .carousel-row > .pcard { width: calc((100% - 1rem) / 3); } }
  @media (min-width: 1024px) { .carousel-row > .pcard { width: calc((100% - 2rem) / 5); } }
  /* Card grid spacing — set directly (not via Tailwind gap-N utilities) so
     it always applies regardless of what's in the compiled stylesheet. */
  #saleRow, #bestsellerRow, #newArrivalsRow, #shopGrid, #searchResults, .carousel-row { gap: .35rem !important; }
  @media (min-width: 768px){
    #saleRow, #bestsellerRow, #newArrivalsRow, #shopGrid, #searchResults, .carousel-row { gap: .5rem !important; }
  }
  .carousel-row { cursor: grab; scrollbar-width: none; -ms-overflow-style: none; scroll-behavior: smooth; }
  .carousel-row::-webkit-scrollbar { display: none; }
  .carousel-row.dragging { cursor: grabbing; scroll-behavior: auto; }
  .carousel-row.dragging > .pcard { pointer-events: none; }
  .carousel-row-wrap { position: relative; }
  /* Touch-target size fix: several icon-only buttons (star-rating picker,
     filter-chip remove) had no explicit box — they shrank to their icon's
     size (10-20px), well under the 24x24 CSS px minimum. Padding here
     grows the tappable area without changing the visible icon size. */
  #reviewStarPicker button { min-width: 28px; min-height: 28px; display: inline-flex; align-items: center; justify-content: center; }
  /* Arrows sit INSIDE the row (not hanging off the edge) so they stay visible
     over product cards on every section, including overflow-hidden parents
     like the flash-sale band. Shown on mobile too — smaller hit targets. */
  .carousel-arrow {
    position: absolute;
    top: 38%;
    transform: translateY(-50%);
    z-index: 6;
    width: 32px;
    height: 32px;
    border-radius: 9999px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgb(var(--c-porcelain) / 0.96);
    box-shadow: 0 6px 18px rgb(var(--c-espresso) / 0.22);
    border: 1px solid rgb(var(--c-taupelight) / 0.6);
    color: rgb(var(--c-espresso));
    transition: background .2s ease, color .2s ease, transform .2s ease, opacity .2s ease, visibility .2s ease;
  }
  .carousel-arrow:hover { background: rgb(var(--c-gold)); color: rgb(var(--c-porcelain)); }
  .carousel-arrow:active { transform: translateY(-50%) scale(0.92); }
  .carousel-arrow-left { left: 6px; }
  .carousel-arrow-right { right: 6px; }
  /* Dimmed (not removed) when that direction has nothing left — so a new
     visitor always sees the controls and understands the row scrolls. */
  .carousel-arrow.is-disabled,
  .carousel-arrow[aria-disabled="true"] {
    opacity: 0.35;
    pointer-events: none;
    cursor: default;
  }
  .carousel-arrow.is-disabled:hover,
  .carousel-arrow[aria-disabled="true"]:hover {
    background: rgb(var(--c-porcelain) / 0.96);
    color: rgb(var(--c-espresso));
  }
  .flash-sale-dark .carousel-arrow.is-disabled:hover,
  .flash-sale-dark .carousel-arrow[aria-disabled="true"]:hover {
    background: rgba(255,255,255,0.94);
    color: rgb(var(--c-espresso));
  }
  /* Only hide arrows entirely when the row does not overflow at all
     (everything already fits on screen — no next page to discover). */
  .carousel-row-wrap:not(.can-scroll) .carousel-arrow {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
  }
  @media (min-width: 768px) {
    .carousel-arrow { width: 38px; height: 38px; top: 40%; }
    .carousel-arrow-left { left: 10px; }
    .carousel-arrow-right { right: 10px; }
  }
  .flash-sale-dark .carousel-arrow { background: rgba(255,255,255,0.94); border-color: rgba(255,255,255,0.3); }
  .flash-sale-dark .carousel-arrow:hover { background: rgb(var(--c-gold)); color: #171715; }

  /* ============ Category section: mobile circular + desktop rail ============ */
  .cat-section {
    position: relative;
    overflow: hidden;
  }
  .cat-section-bg {
    position: absolute; inset: 0; z-index: 0; pointer-events: none;
    background-size: cover; background-position: center;
    background-image: url('https://images.unsplash.com/photo-1441986300917-64674bd600d8?q=80&w=1920&auto=format&fit=crop');
  }
  /* Serve a smaller version of the same image on phones — same photo,
     same crop, just a lighter download since mobile screens don't need
     1920px of source width. */
  @media (max-width: 767px) {
    .cat-section-bg {
      background-image: url('https://images.unsplash.com/photo-1441986300917-64674bd600d8?q=80&w=800&auto=format&fit=crop');
    }
  }
  .cat-section-bg::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(180deg,
      rgba(250,246,238,0.92) 0%,
      rgba(250,246,238,0.78) 40%,
      rgba(250,246,238,0.88) 100%);
  }
  html[data-theme="dark"] .cat-section-bg::after {
    background: linear-gradient(180deg,
      rgba(8,10,20,0.88) 0%,
      rgba(8,10,20,0.75) 45%,
      rgba(8,10,20,0.9) 100%);
  }
  .cat-section > .cat-section-inner { position: relative; z-index: 1; }

  /* Clean minimal category grid — replaces the old rotating hanger rack /
     gold rail with a plain responsive card grid (image + label). */
  .cat-clean-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
  @media (min-width: 640px) { .cat-clean-grid { grid-template-columns: repeat(3, 1fr); gap: 18px; } }
  @media (min-width: 1024px) { .cat-clean-grid { grid-template-columns: repeat(4, 1fr); gap: 18px; } }
  .cat-clean-card { position: relative; display: block; width: 100%; aspect-ratio: 3 / 4; border-radius: 8px; overflow: hidden; border: 1px solid rgb(var(--c-taupelight) / .6); background: rgb(var(--c-blush)); cursor: pointer; }
  .cat-clean-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .5s cubic-bezier(.22,1,.36,1); }
  /* Real hover only — tapping a category tile on touch would otherwise
     leave its image stuck slightly zoomed until another tap elsewhere. */
  @media (hover: hover) {
    .cat-clean-card:hover img { transform: scale(1.045); }
  }
  .cat-clean-card::after { content: ''; position: absolute; inset: 0; background: linear-gradient(0deg, rgb(var(--c-espresso) / .55), transparent 55%); }
  .cat-clean-card .cat-clean-label { position: absolute; left: 12px; right: 12px; bottom: 11px; color: #fff; font-size: 13px; font-weight: 600; letter-spacing: .01em; z-index: 1; }
  @media (min-width: 768px) { .cat-clean-label { font-size: 14px; } }

  /* Hero entrance — fires ONCE, only after the hero photo has actually
     finished loading (see armHeroEntrance/revealHeroEntrance in JS), so the
     decorative eyebrow/CTA/quicklinks never reveal themselves over a
     blank/loading background. Slide auto-rotation afterwards still swaps
     content instantly — this is a one-time first-paint reveal, not a
     per-slide replay. The H1 (heroTagline) is deliberately NOT part of
     this — see the note further down — it's always visible.
     #heroContentInner starts in .hero-pending (children pre-set to their
     "off" position/opacity below); JS swaps that for .hero-ready, which is
     what actually triggers each keyframe animation. */
  @keyframes heroDropIn   { from{ opacity:0; transform: translateY(-20px); } to{ opacity:1; transform: translateY(0); } }
  @keyframes heroFadeIn   { from{ opacity:0; } to{ opacity:1; } }

  #heroContentInner.hero-pending #heroEyebrow,
  #heroContentInner.hero-pending #heroCtaRow,
  #heroContentInner.hero-pending #heroQuickLinks { opacity: 0; }
  #heroContentInner.hero-pending #heroEyebrow { transform: translateY(-20px); }
  /* heroTagline (the page's only <h1>) is intentionally NOT included above —
     it stays visible (opacity:1) from first paint, in the raw HTML, with no
     JS dependency at all. It previously sat in the same hidden-until-JS
     group as the decorative eyebrow/CTA/quicklinks, which is exactly the
     "hide page content using CSS" pattern Lighthouse/PageSpeed flags (real
     Chrome measured it staying invisible for 14+ seconds whenever the
     reveal script was delayed — e.g. slow network, a busy main thread, or
     the backend cold-starting). The H1 is real page content, not a
     decorative flourish, so it shouldn't be gated on JS execution timing
     at all — only the surrounding chrome still gets the animated entrance. */
  /* Content-hiding fix: only apply the pre-reveal hidden state once JS is
     confirmed running (see the early <html class="js"> script in <head>).
     Without this scoping, the eyebrow/CTA/quicklinks were opacity:0 in the
     raw HTML/CSS — invisible to any crawler or tool that doesn't execute
     JavaScript, even though real browsers always reveal them via
     revealHeroEntrance(). */
  html:not(.js) #heroContentInner.hero-pending #heroEyebrow,
  html:not(.js) #heroContentInner.hero-pending #heroCtaRow,
  html:not(.js) #heroContentInner.hero-pending #heroQuickLinks { opacity: 1; transform: none; }

  #heroContentInner.hero-ready #heroEyebrow    { animation: heroDropIn   .9s cubic-bezier(.22,1,.36,1) .10s both; }
  #heroContentInner.hero-ready #heroCtaRow     { animation: heroFadeIn   .9s ease 1.0s both; }
  #heroContentInner.hero-ready #heroQuickLinks { animation: heroFadeIn   .9s ease 1.25s both; }

  /* floating decorative blobs */
  @keyframes floatBlob { 0%,100%{ transform: translate(0,0) scale(1); } 33%{ transform: translate(18px,-24px) scale(1.06); } 66%{ transform: translate(-14px,16px) scale(0.96); } }
  .blob-anim { animation: floatBlob 15s ease-in-out infinite; }
  .blob-anim-slow { animation: floatBlob 22s ease-in-out infinite reverse; }

  /* Gold shimmer on primary buttons — used to run forever via `animation:
     ...infinite`, which forces a continuous repaint on every single
     primary CTA on the site (Buy Now, Add to Cart, Checkout, etc.),
     since background-position is a paint-triggering property, not a
     compositor-only one like transform/opacity. On real Android Chrome
     devices this is a known cause of intermittent tap unresponsiveness —
     a touch landing mid-repaint can get dropped. Now it only plays on
     hover (a deliberate, finite, mouse-only gesture), so it never runs
     continuously on mobile at all. */
  .btn-gold { background-size: 200% 200%; }
  .btn-gold:hover { animation: goldShift 1.4s ease; }
  @keyframes goldShift { 0%{ background-position:0% 50%; } 50%{ background-position:100% 50%; } 100%{ background-position:0% 50%; } }

  /* gentle breathing for accent icons */
  @keyframes breathe { 0%,100%{ transform: scale(1); opacity:.9; } 50%{ transform: scale(1.08); opacity:1; } }
  .breathe { animation: breathe 3.2s ease-in-out infinite; }

  /* whatsapp / floating CTA pulse ring */
  @keyframes waPulseShape { 0%{ transform:scale(0.9); opacity:.6; } 100%{ transform:scale(1.55); opacity:0; } }
  .wa-pulse-shape { transform-origin: 50% 50%; animation: waPulseShape 2.2s cubic-bezier(.22,1,.36,1) infinite; pointer-events: none; }

  /* whatsapp / floating CTA pulse ring */

  /* slow drifting gradient wash for accent sections */
  @keyframes gradientDrift { 0%{ background-position:0% 50%; } 50%{ background-position:100% 50%; } 100%{ background-position:0% 50%; } }
  .gradient-drift { background-size: 200% 200%; animation: gradientDrift 12s ease infinite; }

  .tab-underline { position:relative; }
  .tab-underline.active::after { content:''; position:absolute; left:0; right:0; bottom:-9px; height:2px; background:linear-gradient(90deg,#0F5C46,#B28C4A); }

  .toast { transform: translateY(120%); opacity:0; transition: all .5s cubic-bezier(.22,1,.36,1); }
  .toast.show { transform: translateY(0); opacity:1; }

  .zoom-wrap { overflow:hidden; }
  .zoom-wrap img, .zoom-wrap .zoomable { transition: transform .2s ease; }
  .zoom-wrap.zoom-active { cursor: zoom-in; touch-action: none; }
  .zoom-wrap.zoom-active .zoomable { transition: transform .05s linear; }
  #zoomToggleBtn.active { background: rgb(var(--c-gold)); color: rgb(var(--c-espresso)); }

  .fanned:nth-child(1){ transform: rotate(-3deg) translateY(6px);} 
  .fanned:nth-child(2){ transform: rotate(1.5deg) translateY(-4px);} 
  .fanned:nth-child(3){ transform: rotate(-1deg) translateY(2px);} 
  .fanned:hover{ transform: rotate(0) translateY(-8px) !important; }

  .grain { position:relative; }
  .grain::before{ content:''; position:absolute; inset:0; opacity:.05; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); pointer-events:none; }
  .no-scrollbar{ scrollbar-width:none; -ms-overflow-style:none; }
  .no-scrollbar::-webkit-scrollbar{ display:none; }

  .countdown-box { min-width: 56px; }
  @media (max-width: 380px){
    .countdown-box { min-width: 46px; padding-left:.5rem; padding-right:.5rem; }
    #countdown { gap: .35rem; }
  }

  /* keep floating action buttons clear of the home-indicator / gesture bar on notched phones */
  #backTop, a[href^="https://wa.me"], #cartFab, #toast {
    bottom: calc(1.5rem + env(safe-area-inset-bottom));
  }
  #backTop{ bottom: calc(6rem + env(safe-area-inset-bottom)); }
  @media (min-width: 768px){
    #backTop{ bottom: calc(1.5rem + env(safe-area-inset-bottom)); }
  }

  @media (max-width: 767px){
    .desktop-only{ display:none !important; }
  }
  @media (min-width: 768px){
    .mobile-only{ display:none !important; }
  }

  /* ===========================================================
     COMPACT HAMBURGER VIEW (<768px) — floating widgets sized for
     thumbs, product cards sized for reading. Cards stay at a
     comfortable 2-up grid across the whole mobile range (previously
     squeezed to 3-up between 560–767px, which made cards and their
     type too small to read comfortably) with roomier type, spacing
     and tap targets throughout.
     =========================================================== */
  @media (max-width: 767px){
    /* WhatsApp + floating cart bubbles: 56px -> 44px */
    #whatsappFab, #cartFab{
      width: 44px; height: 44px;
    }
    #whatsappFab .wa-icon-svg{ width: 38px; height: 38px; }
    #cartFab svg{ width: 16px; height: 16px; }
    #cartFab #cartCountMobile{ width: 16px; height: 16px; font-size: 9px; top: -2px; right: -2px; }
    #cartFab.has-items #cartCountMobile{ background: #0F5C46; color: #FFFCF6; font-weight: 700; }

    /* Chat bubble: shrink the circle and sit it just above the WhatsApp
       button with a small, consistent gap — close enough that the two
       clearly belong to the same floating stack, but never overlapping.
       bottom is NOT !important here (see the drag-launcher script):
       that would permanently override the drag script's inline `bottom`
       and lock the bubble to this one height, which was why it could
       only ever slide sideways and never up/down. The plain declaration
       still applies fine for the undragged default position. */
    #chatBubble{
      width: 60px; height: 60px;
      /* Keep mobile-nav offset so we don't sit on top of WhatsApp */
      bottom: calc(1.5rem + 44px + 16px + var(--mobile-nav-h) + env(safe-area-inset-bottom));
    }
    #chatBubble svg{ width: 48px; height: 48px; }
    #chatPanel{ bottom: calc(1.5rem + 44px + 16px + var(--mobile-nav-h) + env(safe-area-inset-bottom)) !important; width: min(88vw, 300px); }

    /* Back-to-top: 44px -> 36px */
    #backTop{ width: 36px; height: 36px; }
    #backTop svg{ width: 14px; height: 14px; }

    /* Product grid gap is set globally near the top of this stylesheet
       (not per-breakpoint here) so mobile and desktop stay in sync. */
    .carousel-row > .pcard{ width: calc((100% - .35rem) / 2) !important; }

    /* Card artwork made a bit taller so the card reads as a longer,
       roomier shape now that the title underneath is set smaller. */
    .pcard > div:first-child{ aspect-ratio: 3 / 4.2 !important; }

    /* Product card typography + spacing */
    .pc-body{ padding-top: .4rem !important; min-width: 0; }
    .pc-cat{ font-size: 9.5px !important; letter-spacing: .05em; }
    .pc-name{
      font-size: 11px !important;
      line-height: 1.3 !important;
      margin-top: 2px !important;
      /* No line-clamp on mobile — cards here are narrower in absolute
         pixels than desktop's, so the same 2-line clamp that never
         triggers on wider desktop cards was genuinely truncating longer
         product names here. Let it wrap to as many lines as it needs. */
      display: block !important;
      -webkit-line-clamp: unset !important;
      -webkit-box-orient: unset !important;
      overflow: visible !important;
      white-space: normal !important;
      word-break: break-word !important;
      overflow-wrap: anywhere !important;
      /* No forced color here — this used to hardcode espresso (near-black
         in light mode) with !important, which overrode the intentional
         always-white text on Sale-badged cards (.pcard.sale-pcard .pc-name
         { color:#fff }), producing black text on a dark card background
         (invisible). Letting the existing rules cascade normally — same as
         desktop — keeps Sale cards white and regular cards theme-correct. */
      max-width: 100% !important;
      width: 100% !important;
    }
    .pcard.shop-pcard { min-width: 0; }
    .pcard.shop-pcard .pc-body { overflow: hidden; }
    .pc-stock{ font-size: 10px !important; margin-top: 2px !important; }
    .pc-pricerow{ padding-top: .3rem !important; }
    .pc-price{ font-size: 14.5px !important; }
    .pc-oldprice{ font-size: 11px !important; }
    .pc-badge{ font-size: 9px !important; padding: .18rem .5rem !important; }
    /* On touch this pill is always visible (see the hover:none block) so it is
       the actual tap target, not a hover affordance — 10px/.35rem made it a
       26px-tall target, well under a comfortable thumb. Widened rather than
       just scaled up so the label keeps its breathing room on a 171px card. */
    .pc-quickbtn{ font-size: 11px !important; padding: .5rem 1.05rem !important; bottom: .6rem !important; }
    .pc-heartbtn{ width: 28px !important; height: 28px !important; }

    /* Mobile nav drawer: slightly taller rows for easier tapping */
    #mobileNav a{ padding-top: .85rem !important; padding-bottom: .85rem !important; }
  }

/* ---- page loader + chat panel (was a second inline <style>) ---- */

  /* Loading animation — a hand-stitched thread traveling across, echoing the
     embroidery motif elsewhere on the site (replaces the old plain spinner). */
  @keyframes loaderPulse { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }
  .loader-pulse { animation: loaderPulse 1.8s ease-in-out infinite; }
  .loader-stitch { width: 130px; height: 8px; position: relative; overflow: hidden; }
  .loader-stitch::before {
    content: ''; position: absolute; inset: 0; margin: auto 0; height: 2px;
    background-image: linear-gradient(90deg, rgb(var(--c-goldlight)) 55%, transparent 45%);
    background-size: 13px 2px; background-repeat: repeat-x;
    animation: stitchTravel .9s linear infinite;
  }
  .loader-stitch::after {
    content: ''; position: absolute; top: 50%; left: 0; width: 7px; height: 7px;
    margin-top: -3.5px; border-radius: 50%; background: rgb(var(--c-gold));
    animation: needleTravel 1.8s cubic-bezier(.45,0,.55,1) infinite;
  }
  @keyframes stitchTravel { to { background-position: 26px 0; } }
  @keyframes needleTravel { 0% { left: -4%; } 50% { left: 96%; } 100% { left: -4%; } }
  /* gentle blink on the "Sale" nav link — a smooth opacity pulse rather than
     a hard on/off flash, so it stays noticeable without being jarring */
  .nav-sale-blink { animation: navSaleBlink 1.7s ease-in-out infinite; }
  .nav-sale-blink:hover { animation-play-state: paused; opacity: 1; }
  @keyframes navSaleBlink { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }

  /* Chat widget FAQ menu — grouped, expandable topics shown when the
     panel opens, so people can browse instead of having to type. */
  .chat-faq-menu { margin-top: 10px; }
  .chat-faq-groups { display: flex; flex-direction: column; gap: 6px; }
  .chat-faq-group { border: 1px solid rgb(var(--c-taupelight) / .4); border-radius: 12px; overflow: hidden; background: rgb(var(--c-ivory) / .6); }
  .chat-faq-group-btn { width: 100%; display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; font-size: 12px; font-weight: 600; color: rgb(var(--c-espresso)); background: transparent; border: none; cursor: pointer; }
  .chat-faq-group-btn:hover { color: rgb(var(--c-gold)); }
  .chat-faq-chevron { transition: transform .25s ease; flex-shrink: 0; }
  .chat-faq-chevron.is-open { transform: rotate(180deg); }
  .chat-faq-questions { padding: 0 10px 9px; display: flex; flex-direction: column; gap: 5px; }
  .chat-faq-q { text-align: left; font-size: 11.5px; line-height: 1.35; color: rgb(var(--c-taupe)); background: rgb(var(--c-porcelain)); border: 1px solid rgb(var(--c-taupelight) / .35); border-radius: 9px; padding: 6px 9px; cursor: pointer; }
  .chat-faq-q:hover { border-color: rgb(var(--c-gold)); color: rgb(var(--c-espresso)); }

  /* Chat typing indicator */
  .chat-typing { display:inline-flex; align-items:center; gap:4px; padding:10px 14px; background:rgb(var(--c-blush)); border-radius:14px; border-top-left-radius:4px; }
  .chat-typing span { width:6px; height:6px; border-radius:50%; background:rgb(var(--c-taupe)); opacity:.45; animation: chatDot 1.2s ease-in-out infinite; }
  .chat-typing span:nth-child(2){ animation-delay:.15s; }
  .chat-typing span:nth-child(3){ animation-delay:.3s; }
  @keyframes chatDot { 0%,80%,100%{ transform:translateY(0); opacity:.35; } 40%{ transform:translateY(-4px); opacity:1; } }

  /* Quick reply chips under bot messages */
  .chat-quick { display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
  .chat-quick button {
    font-size:11px; font-weight:600; padding:5px 10px; border-radius:999px;
    border:1px solid rgb(var(--c-taupelight)); background:rgb(var(--c-porcelain));
    color:rgb(var(--c-espresso)); cursor:pointer; transition: all .18s ease;
  }
  .chat-quick button:hover { border-color:rgb(var(--c-gold)); color:rgb(var(--c-golddark)); background:rgb(var(--c-blush)); }

  /* Richer product card in chat */
  .chat-pcard { display:flex; gap:8px; align-items:center; width:100%; text-align:left; margin-top:8px; padding:8px; border-radius:12px; border:1px solid rgb(var(--c-taupelight)/.35); background:rgb(var(--c-porcelain)); cursor:pointer; transition:border-color .18s ease; }
  .chat-pcard:hover { border-color:rgb(var(--c-gold)); }
  .chat-pcard img { width:44px; height:44px; object-fit:cover; border-radius:8px; flex-shrink:0; background:rgb(var(--c-blush)); }
  .chat-pcard .meta { min-width:0; flex:1; }
  .chat-pcard .name { display:block; font-size:12px; font-weight:600; color:rgb(var(--c-espresso)); line-height:1.3; }
  .chat-pcard .sub { display:block; font-size:10.5px; color:rgb(var(--c-taupe)); margin-top:1px; }
  .chat-pcard .actions { display:flex; gap:4px; margin-top:4px; }
  .chat-pcard .actions button { font-size:10px; font-weight:700; padding:3px 8px; border-radius:6px; border:1px solid rgb(var(--c-espresso)/.25); background:transparent; color:rgb(var(--c-espresso)); cursor:pointer; }
  .chat-pcard .actions button.primary { background:rgb(var(--c-espresso)); color:rgb(var(--c-ivory)); border-color:transparent; }
  .chat-pcard .actions button:hover { opacity:.9; }

  /* Belt-and-braces: these three elements must start invisible and stay
     that way until toggleChatPanel()/showBotGreeting() explicitly reveal
     them. Normally that's handled by the shared Tailwind `.hidden` utility
     class in dist/output.css, but if that stylesheet is ever missing,
     stale, or slow to load, a plain <div> falls back to `display:block`
     and the chat panel (and its greeting bubble) show up on every page
     load with no user interaction — which is exactly the bug this fixes.
     Scoping the rule to these IDs directly in the page's own inline
     <style> means it can never depend on the external CSS file. */
  #chatPanel.hidden, #botGreeting.hidden, #chatBubble.hidden { display: none !important; }

  #chatPanel {
    width: min(92vw, 320px);
    /* Cap the WHOLE panel (header + messages + input) against the real
       visible viewport, not just the messages list. On mobile, `vh` is
       measured against the tallest possible viewport (as if the address
       bar were hidden), so a panel sized only by an inner max-height can
       still end up taller than what's actually on screen once the
       address bar / URL strip is showing — pushing the top of the panel
       up above the visible area. `dvh` tracks the real, current viewport
       and is supported by all current mobile browsers; the plain vh rule
       right before it is a fallback for the rare browser that doesn't
       understand dvh (it's simply overridden wherever dvh works). */
    max-height: min(58vh, 420px);
    max-height: min(58dvh, 420px);
    display: flex;
    flex-direction: column;
  }
  #chatMessages {
    /* Fill whatever room is left under the pinned header/input instead of
       a fixed px/vh number, so header and input are always visible and
       only the message list itself scrolls. */
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
  }
  .chat-header-actions { display:flex; align-items:center; gap:2px; }
  .chat-header-actions button {
    opacity:.9;
    width: 34px; height: 34px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 8px;
  }
  .chat-header-actions button:hover { opacity:1; background: rgb(255 255 255 / .12); }
  .chat-header-actions button:active { background: rgb(255 255 255 / .2); }
  /* A visible divider plus the wider tap targets above give the eye and the
     thumb a clear gap between "clear" (destructive) and "close" (harmless),
     so a slightly-off tap doesn't land on the wrong one. */
  .chat-header-actions .chat-header-divider { width: 1px; height: 18px; background: rgb(255 255 255 / .25); margin: 0 4px; }
