/* ============================================================
   DRAGONFLY HOME GOODS — shared site styles
   Palette: the logo's own enamel jewels, ink-dominant.
   Used by both index.html (hero/landing) and shop.html (catalog).
   Page-specific styles (hero-only, shop-grid-only) live in each
   page's own <style> block — this file is the shared foundation.
   ============================================================ */

:root{
  --ink:#0B1F27; --ink-deep:#050F14; --ink-soft:#12303B; --ink-card:#0E2731;
  --paper:#F4EFE3; --paper-lite:#FBF8F1;
  --gold:#C89B4A; --gold-lite:#DCBB7A; --gold-deep:#7A582D;
  --peacock:#246381; --sky:#8CBCC7; --ice:#E4EDF0; --slate:#9DB4BD;

  --e-sapph:#1D5586; --e-indigo:#2C4282; --e-teal:#1B7C7B; --e-violet:#5B3A7A;
  --e-emerald:#227A66; --e-orchid:#733A73; --e-rose:#6F3B56; --e-ember:#9E4C2B;

  --hot:#E2543C;          /* sale / urgency */
  --display:"Syne", system-ui, sans-serif;
  --ui:"Instrument Sans", system-ui, sans-serif;
  --gut:clamp(1rem,3vw,2.5rem);

  /* Two easing families, not one, per Apple's fluid-interface rule: default UI
     settles with no overshoot (critically damped); only elements a gesture gave
     momentum to (drag release, a card lifting toward the finger) get a touch of
     spring-back. Reach for --ease-spring deliberately, not by default. */
  --ease:cubic-bezier(.22,.68,.24,1);        /* settle — no overshoot */
  --ease-spring:cubic-bezier(.34,1.56,.64,1); /* momentum — slight bounce */
  --press:cubic-bezier(.4,0,.2,1);            /* press/release — fast, linear-ish */

  --line:1px solid rgba(140,188,199,.16);

  /* Wood-plate gradient — ONE named set, referenced everywhere a product
     "plate" background is drawn. Previously index.html and shop.html each
     hard-coded their own slightly-different stops (#EFE3CA/#D8C29B/#C6AC82
     vs #EFE3CA/#DCC7A2/#CBB287) and had drifted apart since neither was a
     token. Also covers the product-art line icon stroke colour. */
  --wood-1:#EFE3CA; --wood-2:#D8C29B; --wood-3:#C6AC82;
  --wood-ink:#3F2C14;

  /* on-accent text: white/black used deliberately for max contrast on a
     coloured surface (gold button hover-dark, toast, tags) — named so they
     read as intentional tokens, not stray hex. */
  --on-accent-light:#fff; --on-accent-dark:#000;

  /* Named z-index scale — six flat levels, referenced by name so a new
     overlay can be slotted in without guessing where 9999 sits relative
     to everything else. */
  --z-base:1; --z-raised:3; --z-sticky:40; --z-header:60; --z-ticker:70; --z-modal:300; --z-toast:400;
}

*,*::before,*::after{box-sizing:border-box}
/* The hidden attribute must always win. The browser's own [hidden] rule is a
   UA style, so ANY author display value (.btn's inline-flex, .iconbtn .n's
   grid) silently overrides it — which is exactly how the bag's checkout
   button kept showing in an empty drawer. */
[hidden]{display:none!important}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;overflow-x:clip}
body{
  margin:0;background:var(--ink-deep);color:var(--ice);
  font-family:var(--ui);font-size:16px;line-height:1.55;overflow-x:clip;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;cursor:pointer}
:focus-visible{outline:2px solid var(--gold);outline-offset:2px;border-radius:3px}
.wrap{width:min(1600px,100% - var(--gut)*2);margin-inline:auto}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
/* shared numeric alignment utility — apply to any element displaying prices,
   dates, or other columns of digits */
.nums{font-variant-numeric:tabular-nums}

h1,h2,h3{font-family:var(--display);font-weight:800;line-height:.95;letter-spacing:-.03em;margin:0;
  overflow-wrap:anywhere;min-width:0}
.kicker{font-size:.66rem;font-weight:700;letter-spacing:.22em;text-transform:uppercase;color:var(--gold);margin:0}

/* ============================================================
   URGENCY TICKER
   ============================================================ */
.ticker{background:var(--gold);color:var(--ink-deep);overflow:hidden;padding:.5rem 0;position:relative;z-index:var(--z-ticker)}
.ticker-track{display:flex;width:max-content;animation:tick 34s linear infinite}
.ticker:hover .ticker-track{animation-play-state:paused}
.ticker-track span{font-size:.7rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  padding:0 1.4rem;white-space:nowrap;display:flex;align-items:center;gap:1.4rem}
.ticker-track span::after{content:"◆";font-size:.55rem;opacity:.5}
@keyframes tick{to{transform:translateX(-50%)}}

/* ============================================================
   HEADER — search-led, shared across pages
   ============================================================ */
header.site{position:sticky;top:0;z-index:var(--z-header);background:rgba(5,15,20,.92);
  backdrop-filter:blur(16px) saturate(1.3);border-bottom:var(--line);
  box-shadow:inset 0 1px 0 rgba(228,237,240,.07)} /* bright top edge = light catching the material */
.head-in{display:grid;grid-template-columns:auto 1fr auto;gap:clamp(1rem,3vw,2.5rem);
  align-items:center;padding:.7rem 0}
.brand{display:flex;align-items:center;gap:.6rem;flex:none}
.brand img{width:46px;height:46px;flex:none}
.brand .txt{font-family:var(--display);font-weight:800;font-size:1.02rem;line-height:1;letter-spacing:-.02em}
.brand .txt small{display:block;font-family:var(--ui);font-size:.53rem;font-weight:600;
  letter-spacing:.26em;text-transform:uppercase;color:var(--slate);margin-top:.3rem}

.searchbar{display:flex;align-items:center;gap:.6rem;background:rgba(140,188,199,.09);
  border:var(--line);border-radius:999px;padding:.6rem 1.1rem;transition:border-color .3s,background .3s}
.searchbar:focus-within{border-color:var(--gold);background:rgba(140,188,199,.14)}
.searchbar svg{width:17px;height:17px;color:var(--slate);flex:none}
.searchbar input{flex:1;min-width:0;background:none;border:none;color:var(--ice);font-size:.92rem;outline:none}
.searchbar input::placeholder{color:var(--slate)}
.search-clear{background:none;border:none;color:var(--slate);font-size:1.1rem;line-height:1;
  display:none;width:24px;height:24px;margin:-4px 0;flex:none;border-radius:999px;
  transition:color .2s,transform .12s var(--press)}
.search-clear:hover{color:var(--ice)}
.search-clear:active{transform:scale(.85);transition-duration:.08s}
.searchbar.has-value .search-clear{display:grid;place-items:center}

.head-cta{display:flex;align-items:center;gap:.5rem;flex:none}
.iconbtn{position:relative;background:rgba(140,188,199,.09);border:var(--line);color:var(--ice);
  width:40px;height:40px;border-radius:999px;display:grid;place-items:center;
  transition:background .3s,transform .12s var(--press),color .3s}
.iconbtn:hover{background:rgba(200,155,74,.2)}
.iconbtn:active{transform:scale(.9);transition-duration:.08s}
.iconbtn .n{position:absolute;top:-3px;right:-3px;background:var(--gold);color:var(--ink-deep);
  border-radius:999px;min-width:17px;height:17px;padding:0 3px;display:grid;place-items:center;
  font-size:.62rem;font-weight:700;line-height:1;box-shadow:0 0 0 2px var(--ink-deep)}
.iconbtn svg{width:18px;height:18px}
/* wishlist toggle — a real pressed state (filled-heart icon swap + gold
   background), not a decorative icon that goes nowhere. The icon itself
   swaps to a solid #heart-filled symbol via JS (catalog.js) rather than
   trying to force :fill through the <use> reference — a <path fill="none">
   inside a referenced <symbol> keeps its own specified value regardless
   of what the outer <svg> inherits. */
.iconbtn[aria-pressed="true"]{background:var(--gold);color:var(--ink-deep);border-color:transparent}
.cartbtn{background:var(--gold);color:var(--ink-deep);border:none;border-radius:999px;
  padding:.65rem 1.15rem;font-weight:700;font-size:.76rem;letter-spacing:.1em;text-transform:uppercase;
  display:flex;align-items:center;gap:.5rem;white-space:nowrap;transition:background .3s,transform .12s var(--press)}
.cartbtn:hover{background:var(--gold-lite);transform:translateY(-1px)}
.cartbtn:active{transform:scale(.95);transition-duration:.08s}
.cartbtn .n{background:var(--ink-deep);color:var(--gold);border-radius:999px;
  min-width:19px;height:19px;display:grid;place-items:center;font-size:.68rem}
.navlink{display:flex;align-items:center;gap:.4rem;font-size:.8rem;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;color:var(--ice);padding:.5rem .2rem;flex:none;
  white-space:nowrap;border-bottom:1px solid transparent;transition:border-color .3s,color .3s}
.navlink:hover{color:var(--gold);border-color:var(--gold)}
.navlink svg{width:14px;height:14px}
@media(max-width:860px){
  .head-in{grid-template-columns:auto auto;row-gap:.6rem}
  .searchbar{grid-column:1/-1;order:3}
  .brand .txt{display:none}
}

/* ============================================================
   BUTTONS — shared
   ============================================================ */
.btn{display:inline-flex;align-items:center;gap:.6rem;border:1px solid transparent;border-radius:999px;
  padding:1rem 1.8rem;font-weight:700;font-size:.76rem;letter-spacing:.14em;text-transform:uppercase;
  white-space:nowrap;transition:background .3s,color .3s,border-color .3s,transform .3s var(--ease)}
.btn svg{width:15px;height:15px;transition:transform .4s var(--ease)}
.btn:hover svg{transform:translateX(4px)}
/* press is instant and reversible — never wait for release to show feedback */
.btn:active{transform:scale(.96);transition:transform .1s var(--press)}
.btn-gold{background:var(--gold);color:var(--ink-deep);border:none;
  box-shadow:0 0 0 0 rgba(200,155,74,.6);animation:pulse 3.4s infinite}
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(200,155,74,.5)}70%{box-shadow:0 0 0 16px rgba(200,155,74,0)}100%{box-shadow:0 0 0 0 rgba(200,155,74,0)}}
.btn-gold:hover{background:var(--gold-lite);transform:translateY(-2px);animation:none}
.btn-gold:active{transform:scale(.96);animation:none}
.btn-ghost{border-color:rgba(140,188,199,.4);color:var(--ice)}
.btn-ghost:hover{border-color:var(--sky);background:rgba(140,188,199,.1)}

/* ============================================================
   ASSURANCE STRIP — shared
   ============================================================ */
.assure{background:var(--ink-soft);border-block:var(--line)}
/* Four tiles, but not four EQUAL tiles — the lead trust signal (handmade)
   gets more room than the other three, which breaks the rigid evenness of
   the classic 3/4-col feature-grid tell. minmax(0,…) so no tile can force
   the row wider than its track at narrow widths. */
.assure-in{display:grid;grid-template-columns:minmax(0,1.3fr) repeat(3,minmax(0,1fr));gap:1px;background:rgba(140,188,199,.14)}
@media(max-width:820px){.assure-in{grid-template-columns:repeat(2,minmax(0,1fr))}}
.assure-item{background:var(--ink-soft);padding:1.1rem clamp(.8rem,2vw,1.6rem);
  display:flex;align-items:center;gap:.85rem}
.assure-item svg{width:22px;height:22px;color:var(--gold);flex:none}
.assure-item b{display:block;font-size:.82rem;font-weight:700}
.assure-item span{font-size:.72rem;color:var(--slate)}

/* ============================================================
   NEWSLETTER + FOOTER — shared
   ============================================================ */
/* Was a 3-stop diagonal indigo→violet→orchid gradient — the full-bleed
   diagonal CTA-band gradient shares structural DNA with the AI "gradient
   hero" tell even with an on-brand palette. Flat indigo ground + a single
   gold rule keeps the jewel-tone identity without the gradient sweep. */
.signup-band{background:var(--e-indigo);border-block:2px solid var(--gold);
  position:relative;overflow:hidden}
.signup-band::after{content:"";position:absolute;inset:0;
  background:repeating-linear-gradient(0deg,rgba(0,0,0,.2) 0 1px,transparent 1px 3px)}
.signup-in{position:relative;z-index:var(--z-base);display:grid;grid-template-columns:1.2fr 1fr;gap:2rem;
  align-items:center;padding:clamp(2.2rem,5vw,3.4rem) 0}
@media(max-width:820px){.signup-in{grid-template-columns:1fr}}
.signup-in h2{font-size:clamp(1.8rem,1.2rem + 2.4vw,3rem);text-transform:uppercase}
.signup-in p{margin:.7rem 0 0;color:rgba(255,255,255,.78);max-width:44ch}
.signup{display:flex;gap:.5rem;flex-wrap:wrap}
.signup input{flex:1 1 200px;min-width:0;background:rgba(0,0,0,.28);border:1px solid rgba(255,255,255,.3);
  border-radius:999px;padding:.95rem 1.2rem;color:var(--on-accent-light);font-size:.92rem;outline:none;transition:border-color .3s}
.signup input::placeholder{color:rgba(255,255,255,.55)}
.signup input:focus{border-color:var(--gold-lite)}
.signup .btn{background:var(--ink-deep);color:var(--on-accent-light);border:none;animation:none}
.signup .btn:hover{background:var(--on-accent-dark)}

/* Statement close (Ft5-style): brand + one real sentence + a slim row of
   working links — left-biased, not a 4-column link index. Replaces the
   old Ft3-shaped "Shop / Made to order / Help" grid. */
footer{background:var(--ink-deep);padding:clamp(2.5rem,6vw,4rem) 0 1.5rem;color:var(--slate);font-size:.85rem}
.f-statement{padding-bottom:2.2rem;border-bottom:var(--line)}
footer .brand img{width:64px;height:64px}
.f-line{margin:1.2rem 0 0;max-width:52ch;font-family:var(--display);font-weight:600;
  font-size:clamp(1.05rem,.95rem + .5vw,1.35rem);line-height:1.4;color:var(--ice);letter-spacing:-.01em}
.f-links{display:flex;flex-wrap:wrap;gap:.5rem 1.4rem;margin-top:1.4rem;
  font-size:.72rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase}
.f-links a{color:var(--gold);transition:color .3s}
.f-links a:hover{color:var(--gold-lite)}
.f-links span a{color:var(--slate)}
.f-links span a:hover{color:var(--paper)}
/* #footCats is one flex CHILD of .f-links holding many category links (or,
   on shop.html, <li> items) joined with no separator in the JS — .f-links'
   own gap only spaces its direct children, not these, so without their own
   flex+gap the links render as one run-on word. */
#footCats{display:inline-flex;flex-wrap:wrap;gap:.5rem 1.2rem;list-style:none;padding:0;margin:0}
.f-bot{display:flex;justify-content:space-between;gap:1.2rem;flex-wrap:wrap;padding-top:1.5rem;
  font-size:.68rem;letter-spacing:.1em;text-transform:uppercase;color:rgba(157,180,189,.6)}

/* toast */
.toast{position:fixed;left:50%;bottom:1.5rem;translate:-50% 0;z-index:var(--z-toast);background:var(--e-emerald);
  color:var(--on-accent-light);padding:.85rem 1.4rem;border-radius:999px;font-size:.8rem;font-weight:600;
  box-shadow:0 18px 40px -14px rgba(0,0,0,.8);opacity:0;transform:translateY(18px);
  transition:opacity .35s,transform .35s var(--ease);pointer-events:none}
.toast.show{opacity:1;transform:none}

/* ================= BAG DRAWER — view what's inside the bag =================
   Real line items (name/price/qty), persisted to localStorage so it survives
   navigating between pages, same pattern as the wishlist toggle. */
.bag-scrim{position:fixed;inset:0;background:rgba(5,15,20,.6);backdrop-filter:blur(2px);
  z-index:var(--z-modal);opacity:0;pointer-events:none;transition:opacity .3s var(--ease)}
.bag-scrim.open{opacity:1;pointer-events:auto}
.bag-drawer{position:fixed;top:0;right:0;bottom:0;width:min(420px,100vw);
  background:var(--ink-card);border-left:var(--line);z-index:calc(var(--z-modal) + 1);
  display:flex;flex-direction:column;transform:translateX(100%);
  transition:transform .4s var(--ease);box-shadow:-24px 0 60px -20px rgba(0,0,0,.6)}
.bag-drawer.open{transform:none}
.bag-head{display:flex;align-items:center;justify-content:space-between;
  padding:1.2rem clamp(1rem,4vw,1.6rem);border-bottom:var(--line);flex:none}
.bag-head h2{font-size:1.2rem}
.bag-close{width:36px;height:36px;display:grid;place-items:center;border-radius:999px;
  background:transparent;border:var(--line);color:var(--ice);font-size:1.3rem;line-height:1;
  transition:background .25s var(--ease),color .25s var(--ease)}
.bag-close:hover{background:var(--gold);color:var(--ink-deep)}
.bag-list{flex:1;overflow-y:auto;padding:.5rem clamp(1rem,4vw,1.6rem)}
.bag-empty{padding:2.5rem 0;text-align:center;color:var(--slate)}
.bag-empty a{display:inline-block;margin-top:.8rem;color:var(--gold);font-weight:700;
  font-size:.8rem;letter-spacing:.04em;border-bottom:1px solid transparent;
  transition:border-color .25s var(--ease)}
.bag-empty a:hover{border-color:var(--gold)}
.bag-item{display:grid;grid-template-columns:56px minmax(0,1fr);gap:.8rem;align-items:start;
  padding:1rem 0;border-bottom:var(--line)}
.bag-item img,.bag-item svg{width:56px;height:56px;border-radius:6px;object-fit:cover;
  background:linear-gradient(158deg,var(--wood-1),var(--wood-2) 62%,var(--wood-3));color:var(--wood-ink);padding:8px}
.bag-item-name{display:block;font-size:.88rem;font-weight:700;line-height:1.3;overflow-wrap:anywhere;
  color:var(--ice);border-bottom:1px solid transparent;transition:color .25s var(--ease),border-color .25s var(--ease)}
a.bag-item-name:hover{color:var(--gold-lite);border-color:var(--gold-lite)}
.bag-item-opt{font-size:.74rem;color:var(--gold-lite);margin:.15rem 0 0}
/* Sibling to .bag-item-opt, not a replacement — an option row can carry
   BOTH its option label and a personalization summary at once. Slate
   rather than gold-lite so the two lines read as primary/secondary. */
.bag-item-personalize{font-size:.74rem;color:var(--slate);margin:.15rem 0 0;overflow-wrap:anywhere}
.bag-item-price{font-size:.8rem;color:var(--sky);margin-top:.2rem}

/* ---- product options in quick view ----
   Her options are mostly visual (five engraved designs of the same planter),
   so the photo IS the label — a text dropdown would make the shopper guess
   what "bluebird" looks like. Options without a photo of their own fall back
   to a text chip rather than borrowing another option's picture. */
.qv-options{margin:.9rem 0 0}
.qv-opt-head{display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap;margin:0 0 .5rem;
  font-size:.68rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--slate)}
.qv-opt-picked{color:var(--gold-lite);letter-spacing:.04em;text-transform:none;font-size:.78rem}
.qv-opt-list{display:flex;flex-wrap:wrap;gap:.5rem}
.qv-opt{display:flex;align-items:center;gap:.5rem;padding:.35rem .7rem .35rem .35rem;
  border:var(--line);border-radius:999px;background:rgba(140,188,199,.07);color:var(--ice);
  font-size:.76rem;font-weight:600;min-height:34px;
  transition:border-color .25s var(--ease),background .25s var(--ease),transform .12s var(--press)}
.qv-opt-nophoto{padding-left:.7rem}
.qv-opt img{width:26px;height:26px;border-radius:999px;object-fit:cover;flex:none;
  background:linear-gradient(158deg,var(--wood-1),var(--wood-2) 62%,var(--wood-3))}
.qv-opt:hover{border-color:var(--gold);background:rgba(200,155,74,.14)}
.qv-opt:active{transform:scale(.97)}
.qv-opt[aria-selected="true"]{border-color:var(--gold);background:var(--gold);color:var(--ink-deep)}
.qv-opt-price{font-variant-numeric:tabular-nums;opacity:.8}
.qv-opt[aria-disabled="true"]{opacity:.45;cursor:not-allowed}
.qv-opt-sold{font-size:.62rem;letter-spacing:.08em;text-transform:uppercase;color:var(--hot)}
/* A long option list shouldn't push the price and Add-to-bag off the modal. */
@media(min-width:641px){ .qv-opt-list{max-height:150px;overflow-y:auto} }
/* Quantity stepper and Remove sit together on one row — both are "things you
   do to this line item", so they belong in the same place. Wraps rather than
   squashing on a narrow drawer. */
.bag-item-actions{display:flex;align-items:center;flex-wrap:wrap;gap:.5rem .8rem;margin-top:.6rem}
.bag-qty{display:inline-flex;align-items:center;gap:.6rem;
  border:var(--line);border-radius:999px;padding:.15rem .3rem}
.bag-qty button{width:26px;height:26px;border-radius:999px;background:transparent;
  color:var(--ice);font-size:1rem;line-height:1;transition:background .2s var(--ease)}
.bag-qty button:hover{background:rgba(200,155,74,.18)}
/* Was an icon-only circle with no text — people didn't read it as "delete".
   Now icon + the actual word, which is the whole point of the fix. */
.bag-remove{display:inline-flex;align-items:center;gap:.35rem;min-height:26px;
  padding:.2rem .6rem;border-radius:999px;background:transparent;border:var(--line);
  color:var(--slate);font-size:.7rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  white-space:nowrap;transition:color .2s var(--ease),background .2s var(--ease),border-color .2s var(--ease)}
.bag-remove:hover{color:var(--hot);background:rgba(226,84,60,.12);border-color:rgba(226,84,60,.5)}
.bag-remove svg{width:14px;height:14px;flex:none}
/* Edit sits beside Remove and borrows its geometry so the two read as one
   pair of row controls. Coloured neutral rather than warning-red: editing is
   the safe, ordinary thing to do, and Remove should stay the one that looks
   like it costs something. */
.bag-edit{display:inline-flex;align-items:center;gap:.35rem;min-height:26px;
  padding:.2rem .6rem;border-radius:999px;background:transparent;border:var(--line);
  color:var(--slate);font-size:.7rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  white-space:nowrap;cursor:pointer;
  transition:color .2s var(--ease),background .2s var(--ease),border-color .2s var(--ease)}
.bag-edit:hover{color:var(--ice);background:rgba(140,188,199,.14);border-color:var(--sky)}
.bag-edit:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.bag-foot{flex:none;padding:1.1rem clamp(1rem,4vw,1.6rem) 1.4rem;border-top:var(--line)}
.bag-subtotal{display:flex;justify-content:space-between;font-weight:700;font-size:1.02rem}
.bag-checkout{width:100%;justify-content:center;margin-top:1rem}
.bag-note{margin:.7rem 0 0;font-size:.68rem;line-height:1.5;color:var(--slate)}
.bag-paused{margin:1rem 0 0;padding:.7rem .9rem;border-radius:8px;font-size:.76rem;line-height:1.5;
  background:rgba(226,84,60,.12);border:1px solid rgba(226,84,60,.45);color:var(--paper)}

/* ---- ordering paused ----
   Announced under the header so it's read before a bag gets filled, not
   discovered at checkout. Browsing, search and the wishlist all keep working:
   the pause stops orders, it doesn't close the shop. */
.order-pause-bar{background:var(--hot);color:var(--ink-deep);position:relative;z-index:var(--z-sticky)}
.order-pause-bar .wrap{display:flex;align-items:baseline;flex-wrap:wrap;gap:.3rem .7rem;
  padding:.7rem 0;font-size:.8rem;line-height:1.45}
.order-pause-bar b{font-size:.7rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase;
  white-space:nowrap}
.order-pause-bar span{opacity:.92;overflow-wrap:anywhere;min-width:0}
/* Deliberately still clickable — the click handler explains why nothing was
   added. A dead button teaches the shopper nothing. */
html.ordering-paused [data-add]{opacity:.45;filter:grayscale(.55);cursor:not-allowed}
html.ordering-paused .btn-gold{animation:none;box-shadow:none}
@media(max-width:480px){.bag-drawer{width:100vw}}

/* ================= QUICK VIEW — per-product popup =================
   Opened from any .pcard (not the quickadd button itself). Real name/price/
   category/photo/description from PRODUCTS; the expanded "full details"
   panel is placeholder copy (Lorem Ipsum, at the user's explicit request —
   real materials/dimensions copy doesn't exist yet). */
.qv-scrim{position:fixed;inset:0;background:rgba(5,15,20,.72);backdrop-filter:blur(3px);
  z-index:var(--z-modal);opacity:0;pointer-events:none;transition:opacity .3s var(--ease)}
.qv-scrim.open{opacity:1;pointer-events:auto}
.qv-modal{position:fixed;top:50%;left:50%;z-index:calc(var(--z-modal) + 1);
  width:min(720px,calc(100vw - 2rem));max-height:min(640px,88vh);overflow-y:auto;
  background:var(--ink-card);border-radius:14px;border:var(--line);
  box-shadow:0 40px 100px -30px rgba(0,0,0,.8);
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  opacity:0;pointer-events:none;transform:translate(-50%,-50%) scale(.96);
  transition:opacity .3s var(--ease),transform .3s var(--ease)}
.qv-modal.open{opacity:1;pointer-events:auto;transform:translate(-50%,-50%) scale(1)}
.qv-close{position:absolute;top:.8rem;right:.8rem;width:36px;height:36px;border-radius:999px;
  background:rgba(5,15,20,.5);border:var(--line);color:var(--ice);font-size:1.3rem;line-height:1;
  z-index:1;transition:background .25s var(--ease),color .25s var(--ease)}
.qv-close:hover{background:var(--gold);color:var(--ink-deep)}
.qv-media{background:linear-gradient(158deg,var(--wood-1),var(--wood-2) 62%,var(--wood-3));
  min-height:220px;display:flex}
.qv-media img{width:100%;height:100%;object-fit:cover}
.qv-media svg{width:44%;height:44%;margin:auto;color:var(--wood-ink)}
.qv-body{padding:clamp(1.2rem,3vw,1.8rem);min-width:0}
.qv-cat{font-size:.68rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--gold)}
.qv-body h2{font-size:clamp(1.3rem,2vw,1.6rem);margin-top:.3rem}
.qv-price{font-size:1.1rem;font-weight:700;color:var(--ice);margin-top:.5rem}
.qv-desc{font-size:.88rem;line-height:1.6;color:var(--sky);margin-top:.8rem}
.qv-full-wrap{display:grid;grid-template-rows:0fr;transition:grid-template-rows .4s var(--ease)}
.qv-full-wrap.open{grid-template-rows:1fr}
.qv-full{overflow:hidden;min-height:0}
.qv-full h3{font-family:var(--display);font-size:.85rem;font-weight:800;margin:1rem 0 .3rem}
.qv-full p{font-size:.82rem;line-height:1.6;color:var(--slate)}
.qv-actions{display:flex;flex-wrap:wrap;align-items:center;gap:1.1rem;margin-top:1.3rem}
/* WAS a quiet typographic link - gold text with a bottom border that only
   appeared on hover - sitting next to a solid gold "Add to bag". At rest it
   read as a caption rather than a control, and because it had no
   :focus-visible rule of its own, clicking it left the browser's own default
   focus ring around it: a pale box that looked like a rendering fault.

   Now a real control. Still quieter than Add to bag - that has to stay the
   loudest thing in the modal - but bordered, padded, and carrying a chevron
   that rotates to say which way it will go. The chevron is a pseudo-element
   on purpose: the toggle handler sets .textContent, which would wipe out any
   real child element the moment it ran. */
.qv-full-btn{
  display:inline-flex;align-items:center;gap:.5rem;
  font-size:.72rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--gold);white-space:nowrap;cursor:pointer;
  padding:.5rem .9rem;border-radius:999px;
  border:1px solid rgba(200,155,74,.42);
  background:rgba(200,155,74,.09);
  transition:background .25s var(--ease),border-color .25s var(--ease),color .25s var(--ease)}
.qv-full-btn::after{
  content:"";width:.4em;height:.4em;flex:none;
  border-right:2px solid currentColor;border-bottom:2px solid currentColor;
  transform:translateY(-.14em) rotate(45deg);
  transition:transform .3s var(--ease)}
.qv-full-btn[aria-expanded="true"]::after{transform:translateY(.06em) rotate(-135deg)}
.qv-full-btn:hover{background:rgba(200,155,74,.18);border-color:var(--gold);color:var(--gold-lite)}
.qv-full-btn:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
@media (prefers-reduced-motion:reduce){
  .qv-full-btn,.qv-full-btn::after{transition:none}
}

/* ---------------- fulfillment (required) ----------------
   Now a direct child of the quick-view body's normal block flow — a sibling
   ABOVE the personalization panel, not nested inside it — so it renders and
   is interactive on every quick-view open, Shipping already pre-checked,
   whether or not the personalization checkbox is ever touched. It no longer
   sits inside .qv-p-panel's 0fr/1fr collapse, so it needs none of its own:
   this is just static layout now. Only .qv-pickup-address below still gets
   a reveal, unchanged. Grid, not flex-wrap chips: three short labels always
   fit "auto 1fr", so they always stack one-per-row at any width from 375px
   up — no separate mobile rule needed for "the radio group should stack
   cleanly". */
.qv-fulfill{display:grid;grid-template-columns:auto 1fr;gap:.55rem .6rem;align-items:center;
  border:none;margin:1rem 0 0;padding:0;min-width:0}
.qv-fulfill legend{grid-column:1/-1;padding:0;margin:0 0 .1rem;font-size:.66rem;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--slate)}
/* Required-field marker, same convention as the WordPress theme's
   page-contact.php (.field-required after the label/legend text, colored
   rather than symbol-only, aria-hidden on the glyph since the legend text
   plus surrounding context already conveys required-ness to AT). */
.field-required{color:var(--gold-lite)}
.qv-fulfill input[type="radio"]{width:24px;height:24px;margin:0;flex:none;accent-color:var(--gold);cursor:pointer}
.qv-fulfill label{font-size:.84rem;color:var(--ice);cursor:pointer;min-height:24px;
  display:flex;align-items:center;overflow-wrap:anywhere}
/* Sibling combinator off the pickup radio's own id — it and this paragraph
   are both direct children of the fieldset, so no wrapper/:has() needed.
   display:none (not [hidden]) on purpose: [hidden] carries !important in
   this file and would permanently defeat the reveal below. */
.qv-pickup-address{grid-column:1/-1;display:none;margin:.15rem 0 0;font-size:.76rem;
  line-height:1.5;color:var(--gold-lite);overflow-wrap:anywhere}
#qvFulfillPickup:checked ~ .qv-pickup-address{display:block}

/* ---------------- personalization panel (optional) ----------------
   Checkbox + label + panel as flat siblings (not label-wraps-panel) so the
   reveal is a plain ":checked ~" sibling rule — no :has(), no JS needed to
   open or close it. Order is set explicitly because the panel itself has to
   come THIRD in paint order (flex-wrap) while staying a DOM sibling of the
   checkbox for the combinator to work at all. Name and message only now —
   Fulfillment moved out to its own always-visible fieldset above; this
   panel stays purely optional, hidden by default. */
.qv-personalize{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;margin:1rem 0 0}
.qv-p-check{order:1;width:24px;height:24px;margin:0;flex:none;accent-color:var(--gold);cursor:pointer}
.qv-p-label{order:2;font-size:.78rem;font-weight:700;color:var(--gold-lite);cursor:pointer;
  min-height:24px;display:inline-flex;align-items:center}
/* Same 0fr/1fr collapse used by .qv-full-wrap above — smooth height with no
   JS measuring, and already covered by the blanket prefers-reduced-motion
   rule further down (transition-duration:.001ms on every element). */
.qv-p-panel{order:3;flex:1 1 100%;display:grid;grid-template-rows:0fr;
  transition:grid-template-rows .4s var(--ease)}
.qv-p-check:checked ~ .qv-p-panel{grid-template-rows:1fr}
.qv-p-panel-in{overflow:hidden;min-height:0}
.qv-p-note{margin:.7rem 0 0;font-size:.72rem;line-height:1.5;color:var(--slate)}
.qv-field{display:flex;flex-direction:column;gap:.35rem;margin-top:.7rem;min-width:0}
.qv-field label{font-size:.66rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--slate)}
/* Character allowance under a per-product personalization label. Square
   holds the number (it is what physically fits on the piece); this just says
   it out loud so a shopper is not surprised when typing stops. */
.qv-field-hint{font-size:.7rem;color:var(--slate);opacity:.75;margin-top:-.15rem}
/* A native select popup is painted by the browser on its own light surface,
   but the options inside it inherit the CONTROL's colour - and that colour is
   pale, because the control sits on a dark panel. The result is pale text on
   white: readable only on whichever row the pointer happens to be over. The
   options therefore get their own explicit pair, independent of the control
   they belong to and of whatever the operating system's own dark mode would
   otherwise paint underneath them. */
.qv-field select.qv-input{appearance:auto}
.qv-field select.qv-input option{color:#12262B;background-color:#FFFFFF}
.qv-input{background:rgba(140,188,199,.09);border:var(--line);color:var(--ice);
  font:inherit;font-size:.86rem;padding:.6rem 1rem;outline:none;width:100%;min-width:0;
  transition:border-color .3s,background .3s}
.qv-input:focus{border-color:var(--gold);background:rgba(140,188,199,.14)}
input.qv-input{border-radius:999px;min-height:24px}
textarea.qv-input{border-radius:14px;resize:vertical;min-height:64px}
/* Quiet typographic link, matching .qv-full-btn's treatment rather than
   sharing its class (that class also carries the Full-details toggle's own
   aria-expanded semantics, which don't apply here). Text wraps rather than
   truncating or forcing nowrap — at 375px the full mandated CTA copy can
   run to two lines, but it stays one <a>, so the whole wrapped label is
   still a single coherent tap target. */
.qv-custom-link{display:inline-flex;align-items:center;min-height:24px;font-size:.76rem;
  font-weight:700;letter-spacing:.04em;color:var(--gold);border-bottom:1px solid transparent;
  transition:border-color .25s var(--ease)}
.qv-custom-link:hover{border-color:var(--gold)}

/* An honest heads-up, not an error — a Custom option needs the shopper's own
   description (and ideally a reference photo) before anything can actually
   be made, so Add-to-bag steps aside for this note + the CTA below rather
   than silently queuing an order nobody can fulfill. Same translucent-gold
   note treatment already used for .qv-opt:hover / the button pulse glow
   (rgba(200,155,74,…)), not the bag drawer's red .bag-paused — this isn't a
   problem, just a "one more step". */
.qv-custom-note{margin:.6rem 0 0;padding:.7rem .9rem;border-radius:8px;font-size:.76rem;
  line-height:1.5;color:var(--gold-lite);background:rgba(200,155,74,.12);
  border:1px solid rgba(200,155,74,.4)}
/* Selecting a Custom option promotes this link from a quiet footnote into
   the actual next step — the same full-width gold-pill weight Add-to-bag
   itself carries (.btn.btn-gold), spelled out here rather than toggling
   those classes on so this rule stays self-contained and doesn't depend on
   load order against .btn/.btn-gold elsewhere in this file. order:-1 pulls
   it ahead of "Full details" in the wrapping .qv-actions flex row, since it
   — not Add-to-bag, now hidden — is the primary action once this is on. */
.qv-custom-link.qv-custom-cta{order:-1;display:flex;width:100%;justify-content:center;
  background:var(--gold);color:var(--ink-deep);border:none;border-radius:999px;
  padding:1rem 1.8rem;font-size:.76rem;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;transition:background .25s var(--ease),transform .12s var(--press)}
.qv-custom-link.qv-custom-cta:hover{background:var(--gold-lite)}
.qv-custom-link.qv-custom-cta:active{transform:scale(.96);transition-duration:.08s}

/* ---------------- showcase slide → quick view ----------------
   The carousel's slide title is now its quick-view trigger too (see
   carousel.js buildShowcase()) — the same job shop.html's own inline
   .qv-trigger rule already does for a product-grid card name, reused here
   rather than reinvented. index.html has no page stylesheet of its own
   (its showcase rules are inline in <head>, off limits to this round), so
   the one carousel-specific rule this feature actually needs lives here
   instead. Scoped under ".slide h2" rather than bare ".qv-trigger" so it
   can never collide with — or depend on load order against — shop.html's
   own identical-looking rule for an unrelated element.
   font:inherit picks up .slide h2's huge clamp() size, tight negative
   tracking and uppercase transform automatically (text-transform and
   letter-spacing are inherited properties regardless of the font
   shorthand); text-align:inherit (not a hardcoded left) is what keeps this
   correctly centered under the ".slide{text-align:center}" mobile layout
   below 900px instead of overriding it back to the left. */
.slide h2 .qv-trigger{display:block;width:100%;text-align:inherit;background:none;border:none;
  padding:0;font:inherit;color:inherit;border-bottom:1px solid transparent;cursor:pointer;
  transition:color .25s var(--ease),border-color .25s var(--ease)}
.slide h2 .qv-trigger:hover,.slide h2 .qv-trigger:focus-visible{color:var(--gold-lite);border-color:var(--gold-lite)}

/* ---------------- quick-view zoom viewer ----------------
   The media pane is a real zoom surface (see DFZoom in catalog.js). Every
   frame of every gesture moves ONLY the img's transform: no width/height/
   top/left anywhere below, so a pinch never touches layout.
   The photo is object-fit:contain rather than the card's cover — a zoom
   viewer that opens by cropping the thing you came to inspect is a
   contradiction, and the pan bounds are computed from the contained box. */
.qv-zoom{position:relative;flex:1;min-width:0;display:flex;overflow:hidden}
.qv-zoom-stage{flex:1;min-width:0;display:grid;overflow:hidden;cursor:zoom-in;
  /* touch-action is the mobile contract, in two states. At rest: pan-y, so a
     finger dragged up the sheet still scrolls it — the viewer must not eat
     the page's scroll just by existing. pinch-zoom is deliberately NOT listed,
     so two fingers arrive as pointer events instead of zooming the page.
     Zoomed in: none, because then every axis belongs to the image. */
  touch-action:pan-y}
.qv-zoom-stage.zoomed{touch-action:none;cursor:grab}
.qv-zoom-stage.grabbing{cursor:grabbing}
/* Inset ring, because the ancestors clip — a 2px outset one would be cut off. */
.qv-zoom-stage:focus-visible{outline:2px solid var(--gold);outline-offset:-3px}
.qv-media .qv-zoom-stage img{grid-area:1/1;width:100%;height:100%;object-fit:contain;
  transform-origin:50% 50%;will-change:transform;user-select:none;-webkit-user-select:none;
  -webkit-user-drag:none;-webkit-touch-callout:none}
.qv-zoom-ui{position:absolute;left:.6rem;bottom:.6rem;z-index:2;display:flex;gap:.35rem}
.qv-zoom-btn{min-width:34px;height:34px;padding:0 .6rem;border-radius:999px;
  background:rgba(5,15,20,.55);border:var(--line);color:var(--ice);
  font-size:.72rem;font-weight:700;line-height:1;white-space:nowrap;
  display:inline-flex;align-items:center;justify-content:center;
  transition:background .25s var(--ease),color .25s var(--ease),
             opacity .25s var(--ease),transform .12s var(--press)}
.qv-zoom-btn:hover{background:var(--gold);color:var(--ink-deep)}
.qv-zoom-btn:active{transform:scale(.94)}
/* aria-disabled rather than [disabled]: the control stays focusable, so it
   never drops the focus ring to the document the moment it goes inert. */
.qv-zoom-btn[aria-disabled="true"]{opacity:.42}
.qv-zoom-btn[aria-disabled="true"]:hover{background:rgba(5,15,20,.55);color:var(--ice)}
.qv-zoom-load{position:absolute;top:.6rem;left:.6rem;z-index:2;margin:0;
  padding:.3rem .6rem;border-radius:999px;background:rgba(5,15,20,.62);border:var(--line);
  color:var(--ice);font-size:.62rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase}
/* Instructions, and the polite zoom-level announcement: for screen readers,
   not for the layout. */
.qv-zoom-help,.qv-zoom-status{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  border:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* Line-art fallback (no photo): nothing to magnify, so don't advertise
   controls that would do nothing. */
.qv-zoom-off .qv-zoom-ui,.qv-zoom-off .qv-zoom-help{display:none}
.qv-zoom-off .qv-zoom-stage{cursor:default}

@media(max-width:640px){.qv-modal{grid-template-columns:1fr;top:auto;bottom:0;left:0;
  transform:translateY(100%);translate:0;width:100vw;max-height:85vh;border-radius:14px 14px 0 0}
  .qv-modal.open{transform:none}
  .qv-media{min-height:180px}
  /* Enough surface to actually pinch on a 375px phone without the sheet
     turning the photo into a letterbox slit. */
  .qv-zoom-stage{min-height:210px}
  .qv-zoom-btn{min-width:38px;height:38px}}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important}
  .slide-copy>*{opacity:1;transform:none}
  .bag-drawer{transition:opacity .15s linear;transform:none}
  .bag-drawer:not(.open){opacity:0;pointer-events:none}
  .qv-modal{transition:opacity .15s linear;transform:translate(-50%,-50%)}
  @media(max-width:640px){.qv-modal{transform:none}}
}

/* Reduced motion ≠ no feedback — it means gentler, non-vestibular feedback.
   Reduced transparency and increased contrast are separate signals; each gets
   its own response rather than being folded into the motion query. */
@media (prefers-reduced-transparency:reduce){
  header.site{background:var(--ink-deep);backdrop-filter:none}
  .filters{background:var(--ink);backdrop-filter:none}
}
@media (prefers-contrast:more){
  :root{--line:1px solid rgba(228,237,240,.4)}
  header.site,.filters{background:var(--ink-deep);backdrop-filter:none}
  .chip,.iconbtn,.arrows button,.playpause{border-width:1.5px}
  .bag-drawer,.qv-modal,.bag-close,.qv-close{border-width:1.5px}
}
