/* ---------------------------------------------------------------------------
   Metric-matched fallbacks.

   149 font-family declarations in this file named only the webfont, with no
   fallback at all. Until the webfont arrives the browser therefore used its own
   default — Times on Windows Chrome — whose metrics are about 11% narrower than
   DM Sans. Every text run reflowed on swap, and on a product page that pushed
   the last breadcrumb onto a second line and dropped everything below it by
   26px: a single layout shift of 0.380, which was the entire CLS for that page.

   size-adjust makes the fallback occupy the same width as the real face, so the
   swap changes the glyphs without moving anything. The percentages are measured
   against the actual fonts, not taken from a table. No fixed heights are
   involved, so nothing can clip.
--------------------------------------------------------------------------- */
@font-face{
  font-family:"DM Sans Fallback";
  src:local("Arial"),local("Helvetica Neue"),local("Liberation Sans");
  /* Measured, not tabulated: Arial at 100% renders the reference string at
     49.45px where DM Sans renders 51px, so the fallback is scaled up to match. */
  size-adjust:103.13%;
  font-weight:100 900;
  font-style:normal;
  ascent-override:99%;
  descent-override:31%;
  line-gap-override:0%;
}
@font-face{
  font-family:"Cormorant Fallback";
  src:local("Georgia"),local("Times New Roman"),local("Liberation Serif");
  size-adjust:103.97%;
  font-weight:100 900;
  font-style:normal;
  ascent-override:92%;
  descent-override:29%;
  line-gap-override:0%;
}

/* ===== BRAND TOKENS ===== */
:root{
  --ivory:#FAF7F1; --pearl:#F2ECE1; --ink:#1C1A17; --pounamu:#21503D;
  --gold:#C2A15B; --gold-cta:#B68A3E; --gold-soft:#E9DCBE; --garnet:#8E2B3A; --muted:#6F685C; --line:#E4DCCD;
  --card-info:#F9F5EE;
  --maxw:1320px; --ease:cubic-bezier(.22,.61,.36,1);
  /* black header */
  --h-bg:#000000; --h-text:#FAF7F1; --h-muted:rgba(250,247,241,.62);
  --h-line:rgba(233,220,190,.16); --h-field:#1B1A17; --h-field-line:rgba(233,220,190,.22);
  --h-accent:#C2A15B; --h-hover:rgba(255,255,255,.06);
  /* drawer (mobile) stays dark so the white logo shows */
  --d-bg:#1C1A17; --d-sub:#211E18; --d-text:#FAF7F1; --d-muted:rgba(250,247,241,.6);
  --d-line:rgba(233,220,190,.14);
  --logo:url("../img/logo.png");
}
*{box-sizing:border-box;}

/*
 * The `hidden` attribute wins over any layout rule.
 *
 * The browser's own [hidden]{display:none} is a UA rule, so ANY author rule
 * that sets display on the same element beats it. `.pdp-buy{display:flex}` did
 * exactly that: the variation Notify Me block is rendered with `hidden` and
 * un-hidden by product.js only for a sold-out variation, but the attribute had
 * never had any effect, so every variable product showed "Notify Me When
 * Available" underneath an enabled Add to Cart — 62 products in this
 * catalogue. Two more elements were silently broken the same way
 * (.plp-more__auto, .plp-more__status, both display:flex and both rendered
 * hidden).
 *
 * Five components had already worked around this individually
 * (.ac-panel[hidden], .cart-drawer[hidden], .cs-modal[hidden],
 * .co-signin__panel[hidden], .qv-modal[hidden]). One rule here replaces the
 * pattern and stops the next component rediscovering it.
 *
 * `!important` is deliberate and is what every modern reset does: `hidden`
 * means hidden, and no layout rule may quietly override a state the markup and
 * the accessibility tree both treat as absolute. To show something, remove the
 * attribute — never restyle around it.
 */
[hidden]{display:none !important;}

html{-webkit-text-size-adjust:100%;}
body{margin:0;background:var(--ivory);color:var(--ink);font-family:"DM Sans",system-ui,sans-serif;
  font-size:16px;line-height:1.6;-webkit-font-smoothing:antialiased;}
a{color:inherit;text-decoration:none;}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit;}
:focus-visible{outline:2px solid var(--gold);outline-offset:2px;border-radius:2px;}
.icon{width:22px;height:22px;display:inline-block;fill:none;stroke:currentColor;stroke-width:1.75;
  stroke-linecap:round;stroke-linejoin:round;vertical-align:middle;}

/* ================= HEADER ================= */
.header{position:relative;z-index:80;}

/* ---- Top bar (WHITE) ---- */
.topbar{background:#FFFFFF;color:var(--ink);border-bottom:1px solid var(--line);
  font-family:"Inter",system-ui,sans-serif;font-size:.8rem;letter-spacing:.01em;}
.topbar .row{max-width:var(--maxw);margin:0 auto;padding:0 22px;height:40px;display:flex;align-items:center;justify-content:space-between;gap:18px;}
.topbar a.phone{display:inline-flex;align-items:center;gap:8px;font-weight:600;white-space:nowrap;color:var(--ink);flex:none;}
.topbar a.phone .icon{width:15px;height:15px;color:var(--gold-cta);}
.topbar a.phone .phone-ic{width:17px;height:17px;fill:var(--gold-cta);flex:none;}
.topbar .promo{flex:1;text-align:center;display:flex;align-items:center;justify-content:center;gap:9px;color:var(--muted);min-width:0;}
.topbar .promo .icon{width:16px;height:16px;color:var(--gold-cta);flex:none;}
.topbar .promo .txt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.topbar .promo b{color:var(--ink);font-weight:700;}
.topbar .afterpay{display:inline-flex;align-items:center;gap:8px;white-space:nowrap;color:var(--muted);font-size:.74rem;flex:none;}
.topbar .afterpay .ap{background:#b2fce4;color:#06382a;font-weight:700;font-size:.74rem;letter-spacing:-.01em;padding:3px 9px;border-radius:4px;}
.topbar .tb-left{display:flex;align-items:center;gap:13px;flex:none;}
.topbar .tb-promo{display:inline-flex;align-items:center;gap:7px;color:var(--ink);font-weight:600;white-space:nowrap;}
.topbar .tb-promo .icon{width:15px;height:15px;color:var(--gold-cta);flex:none;}
.topbar a.tb-promo{transition:color .2s;}
.topbar a.tb-promo:hover{color:var(--gold-cta);}
.topbar .tb-div{width:1px;height:13px;background:var(--line);flex:none;}
.topbar .tb-rotator{display:none;}

/* ---- Main bar (BLACK) : logo left · search centre · actions right ---- */
.mainbar{background:var(--h-bg);color:var(--h-text);border-bottom:1px solid var(--h-line);transition:padding .35s var(--ease);}
.mainbar .row{max-width:var(--maxw);margin:0 auto;padding:18px 22px;display:grid;
  grid-template-columns:auto 1fr auto;align-items:center;gap:22px;transition:padding .35s var(--ease);}
.header.shrink .mainbar .row{padding:11px 22px;}
.logo{justify-self:start;display:flex;align-items:center;}
.logo .logo-img{display:block;width:184px;height:46px;background:var(--logo) no-repeat left center/contain;transition:.35s var(--ease);}
.header.shrink .logo .logo-img{width:150px;height:38px;}
/* The header search field.
 
   Qualified with `form.` deliberately. WordPress puts the class `search` on
   <body> for every search-results page, so an unqualified `.search` styled the
   document body as a search pill: it inherited display:flex and this
   max-width, which collapsed the whole results page to 440px — and under the
   860px breakpoint below, `display:none`, which blanked the page outright on
   phones. The descendant rules leaked just as far, stripping the border and
   background from every input on the page and resizing every .icon. */
form.search{justify-self:center;display:flex;align-items:center;gap:10px;background:var(--h-field);
  border:1px solid var(--h-field-line);border-radius:40px;padding:11px 18px;max-width:440px;width:100%;transition:.25s;}
form.search:focus-within{border-color:var(--h-accent);box-shadow:0 0 0 3px rgba(194,162,91,.18);}
form.search .icon{width:18px;height:18px;color:var(--h-muted);flex:none;}
form.search input{border:none;background:none;outline:none;font-size:.9rem;width:100%;color:var(--h-text);}
form.search input::placeholder{color:var(--h-muted);}
.actions{justify-self:end;display:flex;align-items:center;gap:4px;}
.act{position:relative;display:flex;flex-direction:column;align-items:center;gap:3px;padding:8px 11px;border-radius:4px;
  font-size:.6rem;letter-spacing:.08em;text-transform:uppercase;color:var(--h-text);transition:color .2s;min-width:46px;}
.act:hover{color:var(--h-accent);}
.act .icon{width:21px;height:21px;}
.act .count{position:absolute;top:2px;right:7px;background:var(--gold);color:#1C1A17;font-size:.55rem;font-weight:700;
  min-width:15px;height:15px;border-radius:8px;display:flex;align-items:center;justify-content:center;padding:0 3px;}
.burger{display:none;width:44px;height:44px;align-items:center;justify-content:center;color:var(--h-text);}

/* ---- NAV (BLACK) ---- */
.nav{background:var(--h-bg);position:sticky;top:0;z-index:80;box-shadow:0 10px 30px -22px rgba(0,0,0,.45);}
.nav-row{max-width:var(--maxw);margin:0 auto;padding:0 22px;}
.nav-list{display:flex;justify-content:center;list-style:none;margin:0;padding:0;gap:2px;}
.nav-list>li{display:flex;}
.nav-list>li>a,.nav-list>li>.nav-trigger{display:flex;align-items:center;gap:7px;padding:15px 17px;
  font-size:.8rem;letter-spacing:.1em;text-transform:uppercase;font-weight:600;color:var(--h-text);position:relative;}
.nav-list>li>a .ni,.nav-list>li>.nav-trigger .ni{width:17px;height:17px;color:var(--h-accent);stroke-width:1.9;}
.nav-list>li>a::after,.nav-list>li>.nav-trigger::after{content:"";position:absolute;left:17px;right:17px;bottom:9px;
  height:1.5px;background:var(--h-accent);transform:scaleX(0);transform-origin:center;transition:transform .3s var(--ease);}
.nav-list .chev{width:13px;height:13px;color:var(--h-muted);transition:transform .3s var(--ease),color .25s var(--ease);}

/* ---- the lit parent ----

   One state, four ways to earn it: the pointer is on the item, the pointer is
   inside its open panel, the panel was opened by keyboard or click, or the
   customer is on that section's page.

   The second is the one that used to fail, and it fails silently: the panel is
   a child of its `<li>`, so `li:hover` stays true while the cursor is inside
   it — but only while the two actually touch. The panel is `top:100%` against
   the bar, so there is no gap for the pointer to cross, and the transparent
   positioning layer is `pointer-events:none` so it cannot hold the highlight
   open over the whole width of the page.

   `current-menu-ancestor` and `current-menu-parent` come with it, so a
   customer three levels into Rings still sees Jewellery lit. Those classes are
   allow-listed onto the item by Stonex_Walker_Mega — core assigns them, and
   the walker used to drop them along with everything else. */
.nav-list>li:hover>a,
.nav-list>li:hover>.nav-trigger,
.nav-list>li.is-open>a,
.nav-list>li.is-open>.nav-trigger,
.nav-list>li.current-menu-item>a,
.nav-list>li.current-menu-item>.nav-trigger,
.nav-list>li.current-menu-ancestor>a,
.nav-list>li.current-menu-ancestor>.nav-trigger,
.nav-list>li.current-menu-parent>a,
.nav-list>li.current-menu-parent>.nav-trigger{color:var(--h-accent);}

.nav-list>li:hover>a::after,.nav-list>li:hover>.nav-trigger::after,
.nav-list>li.is-open>a::after,.nav-list>li.is-open>.nav-trigger::after,
.nav-list>li.current-menu-item>a::after,.nav-list>li.current-menu-item>.nav-trigger::after,
.nav-list>li.current-menu-ancestor>a::after,.nav-list>li.current-menu-ancestor>.nav-trigger::after,
.nav-list>li.current-menu-parent>a::after,.nav-list>li.current-menu-parent>.nav-trigger::after{transform:scaleX(1);}

.nav-list>li:hover .chev,
.nav-list>li.is-open .chev,
.nav-list>li.current-menu-item .chev,
.nav-list>li.current-menu-ancestor .chev,
.nav-list>li.current-menu-parent .chev{color:var(--h-accent);}

/* The arrow turns only while the panel is actually open. On the section's own
   page the item is lit but nothing is expanded, and an upside-down chevron
   there would claim otherwise. */
.nav-list>li:hover .chev,
.nav-list>li.is-open .chev{transform:rotate(180deg);}

/* ---- MEGA (premium / editorial) ---- */
/* The panel is a positioning layer, not a surface.

   It still spans the row so its contents can be placed against the page
   gutter, but it paints nothing: the white card is `.mega-inner`, which is
   only as wide as the columns it holds. A full-bleed white bar behind three
   short lists was most of the problem — it read as a panel that had lost its
   content rather than one sized to it.

   `pointer-events:none` matters now that the layer is invisible and wider
   than the card: without it the transparent half would swallow clicks meant
   for the page behind. The card turns them back on. */
.mega{position:absolute;left:0;right:0;top:100%;
  --m-text:#1C1A17;--m-link:#5C564B;--m-muted:#8A8275;--m-line:#ECE4D5;--acc:#B68A3E;--acc2:#C2A15B;
  color:var(--m-text);background:transparent;border:0;box-shadow:none;pointer-events:none;
  opacity:0;visibility:hidden;transform:translateY(-8px);
  transition:opacity .32s var(--ease),transform .32s var(--ease),visibility .32s;z-index:70;}
.nav-list>li:hover>.mega,.nav-list>li.is-open>.mega{opacity:1;visibility:visible;transform:none;}
/* Panel grid: one track per column, sized to the content rather than to the
   page.

   Every track is the same width, and they pack from the left gutter instead of
   being stretched across 1320px. That is the whole fix. The old variants gave
   each panel a bespoke set of `fr` weights — `1fr 1fr 1.3fr` for Greenstone —
   which divided the FULL width between however many columns existed, so a
   panel with two short lists and a card put 356px under four one-word links
   and stranded the card on the far right.

   `--mcols` is the track count, written on the panel by the walker from what
   the panel actually holds: one per column, one for a promotional card, and
   one more for a double-width column. `--mtrack` is then the largest a track
   may be, capped so six columns still fit inside the page and never overflow
   it — `min()` picks the ideal 250px when there is room and the fair share
   when there is not.

   `justify-content:start` is what keeps the pack tight: with fixed tracks and
   no stretching, three columns end where the third column ends. `align-items:
   start` stops a short column from being stretched to the height of the
   tallest one, which is what used to make the promotional card's border reach
   further down the panel than its own content. */
.mega-inner{
  --mgap:38px;
  --mpad:34px;
  --mtrack-max:250px;
  /* The page row's own gutter. `.mainbar .row` is `max-width:var(--maxw)` with
     22px of padding, so the column everything on the page actually lines up
     in — logo, nav items, content — is 44px narrower than --maxw. A panel
     capped at --maxw is that 44px wider than the page it sits under, which is
     why the six-column ones used to hang past the right-hand gutter. */
  --mrow:22px;
  /* Measured from the page's own column, never from this element's width:
     the box is `max-content`, so a percentage here would size the tracks from
     a width that the tracks themselves decide. The responsive rules below
     lower the track COUNT rather than letting tracks shrink, which is what
     keeps this a fixed number the grid can be built from. */
  --mavail:calc(var(--maxw) - var(--mrow) * 2 - var(--mpad) * 2);
  --mtrack:min(var(--mtrack-max), calc((var(--mavail) - (var(--mcols) - 1) * var(--mgap)) / var(--mcols)));

  /* The white card. Only as wide as its columns, capped at the page width. */
  background:#FFFFFF;
  border:1px solid var(--m-line);border-top:0;
  border-radius:0 0 8px 8px;
  box-shadow:0 32px 64px -22px rgba(28,26,23,.22);
  pointer-events:auto;
  width:max-content;
  /* Capped at the page's content column, not at --maxw.
     A six-column panel wants more room than it can have, so it is the one that
     ends up exactly this wide — which is the point: Jewellery and Watches now
     both come out flush with the logo above them and the products below,
     instead of 44px wider than the page and offset from everything.
     Both terms subtract the SAME gutter, which is the part that was wrong
     before: the cap fell back to `100% - 32px` — two 16px gutters — while the
     page row uses 22px. Between --maxw and the breakpoint below it the panel
     came out 12px wider than the column it belongs in and overhung it by 6px
     each side. Measured at 1280px: column 22->1258, card 16->1264. Deriving
     both from --mrow makes the panel the column at every width. */
  max-width:calc(min(var(--maxw), 100%) - var(--mrow) * 2);
  /* Placed under the item that opened it — `--mega-x` is written by
     main.js, which measures the trigger and the panel and clamps the result
     inside the page. The `auto` fallback centres it, which is what a visitor
     sees for the instant before the script runs and what they keep if it
     never does. */
  margin-left:var(--mega-x, auto);
  margin-right:auto;
  /* Tightened from 48/54. On a two-column panel the old bottom padding left
     a band of empty white under the last link that was taller than the gap
     between two links. */
  padding:40px var(--mpad) 40px;
  display:grid;gap:var(--mgap);
  /* `minmax(0, …)`, not a bare width.

     A fixed track is sized from `--maxw` and cannot give way, so between the
     page maximum and the breakpoint below it — 1280px, say — six tracks asked
     for 1320px inside a 1280px box and simply overflowed. And because a closed
     panel is `visibility:hidden` rather than removed, it is still laid out:
     one panel overflowing put a horizontal scrollbar on the whole document
     with no menu open at all. The range lets the tracks give up their ideal
     width when the page is narrower, and take it when it is not. */
  grid-template-columns:repeat(var(--mcols), minmax(0, var(--mtrack)));
  justify-content:start;align-items:start;
}

/* A long label must wrap inside its track rather than widening it and
   throwing every column after it out of alignment. */
.mcol,.mfeature{min-width:0;}

/* Written as classes rather than an inline style so the responsive rules below
   can override them — an inline custom property would win over every media
   query in the stylesheet. */
.mega-cols-1{--mcols:1;}
.mega-cols-2{--mcols:2;}
.mega-cols-3{--mcols:3;}
.mega-cols-4{--mcols:4;}
.mega-cols-5{--mcols:5;}
.mega-cols-6{--mcols:6;}
.mega-cols-7{--mcols:7;}
.mega-cols-8{--mcols:8;}

/* A tighter gutter once there are six tracks.

   Capping the card at the page column (correct) cost each of six tracks about
   7px — 177px became 170px — and that is exactly the width at which
   "Necklaces & Pendants", "Greenstone Jewellery" and "View All Gemstones"
   begin wrapping to two lines. Five gutters at 30px instead of 38 gives the
   40px back and puts the tracks at 176px, where they were.

   Two class names deep so it cannot be undone by source order, and only at
   this density: 30px is still a clear separation between columns of short
   links, but it would look cramped under three. */
.mega-inner.mega-cols-6,
.mega-inner.mega-cols-7,
.mega-inner.mega-cols-8{--mgap:30px;}

/* Two tracks for the Watches brand list: nineteen names in a single 250px
   track read as a ragged wall of one-word lines. It is still on the same
   grid, so nothing beside it falls out of alignment. */
.mcol.brandcol{grid-column:span 2;}
.brand-list{columns:2;column-gap:28px;}
.brand-list li{break-inside:avoid;}
/* section header: small tracked label + short gold rule */
.mcol h4{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;letter-spacing:.22em;text-transform:uppercase;
  color:var(--m-text);font-weight:600;margin:0 0 18px;padding:0 0 13px;position:relative;border:0;line-height:1.2;}
.mcol h4::after{content:"";position:absolute;left:0;bottom:0;width:26px;height:1.5px;background:var(--acc);}
.mcol h4 .icon{display:none;}
.mcol ul + h4{margin-top:30px;}
.mcol ul{list-style:none;margin:0;padding:0;}
/* clean text links with animated gold underline */
.mlink{position:relative;display:flex;align-items:center;padding:6px 0;font-size:.9rem;font-weight:400;
  line-height:1.3;color:var(--m-link);transition:color .18s var(--ease);}
.mlink .t{position:relative;}
.mlink .t::after{content:"";position:absolute;left:0;bottom:-2px;width:0;height:1px;background:var(--acc);transition:width .3s var(--ease);}
.mlink:hover{color:var(--m-text);}
.mlink:hover .t::after{width:100%;}
.mlink .ic{display:none;}
.mlink .new{font-size:.5rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--acc);
  border:1px solid var(--acc2);padding:1px 5px;border-radius:2px;margin-left:9px;line-height:1.4;}
/* material lists: swatch / gem sits in a fixed left gutter so text stays aligned */
.mcol ul.mat .mlink{padding-left:26px;}
.mlink .swatch,.mlink .gemicon,.mlink .gi{position:absolute;left:0;top:50%;transform:translateY(-50%);}
.mlink .gi{width:17px;height:17px;display:flex;align-items:center;justify-content:center;}
.mlink .gi .icon{width:17px;height:17px;color:var(--acc);stroke-width:1.7;}
.mlink .swatch{width:14px;height:14px;border-radius:50%;flex:none;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.12),inset -2px -2px 4px rgba(0,0,0,.16),inset 2px 2px 4px rgba(255,255,255,.5);}
.s-ygold{background:radial-gradient(circle at 35% 30%,#f3da93,#c79a3c);}
.s-wgold{background:radial-gradient(circle at 35% 30%,#fdfdff,#dcdce2);}
.s-rgold{background:radial-gradient(circle at 35% 30%,#f0c2b3,#cf8a76);}
.s-silver{background:radial-gradient(circle at 35% 30%,#f2f3f5,#c2c7cf);}
.s-tungsten{background:radial-gradient(circle at 35% 30%,#8b8f96,#54585f);}
.s-steel{background:radial-gradient(circle at 35% 30%,#c2c7cf,#83888f);}
.s-black{background:radial-gradient(circle at 35% 30%,#4d4d4d,#0a0a0a);}
.s-multi{background:conic-gradient(from 200deg,#e0556a,#f0c065,#5fc79a,#2f88ad,#6e3f9e,#e0556a);}
.s-diamond{background:radial-gradient(circle at 35% 30%,#ffffff,#d7dde6);}
.s-cz{background:radial-gradient(circle at 35% 30%,#fbfbfd,#cfcfd8);}
.mlink .gemicon{width:15px;height:15px;flex:none;display:flex;align-items:center;justify-content:center;}
.mlink .gemicon .gemsvg{width:15px;height:15px;display:block;}
/* view-all link */
.mega-all{display:inline-flex;align-items:center;gap:8px;margin-top:22px;font-size:.64rem;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;color:var(--acc);border:0;padding:0;transition:gap .25s var(--ease);}
.mega-all .icon{width:13px;height:13px;stroke-width:2;transition:transform .3s var(--ease);}
.mega-all:hover{gap:13px;}
.mega-all:hover .icon{transform:translateX(3px);}
/* featured panel: light, editorial */
/* The card is one track wide now, not the 460px it used to be given, so its
   typography is set for that width. At 2rem the Greenstone heading broke at
   its own hyphen — "Hand-" / "Finished" / "Pounamu" — which reads as a
   rendering fault rather than a line break. */
.mfeature{background:linear-gradient(160deg,#FBF7EF,#F1E8D6);border:1px solid var(--m-line);border-radius:12px;
  padding:28px 26px;color:var(--m-text);display:flex;flex-direction:column;justify-content:flex-end;
  position:relative;overflow:hidden;min-height:228px;}
.mfeature.gold{background:linear-gradient(160deg,#FBF7EF,#EFE4CF);}
.mfeature .deco{display:none;}
.mfeature .tag{font-size:.62rem;letter-spacing:.24em;text-transform:uppercase;color:var(--acc);font-weight:600;position:relative;}
.mfeature h3{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-size:1.52rem;font-weight:600;color:var(--ink);
  margin:10px 0 18px;position:relative;line-height:1.12;text-wrap:balance;}
.mfeature .fbtn{display:inline-flex;align-items:center;gap:8px;align-self:flex-start;background:transparent;color:var(--ink);
  font-size:.66rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;padding:0 0 4px;
  border:0;border-bottom:1.5px solid var(--acc);border-radius:0;transition:color .25s,border-color .25s;}
.mfeature .fbtn .icon{width:13px;height:13px;transition:transform .3s var(--ease);}
.mfeature .fbtn:hover{color:var(--acc);}
.mfeature .fbtn:hover .icon{transform:translateX(4px);}
/* compact single-column dropdown (21st Keys) */
/* Deliberately NOT positioned.
   Making the wrapper a containing block is what pinned the compact panel under
   its own trigger; with it static, the panel resolves against the same block
   every other panel does and centres with them. */
.mini-wrap{position:static;}
/* The compact list is centred like every other panel now, rather than dropping
   under its own trigger. Consistency is the point: a customer moving along the
   bar should find each panel in the same place, and one dropdown that appears
   somewhere else reads as a different kind of thing. */
.mega.mini{left:0;right:0;width:auto;}
/* `--mega-x` here too, or this one alone stays centred: a more specific
   `margin-left:auto` silently wins over the placement every other panel
   gets, and the compact list was the one panel still opening in the middle
   of the page while its item sat at the end of the bar. */
.mega.mini .mega-inner{max-width:none;min-width:264px;width:max-content;
  margin-left:var(--mega-x, auto);margin-right:auto;padding:14px;display:block;
  border-radius:0 0 8px 8px;}
.mini-list{list-style:none;margin:0;padding:0;}
.mini-list .mlink{padding:9px 14px;border-radius:6px;white-space:nowrap;}
.mini-list .mlink:hover{background:#F7F2E8;color:var(--m-text);}

/* ---------------------------------------------------------------------------
 * The stock ceiling, told the same way everywhere.
 *
 * Driven by assets/js/qty-limit.js. Three parts: the control takes a red edge
 * and one short shake, a message appears beneath it, and the "+" carries a
 * spent state with a tooltip. Every quantity control on the site uses these
 * same classes, so the product page, Quick View, the drawer, the cart and the
 * checkout review cannot drift apart in how they say no.
 * ------------------------------------------------------------------------- */

/* The refused control. A 2px garnet edge and a ring, so it reads at a glance
   on both the white buy row and the drawer's tinted rows. `!important` is
   deliberate and narrow: `.pdp-qty`, `.cart-qty` and `.qv__qty` each set their
   own border, and this has to win over all three for the two seconds it is
   up. */
.is-limit{
  border-color:var(--garnet)!important;
  box-shadow:0 0 0 3px rgba(142,43,58,.16)!important;
  transition:border-color .18s var(--ease),box-shadow .18s var(--ease);
}

/* Six small movements over a third of a second. Long enough to notice, short
   enough that nobody waits for it to finish — and it never moves the control
   more than 4px, so nothing around it reflows. */
@keyframes stonex-qty-shake{
  0%,100%{transform:translateX(0);}
  15%{transform:translateX(-4px);}
  30%{transform:translateX(4px);}
  45%{transform:translateX(-3px);}
  60%{transform:translateX(3px);}
  80%{transform:translateX(-1px);}
}
.is-shake{animation:stonex-qty-shake .34s var(--ease);}

/* The message. Solid garnet rather than the tinted panel this replaced: at 6%
   opacity on a cream page the old note read as a hint, and the whole point is
   that it was being missed. */
.qty-limit{
  display:none;align-items:center;gap:10px;
  margin:9px 0 0;padding:10px 14px;
  border-radius:10px;
  background:var(--garnet);color:#fff;
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.82rem;font-weight:600;line-height:1.35;
  box-shadow:0 10px 24px -12px rgba(142,43,58,.7);
}
.qty-limit.is-shown{display:flex;animation:stonex-qty-rise .22s var(--ease);}
.qty-limit__ic{flex:none;width:18px;height:18px;display:flex;align-items:center;justify-content:center;}
.qty-limit__ic svg{width:18px;height:18px;display:block;}
.qty-limit__text{min-width:0;}

@keyframes stonex-qty-rise{
  from{opacity:0;transform:translateY(-4px);}
  to{opacity:1;transform:none;}
}

/* The product page's own note is styled where it is declared, further down —
   an override written up here would lose to it on source order. */

/* The spent "+". Dimmed and given a not-allowed cursor, but still pressable —
   see the note in qty-limit.js on why this is aria-disabled and not the
   disabled property. */
.pdp-qty__btn.is-maxed,.pdp-qty__btn[aria-disabled="true"],
.cart-qty__btn.is-maxed,.cart-qty__btn[aria-disabled="true"],
.pdp-sticky__qbtn.is-maxed,.pdp-sticky__qbtn[aria-disabled="true"]{opacity:.38;cursor:not-allowed;}
.cart-qty__btn.is-maxed:hover,.cart-qty__btn[aria-disabled="true"]:hover,
.pdp-sticky__qbtn.is-maxed:hover,.pdp-sticky__qbtn[aria-disabled="true"]:hover{background:none;}

@media(prefers-reduced-motion:reduce){
  .is-shake{animation:none;}
  .qty-limit.is-shown{animation:none;}
}

/* ---------------------------------------------------------------------------
 * WooCommerce's own stylesheet, held off the theme's controls.
 *
 * woocommerce.css ships on product, cart, checkout and account templates — the
 * theme keeps it there deliberately, because the variation form and select2
 * lean on parts of it. The cost is that its button rules are written as
 * `.woocommerce button.button.alt`, which outranks a single `.btn-gold` no
 * matter which stylesheet loads last. On a variable product the Add to Cart
 * button therefore came out in WooCommerce's default purple while every other
 * Add to Cart on the site was gold: same button, same class, different colour,
 * for no reason a customer could see.
 *
 * Matched at equal-or-higher specificity rather than with `!important`, so the
 * theme's own states — hover, disabled, loading — still behave normally.
 * ------------------------------------------------------------------------- */
.woocommerce button.btn-gold.button,
.woocommerce a.btn-gold.button,
.woocommerce input.btn-gold.button,
.woocommerce button.btn-gold.button.alt,
.woocommerce a.btn-gold.button.alt,
.woocommerce input.btn-gold.button.alt{
  background:linear-gradient(135deg,var(--gold),var(--gold-cta));
  color:var(--ink);
  border:0;
  border-radius:8px;
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.78rem;
  letter-spacing:.1em;text-transform:uppercase;
  padding:15px 28px;
}
.woocommerce button.btn-gold.button:hover,
.woocommerce a.btn-gold.button:hover,
.woocommerce button.btn-gold.button.alt:hover,
.woocommerce a.btn-gold.button.alt:hover{
  background:linear-gradient(135deg,var(--gold),var(--gold-cta));
  color:var(--ink);
}
/* A disabled Add to Cart — no variation chosen yet — must not look pressable. */
.woocommerce button.btn-gold.button:disabled,
.woocommerce button.btn-gold.button.alt:disabled,
.woocommerce button.btn-gold.button.alt:disabled[disabled]{
  background:linear-gradient(135deg,var(--gold),var(--gold-cta));
  color:var(--ink);opacity:.45;cursor:not-allowed;
}

/* A quantity fixed at one is not a control. WooCommerce emits it as a hidden
   input; the wrapper collapses so no empty box is left behind, and the input
   still posts from inside a hidden container.

   `!important` is the right tool exactly once here. The competition is not
   the theme's own `.pdp-qty{display:flex}` — two classes settle that — but
   woocommerce.css, which styles this element as
   `.woocommerce div.product form.cart .quantity` and outranks anything
   reasonable a theme can write. The rule does one thing, to one utility class
   whose entire purpose is to hide, so there is no state it can trap. */
.quantity.pdp-qty--hidden{display:none!important;}

/* ================= MOBILE DRAWER (dark) ================= */
.scrim{position:fixed;inset:0;background:rgba(20,18,15,.55);opacity:0;visibility:hidden;transition:.3s;z-index:95;}
/*
 * The open state is `is-on` / `is-open`, not `on`.
 *
 * These two selectors were the last survivors of the static reference build,
 * where the drawer was toggled with a bare `on` class. Everything the theme
 * actually ships — main.js, cart-save.js, quick-view.js, shop.js — uses the
 * `is-` prefix, so the class the script added and the class the stylesheet
 * waited for never met: `#drawer` gained `is-open`, kept
 * `transform:translateX(-100%)`, and sat 343px off the left edge of the screen.
 *
 * The mobile navigation was therefore dead on every page of the site. Nothing
 * about it looked broken in the markup or in the JavaScript; it took opening
 * the menu on a phone-sized viewport and reading the drawer's position to see
 * it, which is the whole reason this audit is measured rather than reviewed.
 */
.scrim.is-on{opacity:1;visibility:visible;}
.drawer{position:fixed;top:0;left:0;bottom:0;width:min(88vw,380px);background:var(--d-bg);color:var(--d-text);
  z-index:100;transform:translateX(-100%);transition:transform .35s var(--ease);display:flex;flex-direction:column;overflow-y:auto;}
/* visibility is the fallback where `inert` is unsupported: it removes the
   panel from the tab order too. Delayed on close so the slide still plays. */
.drawer{visibility:hidden;transition:transform .35s var(--ease),visibility 0s .35s;}
.drawer.is-open{transform:none;visibility:visible;transition:transform .35s var(--ease),visibility 0s;}
.drawer-top{display:flex;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid var(--d-line);}
.drawer-top .logo-img{width:130px;height:32px;background:var(--logo) no-repeat left center/contain;}
.drawer-search{display:flex;align-items:center;gap:10px;margin:14px 18px;padding:12px 16px;background:var(--d-sub);
  border:1px solid var(--d-line);border-radius:40px;}
.drawer-search .icon{width:18px;height:18px;color:var(--d-muted);}
.drawer-search input{border:none;outline:none;background:none;width:100%;font-size:.9rem;color:var(--d-text);}
.drawer-search input::placeholder{color:var(--d-muted);}
.dnav{list-style:none;margin:0;padding:0 10px;}
.dnav>li{border-bottom:1px solid var(--d-line);}
.dnav>li>a,.dacc{width:100%;display:flex;align-items:center;gap:11px;justify-content:flex-start;
  padding:15px 12px;font-size:.85rem;letter-spacing:.06em;text-transform:uppercase;font-weight:600;text-align:left;color:var(--d-text);}
.dnav .ni{width:18px;height:18px;color:var(--h-accent);flex:none;stroke-width:1.9;}
.dacc .chev{width:16px;height:16px;color:var(--d-muted);margin-left:auto;transition:transform .3s var(--ease);}
.dacc[aria-expanded="true"] .chev{transform:rotate(180deg);}
/* The open state had no rule at all.

   main.js toggles `is-open` on this element, `.dsub{max-height:0}` was the only
   height it ever had, and nothing defined what open meant — so tapping a
   section in the mobile menu rotated its chevron (that IS defined, off
   `aria-expanded`) and revealed nothing. The same class of defect as the
   drawer's own `.on` / `is-open` mismatch, one level further in.

   The height comes from `--dsub-h`, which main.js sets from the panel's own
   scrollHeight when it opens. The panels range from 22px to 2,175px, so a
   single hardcoded maximum would either clip the largest menu or make the
   smallest one animate over a range it does not use. The fallback is sized
   above the tallest measured panel so the menu still opens if the script's
   measurement ever fails. */
.dsub{max-height:0;overflow:hidden;transition:max-height .35s var(--ease);background:var(--d-sub);border-radius:6px;margin:0 4px;}
.dsub.is-open{max-height:var(--dsub-h,2400px);}
.dsub-in{padding:8px 4px 14px;}
.dsub h5{font-size:.62rem;letter-spacing:.16em;text-transform:uppercase;color:var(--h-accent);margin:12px 14px 6px;font-weight:700;}
.dsub a{display:flex;align-items:center;gap:10px;padding:9px 16px;font-size:.86rem;color:var(--d-muted);}
.dsub a:hover{color:var(--h-accent);}
.dsub .ni{width:17px;height:17px;color:var(--h-accent);stroke-width:1.9;}

/* Attribute groups inside a drawer section — gemstone, metal, shape, brand,
   function, strap — as wrapping pills rather than a column of full-width rows.
   Eighteen watch brands as rows was eighteen screens of scrolling; as chips it
   is four. Sizing follows the approved design, with the row height raised to
   the 44px touch floor the rest of the site now keeps. */
.dchips{display:flex;flex-wrap:wrap;gap:8px;padding:6px 14px 8px;}
.dchips a{display:inline-flex;align-items:center;min-height:44px;padding:8px 14px;border:1px solid var(--d-line);border-radius:30px;font-size:.78rem;color:var(--d-text);}
.dchips a:hover{border-color:var(--h-accent);color:var(--h-accent);}
.dchips{display:flex;flex-wrap:wrap;gap:7px;padding:6px 14px 4px;}
.dchips a{border:1px solid var(--d-line);border-radius:30px;padding:7px 13px;font-size:.78rem;color:var(--d-text);}
.drawer-foot{margin-top:auto;padding:18px;display:flex;flex-direction:column;gap:13px;border-top:1px solid var(--d-line);}
.drawer-foot a{display:flex;align-items:center;gap:11px;font-size:.86rem;color:var(--d-muted);}
.drawer-foot .icon{width:18px;height:18px;color:var(--h-accent);}

/* ================= HERO BANNER (placeholder) ================= */
.hero{max-width:var(--maxw);margin:0 auto;padding:26px 22px 20px;}
/*
 * Column ratio, gap and image aspect are one system, not three choices.
 *
 * The boxes take their height from their artwork (see the img rule further
 * down), so the two stacked boxes on the right only finish level with the tall
 * one on the left if the columns are split to make that true. At the 1320px
 * container with 22px padding and an 18px gap, the hero art is 1619x971 and the
 * two promos are 1817x866. With the 1619x971 hero that solved to 1.652fr; the
 * gemstone hero is 1774x887 (2.000), which re-solves to 1.992fr. Change the hero
 * artwork's aspect and this number has to be recomputed or the columns drift.
 */
.hero-grid{display:grid;grid-template-columns:1.992fr 1fr;grid-template-rows:auto auto;gap:18px;align-items:start;}
/*
 * No min-height. The background is a load-time ground only: with a fixed height
 * and object-fit:contain it became a black letterbox strip above and below every
 * banner whose art was shorter than the box — most visibly on a phone, where a
 * 346px-wide box was pinned to 190px for a 165px-tall image.
 */
.hero .box{background:#0b0b0b;border-radius:4px;position:relative;overflow:hidden;}
.hero .big{grid-row:1 / span 2;}
.hero .ph{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;}
.hero .phi{width:46px;height:46px;color:rgba(233,220,190,.22);fill:none;stroke:currentColor;stroke-width:1.4;}
@media(max-width:760px){
  .hero-grid{grid-template-columns:1fr;grid-template-rows:auto;gap:14px;}
  .hero .big{grid-row:auto;}
}

/* ================= TRUST / VALUE BAR ================= */
.usp{max-width:var(--maxw);margin:0 auto;padding:0 22px;}
.usp-row{background:var(--pearl);border:1px solid var(--line);border-radius:6px;padding:22px;display:grid;grid-template-columns:repeat(5,1fr);gap:16px;}
.usp-item{display:flex;align-items:center;gap:13px;justify-content:center;}
.usp-item .uic{flex:none;color:var(--ink);display:flex;}
.usp-item .uic .icon{width:30px;height:30px;stroke-width:1.6;}
.usp-item .t{display:flex;flex-direction:column;min-width:0;}
.usp-item .t b{font-size:.76rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ink);line-height:1.25;}
.usp-item .t span{font-size:.8rem;color:var(--muted);margin-top:2px;line-height:1.3;}
@media(max-width:980px){
  .usp-row{grid-template-columns:repeat(3,1fr);row-gap:22px;}
}
@media(max-width:620px){
  .usp-row{grid-template-columns:repeat(2,1fr);gap:18px 14px;padding:18px 16px;}
  .usp-item{justify-content:flex-start;}
}

/* ================= SHOP BY CATEGORY (carousel) ================= */
.shop-cats{padding:48px 0 60px;background:var(--ivory);}
.shop-cats__inner{max-width:var(--maxw);margin:0 auto;padding:0 22px;}
.shop-cats__head{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:16px;margin-bottom:30px;}
.shop-cats__heading{grid-column:2;text-align:center;}
.shop-cats__eyebrow{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;font-weight:600;letter-spacing:.22em;text-transform:uppercase;color:var(--gold-cta);margin:0 0 8px;}
.shop-cats__title{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:clamp(1.8rem,3.2vw,2.4rem);margin:0;line-height:1.05;}
.shop-cats__viewall{grid-column:3;justify-self:end;display:inline-flex;align-items:center;gap:8px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
  font-size:.72rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--ink);
  border-bottom:1.5px solid var(--gold);padding-bottom:4px;white-space:nowrap;transition:color .2s,gap .25s var(--ease);}
.shop-cats__viewall:hover{color:var(--gold-cta);gap:11px;}
.shop-cats__viewall svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.9;transition:transform .3s var(--ease);}
.shop-cats__viewall:hover svg{transform:translateX(3px);}
.shop-cats__viewport{position:relative;}
.shop-cats__track{list-style:none;margin:0;padding:4px 2px;display:flex;gap:18px;overflow-x:auto;
  scroll-snap-type:x mandatory;scroll-behavior:smooth;-ms-overflow-style:none;scrollbar-width:none;}
.shop-cats__track::-webkit-scrollbar{display:none;}
.shop-cats__item{flex:0 0 clamp(150px,16vw,196px);scroll-snap-align:start;}
.shop-cats__link{display:block;}
.shop-cats__media{display:block;position:relative;margin:0;aspect-ratio:4/5;border-radius:8px;overflow:hidden;
  background:var(--pearl);border:1px solid var(--line);}
.shop-cats__media img{width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain;object-position:center;display:block;}
.shop-cats__placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:rgba(28,26,23,.18);}
.shop-cats__placeholder svg{width:38px;height:38px;fill:none;stroke:currentColor;stroke-width:1.4;}
.shop-cats__name{display:block;text-align:center;margin-top:14px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.92rem;
  font-weight:500;color:var(--ink);letter-spacing:.01em;transition:color .2s;}
.shop-cats__link:hover .shop-cats__name{color:var(--gold-cta);}
.shop-cats__hint{display:none;}
.shop-cats__nav{position:absolute;top:calc((100% - 34px) / 2);transform:translateY(-50%);width:48px;height:48px;border-radius:50%;
  background:rgba(255,255,255,.86);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);
  border:1px solid rgba(28,26,23,.07);box-shadow:0 12px 30px -10px rgba(28,26,23,.38);
  display:flex;align-items:center;justify-content:center;color:var(--ink);z-index:3;cursor:pointer;
  transition:background .25s var(--ease),color .25s var(--ease),box-shadow .25s var(--ease),opacity .2s,transform .14s var(--ease);}
.shop-cats__nav:hover{background:var(--ink);color:#fff;box-shadow:0 16px 38px -10px rgba(28,26,23,.5);}
.shop-cats__nav:active{transform:translateY(-50%) scale(.94);}
.shop-cats__nav[disabled]{opacity:0;pointer-events:none;}
.shop-cats__nav--prev{left:6px;}
.shop-cats__nav--next{right:6px;}
.shop-cats__nav svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.9;}
.shop-cats__nav--prev svg{transform:rotate(180deg);}
@media(max-width:600px){
  .shop-cats__track{gap:14px;}
  .shop-cats__item{flex-basis:43vw;}
  .shop-cats__nav{display:none;}
  .shop-cats__head{grid-template-columns:1fr;justify-items:center;gap:14px;}
  .shop-cats__heading{grid-column:auto;}
  .shop-cats__viewall{grid-column:auto;justify-self:center;}
  .shop-cats__hint{display:flex;align-items:center;justify-content:center;gap:7px;margin:12px 0 0;
    font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);}
  .shop-cats__hint svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.9;}
}

/* ================= BROWSE OUR WATCH BRANDS ================= */
.bw-block{background:var(--ivory);padding:54px 0 62px;}
.watch-brands{padding:0;background:transparent;}
.wb__inner{max-width:var(--maxw);margin:0 auto;padding:0 22px;}
.wb__head{text-align:center;margin-bottom:26px;}
.wb__eyebrow{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;font-weight:600;letter-spacing:.22em;text-transform:uppercase;color:var(--gold-cta);margin:0 0 8px;}
.wb__title{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:clamp(1.8rem,3.2vw,2.4rem);margin:0;line-height:1.05;}
.wb__grid{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;}
.wb__card{position:relative;display:flex;align-items:center;justify-content:center;min-height:128px;
  background:#0b0a08;border:none;border-radius:6px;padding:26px;text-align:center;
  box-shadow:0 16px 34px -22px rgba(0,0,0,.85);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease);}
/* gold border is INSET inside the card (not on the outer edge), with even padding all round */
.wb__card::after{content:"";position:absolute;inset:9px;border:2px solid var(--gold);border-radius:3px;pointer-events:none;transition:border-color .3s var(--ease),inset .3s var(--ease);}
.wb__card:hover{transform:translateY(-4px);box-shadow:0 24px 46px -20px rgba(0,0,0,.72);}
.wb__card:hover::after{border-color:var(--gold-soft);}
.wb__logo{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:1.18rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ivory);transition:color .3s var(--ease);
  /* A brand with no bundled logo is set in type. Two lines at most, so one long name cannot stretch its whole grid row. */
  text-align:center;line-height:1.25;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
.wb__card:hover .wb__logo{color:var(--gold-soft);}
.wb__logo-img{max-width:168px;max-height:54px;width:auto;height:auto;object-fit:contain;display:block;}
@media(max-width:900px){.wb__grid{grid-template-columns:repeat(3,1fr);}}
@media(max-width:560px){.wb__grid{grid-template-columns:repeat(2,1fr);gap:14px;}.wb__card{min-height:96px;padding:18px;}.wb__logo{font-size:1rem;}}

/* ================= LIMITED EDITION WATCHES (carousel) ================= */
.limited-ed{padding:48px 0 0;background:transparent;}
.le__inner{max-width:var(--maxw);margin:0 auto;padding:0 22px;}
.le__head{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:16px;margin-bottom:28px;}
.le__heading{grid-column:2;text-align:center;}
.le__eyebrow{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;font-weight:600;letter-spacing:.22em;text-transform:uppercase;color:var(--gold-cta);margin:0 0 8px;}
.le__title{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:clamp(1.8rem,3.2vw,2.4rem);margin:0;line-height:1.05;}
.le__viewall{grid-column:3;justify-self:end;display:inline-flex;align-items:center;gap:8px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.72rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--ink);border-bottom:1.5px solid var(--gold);padding-bottom:4px;white-space:nowrap;transition:color .2s,gap .25s var(--ease);}
.le__viewall:hover{color:var(--gold-cta);gap:11px;}
.le__viewall svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.9;transition:transform .3s var(--ease);}
.le__viewall:hover svg{transform:translateX(3px);}
.le__viewport{position:relative;}
.le__track{list-style:none;margin:0;padding:6px 2px;display:flex;gap:20px;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;-ms-overflow-style:none;scrollbar-width:none;}
.le__track::-webkit-scrollbar{display:none;}
.le__item{flex:0 0 clamp(230px,24vw,272px);scroll-snap-align:start;display:flex;}
.le__card{position:relative;background:#fff;border:1px solid var(--line);border-radius:12px;overflow:hidden;display:flex;flex-direction:column;width:100%;
  transition:box-shadow .3s var(--ease),transform .3s var(--ease),border-color .3s var(--ease);}
.le__card:hover{transform:translateY(-4px);box-shadow:0 22px 50px -22px rgba(28,26,23,.3);border-color:var(--gold-soft);}
.le__media{position:relative;aspect-ratio:1/1;background:var(--pearl);overflow:hidden;}
.le__medialink{display:block;width:100%;height:100%;}
.le__media img{width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain;object-position:center;display:block;}
.le__ph{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:rgba(28,26,23,.16);}
.le__ph svg{width:54px;height:54px;fill:none;stroke:currentColor;stroke-width:1.1;}
.le__badge{position:absolute;left:12px;top:12px;background:var(--ink);color:var(--gold-soft);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.58rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:4px 9px;border-radius:3px;z-index:2;}
.le__wish{position:absolute;right:11px;top:11px;width:40px;height:40px;border-radius:50%;background:rgba(255,255,255,.94);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border:1px solid var(--line);display:flex;align-items:center;justify-content:center;color:var(--ink);z-index:3;cursor:pointer;box-shadow:0 6px 16px -8px rgba(28,26,23,.28);transition:transform .2s var(--ease),border-color .2s var(--ease),color .2s var(--ease),box-shadow .2s var(--ease);}
/* default state: crisp OUTLINE heart */
.le__wish svg{width:19px;height:19px;display:block;fill:none;stroke:currentColor;stroke-width:2;stroke-linejoin:round;stroke-linecap:round;}
.le__wish:hover{transform:scale(1.08);border-color:var(--ink);box-shadow:0 10px 22px -8px rgba(28,26,23,.36);}
.le__wish:active{transform:scale(.96);}
.le__wish:focus-visible{outline:2px solid var(--gold);outline-offset:2px;}
/* added state: FILLED heart in the brand garnet */
.le__wish.is-active{color:var(--garnet);border-color:rgba(142,43,58,.55);}
.le__wish.is-active svg{fill:currentColor;stroke:currentColor;}
.le__quick{position:absolute;left:12px;right:12px;bottom:12px;display:flex;align-items:center;justify-content:center;gap:8px;background:rgba(28,26,23,.92);color:var(--ivory);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.66rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;padding:10px;border-radius:6px;opacity:0;transform:translateY(8px);transition:.3s var(--ease);z-index:2;}
.le__media:hover .le__quick{opacity:1;transform:none;}
.le__quick svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.7;}
.le__body{padding:16px 16px 18px;display:flex;flex-direction:column;flex:1;}
.le__brand{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.62rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--gold-cta);margin:0 0 6px;}
.le__name{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.9rem;font-weight:500;line-height:1.35;color:var(--ink);margin:0 0 12px;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:2.45em;}
.le__name a{color:inherit;transition:color .2s;}
.le__name a:hover{color:var(--gold-cta);}
.le__price{display:flex;align-items:baseline;gap:9px;margin-top:auto;margin-bottom:14px;}
.le__price .now{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:1.4rem;color:var(--ink);}
.le__price .was{font-size:.82rem;color:var(--muted);text-decoration:line-through;}
.le__cart{display:inline-flex;align-items:center;justify-content:center;gap:9px;width:100%;background:var(--gold-cta);color:var(--ink);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;font-weight:700;letter-spacing:.13em;text-transform:uppercase;padding:12px;border-radius:6px;cursor:pointer;transition:background .25s var(--ease),color .25s var(--ease);}
.le__cart:hover{background:var(--ink);color:var(--ivory);}
.le__cart.is-added{background:var(--pounamu);color:#fff;}
.le__cart svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.7;}
.le__nav{position:absolute;top:calc(clamp(230px,24vw,272px) / 2 + 6px);transform:translateY(-50%);width:48px;height:48px;border-radius:50%;background:rgba(255,255,255,.9);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:1px solid rgba(28,26,23,.07);box-shadow:0 12px 30px -10px rgba(28,26,23,.38);display:flex;align-items:center;justify-content:center;color:var(--ink);z-index:4;cursor:pointer;transition:background .25s var(--ease),color .25s var(--ease),box-shadow .25s var(--ease),opacity .2s,transform .14s var(--ease);}
.le__nav:hover{background:var(--ink);color:#fff;box-shadow:0 16px 38px -10px rgba(28,26,23,.5);}
.le__nav:active{transform:translateY(-50%) scale(.94);}
.le__nav[disabled]{opacity:0;pointer-events:none;}
.le__nav--prev{left:-8px;}
.le__nav--next{right:-8px;}
.le__nav svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.9;}
.le__nav--prev svg{transform:rotate(180deg);}
@media(max-width:600px){
  .bw-block{padding:36px 0 44px;}
  .limited-ed{padding-top:16px;}
  .le__item{flex-basis:74vw;}
  .le__nav{display:none;}
  .le__head{grid-template-columns:1fr;justify-items:center;gap:14px;}
  .le__heading{grid-column:auto;}
  .le__viewall{grid-column:auto;justify-self:center;}
}

/* Tablet: fewer tracks, more rows.

   The tracks are capped, not stretched, so a narrower page would otherwise
   squeeze six of them to 140px and break the link text onto two lines each.
   Reducing the count wraps the columns onto a second row at a readable width
   instead.

   The promotional card is no longer hidden here. `.mega-4 .mfeature{display:none}`
   removed a whole panel of editorial content below 1200px — an iPad in
   landscape is 1024px, so the card was missing on a device plenty of people
   shop on, with nothing to indicate it. It now simply takes its place in the
   wrap like any other track. */
@media(max-width:1200px){
  .mega-inner{row-gap:30px;}
  /* Wrapped into balanced rows rather than squeezed. Four tracks go to two —
     two rows of two — not three, which would leave the promotional card alone
     on a second row beside an empty half. */
  .mega-cols-4{--mcols:2;}
  .mega-cols-5,.mega-cols-6,.mega-cols-7,.mega-cols-8{--mcols:3;}
  .brand-list{columns:1;}
}

/* Below this the three-track card (894px) would be wider than the page. The
   nav itself hands over to the drawer at 860px, so this band is narrow — but
   it is the band an iPad in portrait sits in. */
@media(max-width:1000px){
  .mega-cols-3,.mega-cols-4,.mega-cols-5,.mega-cols-6,.mega-cols-7,.mega-cols-8{--mcols:2;}
}
@media(max-width:860px){
  form.search{display:none;}
  .nav{display:none;}
  .burger{display:flex;}

  /* The middle column has to be allowed to shrink.
 
     A bare `1fr` keeps its automatic minimum, and that minimum is the logo's
     fixed 184px — so burger + logo + actions + gaps came to 428px inside a
     390px viewport and pushed the cart icon off the right edge of every page
     on the site. minmax(0,1fr) lets the column go below its content, and the
     logo then scales down against it instead of forcing the row wider. */
  .mainbar .row{padding:12px 18px;grid-template-columns:auto minmax(0,1fr) auto;gap:10px;}
  .logo{justify-self:center;min-width:0;max-width:100%;}
  /* Taller on a phone, on request.

     The logo is a contain-fitted background, so height is the only dimension
     that changes how big the mark reads; width stays generous enough that the
     wordmark is never the constraint. `max-width:100%` still lets the middle
     grid column squeeze it on the narrowest screens rather than pushing the
     cart icon off the row — the reason this rule exists at all. */
  .logo .logo-img{width:170px;max-width:100%;height:48px;}
  /* Visually hidden, NOT display:none.
     display:none removes the label from the accessibility tree as well as the
     screen, which left the account, wishlist and cart icons as links with no
     accessible name at all on mobile — "Links must have discernible text".
     Clipping hides it visually while a screen reader still announces it. */
  .act-label{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}
  .act{min-width:40px;padding:8px 7px;}
  .actions{gap:0;}
  .topbar .tb-left,.topbar .promo,.topbar .afterpay{display:none;}
  .topbar .row{position:relative;justify-content:center;height:38px;padding:0 14px;}
  .topbar .tb-rotator{display:block;position:relative;width:100%;height:100%;}
  .topbar .tb-msg{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;text-align:center;
    opacity:0;transition:opacity .55s ease;color:var(--ink);font-weight:600;font-size:.78rem;letter-spacing:.01em;
    padding:0 14px;pointer-events:none;}
  .topbar .tb-msg.is-on{opacity:1;}
}
@media(max-width:1080px){
  .topbar .promo{display:none;}
}
@media(prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important;}}


/* ================= FIRST PURCHASE REWARD ================= */
.reward{background:var(--ivory);padding:48px 22px;}
.reward__card{position:relative;overflow:hidden;max-width:var(--maxw);margin:0 auto;padding:42px 48px;border-radius:18px;
  background:radial-gradient(125% 150% at 88% 0%,#1b1610 0%,#0b0a08 58%);
  border:1px solid rgba(194,161,91,.3);
  box-shadow:0 32px 72px -34px rgba(0,0,0,.7);
  display:flex;align-items:center;gap:40px;flex-wrap:wrap;}
.reward__card::before{content:"";position:absolute;top:-45%;right:-4%;width:360px;height:360px;border-radius:50%;
  background:radial-gradient(circle,rgba(194,161,91,.16),transparent 66%);pointer-events:none;}
/* left content */
.reward__lead{position:relative;flex:1 1 290px;min-width:0;}
.reward__eyebrow{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;font-weight:700;letter-spacing:.2em;text-transform:uppercase;color:var(--gold);margin:0 0 11px;}
.reward__title{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:clamp(1.9rem,3.4vw,2.65rem);line-height:1.05;color:var(--ivory);margin:0 0 12px;}
.reward__accent{color:var(--gold);font-style:italic;}
.reward__sub{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.93rem;line-height:1.6;color:rgba(250,247,241,.66);margin:0;max-width:34ch;}
/* dividers */
.reward__divider{align-self:stretch;flex:none;width:1px;min-height:104px;background:linear-gradient(transparent,rgba(194,161,91,.42),transparent);}
/* icon medallions */
.reward__features{position:relative;flex:1 1 380px;display:flex;justify-content:space-around;align-items:flex-start;gap:18px;}
.reward__feat{display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;}
.reward__ic{position:relative;width:74px;height:74px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  background:radial-gradient(circle at 50% 32%,#241e16,#0b0a08);
  border:1px solid rgba(194,161,91,.5);
  box-shadow:0 16px 32px -16px rgba(0,0,0,.85),inset 0 1px 1px rgba(233,220,190,.14);
  transition:transform .35s var(--ease),border-color .35s var(--ease),box-shadow .35s var(--ease);}
.reward__ic::after{content:"";position:absolute;inset:7px;border-radius:50%;border:1px solid rgba(194,161,91,.22);transition:border-color .35s var(--ease);}
.reward__ic svg{width:31px;height:31px;fill:none;stroke:var(--gold);stroke-width:1.6;}
.reward__feat:hover .reward__ic{transform:translateY(-5px);border-color:rgba(194,161,91,.9);box-shadow:0 24px 40px -16px rgba(0,0,0,.9),0 0 0 4px rgba(194,161,91,.08),inset 0 1px 1px rgba(233,220,190,.2);}
.reward__feat:hover .reward__ic::after{border-color:rgba(194,161,91,.5);}
.reward__feat-label{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.82rem;font-weight:600;letter-spacing:.02em;color:rgba(250,247,241,.82);line-height:1.3;max-width:13ch;}
/* right CTA */
.reward__action{flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:13px;text-align:center;}
.reward__action .btn-gold{padding:16px 30px;}
.reward__fine{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.72rem;color:rgba(250,247,241,.5);max-width:20ch;line-height:1.45;}
@media(max-width:980px){
  .reward__card{gap:28px 34px;}
  .reward__divider{display:none;}
  .reward__lead{flex:1 1 100%;}
  .reward__features{flex:1 1 58%;justify-content:flex-start;gap:34px;}
  .reward__action{flex:1 1 28%;align-items:flex-start;text-align:left;}
}
@media(max-width:640px){
  .reward{padding:36px 16px;}
  .reward__card{padding:30px 24px;gap:26px;}
  .reward__features{width:100%;justify-content:space-between;gap:12px;}
  .reward__action{width:100%;align-items:stretch;text-align:center;}
  .reward__action .btn-gold{width:100%;justify-content:center;}
  .reward__feat-label{font-size:.75rem;}
}
@media(max-width:430px){
  .reward__ic{width:62px;height:62px;}
  .reward__ic svg{width:27px;height:27px;}
}

/* ================= REUSABLE BUTTONS & SECTION HEADINGS ================= */
.btn-gold{position:relative;display:inline-flex;align-items:center;gap:9px;overflow:hidden;background:linear-gradient(135deg,var(--gold),var(--gold-cta));color:var(--ink);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;padding:15px 28px;border-radius:8px;white-space:nowrap;box-shadow:0 14px 32px -14px rgba(194,161,91,.5);transition:transform .3s var(--ease),box-shadow .3s var(--ease);}
.btn-gold svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2;transition:transform .3s var(--ease);}
.btn-gold::before{content:"";position:absolute;top:0;left:-120%;width:55%;height:100%;background:linear-gradient(100deg,transparent,rgba(255,255,255,.55),transparent);transform:skewX(-18deg);transition:left .6s var(--ease);}
.btn-gold:hover{transform:translateY(-3px);box-shadow:0 20px 40px -14px rgba(194,161,91,.62);}
.btn-gold:hover::before{left:135%;}
.btn-gold:hover svg{transform:translateX(4px);}
.btn-gold:focus-visible{outline:2px solid var(--gold-soft);outline-offset:3px;}
.btn-ghost{display:inline-flex;align-items:center;gap:9px;border:1px solid var(--ink);color:var(--ink);background:transparent;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;padding:14px 26px;border-radius:8px;transition:background .25s var(--ease),color .25s var(--ease);}
.btn-ghost svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;}
.btn-ghost:hover{background:var(--ink);color:var(--ivory);}
.sect-eyebrow{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;font-weight:600;letter-spacing:.22em;text-transform:uppercase;color:var(--gold-cta);margin:0 0 10px;}
.sect-title{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:clamp(1.9rem,3.3vw,2.7rem);line-height:1.06;color:var(--ink);margin:0;}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}

/* ================= LAB GROWN COLLECTION ================= */
.labgrown{background:var(--ivory);padding:60px 0;}
.labgrown__inner{max-width:var(--maxw);margin:0 auto;padding:0 22px;display:grid;grid-template-columns:1fr 1fr;gap:54px;align-items:center;}
.labgrown__media{position:relative;margin:0;border-radius:16px;overflow:hidden;aspect-ratio:4/3;background:#0b0a08;box-shadow:0 30px 60px -30px rgba(28,26,23,.5);}
.labgrown__video{width:100%;height:100%;object-fit:cover;display:block;}
.labgrown__badge{position:absolute;left:16px;top:16px;background:rgba(11,10,8,.6);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border:1px solid rgba(194,161,91,.4);color:var(--gold-soft);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.62rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;padding:6px 12px;border-radius:30px;}
.labgrown__toggle{position:absolute;right:14px;bottom:14px;width:42px;height:42px;border-radius:50%;background:rgba(11,10,8,.55);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border:1px solid rgba(233,220,190,.35);color:var(--ivory);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:.2s var(--ease);}
.labgrown__toggle:hover{background:rgba(11,10,8,.8);transform:scale(1.06);}
.labgrown__toggle svg{width:15px;height:15px;fill:currentColor;stroke:none;}
.labgrown__toggle .ic-play{display:none;}
.labgrown__toggle.is-paused .ic-pause{display:none;}
.labgrown__toggle.is-paused .ic-play{display:block;}
.labgrown__eyebrow{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;font-weight:600;letter-spacing:.22em;text-transform:uppercase;color:var(--gold-cta);margin:0 0 12px;}
.labgrown__title{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:clamp(2.1rem,4vw,3.1rem);line-height:1.04;color:var(--ink);margin:0 0 18px;}
.labgrown__accent{color:var(--gold-cta);font-style:italic;}
.labgrown__desc{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.98rem;line-height:1.7;color:var(--muted);margin:0 0 22px;max-width:52ch;}
.labgrown__points{list-style:none;margin:0 0 28px;padding:0;display:flex;flex-direction:column;gap:12px;}
.labgrown__points li{display:flex;align-items:center;gap:12px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.92rem;color:var(--ink);}
.labgrown__points svg{width:20px;height:20px;flex:none;color:var(--gold-cta);}
.labgrown__cta{display:flex;flex-wrap:wrap;gap:14px;}
@media(max-width:860px){
  .labgrown{padding:52px 0;}
  .labgrown__inner{grid-template-columns:1fr;gap:30px;}
  .labgrown__media{aspect-ratio:16/12;max-height:420px;}
}

/* ================= 21ST KEYS COLLECTION (banner + carousel) ================= */
.keysrev{background:var(--pearl);padding:70px 0;}
.keysrev__inner{max-width:var(--maxw);margin:0 auto;padding:0 22px;display:grid;grid-template-columns:2fr 3fr;gap:38px;align-items:stretch;}
/* LEFT banner */
.keys2__banner{position:relative;margin:0;border-radius:14px;overflow:hidden;min-height:470px;background:linear-gradient(150deg,#16140f,#070706);border:1px solid rgba(194,161,91,.25);box-shadow:0 28px 56px -32px rgba(28,26,23,.45);}
.keys2__banner img{width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain;object-position:center;display:block;}
.keys2__banner::after{content:"";position:absolute;inset:10px;border:1px solid rgba(194,161,91,.3);border-radius:9px;pointer-events:none;}
.keys2__ph{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;color:rgba(233,220,190,.4);}
.keys2__ph svg{width:46px;height:46px;fill:none;stroke:currentColor;stroke-width:1.2;}
.keys2__ph em{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-style:normal;font-size:.74rem;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:rgba(233,220,190,.55);}
/* RIGHT main */
.keys2__main{min-width:0;display:flex;flex-direction:column;}
.keys2__head{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;margin-bottom:22px;}
.keys2__titles{min-width:0;}
.keys2__controls{display:flex;align-items:center;gap:18px;flex:none;}
.keys2__viewall{display:inline-flex;align-items:center;gap:8px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.72rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--ink);border-bottom:1.5px solid var(--gold);padding-bottom:4px;white-space:nowrap;transition:color .2s,gap .25s var(--ease);}
.keys2__viewall:hover{color:var(--gold-cta);gap:11px;}
.keys2__viewall svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.9;transition:transform .3s var(--ease);}
.keys2__viewall:hover svg{transform:translateX(3px);}
.keys2__navs{display:flex;gap:8px;}
.keys2__nav{width:40px;height:40px;border-radius:50%;background:#fff;border:1px solid var(--line);box-shadow:0 8px 20px -12px rgba(28,26,23,.4);display:flex;align-items:center;justify-content:center;color:var(--ink);cursor:pointer;transition:.2s var(--ease);}
.keys2__nav:hover{background:var(--ink);color:#fff;border-color:var(--ink);}
.keys2__nav[disabled]{opacity:.3;pointer-events:none;}
.keys2__nav svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2;}
.keys2__nav[data-keys-prev] svg,.keys2__nav[data-prev] svg{transform:rotate(180deg);}
.keys2__viewport{flex:1;min-height:0;}
.keys2__viewport .le__track{gap:18px;align-items:flex-start;height:100%;}
.keys2__viewport .le__item{flex:0 0 clamp(200px,30%,272px);}
.keys2__viewport .le__media{background:#fff;}
.keys2__viewport .le__card{border-color:#ece4d4;}
@media(max-width:920px){
  .keysrev__inner{grid-template-columns:1fr;gap:26px;}
  .keys2__banner{min-height:0;aspect-ratio:16/10;}
  .keys2__viewport .le__item{flex-basis:clamp(220px,42%,260px);}
}
@media(max-width:560px){
  .keysrev{padding:52px 0;}
  .keys2__head{flex-wrap:wrap;align-items:flex-start;gap:12px;}
  .keys2__viewport .le__item{flex-basis:76%;}
  .keys2__banner{aspect-ratio:4/3;}
}

/* ================= IN-STORE GALLERY (carousel) ================= */
.gallery{background:var(--ivory);padding:64px 0;}
.gallery__head{max-width:var(--maxw);margin:0 auto 30px;padding:0 22px;display:flex;align-items:flex-end;justify-content:space-between;gap:24px;flex-wrap:wrap;}
.gallery__titles{min-width:0;}
.gallery__cta{display:flex;gap:12px;flex-wrap:wrap;flex:none;}
.gallery__viewport{position:relative;max-width:var(--maxw);margin:0 auto;padding:0 22px;}
.gallery__track{list-style:none;margin:0;padding:4px;display:flex;gap:16px;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;-ms-overflow-style:none;scrollbar-width:none;}
.gallery__track::-webkit-scrollbar{display:none;}
/* 3:2 to match the showroom photographs — a 4/3 slide letterboxed every one
   of them against the dark gradient, the same fault the hero had. */
.gallery__slide{position:relative;flex:0 0 clamp(260px,42%,520px);scroll-snap-align:start;aspect-ratio:3/2;border-radius:14px;overflow:hidden;background:linear-gradient(150deg,#16140f,#070706);border:1px solid var(--line);box-shadow:0 20px 44px -30px rgba(28,26,23,.4);}
.gallery__slide img{width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain;object-position:center;display:block;}
.gallery__ph{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:rgba(233,220,190,.2);}
.gallery__ph svg{width:42px;height:42px;fill:none;stroke:currentColor;stroke-width:1.1;}
.gallery__slide::after{content:"";position:absolute;inset:0;border-radius:14px;border:1px solid transparent;transition:border-color .3s var(--ease);pointer-events:none;}
.gallery__slide:hover::after{border-color:rgba(194,161,91,.5);}
.gallery__nav{position:absolute;top:50%;transform:translateY(-50%);width:46px;height:46px;border-radius:50%;background:rgba(255,255,255,.92);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid var(--line);box-shadow:0 10px 26px -12px rgba(28,26,23,.45);display:flex;align-items:center;justify-content:center;color:var(--ink);cursor:pointer;z-index:3;transition:.2s var(--ease);}
.gallery__nav:hover{background:var(--ink);color:#fff;border-color:var(--ink);}
.gallery__nav[disabled]{opacity:0;pointer-events:none;}
.gallery__nav svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:2;}
.gallery__nav--prev{left:30px;}
.gallery__nav--prev svg{transform:rotate(180deg);}
.gallery__nav--next{right:30px;}
@media(max-width:640px){
  .gallery{padding:46px 0;}
  .gallery__head{margin-bottom:22px;align-items:flex-start;}
  .gallery__cta{width:100%;}
  .gallery__cta .btn-gold,.gallery__cta .btn-ghost{flex:1 1 auto;justify-content:center;}
  .gallery__slide{flex-basis:82%;}
  .gallery__nav{width:40px;height:40px;}
  .gallery__nav--prev{left:24px;}.gallery__nav--next{right:24px;}
}

/* ================= NEWSLETTER ================= */
.newsletter{background:var(--pearl);padding:64px 22px;border-top:1px solid var(--line);}
.newsletter__inner{max-width:640px;margin:0 auto;text-align:center;}
.newsletter .sect-eyebrow{margin-bottom:10px;}
.newsletter__title{margin:0 0 12px;}
.newsletter__sub{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.98rem;line-height:1.6;color:var(--muted);margin:0 auto 26px;max-width:46ch;}
.newsletter__sub strong{color:var(--ink);}
.newsletter__form{display:flex;gap:12px;max-width:480px;margin:0 auto;}
.newsletter__input{flex:1;min-width:0;background:#fff;border:1px solid var(--line);border-radius:9px;padding:15px 18px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.92rem;color:var(--ink);transition:border-color .2s,box-shadow .2s;}
.newsletter__input::placeholder{color:var(--muted);}
.newsletter__input:focus{outline:none;border-color:var(--gold);box-shadow:0 0 0 3px rgba(194,161,91,.16);}
.newsletter__btn{flex:none;}
.newsletter__fine{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.76rem;color:var(--muted);margin:16px 0 0;}
@media(max-width:560px){
  .newsletter{padding:48px 16px;}
  .newsletter__form{flex-direction:column;}
  .newsletter__btn{width:100%;justify-content:center;}
}

/* ================= FOOTER ================= */
.site-footer{background:#0a0908;color:rgba(250,247,241,.7);position:relative;}
.site-footer::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;background:linear-gradient(90deg,transparent,rgba(194,161,91,.5),transparent);}
.site-footer__top{max-width:var(--maxw);margin:0 auto;padding:54px 22px 32px;text-align:center;border-bottom:1px solid rgba(233,220,190,.12);}
.footer__brand{display:inline-block;}
.footer__logo{height:46px;width:auto;display:block;margin:0 auto 16px;}
.footer__tagline{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-style:italic;font-size:1.15rem;color:rgba(250,247,241,.62);margin:0 auto 20px;max-width:48ch;line-height:1.4;}
.footer__social{display:flex;justify-content:center;gap:12px;}
.footer__social a{width:42px;height:42px;border-radius:50%;border:1px solid rgba(233,220,190,.45);background:rgba(255,255,255,.05);display:flex;align-items:center;justify-content:center;color:#F0E6CC;transition:.25s var(--ease);}
.footer__social a:hover{background:var(--gold);border-color:var(--gold);color:var(--ink);transform:translateY(-2px);}
.footer__social svg{width:19px;height:19px;fill:currentColor;stroke:none;}
/* Even columns, whatever the count. Fixed fractions meant the link columns
   bunched to the left while the contact column's short lines left a wide
   empty band on the right. auto-fit shares the row equally and keeps doing
   so if a column is ever added or removed. */
.site-footer__grid{max-width:var(--maxw);margin:0 auto;padding:46px 22px;display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:34px;}
.footer__h{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.72rem;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--gold-soft);margin:0 0 18px;}
/* The accordion control is a real <button> inside the <h3>, per the ARIA
   accordion pattern. role="button" used to be stamped onto the heading itself,
   which overrode its heading semantics — the element stopped being a heading
   for assistive technology while still looking like one. */
.footer__h-btn{all:unset;display:flex;align-items:center;justify-content:space-between;width:100%;box-sizing:border-box;font:inherit;color:inherit;cursor:default;}
@media(max-width:767px){ .footer__h-btn{cursor:pointer;} }
.footer__h-btn:focus-visible{outline:2px solid var(--gold,#c2a15b);outline-offset:3px;}
.footer__col ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:11px;}
.footer__col a{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.9rem;color:rgba(250,247,241,.66);transition:color .2s,padding-left .2s var(--ease);}
.footer__col ul a:hover{color:var(--gold-soft);padding-left:4px;}
.footer__contact{display:flex;align-items:center;gap:10px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.9rem;color:rgba(250,247,241,.66);margin-bottom:13px;transition:color .2s;}
.footer__contact svg{width:17px;height:17px;flex:none;fill:none;stroke:var(--gold);stroke-width:1.6;}
.footer__contact:hover{color:var(--gold-soft);}
.footer__hours{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.82rem;color:rgba(250,247,241,.45);margin:16px 0 0;line-height:1.5;}
.site-footer__legal{max-width:var(--maxw);margin:0 auto;padding:0 22px;}
.footer__legal{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:10px 22px;justify-content:center;}
.footer__legal a{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.78rem;color:rgba(250,247,241,.5);letter-spacing:.02em;transition:color .2s;}
.footer__legal a:hover{color:var(--gold-soft);}
/* Legal links are the smallest tap targets in the footer; 44px of vertical
   room keeps them thumb-reachable without changing the visual line height. */
.footer__legal li{display:flex;align-items:center;min-height:44px;}
.site-footer__bar{border-top:1px solid rgba(233,220,190,.12);max-width:var(--maxw);margin:0 auto;padding:22px;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;}
.footer__copy{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.78rem;color:rgba(250,247,241,.5);margin:0;letter-spacing:.02em;}
.footer__pay{display:flex;flex-wrap:wrap;gap:8px;}
.pay-badge{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.62rem;font-weight:700;letter-spacing:.08em;color:rgba(250,247,241,.7);border:1px solid rgba(233,220,190,.22);border-radius:5px;padding:5px 9px;background:rgba(255,255,255,.03);}
@media(max-width:860px){
  .site-footer__grid{grid-template-columns:1fr 1fr;gap:32px 24px;}
}
@media(max-width:520px){
  .site-footer__grid{grid-template-columns:1fr;}
  .site-footer__bar{flex-direction:column;align-items:center;text-align:center;}
  .footer__pay{justify-content:center;}
}


/* ================= CUSTOMER REVIEWS ================= */
.reviews{background:radial-gradient(130% 150% at 85% 0%,#1b1610 0%,#0b0a08 60%);padding:66px 0;}
.reviews__inner{max-width:var(--maxw);margin:0 auto;padding:0 22px;}
.reviews__head{display:flex;align-items:flex-end;justify-content:space-between;gap:26px;flex-wrap:wrap;margin-bottom:32px;}
.reviews .sect-eyebrow{color:var(--gold);}
.reviews .sect-title{color:var(--ivory);}
.reviews__summary{display:flex;align-items:center;gap:14px;flex:none;}
.reviews__score{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:3rem;line-height:1;color:var(--gold);}
.reviews__summary-meta{display:flex;flex-direction:column;gap:4px;}
.reviews__stars{display:inline-flex;gap:3px;}
.reviews__stars svg{width:17px;height:17px;fill:var(--gold);stroke:none;}
.reviews__from{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.84rem;color:rgba(250,247,241,.6);}
.reviews__from strong{color:var(--ivory);}
.reviews__viewport{position:relative;}
.reviews__track{list-style:none;margin:0;padding:4px;display:flex;align-items:stretch;gap:18px;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;-ms-overflow-style:none;scrollbar-width:none;}
.reviews__track::-webkit-scrollbar{display:none;}
.rev__card{flex:0 0 clamp(270px,32%,360px);scroll-snap-align:start;display:flex;flex-direction:column;gap:14px;background:rgba(255,255,255,.04);border:1px solid rgba(194,161,91,.2);border-radius:14px;padding:24px 24px 22px;}
.rev__stars{display:inline-flex;gap:2px;}
.rev__stars svg{width:15px;height:15px;fill:var(--gold);stroke:none;}
.rev__text{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.96rem;line-height:1.6;color:rgba(250,247,241,.9);margin:0;flex:1;}
.rev__who{display:flex;align-items:center;gap:11px;}
.rev__ava{width:34px;height:34px;border-radius:50%;background:linear-gradient(135deg,var(--gold),var(--gold-cta));color:var(--ink);display:flex;align-items:center;justify-content:center;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.86rem;flex:none;}
.rev__name{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.84rem;font-weight:600;color:rgba(250,247,241,.72);}
.reviews__nav{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:44px;border-radius:50%;background:rgba(255,255,255,.06);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid rgba(233,220,190,.22);color:var(--ivory);display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:3;transition:.2s var(--ease);}
.reviews__nav:hover{background:var(--gold);border-color:var(--gold);color:var(--ink);}
.reviews__nav[disabled]{opacity:0;pointer-events:none;}
.reviews__nav svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:2;}
.reviews__nav--prev{left:-14px;}
.reviews__nav--prev svg{transform:rotate(180deg);}
.reviews__nav--next{right:-14px;}

/* Review cards.

   These had no rules at all. The section has only ever rendered its empty
   state on this install — there were no reviews to show — so the cards went
   unstyled and unseen: dark grey body text sat straight on the near-black
   panel at about 2:1 contrast, which is unreadable and fails WCAG by a wide
   margin. Now that a real testimonial is published the cards need to exist.

   Sized in ch rather than px so a card is a comfortable measure whatever the
   font is, and the track scroll-snaps to card boundaries. */
.reviews__item{flex:0 0 auto;width:min(38ch,82vw);scroll-snap-align:start;display:flex;}
.reviews__card{
  display:flex;flex-direction:column;gap:14px;width:100%;
  padding:26px 26px 24px;border-radius:14px;
  background:rgba(255,255,255,.05);
  border:1px solid rgba(233,220,190,.16);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
}
.reviews__card-stars{display:inline-flex;gap:3px;}
.reviews__card-stars svg{width:15px;height:15px;fill:var(--gold);stroke:none;}
/* --ivory at full strength, not a dimmed grey: this is the content of the
   section, and it sits on the darkest surface on the site. */
.reviews__text{
  margin:0;color:var(--ivory);
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
  font-size:.95rem;line-height:1.7;
}
.reviews__author{
  margin:auto 0 0;padding-top:4px;color:var(--gold);
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
  font-size:.76rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
}

@media(max-width:640px){
  .reviews__item{width:min(34ch,86vw);}
  .reviews__card{padding:22px 20px 20px;}
}
@media(max-width:640px){
  .reviews{padding:50px 0;}
  .reviews__head{align-items:flex-start;}
  .rev__card{flex-basis:80%;}
  .reviews__nav{display:none;}
}

/* ============================================================= */
/* ===================== PRODUCT DETAIL PAGE =================== */
/* ============================================================= */
.breadcrumbs{max-width:var(--maxw);margin:0 auto;padding:18px 22px 0;}
.breadcrumbs ol{list-style:none;display:flex;flex-wrap:wrap;gap:6px;margin:0;padding:0;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.78rem;color:var(--muted);}
.breadcrumbs li{display:flex;align-items:center;gap:6px;}
.breadcrumbs li:not(:last-child)::after{content:"/";color:var(--line);margin-left:6px;}
.breadcrumbs a{color:var(--muted);transition:color .2s;}
.breadcrumbs a:hover{color:var(--gold-cta);}
.breadcrumbs [aria-current]{color:var(--ink);}
.pdp{max-width:var(--maxw);margin:0 auto;padding:24px 22px 70px;}
.pdp__grid{display:grid;grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr);gap:50px;align-items:start;}

/* ---- sticky gallery (desktop only) -------------------------------------
   The image column holds position while the details column is read. Three
   things make it work, and all three are load-bearing:

   1. `align-items:start` on the grid (above). A stretched grid item is as tall
      as the row, so it has nothing to travel within and sticky silently does
      nothing — the single most common reason this fails in a CSS grid.
   2. No `overflow` other than `visible` on any ancestor. Verified on this
      template: the chain from .pdp-gallery-wrap to <html> is clean. Note that
      .pdp-related does set overflow:hidden, but it is a SIBLING of .pdp__grid,
      not an ancestor, so it has no effect here. Adding overflow to .pdp or
      .pdp__grid later would break this with no error message.
   3. A top offset that clears the sticky .nav, which is 50px and pinned at
      top:0. 68px leaves a little air rather than butting the gallery against it.

   `max-height` + `overflow-y:auto` handles the case the offset alone cannot.
   Measured on this catalogue, the gallery runs 634-834px while the space under
   the nav is 718-850px depending on viewport — so on a 1280x800 screen it is
   ~57px TALLER than the room available. Pinned by its top edge alone, that last
   strip would be permanently unreachable. With a max-height it scrolls within
   itself only when it genuinely does not fit, and on a taller screen no
   scrollbar appears and nothing changes. Scroll chaining is left at its default
   so reaching the end of the column continues the page rather than trapping the
   wheel.

   Bounded by .pdp__grid automatically: a sticky element cannot travel past its
   own parent, so the gallery releases exactly when the details column ends. */
/* ---- sticky gallery (desktop only) -------------------------------------
   The column has NO height cap and NO scroller of its own. It is exactly as
   tall as its images, ends at the last one, and has no scrollbar — every image
   is reached by scrolling the page normally.

   Sticky is applied only once the script has measured the column and written a
   `top` offset, because the correct offset depends on the column's height:

     fits in the viewport   ->  top = 68px            (clears the sticky nav)
     taller than viewport   ->  top = negative        (see product.js)

   A plain `top: 68px` cannot be used. Measured across this catalogue every
   gallery is taller than the space beneath the nav — 845px for three images and
   2,837px for fourteen, against 808px at 1440x900 — so top-pinning would leave
   the lower part of EVERY gallery permanently below the fold.

   `bottom: 24px` does not work either, and for a subtler reason: bottom-sticky
   means "do not let the bottom edge fall below this line", which is a constraint
   for scrolling UP. Scrolling down, the moment the column's bottom rises above
   the line the constraint is already satisfied and it never pins. Verified: it
   scrolled away linearly with travel of 1,082px available.

   With no class the column is static, which is the safe failure mode: if the
   script never runs, the gallery is an ordinary column and nothing is hidden. */
@media(min-width:861px){
  .pdp__grid.has-sticky-gallery .pdp-gallery-wrap{
    position:sticky;
    /* Sticky depends on the item not being stretched; the grid already
       start-aligns, but this should not rest on an inherited assumption. */
    align-self:start;
  }
}

/* ---- gallery (editorial: stacked grid desktop, swipe carousel mobile) ---- */
.pdp-gallery-wrap{min-width:0;}
.pdp-gallery{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
.pdp-shot{position:relative;margin:0;aspect-ratio:1/1;border-radius:14px;overflow:hidden;background:#fff;border:1px solid var(--line);cursor:zoom-in;outline:none;padding:10px;box-sizing:border-box;}
.pdp-shot:first-child{grid-column:1 / -1;aspect-ratio:4/3;}
.pdp-shot img{width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain;object-position:center;display:block;}
.pdp-shot__ph{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--pearl);color:rgba(28,26,23,.16);}
.pdp-shot__ph svg{width:54px;height:54px;fill:none;stroke:currentColor;stroke-width:1;}
.pdp-shot__zoom{position:absolute;right:12px;bottom:12px;width:38px;height:38px;border-radius:50%;background:rgba(255,255,255,.92);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border:1px solid var(--line);display:flex;align-items:center;justify-content:center;color:var(--ink);opacity:0;transform:translateY(6px);transition:.25s var(--ease);pointer-events:none;}
.pdp-shot:hover .pdp-shot__zoom,.pdp-shot:focus-visible .pdp-shot__zoom{opacity:1;transform:none;/* The resting state sets pointer-events:none so the hidden button cannot
   swallow clicks. Revealing it without restoring this left the control
   visible but inert — every click fell through to the figure. */pointer-events:auto;}
.pdp-shot__zoom svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;}
.pdp-shot__wish{position:absolute;top:14px;right:14px;z-index:3;width:42px;height:42px;border-radius:50%;background:rgba(255,255,255,.92);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border:1px solid var(--line);display:flex;align-items:center;justify-content:center;color:var(--ink);cursor:pointer;transition:.2s var(--ease);}
.pdp-shot__wish svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.8;}
.pdp-shot__wish:hover{transform:scale(1.06);}
.pdp-shot__wish.is-active{color:var(--garnet);}
.pdp-shot__wish.is-active svg{fill:currentColor;stroke:currentColor;}
.pdp-shot:focus-visible{box-shadow:0 0 0 2px var(--gold);}
.pdp-gallery__bar{display:none;}
.pdp-gallery__thumbs{display:none;}
.pdp-thumb{flex:0 0 auto;width:54px;height:54px;border-radius:8px;overflow:hidden;border:1px solid var(--line);background:#fff;cursor:pointer;padding:0;opacity:.55;transition:opacity .2s var(--ease),border-color .2s var(--ease),box-shadow .2s var(--ease);}
.pdp-thumb img{width:100%;height:100%;object-fit:contain;display:block;}
.pdp-thumb__ph{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--pearl);color:rgba(28,26,23,.3);}
.pdp-thumb__ph svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.4;}
.pdp-thumb.is-active{opacity:1;border-color:var(--gold);box-shadow:0 0 0 1px var(--gold);}
.pdp-info{align-self:start;}
/* `.pdp-sticky-col` was defined here and applied to nothing — a grep across all
   PHP and JS returns zero uses. It is removed rather than left, because a
   second, subtly different sticky rule sitting unused is exactly what someone
   reaches for later, and its 66px offset no longer matches the 68px the gallery
   uses. The live rule is on .pdp-gallery-wrap above. */
@media(max-width:860px){
  .pdp-info,.pdp-sticky-col{position:static;top:auto;}
  .pdp-gallery{display:flex;grid-template-columns:none;gap:0;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;border-radius:14px;-ms-overflow-style:none;scrollbar-width:none;}
  .pdp-gallery::-webkit-scrollbar{display:none;}
  .pdp-shot{flex:0 0 100%;scroll-snap-align:center;border-radius:14px;aspect-ratio:1/1;cursor:default;}
  .pdp-shot:first-child{grid-column:auto;aspect-ratio:1/1;}
  .pdp-shot__zoom{display:none;}
  .pdp-gallery__bar{display:flex;margin-top:12px;}
  .pdp-gallery__dots{display:none;}
  .pdp-dot{width:7px;height:7px;border-radius:50%;background:rgba(28,26,23,.22);border:none;padding:0;cursor:pointer;transition:.2s;}
  .pdp-dot.is-active{background:var(--gold-cta);width:20px;border-radius:4px;}
  .pdp-gallery__counter{display:none;}
  .pdp-gallery__thumbs{display:flex;gap:8px;overflow-x:auto;width:100%;padding:2px;-ms-overflow-style:none;scrollbar-width:none;}
  .pdp-gallery__thumbs::-webkit-scrollbar{display:none;}
  .pdp-gallery__bar.is-many .pdp-gallery__dots{display:none;}
  .pdp-gallery__bar.is-many .pdp-gallery__counter{position:static;font-weight:600;color:var(--ink);}
}

/* ---- premium image viewer (lightbox) ---- */
.lb{position:fixed;inset:0;z-index:130;display:flex;flex-direction:column;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity .32s ease,visibility .32s ease;}
.lb.open{opacity:1;visibility:visible;}
.lb__backdrop{position:absolute;inset:0;background:rgba(10,9,8,.94);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);}
.lb__top{position:absolute;top:0;left:0;right:0;z-index:4;display:flex;align-items:center;justify-content:space-between;padding:18px 20px;transform:translateY(-12px);opacity:0;transition:transform .34s ease,opacity .34s ease;}
.lb.open .lb__top{transform:none;opacity:1;}
.lb__counter{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.86rem;font-weight:600;letter-spacing:.05em;color:rgba(250,247,241,.85);}
.lb__counter-sep{color:rgba(250,247,241,.4);margin:0 2px;}
.lb__tools{display:flex;gap:10px;}
.lb__tool{width:42px;height:42px;border-radius:50%;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s var(--ease),transform .2s var(--ease);}
.lb__tool:hover{background:rgba(255,255,255,.2);transform:translateY(-1px);}
.lb__tool svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;}
.lb__tool--close{background:rgba(255,255,255,.12);}
.lb__stage{position:relative;z-index:2;width:min(92vw,1000px);height:min(72vh,820px);display:flex;align-items:center;justify-content:center;overflow:hidden;transform:scale(.94);opacity:0;transition:transform .36s cubic-bezier(.2,.7,.3,1),opacity .3s ease;touch-action:none;}
.lb.open .lb__stage{transform:scale(1);opacity:1;}
.lb__img{max-width:100%;max-height:100%;object-fit:contain;-webkit-user-select:none;user-select:none;cursor:zoom-in;transform-origin:center;will-change:transform;}
.lb__stage.is-zoomed{cursor:grab;}
.lb__stage.is-zoomed .lb__img{cursor:grab;}
.lb__nav{position:absolute;top:50%;transform:translateY(-50%);width:54px;height:54px;border-radius:50%;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:4;opacity:0;transition:background .2s var(--ease),opacity .34s ease,transform .2s var(--ease);}
.lb.open .lb__nav{opacity:1;}
.lb__nav:hover{background:rgba(255,255,255,.22);}
.lb__nav svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:2;}
.lb__nav--prev{left:26px;}.lb__nav--prev svg{transform:rotate(180deg);}
.lb__nav--next{right:26px;}
.lb__strip{position:absolute;bottom:18px;left:50%;transform:translateX(-50%) translateY(14px);opacity:0;z-index:4;display:flex;gap:10px;max-width:92vw;overflow-x:auto;padding:6px;transition:transform .36s ease,opacity .36s ease;-ms-overflow-style:none;scrollbar-width:none;}
.lb.open .lb__strip{transform:translateX(-50%);opacity:1;}
.lb__strip::-webkit-scrollbar{display:none;}
.lb__thumb{flex:0 0 auto;width:58px;height:58px;border-radius:8px;overflow:hidden;border:1px solid rgba(255,255,255,.18);background:#fff;cursor:pointer;padding:0;opacity:.5;transition:opacity .2s var(--ease),border-color .2s var(--ease),box-shadow .2s var(--ease);}
.lb__thumb img{width:100%;height:100%;object-fit:contain;display:block;background:#fff;}
.lb__thumb-ph{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--pearl);color:rgba(28,26,23,.3);}
.lb__thumb-ph svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.4;}
.lb__thumb:hover{opacity:.85;}
.lb__thumb.is-active{opacity:1;border-color:var(--gold);box-shadow:0 0 0 1px var(--gold);}
@media(max-width:640px){
  .lb__stage{width:96vw;height:60vh;}
  .lb__nav{width:44px;height:44px;}
  .lb__nav--prev{left:10px;}.lb__nav--next{right:10px;}
  .lb__thumb{width:48px;height:48px;}
  .lb__top{padding:14px 16px;}
}

/* ---- info ---- */
.pdp-info{min-width:0;}
.pdp-brand{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.74rem;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--gold-cta);margin:0 0 8px;}
/* =========================================================================
   PRODUCT TITLE
   =========================================================================
   Was Cormorant Garamond 600 at clamp(1.7rem,3vw,2.4rem).

   The problem was not that the serif was decorative — it was that Cormorant
   sets OLD-STYLE figures. In that design 0, 1 and 2 sit at x-height while 3, 4,
   7 and 9 descend below the baseline, so a zero is the same size and shape as
   an "o". On this catalogue that is not a nicety: 92.1% of the 3,203 titles
   contain a digit and 44.4% contain an SKU code, and the failure is legible in
   the reported example — "10ct ... 0.01ct" renders as "1oct ... o.o1ct".
   Carat weights and SKUs are the two things a jewellery customer verifies most
   carefully, and they were the least readable text on the page.

   DM Sans is the existing body face, already loaded at 400/500/600/700
   (inc/assets.php), so this adds no font dependency and no extra request. Its
   figures are lining and uniform-width, which is what makes 0.01 and 2.50
   unambiguous. 600 rather than 700: enough weight to hold the top of the page
   without the slab-like density a bold sans gets at display size.

   Sizes are set per breakpoint rather than by one fluid clamp. A sans-serif
   reads noticeably larger than a serif at the same nominal size, so carrying
   2.4rem across would have made these titles heavier, not lighter — the desktop
   size is deliberately smaller than what it replaces while occupying a similar
   optical weight. Tracking is tightened only where the type is large enough to
   need it and left alone at mobile sizes, where negative tracking would undo
   the legibility this change is for.
   ========================================================================= */
.pdp-title{
  /* Generic fallback: fonts load from Google with display=swap, so a failed or
     slow font must not drop the title to the UA serif — the whole point of the
     change was getting away from serif numerals. */
  font-family:"DM Sans",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  /* 400 (Regular) — the lightest weight loaded for DM Sans, and the store
     owner's choice after seeing 600, then 500, then asking for thinner again.

     The trade-off, recorded so it is not rediscovered as a bug: 400 is the same
     weight as the description copy below, so size and colour now carry the
     whole hierarchy between the product name and body text. That works here
     because the gap is large — 21.6px/32px title against .93rem body — but it
     does mean the title cannot be made much smaller without the hierarchy
     flattening. Anything thinner than this needs a 300 added to the Google
     Fonts request in inc/assets.php; it is not currently downloaded, so
     `font-weight:300` would silently render as 400. */
  font-weight:400;
  color:var(--ink);
  margin:0 0 14px;
  /* Mobile first: the smallest, longest-wrapping case. Titles run to 175
     characters, so a large heading here costs the customer most of the first
     screen before they reach the price. */
  font-size:1.35rem;
  line-height:1.3;
  letter-spacing:0;
  /* Force lining figures even if the webfont fails and a system serif with
     old-style numerals substitutes — the digits are the whole point. */
  font-variant-numeric:lining-nums proportional-nums;
  /* An 18-character SKU (JUBE06062JWYGRHT/U is the catalogue's longest token)
     must break rather than push the column, but SKUs must never be hyphenated
     because the hyphen reads as part of the code. */
  overflow-wrap:break-word;
  hyphens:none;
  /* Long titles wrap to 4–6 lines here; `pretty` prevents a single-word last
     line without the line-count ceiling that `balance` carries. */
  text-wrap:pretty;
}

@media(min-width:561px){
  .pdp-title{
    font-size:1.7rem;
    line-height:1.26;
    /* Tracking relaxed to 0 with the drop to 500. Negative tracking closes the
       counters of a lighter face, which works against the legibility the sans
       was adopted for. */
    letter-spacing:0;
    /* Two or three lines at this width, so even line lengths matter more than
       orphan control. */
    text-wrap:balance;
  }
}

@media(min-width:1025px){
  .pdp-title{
    font-size:2rem;
    line-height:1.22;
    /* Eased to 0 at weight 400. Negative tracking closes the counters of a
       light face, and at this weight the strokes have no density to spare —
       tightening would undo the legibility the sans was adopted for. */
    letter-spacing:0;
  }
}
.pdp-meta{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:16px;}
.pdp-rating{display:inline-flex;align-items:center;gap:7px;}
.pdp-stars{display:inline-flex;gap:1px;}
.pdp-stars svg{width:15px;height:15px;fill:var(--gold);stroke:none;}
.pdp-rating__num{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.84rem;color:var(--ink);}
.pdp-rating__count{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.8rem;color:var(--muted);text-decoration:underline;text-underline-offset:2px;}
.pdp-sku{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.78rem;color:var(--muted);}
.pdp-price{display:flex;align-items:baseline;gap:12px;margin-bottom:12px;}
/* DM Sans, not Cormorant.
   Cormorant is the display serif this theme uses for names and section
   headings — it is a reading face, and at 2.2rem its thin strokes and old-style
   figures made the one number a customer must actually parse the softest thing
   in the block, sitting oddly beside the DM Sans old price and SAVE badge
   directly next to it. The price is data, not a title: it belongs in the same
   family as every other figure on the storefront, which is what the product
   card and Quick View already use. Weight carries the emphasis instead of the
   typeface, so it stays dominant without being decorative.
   `tabular-nums` stops the digits shifting width as a variation changes. */
.pdp-price__now{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:2.2rem;color:var(--ink);line-height:1.1;letter-spacing:-.01em;font-variant-numeric:tabular-nums;}
.pdp-price__tax{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.78rem;color:var(--muted);}
.pdp-avail{display:flex;align-items:center;gap:9px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.86rem;color:var(--ink);margin:0 0 10px;}
.pdp-avail__dot{width:9px;height:9px;border-radius:50%;background:#2f9e63;box-shadow:0 0 0 3px rgba(47,158,99,.18);}
.pdp-finance{display:flex;align-items:center;gap:9px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.86rem;color:var(--muted);margin:0 0 18px;padding:11px 14px;background:var(--pearl);border-radius:10px;}
.pdp-finance svg{width:18px;height:18px;flex:none;fill:none;stroke:var(--gold-cta);stroke-width:1.6;}
.pdp-finance strong{color:var(--ink);}
.pdp-finance span{min-width:0;}
.pdp-short{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.92rem;line-height:1.65;color:var(--muted);margin:0 0 22px;}
.pdp-opt{margin-bottom:22px;}
.pdp-opt__label{display:block;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.78rem;font-weight:700;letter-spacing:.04em;color:var(--ink);margin-bottom:10px;}
.pdp-opt__label::after{content:attr(data-val);}
.pdp-opt__row{display:flex;flex-wrap:wrap;gap:10px;}
.pdp-chip{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.82rem;font-weight:600;color:var(--ink);background:#fff;border:1px solid var(--line);border-radius:8px;padding:11px 18px;cursor:pointer;transition:.2s var(--ease);}
.pdp-chip:hover:not(:disabled){border-color:var(--gold);}
.pdp-chip.is-active{border-color:var(--ink);background:var(--ink);color:var(--ivory);}
.pdp-chip:disabled{opacity:.4;cursor:not-allowed;text-decoration:line-through;}
/* `flex-wrap` is required, not cosmetic: the availability notice is a
   full-width child of this row, and without wrapping it is laid out *beside*
   Add to Cart and runs past the viewport (measured 1,214px wide inside a
   1,024px window). It stayed out of scrollWidth only because the collapsed
   notice is overflow:hidden — the overflow was real and invisible. */
.pdp-buy{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:12px;}
.pdp-qty{display:flex;align-items:center;border:1px solid var(--line);border-radius:9px;overflow:hidden;flex:none;background:#fff;}

/* Availability notice beside the stepper.
   Deliberately not an error: the customer has done nothing wrong, they have
   simply reached the end of the stock. It uses the muted ink with a gold rule
   rather than the garnet error treatment, matching how the rest of the PDP
   states facts.

   `flex-basis:100%` drops it onto its own line beneath the stepper and Add to
   Cart, so it can never overlap the CTA however narrow the viewport. It is
   rendered empty and revealed, which keeps it addressable by the live region
   from first paint. */
/* The panel is tinted garnet at 4% over the page rather than given a flat pink:
   on the ivory ground a solid tint reads as a form error, and the customer has
   made no error — the shop simply has no more. Border and icon carry the
   colour; the copy stays in the normal ink so it reads as information.
   `max-height` animates the reveal without reserving space when idle, and the
   collapsed state is emptied by the script so the live region never holds
   stale wording. */
.pdp-qty__note{flex:0 0 100%;width:100%;order:9;margin:0;max-height:0;overflow:hidden;opacity:0;transition:max-height .22s var(--ease),opacity .22s var(--ease),margin .22s var(--ease);}
/* Solid garnet, not a tint. At 6% opacity on a cream page this read as a
   hint, and being missed is the entire problem it exists to solve. Same
   treatment as `.qty-limit`, which every other quantity control uses. */
.pdp-qty__note.is-shown{max-height:12em;opacity:1;margin:9px 0 0;display:flex;align-items:center;gap:11px;padding:11px 14px;border:1px solid var(--garnet);border-radius:10px;background:var(--garnet);box-shadow:0 12px 28px -14px rgba(142,43,58,.75);}
/* Solid disc, white glyph — the icon is the one element carrying full colour,
   which is what makes the panel read at a glance without tinting the copy. */
.pdp-qty__note-icon{flex:none;width:22px;height:22px;border-radius:0;background:transparent;display:flex;align-items:center;justify-content:center;color:#fff;}
.pdp-qty__note-icon svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:2;}
.pdp-qty__note-body{min-width:0;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;padding-top:1px;}
.pdp-qty__note-title{display:block;font-size:.82rem;font-weight:600;color:#fff;line-height:1.35;}
/* The one sentence says both halves now, so the second line is not used. */
.pdp-qty__note-detail{display:none;}

@media(max-width:420px){
  .pdp-qty__note.is-shown{gap:10px;padding:11px 12px;}
  .pdp-qty__note-icon{width:30px;height:30px;}
  .pdp-qty__note-icon svg{width:15px;height:15px;}
  .pdp-qty__note-title{font-size:.84rem;}
  .pdp-qty__note-detail{font-size:.77rem;}
}

/* A maxed "+" must look unavailable. It stays clickable on purpose — see
   product.js — so `not-allowed` communicates the state while the click still
   reaches the handler that explains it. */
.pdp-qty__btn.is-maxed,
.pdp-qty__btn[aria-disabled="true"]{opacity:.32;cursor:not-allowed;}
.pdp-qty__btn.is-maxed:hover,
.pdp-qty__btn[aria-disabled="true"]:hover{background:none;}

@media(prefers-reduced-motion:reduce){.pdp-qty__note{transition:none;}}
.pdp-qty__btn{width:44px;height:52px;display:flex;align-items:center;justify-content:center;background:none;border:none;color:var(--ink);cursor:pointer;transition:background .2s;}
.pdp-qty__btn:hover{background:var(--pearl);}
.pdp-qty__btn svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;}
.pdp-qty__in{width:42px;height:52px;text-align:center;border:none;border-left:1px solid var(--line);border-right:1px solid var(--line);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:600;font-size:.95rem;color:var(--ink);background:#fff;}
.pdp-qty__in:focus{outline:none;}
.pdp-atc{flex:1;height:52px;justify-content:center;border-radius:9px;}
.pdp-atc.is-added{background:linear-gradient(135deg,#2f9e63,#278753);}
.pdp-buynow{width:100%;height:52px;border:1.5px solid var(--ink);background:var(--ink);color:var(--ivory);border-radius:9px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.8rem;letter-spacing:.1em;text-transform:uppercase;cursor:pointer;transition:.25s var(--ease);margin-bottom:18px;}
.pdp-buynow:hover{background:#000;transform:translateY(-2px);}
.pdp-secondary{display:flex;gap:24px;margin-bottom:24px;}
.pdp-sec{display:inline-flex;align-items:center;gap:8px;background:none;border:none;cursor:pointer;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.82rem;font-weight:600;color:var(--muted);transition:color .2s;}
.pdp-sec svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.7;}
.pdp-sec:hover{color:var(--gold-cta);}
.pdp-sec.is-active{color:var(--garnet);}
.pdp-sec.is-active svg{fill:currentColor;}
/*
 * Reassurance strip — one row.
 *
 * Was a vertical stack, which cost ~150px of the info column directly above the
 * fold on mobile and pushed the description further down. Laid out as columns it
 * reads as one band of reassurance rather than four separate claims, and it
 * matches .pdp-builder further down the page, so the two trust rows now share a
 * visual language instead of contradicting each other.
 *
 * auto-fit rather than a fixed repeat(4, 1fr): it is a single row wherever four
 * columns actually fit — the desktop info column and tablets — and folds to 2x2
 * on a narrow phone instead of squeezing "On orders over $100.00" into ~85px,
 * where it would wrap to four lines. The free-shipping threshold is a
 * conversion driver, so it stays legible rather than being hidden at the one
 * breakpoint carrying most of the traffic.
 */
/*
 * Titles only now, so the columns can be narrower — 105px fits "Free Express
 * Shipping" over two lines, which keeps all four on one row further down the
 * viewport range than the 125px the sub-text needed. auto-fit still folds it
 * rather than crushing it on the narrowest phones.
 */
.pdp-delivery{list-style:none;margin:0 0 22px;padding:20px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);display:grid;grid-template-columns:repeat(auto-fit,minmax(105px,1fr));gap:18px 12px;}
/* Top-aligned so one-line and two-line titles share a baseline at the top. */
.pdp-delivery li{display:flex;flex-direction:column;align-items:center;text-align:center;justify-content:flex-start;gap:9px;}
.pdp-delivery svg{width:24px;height:24px;flex:none;fill:none;stroke:var(--gold-cta);stroke-width:1.6;}
.pdp-delivery strong{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.82rem;color:var(--ink);font-weight:600;line-height:1.3;}
.pdp-trust{list-style:none;margin:0 0 22px;padding:0;display:grid;grid-template-columns:repeat(4,1fr);gap:12px;}
.pdp-trust li{display:flex;flex-direction:column;align-items:center;text-align:center;gap:7px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;font-weight:600;color:var(--muted);}
.pdp-trust svg{width:24px;height:24px;fill:none;stroke:var(--gold-cta);stroke-width:1.5;}
.pdp-pay__h{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink);margin:0 0 12px;}
.pdp-pay__cards{display:flex;flex-direction:column;gap:10px;}
.pdp-pay__card{display:flex;align-items:center;gap:12px;border:1px solid var(--line);border-radius:10px;padding:12px 14px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.84rem;color:var(--muted);}
.pdp-pay__card svg{width:22px;height:22px;flex:none;fill:none;stroke:var(--gold-cta);stroke-width:1.6;}
.pay-badge--ap{background:#b2fce4;color:#06382a;border:none;}
.pay-badge--zip{background:#1a1a2e;color:#fff;border:none;}

/* ---- trust badges (single horizontal row) ---- */
.pdp-badges{list-style:none;margin:0 0 22px;padding:18px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);display:grid;grid-template-columns:repeat(4,1fr);gap:14px;}
.pdp-badge{display:flex;flex-direction:column;align-items:center;text-align:center;gap:8px;}
.pdp-badge svg{width:24px;height:24px;flex:none;fill:none;stroke:var(--gold-cta);stroke-width:1.6;}
.pdp-badge strong{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.76rem;font-weight:700;color:var(--ink);line-height:1.25;}
.pdp-badge span{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.7rem;color:var(--muted);line-height:1.3;}
@media(max-width:560px){.pdp-badges{grid-template-columns:repeat(2,1fr);gap:20px 12px;}}
/* =========================================================================
   PRODUCT DESCRIPTION — one formatting system for the whole catalogue
   =========================================================================
   The description is editor-authored HTML and its shape varies enormously.
   Measured across all 3,203 published products:

     headings + flat list   2,298  (71.7%)
     nested list, depth 2     371  (11.6%)
     prose only               439  (13.7%)
     contains a pasted table   87   (2.7%)
     nested list, depth 3       7
     <div> wrappers         1,387  (43.3%)
     &nbsp; / empty <p>       534  (16.7%)
     <br>                     483  (15.1%)
     inline style=            218   (6.8%)  including fixed px widths
     <h1> inside content       15   (0.5%)

   Lengths run from 12 to 927 words. So the rules below are written against
   ELEMENTS rather than against any assumed document shape: whatever an editor
   pasted, the same vertical rhythm, indentation and measure apply. Nothing here
   edits content — it is presentation only, which is also the only way to fix
   3,500 products without touching 3,500 rows.
   ========================================================================= */
/* The description is only ever rendered inside `.pdp-block`, which already
   supplies the panel's padding and dividing rule — so this element carries
   typography only. Anything else here would double the block's separators. */
.pdp-desc{margin:0;font-family:"DM Sans",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;font-size:.93rem;line-height:1.75;color:var(--muted);overflow-wrap:break-word;}
/* `.pdp-desc__h` removed: nothing emits it. details.php:197 renders the
   Description heading as <h2 class="pdp-block__h"> OUTSIDE this wrapper. It
   was also a latent conflict — being a class rather than an element selector,
   it would have out-specified the heading scale above if anyone had ever moved
   that heading inside .pdp-desc. */

/* `overflow-wrap` above is load-bearing, not defensive. Seven descriptions
   (#3633–#3642) were pasted from a chat UI that joins every word with U+00A0:
   38 non-breaking spaces and not one ordinary space in a single paragraph. To
   the line breaker that is ONE word, so it never wraps — measured at 959px
   inside a 296px column on a 390px screen, with the remainder clipped and
   simply unreadable. No CSS can make U+00A0 a break opportunity, so the only
   presentation-level remedy is to permit a break inside the run. It costs
   nothing on the other 3,196 products, where ordinary spaces are always
   preferred, and it also protects any long SKU or URL pasted into copy. */

/* --- rhythm ---------------------------------------------------------------
   One margin convention everywhere: space BELOW a block, never above, so
   stacked blocks cannot double up. The `:first-child` reset stops a leading
   heading pushing the section away from its rule. */
/* The second selector in each pair is not redundant. 1,387 descriptions (43.3%)
   are wrapped in a stray <div> from a paste, which makes the real first and last
   blocks grandchildren — a bare child combinator silently skips all of them, so
   43% of the catalogue would keep the leading/trailing space these rules exist
   to remove. The wrapper itself is already stripped of spacing further down. */
.pdp-desc > :first-child,
.pdp-desc > div:first-child > :first-child{margin-top:0;}
.pdp-desc > :last-child,
.pdp-desc > div:last-child > :last-child{margin-bottom:0;}
.pdp-desc p{margin:0 0 14px;}

/* --- headings -------------------------------------------------------------
   Editors used h1 through h4 with no consistency, and 15 products carry an
   <h1> inside the description — which cannot be removed here, but must not be
   allowed to out-shout the real page title. The scale is compressed so every
   level reads as a sub-heading of this section regardless of which tag was
   chosen, and h1/h2 resolve to the same size. */
.pdp-desc h1,.pdp-desc h2,.pdp-desc h3,.pdp-desc h4,.pdp-desc h5,.pdp-desc h6{
  color:var(--ink);margin:22px 0 8px;
  /* !important is deliberate and narrow. Pasted headings carry a full inline
     style block from the source site — #4160's h3 declares -apple-system,
     font-weight:400 and font-size:32px, which renders in the wrong face AND
     larger than the 27.2px product title on a 390px screen. An inline style
     outranks any selector, so the scale can only be guaranteed this way.
     Measured: 7 headings with inline font-family, 7 with font-size, 8 with
     font-weight. Scoped to headings inside .pdp-desc and to the four
     properties that define the scale — nothing else is forced. */
  font-family:"DM Sans",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif!important;
  line-height:1.35!important;font-weight:700!important;
}
.pdp-desc h1,.pdp-desc h2{font-size:1.02rem!important;}
.pdp-desc h3{font-size:.96rem!important;}
.pdp-desc h4,.pdp-desc h5,.pdp-desc h6{font-size:.93rem!important;}

/* 92 descriptions carry a pasted inline font-family (-apple-system, Segoe UI,
   Roboto…) inherited from whatever site the copy came from. There is no case
   in which a product description should abandon the store's typeface, so the
   family — and only the family — is enforced for body copy too. Inline
   font-size on body text is deliberately NOT overridden: a larger lead
   paragraph can be intentional, and that is a content decision, not a
   formatting defect. */
.pdp-desc,.pdp-desc p,.pdp-desc li,.pdp-desc span,.pdp-desc div,
.pdp-desc td,.pdp-desc th,.pdp-desc strong,.pdp-desc em{
  font-family:"DM Sans",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif!important;}

/* --- lists ----------------------------------------------------------------

   Measured across 3,203 published products: 2,739 carry a list, 2,738 of them
   unordered and exactly one ordered. 2,360 are flat, 372 nest one level, 7 nest
   two. 1,851 sit directly under a heading.

   Scoped to `.pdp-desc` and the accordion bodies — product content only. The
   navigation, footer, filters, cart and reviews keep their own list styling and
   are not reached by anything here.

   Bullets are drawn with ::marker rather than a background image or a
   pseudo-element, so they stay locked to the first line of text at any font
   size and never drift when an item wraps. */
.pdp-desc ul,.pdp-desc ol{margin:0 0 14px;padding-left:20px!important;}

/* 10px between items: enough to separate a spec line from the next, short of
   the airy 20px+ that made a nine-point list scroll. */
.pdp-desc li{margin:0 0 10px;padding-left:2px;}
.pdp-desc li:last-child{margin-bottom:0;}

.pdp-desc ul{list-style:disc;}
.pdp-desc ol{list-style:decimal;}
.pdp-desc li::marker{color:var(--gold-cta);font-size:.92em;}

/* Nested items read as subordinate: a hollow ring rather than a filled dot,
   tighter spacing, and an indent small enough to stay inside the column but
   clearly stepped in from the parent. */
.pdp-desc ul ul,.pdp-desc ul ol,
.pdp-desc ol ul,.pdp-desc ol ol{margin:8px 0 0;padding-left:18px!important;}
.pdp-desc ul ul{list-style:circle;}
.pdp-desc ul ul ul{list-style:square;}
.pdp-desc ul ul li,.pdp-desc ol ol li,
.pdp-desc ul ol li,.pdp-desc ol ul li{margin-bottom:7px;font-size:.97em;}
.pdp-desc ul ul li::marker,.pdp-desc ol ul li::marker{color:var(--gold);}

/* Ordered lists keep real numbers, aligned on one column. Exactly one product
   in the catalogue uses them — a clock's chime sequence — and turning steps
   into bullets would lose the order the steps depend on. */
.pdp-desc ol > li{padding-left:4px;}
.pdp-desc ol > li::marker{font-weight:600;}

/* A heading immediately above a list belongs to it: close below, generous
   above, so the groups on a specification read as groups. */
.pdp-desc h2 + ul,.pdp-desc h3 + ul,.pdp-desc h4 + ul,
.pdp-desc h2 + ol,.pdp-desc h3 + ol,.pdp-desc h4 + ol,
.pdp-desc p + ul,.pdp-desc p + ol{margin-top:0;}
.pdp-desc ul + h2,.pdp-desc ul + h3,.pdp-desc ul + h4,
.pdp-desc ol + h2,.pdp-desc ol + h3,.pdp-desc ol + h4{margin-top:26px;}

/* The label half of a "Label: Value" line. Only ever applied to markup that
   already carries the <strong>, which the migration adds solely to lists whose
   items are confirmed specifications — never by matching a colon in prose.

   The second selector covers the shape most of this catalogue actually stores:
   the label sits inside an <em>/<i>/<span> that wraps the whole item, so the
   <strong> is a grandchild of the <li>, not a child. */
.pdp-desc li > strong:first-child,
.pdp-desc li > em > strong:first-child,
.pdp-desc li > i > strong:first-child,
.pdp-desc li > u > strong:first-child,
.pdp-desc li > span > strong:first-child{color:var(--ink);font-weight:600;}

/* A <br> inside an item is someone's line break, not a paragraph: keep it from
   opening a gap the list rhythm has to fight. */
.pdp-desc li br{line-height:1.2;}

/* --- inline emphasis ------------------------------------------------------
   1,074 products (33.5%) had the editor wrap whole spec bullets in <em>, so an
   identical Details list renders italic on one product and upright on the next
   — and in 341 of them the italics are inconsistent within a single list. An
   emphasis that covers the entire item conveys nothing, and exactly ONE product
   in the catalogue uses <em> inline for genuine stress, so normalising the
   whole-item case is safe. The markup is untouched; only its rendering is
   levelled, and real inline emphasis still italicises. */
/* An <em> that wraps a whole item is this catalogue's storage habit, not
   emphasis — 1,074 products do it — so it renders upright. The :has() pairs
   extend that to a group heading whose only sibling is its own nested list,
   matching the rule the migration used to decide what counts as a wrapper.
   Browsers without :has() simply keep the italic on those few items. */
.pdp-desc li > em:only-child,
.pdp-desc li > i:only-child,
.pdp-desc li:has(> em:first-child + ul) > em:first-child,
.pdp-desc li:has(> em:first-child + ol) > em:first-child,
.pdp-desc li:has(> i:first-child + ul) > i:first-child,
.pdp-desc li:has(> i:first-child + ol) > i:first-child{font-style:normal;}

.pdp-desc strong,.pdp-desc b{font-weight:700;color:var(--ink);}
.pdp-desc a{color:var(--ink);text-decoration:underline;text-underline-offset:2px;}
.pdp-desc a:hover{color:var(--gold-cta);}

/* --- pasted structures ----------------------------------------------------
   43% of descriptions are wrapped in stray <div>s from a paste. They carry no
   meaning here, so they are stripped of any spacing of their own and let the
   elements inside set the rhythm. */
.pdp-desc div{margin:0;padding:0;max-width:100%;}

/* Truly empty paragraphs contribute a phantom line. Those containing only
   &nbsp; cannot be selected in CSS and are left alone — the content is not
   ours to edit — but the tightened paragraph margin above limits the damage. */
.pdp-desc p:empty{display:none;}

/* --- description tables (87 products, 90 tables) --------------------------

   All 90 are label/value specification tables. They arrive with hardcoded
   pixel widths on their cells (measured: 250.828px, 283.172px), which would
   overflow a phone column.

   `table-layout:fixed` contains them without a scroll container. An earlier
   version used `display:block;overflow-x:auto`, which worked visually and was
   wrong: it drops the element's table role, so a screen reader loses the row
   and column associations. These are real data tables and keep their
   semantics at every width — nothing here changes `display`. */
.pdp-desc table{width:100%;max-width:100%;table-layout:fixed;border-collapse:collapse;margin:0 0 16px;font-size:.9rem;}
.pdp-desc table th,.pdp-desc table td{
  border:1px solid var(--line);padding:9px 12px;text-align:left;vertical-align:top;
  line-height:1.55;overflow-wrap:break-word;
  /* Kills the inline pixel widths. Under fixed layout the first row's cells
     dictate every column, so leaving them in place set a floor the table could
     not shrink below. */
  width:auto!important;
}
.pdp-desc table th{font-weight:600;color:var(--ink);}

/* Label column, on two-column tables only.

   `:has()` is what keeps this honest — it matches a table with no third cell,
   so a genuine multi-column table is never squeezed into a label/value shape.
   Where `:has()` is unsupported the whole rule is dropped and the columns
   simply divide evenly, which is the old behaviour rather than a broken one. */
.pdp-desc table:not(:has(tr > *:nth-child(3))) tr > *:first-child{
  width:38%!important;font-weight:600;color:var(--ink);
}

/* Alternating rows, at the lightest tint that still separates them. Anything
   stronger reads as a highlight rather than a guide. */
.pdp-desc table tbody tr:nth-child(odd){background:rgba(242,236,225,.4);}

/* Media pasted into copy must never exceed the column. Embedded video arrives
   from the [video] shortcode with a hardcoded 750px on BOTH the wrapper div and
   the player (e.g. #1646), which is 454px past a 390px screen. `max-width`
   outranks a `width` declaration without needing !important, so the inline
   attribute is honoured as an intent and simply clamped. */
.pdp-desc img,.pdp-desc iframe,.pdp-desc video,.pdp-desc embed,.pdp-desc object{max-width:100%;height:auto;}
.pdp-desc .wp-video,.pdp-desc .mejs-container{max-width:100%;}

/* A comfortable measure. Long prose (up to 927 words) becomes hard to track
   across a full-width column; this caps the line without altering the layout
   of anything shorter. The `> div >` variants carry it into the 43.3% of
   descriptions wrapped in a stray <div>, which would otherwise be the only
   products on the site rendering at full column width. */
.pdp-desc > p,.pdp-desc > ul,.pdp-desc > ol,
.pdp-desc > div > p,.pdp-desc > div > ul,.pdp-desc > div > ol{max-width:70ch;}

@media(max-width:520px){
  .pdp-desc{font-size:.9rem;line-height:1.65;}
  /* !important repeated here on purpose. The base list rules need it to beat
     pasted inline padding, and an equally-specific rule WITHOUT it would lose
     to them — the mobile step-down would silently stop applying. Selectors also
     mirror the descendant form used above. */
  /* Narrower indent: at 296px of column every pixel of left inset is a pixel
     of line length, and a long spec value should not wrap three times because
     of decoration. Markers stay outside the text, so alignment is unchanged. */
  .pdp-desc ul,.pdp-desc ol{padding-left:17px!important;}
  .pdp-desc ul ul,.pdp-desc ul ol,
  .pdp-desc ol ul,.pdp-desc ol ol{padding-left:15px!important;}
  .pdp-desc li{margin-bottom:9px;}
  /* h5/h6 included: 160 products use them, and stepping only h1–h4 left those
     headings on the desktop margin beside siblings that had dropped. */
  .pdp-desc h1,.pdp-desc h2,.pdp-desc h3,
  .pdp-desc h4,.pdp-desc h5,.pdp-desc h6{margin-top:18px;}
}
/* ---- accordions ---- */
.pdp-acc{max-width:880px;margin:54px auto 0;}
.pdp-acc__item{border-top:1px solid var(--line);}
.pdp-acc__item:last-child{border-bottom:1px solid var(--line);}
.pdp-acc__item summary{list-style:none;display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:20px 4px;font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:1.35rem;color:var(--ink);}
.pdp-acc__item summary::-webkit-details-marker{display:none;}
.pdp-acc__chev{width:18px;height:18px;fill:none;stroke:var(--gold-cta);stroke-width:2;transition:transform .3s var(--ease);}
.pdp-acc__item[open] .pdp-acc__chev{transform:rotate(180deg);}
.pdp-acc__body{padding:0 4px 22px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.92rem;line-height:1.7;color:var(--muted);animation:accIn .28s var(--ease);}
.pdp-acc__body p{margin:0 0 12px;}.pdp-acc__body p:last-child{margin:0;}
@keyframes accIn{from{opacity:0;transform:translateY(-5px);}to{opacity:1;transform:none;}}
/* The Specifications table styles that lived here were removed on 2026-08-18
   together with the markup that emitted them (template-parts/product/details.php).
   Nothing renders `.pdp-specs` any more, so the rules had no selector to match.
   Do not re-add them without the template block — dead CSS for a section that
   does not exist is exactly what made the original `.pdp-spec` / `.pdp-specs`
   mismatch so hard to spot. */

/* Auto-load measurement point. No height, no margin, nothing to paint — it
   exists so the observer has something anchored to the END of the grid to
   watch, rather than the controls block that sits below the page's trailing
   content. */
.plp-sentinel{display:block;width:100%;height:1px;margin:0;padding:0;pointer-events:none;}

/* ---- product-card image slider ------------------------------------------
   A flex track translated by whole card widths. No library, no absolutely
   positioned slides, no JS-driven sizing: the track is as wide as its children
   and the card clips it, so a card with one image is byte-for-byte the layout
   it always was. */
/* The gallery takes over the absolute fill that .product-card__imglink used to
   do on its own. That anchor is now a flex item as well as the first slide, and
   an absolutely positioned box is removed from flex layout — leaving the track
   with no in-flow children and the image measuring 0px wide. A zero-width image
   also breaks `sizes`, so the browser fell back to a much larger srcset
   candidate: measured, seven images jumped from 300x300 to 640x640 and the page
   gained 781KB. Hence the reset below. */
.product-card__gallery{position:absolute;inset:0;overflow:hidden;}
.product-card__track{display:flex;width:100%;height:100%;transition:transform .35s var(--ease);will-change:transform;}
.product-card__slide{flex:0 0 100%;min-width:0;height:100%;display:block;padding:10px;box-sizing:border-box;}
.product-card__gallery .product-card__imglink{position:relative;inset:auto;}
/* Not draggable: the browser's native link/image drag fires pointercancel
   and kills the swipe gesture mid-drag. */
.product-card__slide img{display:block;width:100%;height:100%;object-fit:contain;-webkit-user-drag:none;user-select:none;}
.product-card__slide{-webkit-user-drag:none;}

/* Label sits beside the icon; the button keeps one width across states so an
   "Added" confirmation cannot make the card twitch. */
.product-card__add{gap:7px;min-width:0;white-space:nowrap;}
.product-card__add svg{flex:none;}

/* Dots sit over the foot of the image so they cost no layout height, and a card
   without a gallery never renders them at all — there is no reserved space to
   collapse. */
/* Control rail, over the foot of the image.

   Absolutely positioned, so it costs the card no height at all: the title sits
   as close to the picture as it would with no controls, nothing is reserved
   for a button that may never show, and the grid cannot reflow as the pointer
   crosses it.

   The rail itself is transparent to the pointer and only the add button takes
   events, so the empty middle of the strip never intercepts a click meant for
   the image beneath it. The bullets are markers and take no clicks either. */
.product-card__rail{
  position:absolute;left:0;right:0;bottom:0;z-index:3;
  display:flex;align-items:center;gap:10px;
  padding:0 10px 10px;
  pointer-events:none;
}
/* The bullets are an indicator now, not a control, so they take no clicks —
   only the add button does. */
.product-card__dots{position:relative;}
.product-card__add{pointer-events:auto;position:relative;}

/* Bullets left, button right. The auto margin does the separating, so the two
   cannot meet however many images a product has or however long the label
   runs; with no dot row at all the button still holds the right edge. */
.product-card__add{margin-left:auto;}

/* A wash behind the strip, no stronger than it needs to be. Tiles and product
   photography are both white, so this earns its place only where an image
   bleeds dark to the bottom edge — and it fades in with the button rather than
   sitting permanently over the piece.

   "Behind" is why the dots and the button above are position:relative. As a
   positioned pseudo-element against static flex children, this painted OVER
   them: the near-black pill came out washed grey and the icon lost its
   contrast. Positioning the controls puts them back on top. */
.product-card__rail::before{
  content:"";position:absolute;inset:auto 0 0;height:76px;
  background:linear-gradient(to top,rgba(255,255,255,.9),rgba(255,255,255,0));
  opacity:0;transition:opacity .28s var(--ease);pointer-events:none;
}
.product-card:hover .product-card__rail::before,
.product-card:focus-within .product-card__rail::before{opacity:1;}


.product-card__dots{
  display:flex;align-items:center;gap:6px;
  min-height:18px;
}
/* A marker, not a target. The box stays 18px so the row keeps the rhythm it
   had as buttons, but nothing here is pressable: the arrows are the only way
   an image changes. */
.product-card__dot{
  position:relative;display:block;
  width:18px;height:18px;padding:0;flex:none;
  border:none;background:none;
  pointer-events:none;
}
.product-card__dot::after{
  content:"";position:absolute;top:50%;left:50%;
  width:6px;height:6px;margin:-3px 0 0 -3px;
  border-radius:50%;background:rgba(28,26,23,.22);
  transition:background-color .2s var(--ease),transform .2s var(--ease);
}
.product-card__dot.is-active::after{background:var(--gold-cta);transform:scale(1.4);}


@media(prefers-reduced-motion:reduce){
  .product-card__track{transition:none;}
  .product-card__dot::after{transition:none;}
}

/* ---- View More: arrow, auto-load switch, status ---------------------------
   The button keeps its gold treatment; the arrow is an inline SVG so it takes
   currentColor and stays sharp at any zoom. It nudges down on hover to hint at
   the direction the page is about to grow — a transform, so it costs no layout
   and cannot shift the button's neighbours. */
.plp-more__btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;min-height:48px;}
.plp-more__arrow{width:15px;height:15px;flex:none;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;transition:transform .22s var(--ease);}
.plp-more__btn:hover .plp-more__arrow{transform:translateY(3px);}
.plp-more__btn.is-loading .plp-more__arrow{opacity:.5;}
@media(prefers-reduced-motion:reduce){
  .plp-more__arrow{transition:none;}
  .plp-more__btn:hover .plp-more__arrow{transform:none;}
}

/* The whole control is one <label>, so the text is part of the hit area and the
   native checkbox keeps its own keyboard behaviour — it is only visually
   replaced, never removed, which is what keeps it focusable and announced. */
.plp-more__auto{margin-top:14px;display:flex;justify-content:center;}
.plp-more__switch{display:inline-flex;align-items:center;gap:10px;min-height:44px;padding:0 6px;cursor:pointer;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.84rem;color:var(--muted);}
.plp-more__switch-input{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
.plp-more__switch-track{position:relative;flex:none;width:40px;height:23px;border-radius:23px;background:#DCD3C2;border:1px solid var(--line);transition:background-color .2s var(--ease);}
.plp-more__switch-thumb{position:absolute;top:2px;left:2px;width:17px;height:17px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(60,40,10,.28);transition:transform .2s var(--ease);}
.plp-more__switch-input:checked + .plp-more__switch-track{background:linear-gradient(135deg,var(--gold),var(--gold-cta));border-color:var(--gold-cta);}
.plp-more__switch-input:checked + .plp-more__switch-track .plp-more__switch-thumb{transform:translateX(17px);}
.plp-more__switch-input:focus-visible + .plp-more__switch-track{outline:2px solid var(--gold-cta);outline-offset:3px;}
.plp-more__switch:hover .plp-more__switch-text{color:var(--ink);}

.plp-more__status{display:flex;align-items:center;justify-content:center;gap:9px;margin:12px 0 0;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.85rem;color:var(--muted);min-height:22px;}
.plp-more__spinner{display:none;width:15px;height:15px;flex:none;border:2px solid var(--line);border-top-color:var(--gold-cta);border-radius:50%;animation:plpSpin .7s linear infinite;}
.plp-more__status.is-spinning .plp-more__spinner{display:block;}
@keyframes plpSpin{to{transform:rotate(360deg);}}
@media(prefers-reduced-motion:reduce){
  .plp-more__spinner{animation-duration:2s;}
}

/* Finished listing: only the count and the completion line remain, so the
   block closes up rather than leaving the gap the button used to fill. */
.plp-more.is-complete{padding-top:6px;}
.plp-more.is-complete .plp-more__count{margin-top:0;}
.plp-more.is-complete .plp-more__status{margin-top:6px;}

.plp-more__retry{margin-top:10px;min-height:44px;padding:0 22px;border:1px solid var(--ink);border-radius:9px;background:#fff;color:var(--ink);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.76rem;letter-spacing:.08em;text-transform:uppercase;cursor:pointer;transition:background-color .18s var(--ease),color .18s var(--ease);}
.plp-more__retry:hover{background:var(--ink);color:var(--ivory);}
.plp-more__retry:focus-visible{outline:2px solid var(--gold-cta);outline-offset:2px;}

/* ---- trust builders band ---- */
.pdp-builders{background:var(--ink);margin-top:60px;}
.pdp-builders__inner{max-width:var(--maxw);margin:0 auto;padding:32px 22px;display:grid;grid-template-columns:repeat(5,1fr);gap:24px;}
.pdp-builder{display:flex;flex-direction:column;align-items:center;text-align:center;gap:6px;color:rgba(250,247,241,.7);}
.pdp-builder svg{width:26px;height:26px;fill:none;stroke:var(--gold);stroke-width:1.5;}
.pdp-builder__stars svg{fill:var(--gold);stroke:none;}
.pdp-builder strong{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.92rem;color:var(--ivory);font-weight:700;}
.pdp-builder span{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.74rem;color:rgba(250,247,241,.55);}

/* ---- related ---- */
.pdp-related{max-width:var(--maxw);margin:0 auto;padding:60px 22px 0;display:flex;flex-direction:column;gap:50px;}
.pdp-rel__head{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;margin-bottom:22px;}
.pdp-rel__head .sect-title{font-size:clamp(1.5rem,2.6vw,2rem);}
.pdp-rel__navs{display:flex;gap:8px;flex:none;}
.pdp-rel__vp{position:relative;}
.pdp-rel__vp .le__item{flex:0 0 clamp(200px,23%,250px);}

/* ---- sticky mobile buy bar ---- */
.pdp-sticky{position:fixed;left:0;right:0;bottom:0;z-index:70;display:none;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;background:rgba(10,9,8,.97);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border-top:1px solid rgba(233,220,190,.18);transform:translateY(100%);transition:transform .3s var(--ease);}
/* `is-on` is what product.js sets — see initStickyBar(), which toggles it from
   an IntersectionObserver on `.pdp-buy`. This rule listened for `show`, so the
   mobile sticky Add to Cart was rendered, positioned and observed, and then
   never slid into view: a fourth case of the same class-name drift, on the one
   control whose whole job is to keep the buy button reachable on a long
   product page. `.show` is kept as an alias so nothing that may set it breaks. */
.pdp-sticky.is-on,.pdp-sticky.show{transform:translateY(0);}
.pdp-sticky__info{display:flex;flex-direction:column;min-width:0;}
.pdp-sticky__name{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.72rem;color:rgba(250,247,241,.6);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.pdp-sticky__price{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:1.25rem;color:var(--ivory);line-height:1;}
.pdp-sticky__qty{display:inline-flex;align-items:center;gap:2px;align-self:flex-start;background:rgba(255,255,255,.1);border:1px solid rgba(233,220,190,.22);border-radius:8px;padding:2px;margin-bottom:5px;}
.pdp-sticky__qbtn{width:26px;height:26px;border:none;background:transparent;color:var(--ivory);display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:6px;transition:background .15s var(--ease);}
.pdp-sticky__qbtn:hover{background:rgba(255,255,255,.14);}
.pdp-sticky__qbtn svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2.2;}
.pdp-sticky__qval{min-width:24px;text-align:center;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:600;font-size:.84rem;color:var(--ivory);}
.pdp-sticky__cta{display:flex;gap:8px;flex:none;}
.pdp-sticky__atc,.pdp-sticky__buy{height:46px;border-radius:8px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.74rem;letter-spacing:.06em;text-transform:uppercase;cursor:pointer;border:none;display:inline-flex;align-items:center;gap:6px;padding:0 16px;}
.pdp-sticky__atc{background:rgba(255,255,255,.12);color:var(--ivory);}
.pdp-sticky__atc svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;}
.pdp-sticky__buy{background:linear-gradient(135deg,var(--gold),var(--gold-cta));color:var(--ink);}

/* ---- PDP responsive ---- */
@media(max-width:860px){
  .pdp__grid{grid-template-columns:1fr;gap:30px;}
  .pdp-builders__inner{grid-template-columns:repeat(3,1fr);gap:20px 16px;}
  .pdp-builder:nth-child(4),.pdp-builder:nth-child(5){grid-column:span 1;}
  .pdp-rel__vp .le__item{flex-basis:46%;}
  .pdp-sticky{display:flex;}
  .pdp{padding-bottom:90px;}
}
@media(max-width:560px){
  .pdp-trust{grid-template-columns:repeat(2,1fr);gap:16px;}
  .pdp-builders__inner{grid-template-columns:repeat(2,1fr);}
  .pdp-rel__vp .le__item{flex-basis:72%;}
}
/* Keep the quantity stepper and Add to Cart on one line — but only where the
   two actually fit. Below 360px the stepper (145px) plus the button's
   min-content width (177px) exceed the column, and because neither flex item
   can shrink past its own content the row pushed the whole PDP 40px wider than
   the viewport at 320px. There the base `flex-wrap:wrap` applies instead and
   the button drops to its own full-width line, which is the better small-screen
   layout anyway. */
@media(min-width:360px) and (max-width:560px){
  .pdp-buy{flex-wrap:nowrap;position:relative;}

  /*
   * The stock-limit note is a THIRD child of this row, and it asks for
   * `flex:0 0 100%` so that it wraps onto its own line. With wrapping switched
   * off it could no longer wrap and could not shrink either, so the only
   * flexible item left — Add to Cart — absorbed the whole overflow and
   * collapsed to 56px. The label is `white-space:nowrap` inside
   * `overflow:hidden`, so it did not spill or wrap; it was simply cut, and the
   * primary button on every product page read "DD TO CA".
   *
   * Taking the note out of flow gives the row back to the two controls that
   * belong on it. It still appears in the same place, directly under them, and
   * its own transition is untouched — `position` does not affect max-height.
   */
  .pdp-qty__note{position:absolute;left:0;top:100%;width:100%;}
  .pdp-qty__note.is-shown{margin-top:9px;}
}

/* ===================================================================
   COLLECTION / PRODUCT LISTING PAGE (PLP) — image-forward, no filters
   =================================================================== */
.plp-hero{background:radial-gradient(120% 140% at 85% -10%,#fff 0%,var(--ivory) 45%,var(--pearl) 100%);border-bottom:1px solid var(--line);}
.plp-hero__inner{max-width:var(--maxw);margin:0 auto;padding:34px 22px 24px;}
.plp-hero__eyebrow{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.72rem;font-weight:600;letter-spacing:.18em;text-transform:uppercase;color:var(--gold-cta);margin:0 0 8px;}
.plp-hero__title{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:clamp(1.9rem,4.4vw,3rem);line-height:1.05;color:var(--ink);margin:0 0 12px;}
.plp-hero__desc{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.98rem;line-height:1.6;color:var(--muted);max-width:680px;margin:0;}

/* Short banner intro. Slightly larger and darker than the long description
   beneath it, so the two read as lead-then-detail rather than one block of
   grey. `max-width` holds the measure near 70 characters — the description
   already uses 680px and this sits a touch wider because it is one or two
   lines, not several paragraphs. Nothing is emitted when the field is empty,
   so there is no margin to collapse. */
.plp-hero__lead{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:1.04rem;line-height:1.6;color:var(--ink);max-width:720px;margin:0 0 10px;}
.plp-hero__lead p{margin:0 0 8px;}
.plp-hero__lead p:last-child{margin-bottom:0;}

/* Child collections, as chips.

   Replaces a plain wrapping row of text links. A chip gives each collection a
   real target instead of a word to aim at: every one is at least 44px tall,
   and the outline makes the row read as navigation rather than as a sentence
   that happens to be linked.

   Presentation only. The markup stays a <nav> holding a <ul> of ordinary <a>
   elements, so the links remain crawlable, tabbable and independent of any
   JavaScript. The heading above is a real <h2> referenced by aria-labelledby,
   which also names the landmark for screen readers. */
.plp-hero__explore{margin:14px 0 0;padding:12px 0 0;border-top:1px solid var(--line);}
/* Heading and hint share one row, so the affordance costs no vertical space. */
/* `center`, not `baseline`. Baseline alignment offsets two items with
   different font sizes against each other, which made the flex line
   taller than either of them and added height the hint was not supposed
   to cost. Centred, the row is exactly as tall as the heading. */
.plp-hero__explore-head{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:0 0 7px;min-height:0;}
.plp-hero__explore-h{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.66rem;font-weight:700;letter-spacing:.15em;text-transform:uppercase;color:var(--muted);margin:0;}

/* Hidden by default in both senses: `display:none` here, and the `hidden`
   attribute until the script confirms the row actually overflows. The media
   query is what keeps it off touch devices, where a swipe already tells the
   reader everything this sentence would. */
.plp-chips__hint{display:none;align-items:center;gap:6px;margin:0;flex:none;
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.63rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);opacity:.75;white-space:nowrap;-webkit-user-select:none;user-select:none;}
.plp-chips__hint-ic{width:14px;height:14px;flex:none;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;}
@media(min-width:861px){
  .plp-chips__hint:not([hidden]){display:inline-flex;}
}

/* One row, always. Wrapping was costing four extra rows of banner on a
   collection with a dozen children, which pushed the first product line off
   the first screen. Scrolling keeps the whole set reachable in a fixed amount
   of vertical space. */
.plp-chips-wrap{position:relative;}
.plp-chips{
  list-style:none;margin:0;padding:0;
  display:flex;flex-wrap:nowrap;gap:6px;
  /* `auto`, not `scroll`: the strip only becomes scrollable when the chips
     genuinely exceed the width, so a row that fits has no scroll behaviour at
     all attached to it. */
  overflow-x:auto;overflow-y:hidden;
  /* Applies to programmatic scrolls only — focus moving a chip into view. User
     scrolling stays native, which is what makes it feel smooth. */
  scroll-behavior:smooth;
  /* Deliberately NOT scroll-snapped. `scroll-snap-align:start` aligned chip one
     to the scrollport edge, which on mobile sits 22px outside the page gutter
     because the strip bleeds — the browser corrected by setting scrollLeft to
     22 on load, so the row opened already scrolled and the back arrow was live
     before the reader had touched it. Snapping also pulls chips flush, which
     removes the half-visible next chip that signals there is more to swipe. */
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.plp-chips::-webkit-scrollbar{display:none;}
.plp-chips__item{display:flex;flex:0 0 auto;}

/* The fade is a mask on the strip itself rather than a gradient overlay. The
   banner sits on a radial gradient, so an overlay would have to match a colour
   that changes across the width; masking the content fades it against whatever
   is behind. Applied only on the side that can actually scroll, so a strip that
   fits shows no fade at all. */
.plp-chips-wrap.can-next .plp-chips{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 34px),transparent 100%);mask-image:linear-gradient(90deg,#000 calc(100% - 34px),transparent 100%);}
.plp-chips-wrap.can-prev .plp-chips{-webkit-mask-image:linear-gradient(90deg,transparent 0,#000 34px);mask-image:linear-gradient(90deg,transparent 0,#000 34px);}
.plp-chips-wrap.can-prev.can-next .plp-chips{-webkit-mask-image:linear-gradient(90deg,transparent 0,#000 34px,#000 calc(100% - 34px),transparent 100%);mask-image:linear-gradient(90deg,transparent 0,#000 34px,#000 calc(100% - 34px),transparent 100%);}

/* Click-and-drag on a pointer device, in addition to wheel, trackpad and
   swipe. `cursor:grab` only appears when the strip actually overflows, so a
   row that fits does not advertise an interaction it does not have. */
@media(min-width:861px){
  .plp-chips-wrap.has-overflow .plp-chips{cursor:grab;}
  /* scroll-behavior:auto while dragging and gliding — smooth would animate
     every incremental scrollLeft write and the row would lag the cursor. */
  .plp-chips-wrap.has-overflow .plp-chips.is-dragging,
  .plp-chips-wrap.has-overflow .plp-chips.is-gliding{scroll-behavior:auto;}
  .plp-chips-wrap.has-overflow .plp-chips.is-dragging{cursor:grabbing;}
  .plp-chips-wrap.has-overflow .plp-chips.is-dragging,
  .plp-chips-wrap.has-overflow .plp-chips.is-dragging *{
    -webkit-user-select:none;user-select:none;
  }
}
/* A link is natively draggable, so a press-and-pull starts a browser drag of
   the URL instead of scrolling the row. Suppressed on the chips only. */
.plp-chip{-webkit-user-drag:none;}

/* min-height rather than a padding-derived height: a few child names wrap to
   two lines, and a padded box would then stand taller than its neighbours.
   This keeps every chip on one baseline whatever the label does. */
/* min-height holds the 44px tap target while the horizontal padding comes
   down, so the row is tighter without becoming harder to hit. nowrap is what
   keeps a two-word label on one line inside a single-row strip. */
.plp-chip{
  display:inline-flex;align-items:center;justify-content:center;
  /* 40px is the floor for a comfortable tap while letting several more chips
     fit across a row. Trimmed from 44px with the padding and type, which is
     what was pushing the last collections out of view. */
  min-height:40px;padding:0 11px;white-space:nowrap;
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.8rem;font-weight:500;line-height:1.25;
  color:var(--ink);text-decoration:none;text-align:center;
  background:rgba(255,255,255,.72);
  border:1px solid var(--line);border-radius:999px;
  transition:border-color .18s var(--ease),background-color .18s var(--ease),color .18s var(--ease);
}
.plp-chip:hover{border-color:var(--gold-cta);color:var(--gold-cta);background:#fff;}
.plp-chip:focus-visible{outline:2px solid var(--gold-cta);outline-offset:2px;}
.plp-chip:active{background:var(--pearl);}

@media(max-width:640px){
  .plp-hero__lead{font-size:.98rem;}
  .plp-hero__explore{margin-top:12px;padding-top:11px;}

  /* Bleed the strip to both screen edges: the negative margin takes it past
     the page gutter while the matching padding holds the first chip on that
     gutter, so the next chip is cut mid-shape at the right edge. That partial
     chip is the cue that there is more to swipe. Overflow belongs to the strip,
     so the page itself never scrolls sideways. */
  .plp-chips{margin-inline:-22px;padding-inline:22px;}
  .plp-chips-wrap.can-next .plp-chips,
  .plp-chips-wrap.can-prev .plp-chips,
  .plp-chips-wrap.can-prev.can-next .plp-chips{-webkit-mask-image:none;mask-image:none;}
}

.plp{padding:0 0 10px;}
.plp__wrap{max-width:var(--maxw);margin:0 auto;padding:0 22px;}

/* toolbar: count · sort · density */
/* Toolbar controls.

   Both groups are driven from one height token so they cannot drift apart.
   They were each 42px by two different routes — the select from vertical
   padding, the view group from padding + button + border — which is why they
   never quite lined up. One number now governs both. */
:root{--plp-ctl-h:46px;}

.plp-toolbar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:18px 0 16px;border-bottom:1px solid var(--line);background:var(--ivory);}
.plp-count{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.9rem;color:var(--muted);}
.plp-count strong{color:var(--ink);font-weight:700;}
.plp-toolbar__right{margin-left:auto;display:flex;align-items:center;gap:12px;}
/* WooCommerce prints the ordering <select> inside its own <form>, and that
   form carries a 16px bottom margin. `align-items:center` centres a flex item
   by its MARGIN box, so the select was being pushed exactly half that — 8px —
   above the view buttons beside it. Both boxes measured 46px tall the whole
   time, which is why the misalignment did not look like a height problem.
   Zeroing the form's margin is the actual fix. */
.plp-sort{display:flex;align-items:center;gap:8px;}
.plp-sort form,.plp-sort .woocommerce-ordering{margin:0;padding:0;display:flex;align-items:center;}
.plp-sort label{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.8rem;color:var(--muted);}
/* Explicit height with a matching line-height, rather than vertical padding:
   padding left the label riding on the box's text baseline, which is what set
   it low against the icons beside it. */
.plp-sort select{
  appearance:none;-webkit-appearance:none;
  height:var(--plp-ctl-h);line-height:calc(var(--plp-ctl-h) - 2px);
  padding:0 40px 0 16px;
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.84rem;color:var(--ink);
  background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236F685C' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 14px center;
  border:1px solid var(--line);border-radius:10px;cursor:pointer;
  transition:border-color .18s var(--ease);
}
.plp-sort select:hover{border-color:var(--gold-cta);}
.plp-sort select:focus-visible{outline:2px solid var(--gold-cta);outline-offset:2px;border-color:var(--gold-cta);}

/* `align-items:stretch` with no padding on the group is what makes the buttons
   fill it exactly: each becomes a full-height, 44px-wide target and the active
   fill reaches the group's edge instead of floating inside a 3px inset. The
   radius is clipped by the group, so every button keeps identical dimensions
   whether or not it is the active one. */
.plp-view{display:inline-flex;align-items:stretch;height:var(--plp-ctl-h);background:#fff;border:1px solid var(--line);border-radius:10px;overflow:hidden;}
.plp-view__btn{
  min-width:44px;height:100%;padding:0;margin:0;
  border:none;background:transparent;color:var(--muted);
  display:inline-flex;align-items:center;justify-content:center;
  cursor:pointer;transition:background-color .18s var(--ease),color .18s var(--ease);
}
.plp-view__btn + .plp-view__btn{border-left:1px solid var(--line);}
/* display:block removes the inline descender gap that pushed the glyphs off
   centre inside their buttons. */
.plp-view__btn svg{width:18px;height:18px;display:block;fill:none;stroke:currentColor;stroke-width:1.8;}
.plp-view__btn:hover{background:var(--pearl);color:var(--ink);}
/* Inset ring, so it is not clipped by the group's overflow:hidden. */
.plp-view__btn:focus-visible{outline:2px solid var(--gold-cta);outline-offset:-2px;color:var(--ink);}
.plp-view__btn.is-active{background:var(--ink);color:var(--ivory);}
.plp-view__btn.is-active:hover{background:var(--ink);color:var(--ivory);}

.plp__cols{padding:22px 0 8px;}

/* grid + density (2 / 4 columns) */
/* Density.
   --n-* is how many columns each view resolves to at the current width. The
   switcher reads them back so its labels always say what the button will
   actually do, rather than repeating a number that was only ever true on a
   desktop. minmax(0,1fr) so a long product title cannot push a narrow column
   wider than its share. */
.plp-grid{--n-list:1;--n-2:2;--n-4:3;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;}
@media(min-width:1000px){.plp-grid{--n-4:4;}}

.plp-grid.cols-4{grid-template-columns:repeat(3,minmax(0,1fr));}
@media(min-width:1000px){.plp-grid.cols-4{grid-template-columns:repeat(4,minmax(0,1fr));}}
.plp-grid.cols-2{grid-template-columns:repeat(2,minmax(0,1fr));gap:24px;}

/* The default before anyone has chosen.
   PHP prints cols-4 because it cannot know the viewport, and on a phone the
   densest view is not the right thing to open with — two up is. The class is
   dropped by the switcher the moment a view is applied, so this only ever
   describes the untouched state. */
@media(max-width:699px){
  .plp-grid--default.cols-4{--n-4:2;grid-template-columns:repeat(2,minmax(0,1fr));}
}

/* image-forward product card */
/* The whole card carries the frame, not the image.

   Almost every product shot in this catalogue is cut out on white. With the
   frame and a ground on the media panel, those shots showed a white rectangle
   sitting inside a tinted card — the image's own background became a visible
   patch rather than disappearing into the card. Pure white behind the image
   makes a white-background photo edgeless, and the cream that used to sit
   there moves down to the information block, which is what gives the card its
   warmth without touching the photography.

   Transparent PNGs get the same white ground, so they read as floating on the
   card rather than on a tint. Lifestyle shots fill the panel and never see it.

   The frame, radius, overflow and hover all move up here from the media panel;
   nothing about the 1:1 ratio, the padding inside the image or the type below
   it changes. */
/* The card is two grounds, and which colour goes where is load-bearing.

   The image sits on pure white because most of the catalogue is shot on a white
   sweep: any other colour there draws a rectangle around the product. The
   information below it sits on a warm tint so the card reads as an object with
   a base rather than a floating photo.

   That tint is NOT --pearl. Pearl is the section background used behind whole
   blocks; at card scale, repeated across a grid next to a gold button, it reads
   as a heavy beige slab. --card-info is the same hue at roughly a third of the
   strength — enough to separate the text from the photograph, not enough to
   compete with either. */
.product-card{
  position:relative;display:flex;flex-direction:column;
  background:#fff;
  border:1px solid var(--line);
  border-radius:16px;
  overflow:hidden;
  transition:box-shadow .28s var(--ease),border-color .28s var(--ease);
}
.product-card__media{position:relative;aspect-ratio:1/1;background:#fff;border:0;border-radius:0;overflow:hidden;}
.product-card__imglink{position:absolute;inset:0;display:block;padding:10px;box-sizing:border-box;}
.product-card__imglink img{width:100%;height:100%;object-fit:contain;}
.product-card:hover{box-shadow:0 22px 46px -26px rgba(28,26,23,.45);border-color:var(--gold-soft);}
/* Badge. Shape, type, padding and position are shared; only the colour carries
   the meaning, and the two meanings are different:

     --sale  burgundy   an active promotion, worth stopping for
     --oos   warm grey  unavailable, nothing to act on

   Both were burgundy, so a sold-out tile shouted as loudly as a discount and a
   shopper scanning a grid could not tell them apart at a glance. The grey is
   the palette's existing --muted rather than a new colour, and white on it
   measures 5.51:1 — above the 4.5:1 AA floor, which matters here because the
   label is small and bold. The two badges also differ in wording, so the
   meaning never rests on colour alone. */
.product-card__badge{position:absolute;top:12px;left:12px;z-index:2;background:var(--garnet);color:#fff;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.64rem;letter-spacing:.04em;padding:4px 9px;border-radius:6px;}
.product-card__badge--sale{background:var(--garnet);}
.product-card__badge--oos{background:var(--muted);}
.product-card__wish{position:absolute;top:11px;right:11px;z-index:2;width:36px;height:36px;border-radius:50%;background:rgba(255,255,255,.92);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border:1px solid var(--line);display:flex;align-items:center;justify-content:center;color:var(--ink);cursor:pointer;transition:.18s var(--ease);}
.product-card__wish svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.8;}
.product-card__wish:hover{transform:scale(1.08);}
.product-card__wish.is-active,
.product-card__wish.is-on{color:var(--garnet);}
.product-card__wish.is-active svg,
.product-card__wish.is-on svg{fill:currentColor;}
/* Compact pill at the bottom-right of the image, sized to its label rather
   than the card so it can never become a full-width bar. 44px tall keeps the
   tap target honest at its smallest, with tight horizontal padding. */
.product-card__add{flex:0 0 auto;height:44px;padding:0 16px;width:auto;border:none;border-radius:10px;background:var(--ink);color:var(--ivory);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.74rem;letter-spacing:.06em;text-transform:uppercase;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:7px;opacity:0;transform:translateY(12px);transition:opacity .28s var(--ease),transform .28s var(--ease),background .2s var(--ease);}
.product-card__add svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.9;}
.product-card__add:hover{background:var(--gold-cta);color:var(--ink);}
/* Revealed on hover or when anything inside the card takes focus, so a
   keyboard user reaches it without a pointer. */
.product-card:hover .product-card__add,
.product-card:focus-within .product-card__add{opacity:1;transform:none;}
/* Mobile: primary image only.

   The gallery, its bullets and every slider affordance are gone — not hidden
   behind a gesture, absent. Touch users get the featured photograph and an
   always-available add control, which is what the brief asks for and also what
   removes the strip's reserved height from the smallest screens. */
@media(hover:none){
  .product-card__dots{display:none;}
  .product-card__nav{display:none;}
  .product-card__track{transform:none!important;}
  .product-card__rail{padding:0 8px 8px;}

  /* Icon-only. At 168px of card the labelled pill measured 144px — a
     full-width bar in all but name, which is what the brief rules out. The
     accessible name stays on the element, so the circle is never unnamed. */
  .product-card__add{opacity:1;transform:none;width:44px;padding:0;border-radius:50%;}

  /* The label, and the ones cart.js swaps in for the loading and added states.
     Without the second selector "Adding…" would reappear inside a 44px circle
     and burst it — the very layout shift the pinned width exists to prevent. */
  .product-card__add-label,
  .product-card__add .stonex-btn-label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}

  /* Permanently available, so the wash is permanent too — it is what keeps the
     circle legible against a photograph that reaches the bottom edge. */
  .product-card__rail::before{opacity:1;height:64px;}
}
/* Desktop hover layout: bullets centred directly above a centred, compact
   button, both revealed together.

   Confined to hover-capable pointers so the touch layout — primary image only,
   no bullets, the add control held bottom-right — is left exactly as it is.
   Everything here is an override of the shared row above, which remains what
   phones get. */
@media(hover:hover){
  .product-card__rail{
    flex-direction:column;align-items:center;justify-content:flex-end;
    gap:6px;padding:0 10px 11px;
  }

  /* The auto margin that pinned the button right belongs to the touch layout. */
  .product-card__add{margin-left:0;}

  /* Small and closely spaced: 18px between centres rather than 24px. */
  .product-card__dots{gap:4px;min-height:0;}
  .product-card__dot{width:14px;height:14px;}
  .product-card__dot::after{width:5px;height:5px;margin:-2.5px 0 0 -2.5px;}

  /* Compact: 38px tall with tighter type. The interactive target is restored
     to 44px by the pseudo-element below, so the control is smaller to look at
     without being smaller to hit. It reaches 3px into the 6px gap above and
     stops short of the bullets, so the two hit areas never meet. */
  .product-card__add{height:38px;padding:0 20px;font-size:.7rem;}
  .product-card__add::after{
    content:"";position:absolute;left:0;right:0;top:50%;height:44px;
    transform:translateY(-50%);
  }

  /* Hidden until the card is hovered or something inside it takes focus. The
     bullets travel with the button rather than sitting there permanently, so
     the picture is uninterrupted until the customer engages with it. */
  .product-card__dots{
    opacity:0;transform:translateY(6px);
    transition:opacity .26s var(--ease),transform .26s var(--ease);
  }
  .product-card__add{transform:translateY(8px);}
  .product-card:hover .product-card__dots,
  .product-card:focus-within .product-card__dots{opacity:1;transform:none;}

  /* The wash has a taller stack to sit behind now. */
  .product-card__rail::before{height:96px;}
}

@media(hover:hover) and (prefers-reduced-motion:reduce){
  .product-card__dots{transition:none;transform:none;}
  .product-card__add{transform:none;}
}

/* The information block is the cream one now, so it needs real gutters: the
   old 4px sides were fine when the text sat on the page, and would read as
   type jammed against the card edge once it has a ground of its own. */
.product-card__body{padding:14px 14px 16px;display:flex;flex-direction:column;gap:5px;
  background:var(--card-info);border-top:1px solid var(--line);}
.product-card__brand{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.62rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--gold-cta);}
.product-card__title{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.84rem;line-height:1.35;color:var(--ink);text-decoration:none;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:2.3em;}
.product-card__title:hover{color:var(--pounamu);}
.product-card__price{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-top:1px;}
.product-card__now{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:1rem;color:var(--ink);}
.product-card__was{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.82rem;color:var(--muted);text-decoration:line-through;}

/* view more + count */
.plp-more{display:flex;flex-direction:column;align-items:center;gap:14px;padding:34px 0 8px;}
.plp-more__btn{min-width:240px;}
.plp-more__btn[disabled]{opacity:.45;pointer-events:none;}
.plp-more__count{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.86rem;color:var(--muted);margin:0;}
.plp-more__count strong{color:var(--ink);font-weight:700;}

/* FAQ */
.plp-faq{border-top:1px solid var(--line);margin-top:18px;}
.plp-faq__inner{max-width:860px;margin:0 auto;padding:48px 22px;}
.plp-faq__inner>h2{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:clamp(1.6rem,3.4vw,2.2rem);color:var(--ink);text-align:center;margin:0 0 26px;}
.faq-item{border-bottom:1px solid var(--line);}
.faq-item>summary{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:16px;padding:18px 2px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:600;font-size:1rem;color:var(--ink);}
.faq-item>summary::-webkit-details-marker{display:none;}
.faq-item__icon{position:relative;width:18px;height:18px;flex:none;}
.faq-item__icon::before,.faq-item__icon::after{content:"";position:absolute;background:var(--gold-cta);border-radius:2px;transition:.25s var(--ease);}
.faq-item__icon::before{left:0;top:8px;width:18px;height:2px;}
.faq-item__icon::after{left:8px;top:0;width:2px;height:18px;}
.faq-item[open] .faq-item__icon::after{transform:rotate(90deg);opacity:0;}
.faq-item__body{padding:0 2px 20px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.92rem;line-height:1.65;color:var(--muted);}
.faq-item__body a{color:var(--pounamu);}

/* SEO content */
/* Collection copy, moved out of the hero and below the product grid.

   Only the container moved. `.plp-hero__desc` still carries the type, so the
   copy renders exactly as it did before — that class is shared with
   templates/wishlist.php, which is why it was not renamed. This wrapper just
   restores the page gutter `.plp-hero__inner` used to supply (same --maxw, same
   22px side padding); the vertical padding is its own, since it no longer sits
   on the hero's gradient. */
/* Long collection copy, below the products.

   Given its own measure and a warm ground so it reads as a considered closing
   section rather than leftover text under the grid. The measure is the point:
   this copy runs to four headings and several hundred words, and at full page
   width the eye loses the line between them. */
.plp-about{background:linear-gradient(180deg,var(--ivory) 0%,var(--pearl) 100%);border-top:1px solid var(--line);}
.plp-about__inner{max-width:860px;margin:0 auto;padding:52px 22px 56px;}
.plp-about__body{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.95rem;line-height:1.75;color:var(--muted);}
.plp-about__body > :first-child{margin-top:0;}
.plp-about__body > :last-child{margin-bottom:0;}
.plp-about__body p{margin:0 0 15px;}

.plp-about__body h2,.plp-about__body h3{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;color:var(--ink);line-height:1.25;margin:34px 0 12px;}
.plp-about__body h2{font-size:clamp(1.35rem,2.4vw,1.65rem);}
.plp-about__body h3{font-size:clamp(1.15rem,2vw,1.35rem);}

/* The migrated copy wraps every heading's text in
   `<span style="font-weight:400">`, so the headings rendered at body weight and
   the section had no hierarchy at all. An inline style can only be beaten this
   way, and it is scoped to headings so body spans keep their own weight.
   Presentation only: the markup and the text are untouched. */
.plp-about__body h2 *,.plp-about__body h3 *{font-weight:inherit!important;}

.plp-about__body a{color:var(--pounamu);text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px;transition:color .18s var(--ease);}
.plp-about__body a:hover{color:var(--gold-cta);}
.plp-about__body a:focus-visible{outline:2px solid var(--gold-cta);outline-offset:2px;border-radius:3px;}
.plp-about__body b,.plp-about__body strong{color:var(--ink);font-weight:700;}
.plp-about__body ul,.plp-about__body ol{margin:0 0 15px;padding-left:20px;}
.plp-about__body li{margin:0 0 6px;}
.plp-about__body img{max-width:100%;height:auto;}
@media(max-width:640px){
  .plp-about__inner{padding:38px 22px 40px;}
  .plp-about__body{font-size:.92rem;}
  .plp-about__body h2,.plp-about__body h3{margin-top:26px;}
}

.plp-seo{background:var(--pearl);border-top:1px solid var(--line);}
.plp-seo__inner{max-width:940px;margin:0 auto;padding:48px 22px 54px;}
.plp-seo__inner h2{font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:clamp(1.4rem,3vw,1.9rem);color:var(--ink);margin:30px 0 12px;}
.plp-seo__inner h2:first-child{margin-top:0;}
.plp-seo__inner p{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.92rem;line-height:1.75;color:var(--muted);margin:0 0 14px;}
.plp-seo__inner a{color:var(--pounamu);}
.plp-seo__pop{border-top:1px solid var(--line);padding-top:18px;margin-top:22px;font-size:.88rem !important;}
.plp-seo__pop strong{color:var(--ink);}

@media(max-width:560px){
  .plp-grid{gap:12px;}
  /* Three up on a 360px phone leaves roughly 100px a card; the gutter and the
     title have to give a little or the price wraps under its own row. */
  .plp-grid.cols-4{gap:8px;}
  .plp-grid.cols-4 .product-card__title{font-size:.72rem;-webkit-line-clamp:2;}
  .plp-grid.cols-4 .product-card__body{padding:10px 8px;gap:6px;}
  .plp-grid.cols-4 .product-card__now{font-size:.86rem;}
  .plp-grid.cols-4 .product-card__was{font-size:.72rem;}
  .product-card__title{font-size:.8rem;}
  /* The right-hand pair takes its own row: the sort control expands into the
     space it needs while the view selector stays at its natural width, so
     neither is squeezed and nothing wraps into the gutter. */
  .plp-toolbar__right{width:100%;margin-left:0;gap:10px;}
  .plp-sort{flex:1 1 auto;min-width:0;}
  .plp-sort select{width:100%;}
  .plp-view{flex:0 0 auto;}
  .plp-sort label{display:none;}
}

/* ===================================================================
   CRO / UI-UX ENHANCEMENTS (batch)
   =================================================================== */

/* ---- Wishlist icon: consistent fill/hover everywhere ---- */
.act svg use[href="#i-heart"]{}
.le__wish.is-active svg,.product-card__wish.is-active svg,
.product-card__wish.is-on svg{fill:currentColor;}

/* ---- 1. Floating cart (home only) ---- */
.floatcart{position:fixed;right:20px;bottom:22px;z-index:90;width:58px;height:58px;border-radius:50%;background:var(--ink);color:var(--ivory);display:flex;align-items:center;justify-content:center;box-shadow:0 18px 40px -14px rgba(28,26,23,.7);border:1px solid rgba(233,220,190,.25);opacity:0;visibility:hidden;transform:translateY(14px) scale(.9);transition:opacity .3s var(--ease),transform .3s var(--ease),visibility .3s,background .25s var(--ease);}
.floatcart.show{opacity:1;visibility:visible;transform:none;}
.floatcart:hover{background:var(--pounamu);transform:translateY(-3px);}
.floatcart__ic{width:23px;height:23px;fill:none;stroke:currentColor;stroke-width:1.7;}
.floatcart__count{position:absolute;top:-3px;right:-3px;min-width:20px;height:20px;padding:0 5px;border-radius:11px;background:var(--gold-cta);color:#1a160e;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.66rem;line-height:1;display:flex;align-items:center;justify-content:center;border:2px solid var(--ivory);}
@media(max-width:560px){.floatcart{right:16px;bottom:16px;width:54px;height:54px;}}

/* Clear the PDP's sticky buy bar.

   Both sit bottom-fixed on a phone, and the bar is the wider control, so the
   bag steps up over it rather than the other way round. Keyed to the bar's
   visible state, so on the rest of the product page — and the moment the bar
   retracts — the bag is back at its normal corner. Matched on both class
   names: the stylesheet expects `.show` and product.js currently writes
   `is-on`, and this rule should hold whichever of the two that disagreement
   is settled on. */
@media(max-width:860px){
  body:has(.pdp-sticky.show) .floatcart,
  body:has(.pdp-sticky.is-on) .floatcart{bottom:84px;}
}

@media(prefers-reduced-motion:reduce){
  .floatcart{transition:opacity .01s linear,visibility .01s;transform:none;}
  .floatcart.show{transform:none;}
  .floatcart:hover{transform:none;}
}

/* ---- 3. Newsletter sub: single line, responsive ---- */
.newsletter__sub{white-space:nowrap;max-width:none;font-size:clamp(.56rem,2.45vw,1rem);letter-spacing:-.005em;}

/* ---- 4. Watch Brands: overflow-proof grid + View All ---- */
.wb__grid{grid-template-columns:repeat(4,minmax(0,1fr));}
.wb__card{min-width:0;}
.wb__logo{max-width:70%;}
.wb__logo-img{max-width:70%;}
.wb__more{text-align:center;margin-top:28px;}
.wb__viewall{display:inline-flex;align-items:center;gap:8px;}
.wb__viewall svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.9;transition:transform .3s var(--ease);}
.wb__viewall:hover svg{transform:translateX(3px);}
@media(max-width:900px){.wb__grid{grid-template-columns:repeat(3,minmax(0,1fr));}}
@media(max-width:560px){
  .wb__grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;}
  .wb__card{min-height:86px;padding:14px;}
  .wb__logo{font-size:.9rem;letter-spacing:.07em;}
  .wb__logo-img{max-height:40px;}
}

/* ---- 5. Shop by Category: mobile scroll affordance ---- */
.shop-cats__progress{display:none;}
.shop-cats__hint{display:none;}
@media(max-width:600px){
  .shop-cats__progress{display:block;position:relative;height:3px;background:var(--line);border-radius:3px;margin:20px 40px 0;overflow:hidden;}
  .shop-cats__bar{position:absolute;left:0;top:0;height:100%;width:30%;background:var(--gold-cta);border-radius:3px;will-change:transform;}
  .shop-cats__hint{display:flex;align-items:center;justify-content:center;gap:7px;margin:12px 0 0;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.72rem;font-weight:500;letter-spacing:.08em;color:var(--muted);text-transform:uppercase;}
  .shop-cats__hint svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.8;animation:swipeHint 1.8s var(--ease) infinite;}
  @keyframes swipeHint{0%,100%{transform:translateX(0);}50%{transform:translateX(5px);}}
}

/* ---- 6. Lab Collection: image above content on mobile ---- */
@media(max-width:860px){
  .labgrown__media{order:-1;}
}

/* ---- 7. Footer address: full address, readable on mobile ---- */
.footer__contact--addr{align-items:flex-start;}
.footer__contact--addr svg{margin-top:2px;}
.footer__contact--addr span{line-height:1.45;}

/* ===== 8. PDP details accordion now lives in the right info column ===== */
.pdp-info .pdp-acc{max-width:none;margin:16px 0 0;}
.pdp-info .pdp-acc__item summary{font-size:1.2rem;padding:16px 2px;}

/* ===================================================================
   10-11. COLLECTION: list view + image quick-zoom + quick-view lightbox
   =================================================================== */
/* Quick View button.
   Occupies the slot the image-zoom button used to, with the same treatment, so
   the card looks unchanged — only what the control does has changed. */
/* Previous / next, centred on the image.

   Same treatment as the wishlist and Quick View buttons at the top corners, so
   the four controls read as one family. Vertically centred means they clear the
   badge and those buttons above and the control rail below at every card size,
   with no geometry to keep in sync.

   The sprite's chevron points down, so each is rotated a quarter turn. */
.product-card__nav{position:absolute;top:50%;z-index:3;width:34px;height:34px;margin-top:-17px;
  border-radius:50%;background:rgba(255,255,255,.92);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  border:1px solid var(--line);display:flex;align-items:center;justify-content:center;color:var(--ink);
  cursor:pointer;opacity:0;transition:opacity .26s var(--ease),transform .26s var(--ease),background .2s var(--ease);}
.product-card__nav svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;}
.product-card__nav--prev{left:9px;transform:translateX(-6px);}
.product-card__nav--next{right:9px;transform:translateX(6px);}
.product-card__nav--prev svg{transform:rotate(90deg);}
.product-card__nav--next svg{transform:rotate(-90deg);}

/* A 44px target behind a 34px control, the same trick the dots use. */
.product-card__nav::after{content:"";position:absolute;top:50%;left:50%;width:44px;height:44px;transform:translate(-50%,-50%);}

.product-card:hover .product-card__nav,
.product-card:focus-within .product-card__nav{opacity:1;transform:none;}
.product-card__nav:hover{background:#fff;}
.product-card__nav:focus-visible{opacity:1;transform:none;outline:2px solid var(--gold-cta);outline-offset:2px;}

/* At either end the step is dimmed rather than removed: a control that
   disappears at the boundary makes the pair jump about as the customer moves
   through the gallery. */
.product-card:hover .product-card__nav:disabled,
.product-card:focus-within .product-card__nav:disabled{opacity:.28;cursor:default;}

/* Touch: gone, and it has to be said HERE.

   The mobile block up at `@media(hover:none)` already carries
   `.product-card__nav{display:none;}`, and it never applied: the base rule
   above is written later in the file at the same specificity, so the cascade
   settles on `display:flex` and the media query loses. The arrows were left at
   `opacity:0` — invisible, because nothing hovers on a phone, but still laid
   out and still taking taps, which is the worst of both. Same declaration,
   correct position. */
@media(hover:none){
  .product-card__nav{display:none;}
}

.product-card__qv{position:absolute;top:11px;right:55px;z-index:2;width:36px;height:36px;border-radius:50%;background:rgba(255,255,255,.92);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border:1px solid var(--line);display:flex;align-items:center;justify-content:center;color:var(--ink);cursor:pointer;opacity:0;transform:translateY(-4px);transition:.2s var(--ease);}
.product-card__qv svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;}
.product-card__qv:hover{transform:scale(1.08);}
.product-card__qv:focus-visible{opacity:1;transform:none;outline:2px solid var(--gold-cta);outline-offset:2px;}
.product-card:hover .product-card__qv{opacity:1;transform:none;}
@media(hover:none){
  .product-card__qv{opacity:1;transform:none;}
  /* The circle stays 36px so the tile is unchanged, while the tap target grows
     to 44px. Sizing the button itself would have pushed it into the wishlist
     heart and made the two easy to mis-tap. */
  .product-card__qv::after{content:"";position:absolute;top:50%;left:50%;width:44px;height:44px;transform:translate(-50%,-50%);}
}

/* Card furniture at phone densities.

   The tile carries four things pinned to its corners — a discount badge, a
   wishlist heart, a quick-view eye and an add button — and they were all sized
   for a desktop tile. On a 360px phone two up leaves about 150px of card, and
   the eye (right:55px) landed on top of the badge and clipped "47% Off" to
   "47% O". Three up leaves about 100px, where the same four controls cover the
   ring they are meant to be decorating. So the furniture thins out as the grid
   gets denser. */
@media(max-width:560px){
  /* Two up: the eye drops to the free corner instead of sharing the top edge
     with the badge. */
  .plp-grid.cols-2 .product-card__qv,
  .plp-grid--default .product-card__qv{
    top:auto;bottom:11px;left:11px;right:auto;transform:none;
  }

  /* Three up: the eye goes entirely. Tapping the card already opens the
     product, and at this size the eye cost more of the photograph than the
     shortcut was worth. What remains shrinks so the ring stays the largest
     thing on the tile. */
  .plp-grid.cols-4 .product-card__qv{display:none;}

  .plp-grid.cols-4 .product-card__badge{
    top:6px;left:6px;font-size:.52rem;padding:2px 5px;border-radius:4px;
  }

  .plp-grid.cols-4 .product-card__wish{top:6px;right:6px;width:28px;height:28px;}
  .plp-grid.cols-4 .product-card__wish svg{width:14px;height:14px;}

  /* The visual shrinks; the target does not. Opposite corners, so the two
     44px areas never meet. */
  .plp-grid.cols-4 .product-card__wish::after{
    content:"";position:absolute;top:50%;left:50%;
    width:44px;height:44px;transform:translate(-50%,-50%);
  }

  .plp-grid.cols-4 .product-card__add{width:32px;height:32px;}
  .plp-grid.cols-4 .product-card__add svg{width:13px;height:13px;}
  .plp-grid.cols-4 .product-card__add::after{
    content:"";position:absolute;top:50%;left:50%;
    width:44px;height:44px;transform:translate(-50%,-50%);
  }
  .plp-grid.cols-4 .product-card__rail{padding:0 6px 6px;}
}

/* list-view add button (hidden in grid) */
.product-card__add2{display:none;align-items:center;justify-content:center;gap:7px;align-self:flex-start;height:42px;padding:0 22px;border:1px solid var(--ink);background:#fff;color:var(--ink);border-radius:9px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.74rem;letter-spacing:.06em;text-transform:uppercase;cursor:pointer;transition:.2s var(--ease);}
.product-card__add2 svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.8;}
.product-card__add2:hover{background:var(--ink);color:var(--ivory);}

/* list view layout */
.plp-grid.is-list{grid-template-columns:1fr;gap:14px;}
.plp-grid.is-list .product-card{flex-direction:row;align-items:stretch;background:#fff;border:1px solid var(--line);border-radius:16px;overflow:hidden;transition:box-shadow .25s var(--ease);}
.plp-grid.is-list .product-card:hover{box-shadow:0 18px 40px -26px rgba(28,26,23,.4);}
.plp-grid.is-list .product-card__media{width:210px;flex:none;background:#fff;border:none;border-right:1px solid var(--line);border-radius:0;}
.plp-grid.is-list .product-card:hover .product-card__media{box-shadow:none;}
.plp-grid.is-list .product-card__add{display:none;}
/* In a row the media panel already draws the divider — on its right edge on
   desktop, on its bottom edge once the card stacks — so the body must not draw
   a second one across the top of the text. */
.plp-grid.is-list .product-card__body{flex:1;justify-content:center;padding:18px 22px;gap:10px;border-top:none;}
.plp-grid.is-list .product-card__title{font-size:1rem;-webkit-line-clamp:3;min-height:0;max-width:62ch;}
.plp-grid.is-list .product-card__now{font-size:1.1rem;}
.plp-grid.is-list .product-card__add2{display:inline-flex;margin-top:4px;}
/*
 * List view stays a ROW on a phone.
 *
 * It used to stack below 560px — image on top, text beneath — which is the
 * two-up card with one per line rather than a list. The whole point of list
 * view is the scan: a column of thumbnails down the left edge with the name
 * and price beside each, so ten products can be compared without ten
 * screenfuls of photography. Stacking gave one product per screen and made the
 * view indistinguishable from the one-up grid it sits next to in the switcher.
 *
 * So the desktop arrangement is kept and only its proportions change. The
 * thumbnail goes from 210px to a share of the width, which at 390px is about
 * 128px and leaves roughly 210px for the words — enough for a two-line name,
 * a price with its struck-through original, and the button beneath.
 */
@media (max-width: 560px) {
  .plp-grid.is-list .product-card__media {
    /* A share, not a fixed width: 128px at 390 and 105 at 320, so the text
       column never falls below about 180px. */
    width: 34%;
    max-width: 150px;
    min-width: 96px;
  }

  .plp-grid.is-list .product-card__body {
    padding: 12px 14px;
    gap: 6px;
    justify-content: center;
  }

  .plp-grid.is-list .product-card__title {
    font-size: .82rem;
    line-height: 1.35;
    -webkit-line-clamp: 3;
  }

  .plp-grid.is-list .product-card__brand { font-size: .58rem; }

  .plp-grid.is-list .product-card__now { font-size: .95rem; }
  .plp-grid.is-list .product-card__was { font-size: .78rem; }

  .plp-grid.is-list .product-card__price { gap: 6px; margin: 0; padding: 0; }

  /*
   * The button keeps its own width rather than stretching: in a row the text
   * column is the measure, and a full-width button under a 210px column reads
   * as a third element rather than as this product's action.
   */
  .plp-grid.is-list .product-card__add2 {
    align-self: flex-start;
    height: 38px;
    padding: 0 14px;
    margin-top: 2px;
    font-size: .62rem;
    letter-spacing: .04em;
    gap: 6px;
    white-space: nowrap;
    position: relative;
  }

  /* 38px drawn, 46px to a thumb. Same trade as the grid and the rails. */
  .plp-grid.is-list .product-card__add2::after {
    content: "";
    position: absolute;
    inset: -4px 0;
  }

  .plp-grid.is-list .product-card__add2 svg { width: 13px; height: 13px; }

  /* The badge is over a 128px thumbnail now, not a full-width photograph. */
  .plp-grid.is-list .product-card__badge {
    top: 6px;
    left: 6px;
    font-size: .52rem;
    padding: 2px 6px;
  }

  /* The image carousel's arrows and dots have no room on a 128px panel, and
     the card is a link to the product where every photograph is available. */
  .plp-grid.is-list .product-card__nav,
  .plp-grid.is-list .product-card__dots { display: none; }
}

/*
 * Below 360px the labelled button is wider than the text column can spare, so
 * it drops the icon and keeps the words — the same trade the two-up grid makes.
 */
@media (max-width: 359px) {
  .plp-grid.is-list .product-card__add2 { padding: 0 10px; font-size: .58rem; }
  .plp-grid.is-list .product-card__add2 svg { display: none; }
  .plp-grid.is-list .product-card__body { padding: 10px 12px; }
}

/* The Quick View panel is styled in assets/css/quick-view.css.
   An earlier, abandoned quick-view lightbox left a .qv{position:fixed;opacity:0;
   visibility:hidden} block here with a .qv.open state class that nothing emitted.
   It was invisible dead weight until the real panel adopted the .qv root, at which
   point it silently hid the panel instead. Removed. */

/* ===================================================================
   ROUND 4 — store CTA, footer accordion, PDP details redesign
   =================================================================== */

/* ---- Store CTA: compact, centered, at the bottom ---- */
.gallery__cta--btm{max-width:var(--maxw);margin:30px auto 0;padding:0 22px;justify-content:center;}
.btn--sm{padding:11px 22px;font-size:.74rem;letter-spacing:.09em;}
.btn--sm svg{width:14px;height:14px;}
@media(max-width:560px){
  .gallery__cta--btm{gap:10px;}
  .gallery__cta--btm .btn-gold,.gallery__cta--btm .btn-ghost{flex:1 1 0;justify-content:center;padding:11px 12px;font-size:.68rem;letter-spacing:.06em;}
}

/* ---- Brands View All (Limited-Edition style), centered ---- */
.wb__more{text-align:center;margin-top:30px;}
.wb__viewall{justify-self:center;}

/* ---- Footer link columns: accordion on mobile ---- */
.footer__h-chev{display:none;}
@media(max-width:520px){
  .site-footer__grid{grid-template-columns:1fr;gap:0;}
  .footer__col--acc{border-top:1px solid rgba(233,220,190,.14);}
  .footer__col--acc .footer__h{display:flex;align-items:center;justify-content:space-between;cursor:pointer;margin:0;padding:16px 2px;-webkit-user-select:none;user-select:none;}
  .footer__h-chev{display:block;width:16px;height:16px;fill:none;stroke:var(--gold);stroke-width:2;transition:transform .3s var(--ease);flex:none;}
  /* `is-open`, not `open`.

     main.js toggles `is-open` on the column; these two rules waited for a bare
     `open` that nothing has ever set, so the footer sections showed a chevron
     and never expanded. The third instance of this same mismatch — after the
     drawer and its submenus — all of them left over from the static build,
     where the toggles used unprefixed state classes. */
  .footer__col--acc.is-open .footer__h-chev{transform:rotate(180deg);}
  .footer__col--acc ul{max-height:0;overflow:hidden;padding-bottom:0;transition:max-height .35s var(--ease),padding-bottom .35s var(--ease);}
  .footer__col--acc.is-open ul{max-height:520px;padding-bottom:16px;}
  .footer__col--contact{border-top:1px solid rgba(233,220,190,.14);padding-top:16px;}
}

/* ===================================================================
   PDP — premium details panel (Description / Shipping·Warranty·Returns / Payment)
   =================================================================== */
/* View More on the details panel.

   The clamp is bound to `.is-clamped`, which only script applies, and only on
   narrow screens where the panel is genuinely long. Nothing here hides content
   on its own: with JavaScript off, `.pdp-details` is unstyled by these rules
   and the button stays `hidden`. */
@media(max-width:860px){
  .pdp-details.is-clamped{max-height:520px;overflow:hidden;}
  .pdp-details.is-clamped::after{content:"";position:absolute;left:1px;right:1px;bottom:1px;height:120px;
    background:linear-gradient(to bottom,rgba(250,247,241,0),var(--ivory) 78%);pointer-events:none;border-radius:0 0 17px 17px;}
}
.pdp-more{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;margin-top:12px;
  min-height:46px;padding:0 20px;border:1px solid var(--line);border-radius:12px;background:#fff;
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:600;font-size:.85rem;color:var(--ink);cursor:pointer;
  transition:border-color .2s var(--ease),background .2s var(--ease);}
.pdp-more[hidden]{display:none;}
.pdp-more:hover{border-color:var(--gold-soft);background:var(--ivory);}
.pdp-more:focus-visible{outline:2px solid var(--gold-cta);outline-offset:2px;}
.pdp-more svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2;transition:transform .3s var(--ease);}
.pdp-more[aria-expanded="true"] svg{transform:rotate(180deg);}

.pdp-details{margin-top:26px;background:linear-gradient(180deg,#ffffff 0%,var(--ivory) 100%);border:1px solid var(--line);border-radius:18px;padding:4px 24px;box-shadow:0 24px 54px -36px rgba(28,26,23,.32);position:relative;}
.pdp-block{padding:24px 0;border-bottom:1px solid var(--line);}
.pdp-block:last-child{border-bottom:none;}
.pdp-block__h{position:relative;padding-left:15px;margin:0 0 15px;font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:1.45rem;line-height:1.15;color:var(--ink);}
.pdp-block__h::before{content:"";position:absolute;left:0;top:4px;bottom:4px;width:3px;border-radius:3px;background:linear-gradient(180deg,var(--gold),var(--gold-cta));}
/* Description typography lives with the rest of the description system above —
   a second copy here would out-specify it and silently win. */

/* accordion */
.pdp-details .pdp-acc{max-width:none;margin:0;}
.pdp-details .pdp-acc__item{border:1px solid var(--line);border-radius:13px;margin-bottom:10px;background:#fff;overflow:hidden;transition:border-color .25s var(--ease),box-shadow .25s var(--ease);}
.pdp-details .pdp-acc__item:last-child{margin-bottom:0;}
.pdp-details .pdp-acc__item[open]{border-color:var(--gold-soft);box-shadow:0 16px 32px -24px rgba(28,26,23,.45);}
.pdp-details .pdp-acc__item summary{list-style:none;display:flex;align-items:center;gap:13px;padding:14px 16px;cursor:pointer;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:600;font-size:.95rem;color:var(--ink);}
.pdp-details .pdp-acc__item summary::-webkit-details-marker{display:none;}
.pdp-acc__ic{width:36px;height:36px;flex:none;border-radius:50%;background:var(--pearl);display:flex;align-items:center;justify-content:center;color:var(--gold-cta);transition:.25s var(--ease);}
.pdp-acc__ic svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.7;}
.pdp-acc__label{flex:1;}
.pdp-details .pdp-acc__item[open] .pdp-acc__ic{background:linear-gradient(135deg,var(--gold),var(--gold-cta));color:#fff;}
.pdp-details .pdp-acc__chev{width:18px;height:18px;flex:none;fill:none;stroke:var(--muted);stroke-width:2;transition:transform .3s var(--ease);}
.pdp-details .pdp-acc__item[open] .pdp-acc__chev{transform:rotate(180deg);}
.pdp-details .pdp-acc__body{padding:0 16px 16px 65px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.9rem;line-height:1.7;color:var(--muted);}
.pdp-details .pdp-acc__body p{margin:0;}
/* Policy accordions can hold lists as well; same rhythm, same markers. */
.pdp-details .pdp-acc__body ul,.pdp-details .pdp-acc__body ol{margin:8px 0 0;padding-left:20px;}
.pdp-details .pdp-acc__body li{margin:0 0 8px;}
.pdp-details .pdp-acc__body li:last-child{margin-bottom:0;}
.pdp-details .pdp-acc__body li::marker{color:var(--gold-cta);}

/* payment cards inside panel */
.pdp-details .pdp-pay__cards{display:flex;flex-direction:column;gap:10px;}
.pdp-details .pdp-pay__card{display:flex;align-items:center;gap:12px;background:#fff;border:1px solid var(--line);border-radius:12px;padding:13px 15px;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.86rem;color:var(--ink);}
.pdp-details .pdp-pay__card .pay-badge{flex:none;}
.pdp-details .pdp-pay__card svg{width:20px;height:20px;fill:none;stroke:var(--gold-cta);stroke-width:1.6;flex:none;}
.pdp-details .pdp-pay__card span:last-child{color:var(--muted);}

/* ---- "View More" collapse: REMOVED 2026-08-18, it was hiding the content ----

   This block used to read:

     @media(max-width:860px){
       .pdp-details{max-height:330px;overflow:hidden;...}
       .pdp-details.expanded{max-height:3000px;}
       .pdp-details::after{ ...fade to ivory... }
       .pdp-more{display:flex; ...}
     }

   The clamp applied UNCONDITIONALLY — it was not gated on a state class — while
   the only way out of it was `.pdp-details.expanded`. Nothing could ever add
   that class:

     * assets/js/product.js:349 looks for `document.getElementById('pdpMore')`
       and returns early when it is absent. No template has ever emitted that
       button, so initViewMore() exits before doing anything.
     * Even had the button existed, product.js:365 toggles `is-clamped`, not
       `expanded`. The script and the stylesheet disagreed on the class name, so
       the two halves of this feature could never have met.

   Measured consequence on a real product page (#2041) before removal:

     390px — panel 1087px tall, clipped to 330px, 757px cut, 40 of 51 text
             lines (78%) invisible with no control to reveal them
     768px — 885px clipped to 330px, 19 of 29 lines (66%) invisible
     1440px — unaffected

   Everything below the fold of that 330px box — the rest of the description,
   the whole Shipping/Warranty/Returns accordion and Flexible Payment Options —
   was unreachable on mobile, which is 80–90% of this store's traffic. The fade
   gradient made it look deliberate, which is why it survived: it reads as a
   working "read more" affordance rather than as content loss.

   The panel now renders at its natural height on every width, matching desktop.
   If a collapsed panel is wanted on mobile, it needs the `#pdpMore` button in
   details.php AND one agreed class name — not this stylesheet alone. Ticketed.

   `.pdp-more` styles are dropped with it: nothing emits that element, and dead
   CSS for a control that does not exist is what disguised this for so long. */

/* ===================================================================
   PDP — PREMIUM ENGRAVING ADD-ON (engraving product page)
   =================================================================== */
.pdp-price__eng{display:none;flex-basis:100%;width:100%;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.78rem;font-weight:600;color:var(--gold-cta);margin-top:3px;}
.pdp-price__eng.show{display:block;}

.pdp-engrave{margin:22px 0 6px;border:1px solid var(--gold-soft);border-radius:16px;background:linear-gradient(180deg,#fffdf8 0%,#fbf6ea 100%);overflow:hidden;box-shadow:0 20px 44px -32px rgba(182,138,62,.6);}
.pdp-engrave.is-on{border-color:var(--gold);box-shadow:0 22px 50px -28px rgba(182,138,62,.7);}
.pdp-engrave__head{width:100%;display:flex;align-items:center;gap:14px;padding:16px 18px;background:none;border:none;cursor:pointer;text-align:left;font-family:inherit;}
.pdp-engrave__ic{width:46px;height:46px;flex:none;border-radius:13px;background:linear-gradient(135deg,var(--gold),var(--gold-cta));display:flex;align-items:center;justify-content:center;color:#fff;box-shadow:0 10px 20px -8px rgba(182,138,62,.75);}
.pdp-engrave__ic svg{width:23px;height:23px;fill:none;stroke:currentColor;stroke-width:1.7;}
.pdp-engrave__lead{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;}
.pdp-engrave__title{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:1rem;color:var(--ink);letter-spacing:.01em;}
.pdp-engrave__sub{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.8rem;color:var(--muted);line-height:1.35;}
.pdp-engrave__price{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.88rem;color:var(--gold-cta);white-space:nowrap;}
.pdp-switch{position:relative;width:52px;height:30px;flex:none;border-radius:30px;background:#d9cfbb;transition:background .3s var(--ease);}
.pdp-switch__thumb{position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;background:#fff;box-shadow:0 2px 6px rgba(60,40,10,.3);transition:transform .32s var(--ease);}
.pdp-engrave.is-on .pdp-switch{background:linear-gradient(135deg,var(--gold),var(--gold-cta));}
.pdp-engrave.is-on .pdp-switch__thumb{transform:translateX(22px);}
.pdp-engrave__head:focus-visible{outline:2px solid var(--gold-cta);outline-offset:3px;border-radius:14px;}

.pdp-engrave__panel{max-height:0;opacity:0;overflow:hidden;transition:max-height .45s var(--ease),opacity .35s var(--ease);}
.pdp-engrave.is-on .pdp-engrave__panel{max-height:1100px;opacity:1;}
/* A mandatory group is open and stays open: there is no switch to close it,
   and a transition would animate on load for no reason. */
.pdp-engrave.is-required .pdp-engrave__panel{max-height:none;opacity:1;transition:none;}
.pdp-engrave.is-required .pdp-engrave__head{cursor:default;}
.pdp-engrave__badge{flex:none;align-self:center;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.62rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:#5c4310;background:var(--gold-soft);border:1px solid var(--gold);border-radius:999px;padding:5px 11px;}
.pdp-engrave__inner{padding:18px 18px 20px;border-top:1px solid var(--gold-soft);}
.pdp-engrave__preview-label{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.68rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--gold-cta);margin:16px 0 8px;}

/* live golden plaque preview */
.pdp-plaque{position:relative;margin:16px 0 20px;border-radius:12px;padding:22px 20px;min-height:98px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;text-align:center;
  background:linear-gradient(135deg,#c9a55f 0%,#ecdca6 22%,#b78d40 52%,#efd89b 78%,#c2a05b 100%);
  box-shadow:inset 0 1px 3px rgba(255,255,255,.55),inset 0 -10px 22px rgba(120,86,20,.35),0 14px 28px -16px rgba(120,86,20,.55);overflow:hidden;}
.pdp-plaque__shine{position:absolute;top:0;left:-60%;width:38%;height:100%;background:linear-gradient(100deg,transparent,rgba(255,255,255,.55),transparent);transform:skewX(-18deg);animation:plaqueShine 7s var(--ease) infinite;}
@keyframes plaqueShine{0%,70%{left:-60%;}84%{left:135%;}100%{left:135%;}}
.pdp-plaque__line{position:relative;font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;font-weight:600;font-size:1.18rem;line-height:1.25;color:#4a3611;letter-spacing:.02em;text-shadow:0 1px 0 rgba(255,255,255,.45),0 -1px 1px rgba(90,60,10,.35);word-break:break-word;max-width:100%;}
.pdp-plaque__line:empty{display:none;}
.pdp-plaque__line.is-placeholder{color:rgba(74,54,17,.5);font-style:italic;}

/* engraving fields */
.pdp-eng-fields{display:flex;flex-direction:column;gap:15px;}
.pdp-field label{display:block;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:600;font-size:.76rem;letter-spacing:.02em;color:var(--ink);margin:0 0 7px;}
.pdp-field__box{position:relative;}
.pdp-eng-input{width:100%;height:52px;box-sizing:border-box;padding:0 68px 0 16px;border:1.5px solid var(--line);border-radius:11px;background:#fff;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.95rem;color:var(--ink);transition:border-color .2s var(--ease),box-shadow .2s var(--ease);}
.pdp-eng-input::placeholder{color:#b6ac99;}
.pdp-eng-input:focus{outline:none;border-color:var(--gold-cta);box-shadow:0 0 0 4px rgba(182,138,62,.15);}
.pdp-field__count{position:absolute;right:14px;top:50%;transform:translateY(-50%);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.72rem;color:var(--muted);pointer-events:none;}
.pdp-engrave__note{display:flex;gap:9px;align-items:flex-start;margin:16px 0 0;font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.8rem;line-height:1.55;color:var(--muted);}
.pdp-engrave__note svg{width:16px;height:16px;flex:none;margin-top:2px;fill:none;stroke:var(--gold-cta);stroke-width:1.6;}

@media(max-width:560px){
  .pdp-engrave__head{padding:14px;gap:12px;}
  .pdp-engrave__ic{width:42px;height:42px;}
  .pdp-engrave__price{font-size:.8rem;}
  .pdp-engrave__inner{padding:16px 14px 18px;}
  .pdp-plaque__line{font-size:1.06rem;}
  .pdp-eng-input{height:54px;font-size:1rem;}
}


/* ---- Uploaded media: hero banners, keys video ---- */
.hero .box{display:block;}
/*
 * In flow, not absolute: the image defines the box's height, so the box is
 * exactly the shape of its artwork and there is nothing left to letterbox.
 * The width/height attributes on the tag reserve the right space before it
 * loads, so this costs no layout shift. object-fit is deliberately absent —
 * these banners carry their own headline and call to action, and any crop
 * would eventually eat a word of it.
 */
.hero .box img{display:block;width:100%;height:auto;max-width:100%;}
.keys2__banner video{width:100%;height:100%;object-fit:cover;display:block;position:absolute;inset:0;}


/* 21st Keys carousel: show the full wide key on white */
.keys2__viewport .le__media{background:#fff;}
.keys2__viewport .le__media img{object-fit:contain;padding:16px;box-sizing:border-box;}


/* ============================================================================
   GLOBAL IMAGE POLICY
   ----------------------------------------------------------------------------
   One place that decides how every image on the site is fitted to its box.

   The rule is: scale to fit, never crop. An image is scaled down until it fits
   inside its container and is centred there; the container keeps its shape so
   cards stay aligned whatever the source aspect ratio. `object-fit: cover`,
   hover zoom, negative margins and forced upscaling are all deliberately absent
   — every one of them works by pushing part of the picture outside the frame.

   Scoped to <img> so inline SVG icons and sprite glyphs are untouched.

   Anything the theme renders is already handled at its own rule above. This
   section covers markup the theme does not author: WooCommerce's default
   templates, block output, editor content on CMS pages, and any component
   added later. New markup inherits the policy without another fix.
   ========================================================================== */

/* Baseline: no image may overflow its container, and its ratio is preserved. */
img {
	max-width: 100%;
	height: auto;
}

/*
 * Reusable media box.
 *
 * Give a wrapper .stonex-media (optionally with an aspect-ratio) and any <img>
 * inside it is centred and fully visible. This is the shape every new component
 * should use rather than restating object-fit rules.
 */
.stonex-media {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	background: #fff;
}

.stonex-media > img,
.stonex-media > a > img,
.stonex-media picture,
.stonex-media picture > img {
	width: 100%;
	height: 100%;
	max-width: 100%;
	max-height: 100%;
	object-fit: contain;
	object-position: center;
	display: block;
	margin: 0 auto;
}

/* WooCommerce default templates: loops, single product, gallery, cart. */
.woocommerce ul.products li.product a img,
.woocommerce div.product div.images img,
.woocommerce-product-gallery__image img,
.woocommerce-product-gallery__wrapper img,
.woocommerce table.shop_table img,
.woocommerce .cart_item img,
.woocommerce-checkout-review-order img,
.woocommerce .related img,
.woocommerce .up-sells img,
.woocommerce .cross-sells img,
.woocommerce-mini-cart img,
.woocommerce-order-details img {
	width: 100%;
	max-width: 100%;
	max-height: 100%;
	height: auto;
	object-fit: contain;
	object-position: center;
	display: block;
	margin: 0 auto;
}

/* Editor and block output: CMS pages, blog listing and blog detail. */
.prose img,
.page-article__content img,
.entry-content img,
.wp-block-image img,
.wp-block-gallery img,
.wp-block-media-text__media img,
.wp-block-post-featured-image img,
figure.wp-caption img {
	max-width: 100%;
	max-height: 100%;
	height: auto;
	object-fit: contain;
	object-position: center;
	margin-left: auto;
	margin-right: auto;
}

/*
 * Any image dropped into a fixed-ratio box.
 *
 * A container with an aspect-ratio is the one place a stray image can still be
 * cropped, because the box refuses to grow. Kept last and deliberately loose so
 * a component added without its own rule is still safe.
 */
[style*="aspect-ratio"] > img,
[class*="__media"] > img,
[class*="__media"] > a > img,
[class*="__thumb"] > img,
[class*="__shot"] > img {
	object-fit: contain;
	object-position: center;
	max-width: 100%;
	max-height: 100%;
}

/* Transparent PNG/WebP/SVG read as floating without a ground behind them. */
.product-card__media,
.qv__img,
.pdp-thumb,
.pdp-shot {
	background: #fff;
}

@media (max-width: 640px) {
	/* Mobile: the box may be short, so cap by height and let width follow. */
	.stonex-media > img,
	.product-card__imglink img,
	.le__media img,
	.shop-cats__media img {
		max-height: 100%;
		width: auto;
		max-width: 100%;
		margin: 0 auto;
	}
}


/* ============================================================================
   ENGRAVING — switch state
   ----------------------------------------------------------------------------
   The head is a <label> wrapping a real checkbox so the choice posts with the
   form and is keyboard and screen-reader accessible. The checkbox is moved out
   of sight rather than hidden with display:none, which would remove it from the
   focus order and the accessibility tree.

   State is driven by :has() so the panel opens with JavaScript disabled; the
   .is-on class the script also sets keeps browsers without :has() working.
   ========================================================================== */

.pdp-engrave__head { position: relative; }

.pdp-engrave__check {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	border: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
}

.pdp-engrave:has(.pdp-engrave__check:checked) {
	border-color: var(--gold);
	box-shadow: 0 22px 50px -28px rgba(182, 138, 62, .7);
}

.pdp-engrave:has(.pdp-engrave__check:checked) .pdp-switch {
	background: linear-gradient(135deg, var(--gold), var(--gold-cta));
}

.pdp-engrave:has(.pdp-engrave__check:checked) .pdp-switch__thumb { transform: translateX(22px); }

.pdp-engrave:has(.pdp-engrave__check:checked) .pdp-engrave__panel {
	max-height: 1100px;
	opacity: 1;
}

/* Focus must be visible on the switch, since the input itself is off-screen. */
.pdp-engrave__check:focus-visible ~ .pdp-switch {
	outline: 2px solid var(--gold-cta);
	outline-offset: 3px;
}

.pdp-field__req { color: var(--garnet); margin-left: 2px; }

.pdp-eng-input.has-error {
	border-color: var(--garnet);
	box-shadow: 0 0 0 4px rgba(160, 40, 40, .12);
}

.pdp-engrave__error {
	display: block;
	margin: 7px 0 0;
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-size: .76rem;
	color: var(--garnet);
}

/* Engraving lines as shown in cart, drawer, checkout and order emails. */
.stonex-eng { display: block; }
.stonex-eng__line { display: block; line-height: 1.5; }
.stonex-eng__label { font-weight: 600; }
.stonex-eng__price { font-weight: 600; color: var(--gold-cta); }


/* ============================================================================
   NAV — fitting the eight top-level items
   ----------------------------------------------------------------------------
   The bar needs about 1277px to lay out at full size: 1233px of items plus the
   44px row padding. The drawer only took over at 860px, which left a ~415px
   band — tablets in landscape and smaller laptops — where the desktop nav was
   shown but could not fit.

   Because .nav-list is centred, the excess did not simply run off one edge; it
   spilled equally off both, putting the first and last items outside the bar
   entirely.

   Rather than move the drawer breakpoint up to 1280px and lose the desktop nav
   on a great many laptops, the bar tightens in two steps first and only hands
   over to the drawer once it genuinely cannot fit. The icons stay throughout —
   they are part of the design, not decoration to drop when space is tight.
   ========================================================================== */

/* ~1104px needed after this step. */
@media (max-width: 1280px) {
	.nav-list { gap: 0; }

	.nav-list > li > a,
	.nav-list > li > .nav-trigger {
		padding: 15px 11px;
		letter-spacing: .06em;
	}

	.nav-list > li > a::after,
	.nav-list > li > .nav-trigger::after { left: 11px; right: 11px; }
}

/* ~968px needed after this step. Set at 1140px, not 1120px: the step above
   needs 1133px, so a 1120px handover would leave a 13px band still spilling. */
@media (max-width: 1140px) {
	.nav-list > li > a,
	.nav-list > li > .nav-trigger {
		padding: 15px 8px;
		font-size: .72rem;
		gap: 5px;
	}

	.nav-list > li > a::after,
	.nav-list > li > .nav-trigger::after { left: 8px; right: 8px; }

	.nav-list > li > a .ni,
	.nav-list > li > .nav-trigger .ni { width: 15px; height: 15px; }

	.nav-list .chev { width: 11px; height: 11px; }
}

/*
 * Below this the eight items cannot be made to fit without shrinking the type
 * past legibility, so the drawer takes over. The search bar keeps its own
 * 860px breakpoint — losing the nav does not mean losing search.
 */
@media (max-width: 980px) {
	.nav { display: none; }
	.burger { display: flex; }
}

/*
 * Topbar: drop the left-hand promos before the delivery message degrades.
 *
 * The row holds three groups — the two left promos (~340px), the free-delivery
 * message (~330px) and the Afterpay badge (~200px). Below roughly 1040px they
 * no longer fit, and because `.promo` is the only flexible one it absorbed all
 * the loss: the message truncated to the single letter "F" with the badge
 * pressed against it.
 *
 * The delivery threshold and the payment badge are the two that influence a
 * purchase, so the left-hand pair goes first and those two keep their space.
 * Everything still hides together at 860px.
 */
@media (max-width: 1040px) {
	.topbar .tb-left { display: none; }
	.topbar .promo { justify-content: flex-start; }
}


/* ============================================================================
   VARIATION CHIPS
   ----------------------------------------------------------------------------
   Chips are built from WooCommerce's selects by assets/js/variations.js. The
   selects stay in the DOM — WooCommerce reads and writes them — but are taken
   out of sight once the chips exist. Without JavaScript the class is never
   added and the native selects render normally.
   ========================================================================== */

.stonex-has-chips table.variations,
.stonex-has-chips table.variations tbody,
.stonex-has-chips table.variations tr,
.stonex-has-chips table.variations td { display: block; width: 100%; }

.stonex-has-chips table.variations th.label { display: none; }

.stonex-has-chips table.variations select {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	border: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
}

/* The selected value reads as part of the label: "Strap: Black Silicone". */
.pdp-opt__chosen { font-weight: 500; color: var(--muted); letter-spacing: 0; }

.pdp-chip:focus-visible { outline: 2px solid var(--gold-cta); outline-offset: 2px; }

/*
 * Unavailable with the current selection.
 *
 * The design mocked this as opacity .4 with a line through it. At that opacity
 * the text sits near 2:1 against the card and is genuinely hard to read, and
 * the strikethrough compounds it — the customer cannot tell what the option
 * even was. The chip stays legible here and carries the meaning in the border
 * and colour instead; the reason is exposed to assistive tech through the
 * label the script sets.
 *
 * It also stays clickable. Choosing it re-opens the other attributes around it
 * rather than leaving a dead control, which is how a shopper actually explores
 * "I want the stainless one, what sizes come in that?".
 */
.pdp-chip.is-unavailable {
	color: var(--muted);
	background: repeating-linear-gradient(
		135deg,
		#fff,
		#fff 6px,
		rgba(28, 26, 23, .045) 6px,
		rgba(28, 26, 23, .045) 12px
	);
	border-color: var(--line);
	border-style: dashed;
}

.pdp-chip.is-unavailable:hover { border-color: var(--gold); color: var(--ink); }

.pdp-chip.is-unavailable.is-active {
	background: var(--ink);
	color: var(--ivory);
	border-style: dashed;
	border-color: var(--ink);
}

.stonex-has-chips .reset_variations {
	display: inline-block;
	margin: 2px 0 14px;
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-size: .74rem;
	font-weight: 600;
	letter-spacing: .04em;
	color: var(--muted);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.stonex-has-chips .reset_variations:hover { color: var(--gold-cta); }

/* =========================================================================
   VARIABLE PRODUCTS — one buy row for every product type
   =========================================================================
   A variable product is served by WooCommerce's form, not the theme's buy
   template, so without these rules it rendered a browser number spinner and a
   default submit button next to the simple product's 52px gold row. Half the
   catalogue is variable, so this was the storefront's most visible
   inconsistency. The markup now matches (see
   woocommerce/single-product/add-to-cart/variation-add-to-cart-button.php);
   these rules cover what WooCommerce still puts on the page around it.
   ========================================================================= */

/* The native selects stay in the DOM for wc-add-to-cart-variation.js and for
   no-JS visitors; the chips replace them visually only once JS has built them.
   Hiding the table outright would break both. */
.stonex-has-chips table.variations { display: none; }

.variations_form .woocommerce-variation-add-to-cart { display: block; }

/* Match the theme's own spacing between the price block and the buy row. */
.variations_form .single_variation_wrap { margin-top: 4px; }

/* WooCommerce prints the variation's own price and stock here. The price is
   already shown above, so only the availability line earns its place. */
.woocommerce-variation-price { display: none; }

.woocommerce-variation-availability p {
	margin: 0 0 12px;
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-size: .82rem;
	font-weight: 600;
	color: var(--muted);
}

/* Reset the browser spinner: the stepper buttons are the affordance, and the
   spinner would sit on top of them. `appearance` is still prefixed for
   WebKit, which has not aliased the standard property for number inputs. */
.pdp-qty__in::-webkit-outer-spin-button,
.pdp-qty__in::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pdp-qty__in { -moz-appearance: textfield; appearance: textfield; }

/* A readonly quantity (sold individually) renders no buttons, so the input
   must not keep the width it had between them. */
.pdp-qty--fixed .pdp-qty__in { border-left: none; border-right: none; width: 56px; }

/* Every control in the buy column is the same height. This is the rule the
   design asked for and the one the old markup could not honour, because
   WooCommerce's button sized itself from its padding. */
.pdp-buy .single_add_to_cart_button { height: 52px; flex: 1; justify-content: center; }

/*
 * Disabled = a variation has not been chosen yet.
 *
 * WooCommerce puts `wc-variation-selection-needed` on the wrapper and disables
 * the button. Greying it out alone reads as "sold out"; keeping the gold but
 * muting it reads as "not yet", which is what it means.
 */
.woocommerce-variation-add-to-cart-disabled .pdp-atc,
.woocommerce-variation-add-to-cart-disabled .pdp-buynow,
.pdp-buynow:disabled,
.pdp-atc:disabled,
.pdp-buynow:disabled {
	opacity: .45;
	cursor: not-allowed;
	box-shadow: none;
	transform: none;
}

.woocommerce-variation-add-to-cart-disabled .pdp-atc:hover,
.pdp-buynow:disabled:hover,
.pdp-atc:disabled:hover,
.pdp-buynow:disabled:hover { transform: none; }

/* Loading state, set by cart.js while the add is in flight. */
.pdp-atc.is-loading,
.pdp-buynow.is-loading { pointer-events: none; opacity: .75; }

.pdp-atc:focus-visible,
.pdp-buynow:focus-visible,
.pdp-qty__btn:focus-visible {
	outline: 2px solid var(--gold-cta);
	outline-offset: 2px;
}

.pdp-buynow:active { transform: translateY(0); }

/*
 * Options highlighted after a sticky-bar tap with nothing chosen.
 *
 * Animates box-shadow rather than a border so the chips do not shift by a
 * pixel — this fires next to the buy button, where reflow is most visible.
 */
.pdp-opt.is-attention {
	animation: stonex-attention 1.2s var(--ease);
	border-radius: 12px;
}

@keyframes stonex-attention {
	0%, 100% { box-shadow: 0 0 0 0 rgba(194, 161, 91, 0); }
	30%      { box-shadow: 0 0 0 6px rgba(194, 161, 91, .28); }
}

@media (prefers-reduced-motion: reduce) {
	.pdp-opt.is-attention { animation: none; box-shadow: 0 0 0 3px rgba(194, 161, 91, .35); }
}

/* WooCommerce's out-of-stock line inside the form. */
.variations_form .stock.out-of-stock {
	margin: 0 0 16px;
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-weight: 600;
	color: #a3341f;
}

/* =========================================================================
   PDP RAILS — "Customers Also Bought" / "Recently Viewed"
   =========================================================================
   The markup now emits .le__track / .le__item (see
   template-parts/product/rails.php), so the slider rules defined for the
   homepage rails apply here too. What is left is the arrow state and the card
   equalisation the PDP viewport needs.
   ========================================================================= */

/* The track is the scroll container, so its own padding cannot show the card
   shadows at the edges — this bleeds them back into the section gutter. */
.pdp-rel__vp { margin: 0 -2px; }

/*
 * Equal heights.
 *
 * .le__item is a flex box; making the card fill it is what stops a two-line
 * product title from leaving its neighbour's Add to Cart button floating
 * halfway up the row — the ragged-button problem in the rails.
 */
.pdp-rel__vp .le__item > * { width: 100%; }

/*
 * Push the CTA to the bottom of whatever height the row settles on, so every
 * button in a rail sits on one line regardless of title length.
 *
 * `__add2` is the visible button in the card body. (`__add` is the absolutely
 * positioned overlay revealed on hover over the image — it is taken out of
 * flow and must not be touched here.) The body has to stretch first, or there
 * is no free space for `margin-top: auto` to consume.
 */
.pdp-rel__vp .le__item .product-card__body { flex: 1; }
.pdp-rel__vp .le__item .product-card__add2 { margin-top: auto; }

/*
 * Arrow states.
 *
 * Disabled rather than hidden at the ends: a control that vanishes shifts the
 * heading row and makes the remaining arrow jump sideways.
 */
.pdp-rel__navs .keys2__nav:disabled {
	opacity: .3;
	cursor: not-allowed;
	pointer-events: none;
}

.pdp-rel__navs .keys2__nav:focus-visible {
	outline: 2px solid var(--gold-cta);
	outline-offset: 2px;
}

/*
 * Nothing to scroll — hide the arrows entirely.
 *
 * A rail holding four cards on a wide desktop has no overflow, and two dead
 * arrows next to it read as a broken slider rather than a full one. The class
 * is set by initRails() in product.js, which measures the real overflow.
 */
.pdp-rel:not(.has-overflow) .pdp-rel__navs { display: none; }

/*
 * Touch devices get the swipe, not the arrows.
 *
 * Coarse pointers already drag the track directly, and the arrows cost 2×44px
 * of the heading row on the screens with least of it to spare.
 */
@media (hover: none) and (pointer: coarse) {
	.pdp-rel__navs { display: none; }
}

/* =========================================================================
   PRICE BLOCK — was / save
   =========================================================================
   `.pdp-price__was` and `.pdp-price__off` had NO rules at all, so a discounted
   product rendered "$2,260.00 $4,346.96 Save 48%" as three near-identical runs
   of text. The old price was not struck through, the saving was not a badge,
   and nothing told the eye which number it was being asked to pay.
   ========================================================================= */

.pdp-price__was {
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-size: 1rem;
	font-weight: 500;
	color: var(--muted);
	/* A line, not a colour change: colour alone does not survive greyscale or
	   colour-blind vision, and "old price" is not a decorative distinction. */
	text-decoration: line-through;
	text-decoration-thickness: 1.5px;
	text-decoration-color: rgba(111, 104, 92, .6);
}

/*
 * The saving reads as a badge, matching the "44% Off" flag already used on
 * product cards — the same fact should not have two visual languages on one
 * storefront.
 *
 * Garnet rather than gold: gold is this theme's "buy" colour and belongs to the
 * Add to Cart button. A discount competing with the CTA for the same colour is
 * how a page ends up with two primary actions.
 */
.pdp-price__off {
	display: inline-flex;
	align-items: center;
	padding: 4px 9px;
	border-radius: 6px;
	background: var(--garnet);
	color: #fff;
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-size: .68rem;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	white-space: nowrap;
	/* The -3px nudge here compensated for Cormorant sitting high in its line
	   box. DM Sans does not, so with the price now set in it the badge aligns
	   on the shared baseline without help. */
}

/* Tabular figures stop the price shifting horizontally when a variation swaps
   $607.83 for $1,216.52 — a jitter directly beside the buy button. */
.pdp-price__now,
.pdp-price__was { font-variant-numeric: tabular-nums; }

/* Wrap rather than overflow when a long "was" price and badge follow a
   four-figure price on a narrow phone. */
.pdp-price { flex-wrap: wrap; row-gap: 6px; }

/* =========================================================================
   RAIL OVERFLOW — the classic flex min-width blowout
   =========================================================================
   .pdp-related is a flex column, so each .pdp-rel is a flex item. A flex item
   defaults to min-width:auto, meaning it refuses to shrink below its content —
   and its content is a track holding every card at full width. So instead of
   the track scrolling inside a fixed-width rail, the rail grew to the width of
   all its cards and pushed the whole section past the viewport, dragging the
   "Recently Viewed" heading off the left edge with it.

   overflow-x on .le__track can only do its job once an ancestor actually
   constrains the width, which min-width:0 is what restores.
   ========================================================================= */

.pdp-related,
.pdp-rel,
.pdp-rel__vp { min-width: 0; }

/* Belt and braces: the section must never be the thing that makes the page
   scroll sideways, whatever a future card is allowed to contain. */
.pdp-related { max-width: 100%; overflow: hidden; }

/* The track is the only thing permitted to scroll horizontally, and it needs
   its own padding back since the section now clips. */
.pdp-rel__vp { overflow: visible; }

/* =========================================================================
   TRUST BAND — fits however many claims are configured
   =========================================================================
   Fixed at repeat(5,1fr). Each item is conditional on its setting, so a store
   with three configured (as here) got three items crammed into the left 60% of
   a black band with two empty columns trailing off to the right.
   ========================================================================= */

.pdp-builders__inner {
	grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
	justify-content: center;
}

/* =========================================================================
   REVIEWS
   =========================================================================
   Had no styling whatsoever — WooCommerce's default comment form rendered raw
   on a luxury PDP: browser-chrome inputs, a grey Submit button, unstyled star
   links. It is the last thing on the page a hesitant buyer reads, so it was
   undoing the trust the rest of the template builds.

   Styled rather than replaced: WooCommerce's markup carries comment
   moderation, verified-owner badges and the rating aggregation that feeds the
   Product schema, none of which is worth reimplementing for a skin.
   ========================================================================= */

.pdp-reviews { background: var(--pearl); margin-top: 60px; padding: 54px 0; }
.pdp-reviews__inner { max-width: var(--maxw); margin: 0 auto; padding: 0 22px; }

.pdp-reviews #reviews { display: flex; flex-direction: column; gap: 26px; }

.pdp-reviews .woocommerce-Reviews-title,
.pdp-reviews #reply-title {
	font-family:"Cormorant Garamond","Cormorant Fallback",Georgia,"Times New Roman",serif;
	font-weight: 600;
	font-size: clamp(1.5rem, 2.6vw, 2rem);
	color: var(--ink);
	margin: 0;
}

.pdp-reviews .woocommerce-noreviews,
.pdp-reviews .comment-notes {
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-size: .9rem;
	color: var(--muted);
	margin: 0;
}

/* ---- existing reviews ---- */
.pdp-reviews .commentlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.pdp-reviews .commentlist .review { background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 18px 20px; }
.pdp-reviews .comment_container { display: flex; gap: 14px; align-items: flex-start; }
.pdp-reviews .comment_container img.avatar { width: 44px; height: 44px; border-radius: 50%; flex: none; }
.pdp-reviews .comment-text { flex: 1; min-width: 0; }
.pdp-reviews .woocommerce-review__author { font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif; font-weight: 700; font-size: .9rem; color: var(--ink); }
.pdp-reviews .woocommerce-review__published-date { font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif; font-size: .78rem; color: var(--muted); }
.pdp-reviews .description p { font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif; font-size: .92rem; line-height: 1.7; color: var(--muted); margin: 8px 0 0; }

/* ---- review form ---- */
.pdp-reviews .comment-respond { background: #fff; border: 1px solid var(--line); border-radius: 18px; padding: 26px; box-shadow: 0 24px 54px -40px rgba(28, 26, 23, .3); }
.pdp-reviews .comment-form { display: flex; flex-direction: column; gap: 16px; margin: 0; }
.pdp-reviews .comment-form p { margin: 0; display: flex; flex-direction: column; gap: 7px; }

.pdp-reviews .comment-form label {
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-size: .8rem;
	font-weight: 600;
	letter-spacing: .03em;
	color: var(--ink);
}

.pdp-reviews .comment-form input[type="text"],
.pdp-reviews .comment-form input[type="email"],
.pdp-reviews .comment-form textarea {
	width: 100%;
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-size: .92rem;
	color: var(--ink);
	background: #fff;
	border: 1px solid var(--line);
	border-radius: 10px;
	padding: 12px 14px;
	transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}

.pdp-reviews .comment-form textarea { min-height: 130px; resize: vertical; }

.pdp-reviews .comment-form input:focus,
.pdp-reviews .comment-form textarea:focus {
	outline: none;
	border-color: var(--gold-cta);
	box-shadow: 0 0 0 3px rgba(182, 138, 62, .16);
}

/* The consent checkbox is the one row that is not a stacked label + field. */
.pdp-reviews .comment-form-cookies-consent { flex-direction: row; align-items: center; gap: 9px; }
.pdp-reviews .comment-form-cookies-consent label { font-weight: 400; color: var(--muted); }

/* Stars: WooCommerce renders them as links, which arrive underlined and blue. */
.pdp-reviews .comment-form-rating .stars a { color: var(--gold-cta); text-decoration: none; }
.pdp-reviews .comment-form-rating select { display: none; }

.pdp-reviews .form-submit { margin: 0; }

.pdp-reviews .form-submit input[type="submit"] {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	height: 52px;
	padding: 0 34px;
	border: none;
	border-radius: 9px;
	background: linear-gradient(135deg, var(--gold), var(--gold-cta));
	color: var(--ink);
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-weight: 700;
    font-size: .78rem;
	letter-spacing: .1em;
	text-transform: uppercase;
	cursor: pointer;
	box-shadow: 0 14px 32px -14px rgba(194, 161, 91, .5);
	transition: transform .25s var(--ease), box-shadow .25s var(--ease);
}

.pdp-reviews .form-submit input[type="submit"]:hover { transform: translateY(-2px); }
.pdp-reviews .form-submit input[type="submit"]:focus-visible { outline: 2px solid var(--gold-cta); outline-offset: 2px; }

@media (max-width: 600px) {
	.pdp-reviews { padding: 38px 0; }
	.pdp-reviews .comment-respond { padding: 20px 16px; }
	/* Full-width CTA on the screens where a 34px side padding wastes the row. */
	.pdp-reviews .form-submit input[type="submit"] { width: 100%; }
}

/* ---------------------------------------------------------------------------
 * Wishlist: the working heart, and the guest account prompt.
 * ------------------------------------------------------------------------- */

/* While a save is in flight the icon is replaced by a ring, not covered by one.
   That is what lets one treatment serve both surfaces: on a card the heart is
   the button's only content so the ring lands dead centre, and on the product
   page the button is an icon beside the word "Wishlist", where the label stays
   put while the icon spins.

   `stonex-spin` is defined in cart.css, which ships wherever WooCommerce does —
   and a wishlist heart cannot exist without WooCommerce. */
[data-stonex-wishlist].is-busy>svg{display:none;}
[data-stonex-wishlist]:disabled{cursor:default;}
/* The button must not grow or lift while it is working: a control that
   animates under a cursor that is already on it reads as a second click
   having registered. */
[data-stonex-wishlist].is-busy:hover{transform:none;}
.wl-spin{width:16px;height:16px;flex:none;box-sizing:border-box;
  border:2px solid currentColor;border-right-color:transparent;border-radius:50%;
  animation:stonex-spin .6s linear infinite;}

/* The prompt is shown once per browsing session, after a logged-out visitor's
   first save.

   It used to sit at the bottom of the page beside the toast, on the reasoning
   that the piece was already saved and interrupting the shopper to say so was
   worse than the problem. On a tall collection page that put it below the fold
   of attention, a second after the click that caused it, and it was missed. It
   is a dialog now — centred, over a dimmed and blurred page, focus moved into
   it and trapped, dismissable four ways.

   Once per session is what keeps that from being hostile: the offer is made at
   the one moment it is useful, and every later save is a quiet toast. */
.wl-prompt{position:fixed;inset:0;z-index:1200;display:flex;align-items:center;justify-content:center;
  padding:20px;opacity:0;visibility:hidden;transition:opacity .26s var(--ease),visibility .26s;}
.wl-prompt[hidden]{display:none;}
.wl-prompt.is-on{opacity:1;visibility:visible;}
.wl-prompt__scrim{position:absolute;inset:0;background:rgba(28,26,23,.52);
  -webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);}
/* `dvh` so the panel is measured against the viewport the browser's own chrome
   leaves behind, not the one it occupies — on a phone `vh` puts the foot of a
   full-height panel under the address bar. The `vh` line is the fallback for
   browsers that do not know the unit and must stay directly above it. */
.wl-prompt__panel{position:relative;width:min(420px,100%);
  max-height:calc(100vh - 40px);max-height:calc(100dvh - 40px);overflow-y:auto;
  background:var(--ink);color:var(--ivory);border-radius:16px;
  padding:28px 24px 22px;box-shadow:0 30px 80px -20px rgba(0,0,0,.7);
  transform:translateY(14px) scale(.985);transition:transform .3s var(--ease);}
.wl-prompt.is-on .wl-prompt__panel{transform:none;}
.wl-prompt__panel:focus{outline:none;}
.wl-prompt__panel:focus-visible{outline:2px solid var(--gold-soft);outline-offset:3px;}
.wl-prompt__title{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:1.02rem;margin:0 0 8px;color:var(--ivory);
  /* Clears the 44px close control in the corner. */
  padding-right:38px;}
.wl-prompt__body{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.83rem;line-height:1.55;margin:0 0 18px;color:rgba(250,247,241,.78);}
.wl-prompt__actions{display:flex;flex-wrap:wrap;gap:8px;}
.wl-prompt__btn{display:inline-flex;align-items:center;justify-content:center;min-height:44px;
  padding:0 16px;border-radius:8px;border:1px solid rgba(250,247,241,.34);background:transparent;
  color:var(--ivory);font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:600;font-size:.76rem;letter-spacing:.04em;
  text-decoration:none;cursor:pointer;transition:background .2s var(--ease),border-color .2s var(--ease);}
.wl-prompt__btn:hover{background:rgba(250,247,241,.12);}
.wl-prompt__btn--primary{background:var(--gold-cta);border-color:var(--gold-cta);color:var(--ink);}
.wl-prompt__btn--primary:hover{background:var(--gold);border-color:var(--gold);}
.wl-prompt__btn--quiet{border-color:transparent;color:rgba(250,247,241,.72);}
.wl-prompt__close{position:absolute;top:8px;right:8px;width:44px;height:44px;display:flex;
  align-items:center;justify-content:center;background:transparent;border:0;color:rgba(250,247,241,.72);
  font-size:1.3rem;line-height:1;cursor:pointer;border-radius:8px;}
.wl-prompt__close:hover{color:var(--ivory);}
.wl-prompt__btn:focus-visible,.wl-prompt__close:focus-visible{outline:2px solid var(--gold-soft);outline-offset:2px;}

/* Phones: the same centred dialog, with the actions stacked.

   Wrapped, the three controls broke two-and-one at 390px and left "Continue
   Shopping" sitting alone under a half-empty row. Full width each is both
   tidier and a larger target, which is the trade this catalogue's traffic
   wants. The safe-area inset keeps the last button clear of a home indicator. */
@media(max-width:430px){
  .wl-prompt{padding:16px;}
  .wl-prompt__panel{padding:24px 18px calc(18px + env(safe-area-inset-bottom));border-radius:14px;}
  .wl-prompt__actions{flex-direction:column;align-items:stretch;}
  .wl-prompt__btn{width:100%;}
}

@media(prefers-reduced-motion:reduce){
  .wl-prompt{transition:opacity .01s linear,visibility .01s;}
  .wl-prompt__panel{transition:none;transform:none;}
  .wl-spin{animation-duration:1.6s;}
}

/* Wishlist page: the same offer, inline above the saved pieces. */
.wl-banner{display:flex;flex-wrap:wrap;align-items:center;gap:14px;justify-content:space-between;
  background:var(--pearl);border:1px solid var(--line);border-radius:12px;padding:18px 20px;margin:0 0 26px;}
.wl-banner__text{flex:1 1 260px;min-width:0;}
.wl-banner__title{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:700;font-size:.94rem;margin:0 0 4px;color:var(--ink);}
.wl-banner__body{font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:.82rem;line-height:1.5;margin:0;color:var(--muted);}
.wl-banner__actions{display:flex;flex-wrap:wrap;gap:8px;}
.wl-banner__btn{display:inline-flex;align-items:center;justify-content:center;min-height:44px;padding:0 18px;
  border-radius:8px;border:1px solid var(--ink);background:transparent;color:var(--ink);
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-weight:600;font-size:.76rem;letter-spacing:.04em;text-decoration:none;
  transition:background .2s var(--ease),color .2s var(--ease);}
.wl-banner__btn:hover{background:var(--ink);color:var(--ivory);}
.wl-banner__btn--primary{background:var(--gold-cta);border-color:var(--gold-cta);color:var(--ink);}
.wl-banner__btn--primary:hover{background:var(--gold);border-color:var(--gold);color:var(--ink);}
.wl-banner__btn:focus-visible{outline:2px solid var(--gold-cta);outline-offset:2px;}

@media(max-width:560px){
  .wl-prompt{left:16px;right:16px;width:auto;transform:translateY(14px);}
  .wl-prompt.is-on{transform:translateY(0);}
  .wl-prompt__actions,.wl-banner__actions{width:100%;}
  .wl-prompt__btn,.wl-banner__btn{flex:1 1 auto;}
}

/* ---------------------------------------------------------------------------
 * Wishlist heart: 44px touch target without changing the visual circle.
 *
 * The heart is drawn at 36px (40px inside a homepage rail) to sit correctly in
 * the card, which is below the 44px minimum for a touch control. Rather than
 * resize the button — which would shift every card that has one — the hit area
 * is extended with a centred pseudo-element. Nothing moves, and a finger or a
 * pointer anywhere in the 44px square activates it.
 * ------------------------------------------------------------------------- */
.product-card__wish::after,
.pdp-sec[data-stonex-wishlist]::after{content:"";position:absolute;top:50%;left:50%;
  width:44px;height:44px;min-width:100%;min-height:100%;transform:translate(-50%,-50%);
  border-radius:50%;}
.pdp-sec[data-stonex-wishlist]{position:relative;}

/* ---------------------------------------------------------------------------
 * Sale prices rendered by WooCommerce itself.
 *
 * The theme's own card/PDP spans (`__now` / `__was`) are styled above. Anywhere
 * core builds the markup instead — variable products, the price a variation
 * switch injects, the sticky bar, cart and checkout — it emits <ins> and <del>,
 * which arrive unstyled: browsers underline <ins>, and the struck original
 * renders at the same size and weight as the price being paid.
 *
 * Scoped with :has() to elements that actually wrap a WooCommerce price, so
 * genuine editorial <ins>/<del> in blog content is untouched. Sizing is in `em`
 * so one rule serves a 16px card and a 35px product page alike.
 * ------------------------------------------------------------------------- */
ins:has(> .woocommerce-Price-amount),
ins:has(> bdi > .woocommerce-Price-amount){
  text-decoration:none;background:transparent;color:inherit;font-weight:inherit;}

del:has(> .woocommerce-Price-amount),
del:has(> bdi > .woocommerce-Price-amount){
  text-decoration:line-through;color:var(--muted);font-size:.72em;font-weight:400;
  margin-left:.5em;white-space:nowrap;}

/* The card helper wraps core's markup inside __now for variable products, so
   the struck figure must not inherit the bold card price size. */
.product-card__now del:has(> .woocommerce-Price-amount),
.product-card__now del:has(> bdi > .woocommerce-Price-amount){font-size:.8em;}

/* Keep both figures on one line where there is room, and wrap sale-first when
   there is not — the order stays correct either way because it is DOM order. */
.product-card__price,.pdp-price,.qv__price{flex-wrap:wrap;}

/* Listing chrome: Refine drawer, busy states, search header ---------------- */

/* The button lives in the toolbar, before the count, on both listings. It is
   only rendered when the listing actually has facets, so a collection — which
   has none today — gets the row it has always had. */
.plp-refine__btn{
  display:inline-flex;align-items:center;gap:8px;height:var(--plp-ctl-h);padding:0 14px;
  border:1px solid var(--line);border-radius:10px;background:#fff;color:var(--ink);
  font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
  font-size:.8rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;cursor:pointer;
  transition:border-color .18s var(--ease),background-color .18s var(--ease);
}
.plp-refine__btn:hover{border-color:var(--gold-cta);}
.plp-refine__btn:focus-visible{outline:2px solid var(--gold-cta);outline-offset:2px;}
.plp-refine__btn svg{width:17px;height:17px;flex:none;fill:none;stroke:currentColor;stroke-width:1.8;}
/* The applied count. Once the drawer is shut this is the only thing on the
   page that says the grid is filtered, so it is a filled pill rather than a
   quiet number. */
.plp-refine__badge{
  display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;
  border-radius:999px;background:var(--ink);color:var(--ivory);font-size:.7rem;font-weight:700;
  letter-spacing:0;font-variant-numeric:tabular-nums;
}

/* Drawer -------------------------------------------------------------------
   Rendered `hidden`, revealed by assets/js/collection.js. Between those two
   moments nothing paints — which is the point: the facets must never flash
   above the products on the way to being a drawer. With scripting off the
   <noscript> block in template-parts/listing/refine.php drops it back into the
   flow as a plain block instead. */
.plp-refine{position:fixed;inset:0;z-index:1200;pointer-events:none;}
.plp-refine.is-ready{visibility:hidden;}
.plp-refine.is-open{visibility:visible;pointer-events:auto;}

.plp-refine__scrim{
  position:absolute;inset:0;background:rgba(28,26,23,.42);
  opacity:0;transition:opacity .25s var(--ease);
}
.plp-refine.is-open .plp-refine__scrim{opacity:1;}

.plp-refine__panel{
  position:absolute;top:0;right:0;height:100%;width:min(420px,92vw);
  display:flex;flex-direction:column;background:var(--ivory);
  box-shadow:-24px 0 60px -30px rgba(28,26,23,.55);
  transform:translateX(100%);transition:transform .28s var(--ease);
}
.plp-refine.is-open .plp-refine__panel{transform:translateX(0);}
.plp-refine__panel:focus{outline:none;}

@media(prefers-reduced-motion:reduce){
  .plp-refine__panel,.plp-refine__scrim{transition:none;}
}

.plp-refine__head{
  flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:18px 20px;border-bottom:1px solid var(--line);
}
.plp-refine__title{margin:0;font-size:1rem;letter-spacing:.08em;text-transform:uppercase;}
.plp-refine__close{
  flex:none;width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--line);border-radius:10px;background:#fff;color:var(--ink);cursor:pointer;
}
.plp-refine__close:hover{border-color:var(--gold-cta);}
.plp-refine__close:focus-visible{outline:2px solid var(--gold-cta);outline-offset:2px;}
.plp-refine__close svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;}

/* The scrolling part. `overscroll-behavior` stops a flick at the end of the
   filter list from carrying on into the page behind the drawer. */
.plp-refine__body{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;padding:4px 20px;}

.plp-refine__foot{
  flex:none;display:flex;align-items:center;gap:12px;padding:14px 20px;
  border-top:1px solid var(--line);background:var(--ivory);
  padding-bottom:calc(14px + env(safe-area-inset-bottom));
}
.plp-refine__clear{
  color:var(--muted);font-size:.85rem;text-decoration:underline;text-underline-offset:3px;
  min-height:44px;display:inline-flex;align-items:center;
}
.plp-refine__clear:hover{color:var(--ink);}
.plp-refine__done{margin-left:auto;min-height:46px;padding:0 22px;}

/* Scroll lock while the drawer is modal. */
html.has-drawer,html.has-drawer body{overflow:hidden;}

/* Busy states --------------------------------------------------------------
   A filter, a sort and a page link are all real navigations here, exactly as
   they are on a collection. What this adds is the feedback View More already
   gives: the grid stays where it is, dims, and reports itself busy. */
.plp-grid.is-updating{opacity:.45;transition:opacity .15s var(--ease);pointer-events:none;}
.plp-grid.is-replacing{opacity:1;pointer-events:none;}
@media(prefers-reduced-motion:reduce){.plp-grid.is-updating{transition:none;}}

/* Search header ------------------------------------------------------------
   One line and nothing else. Every pixel above the first row of results is a
   pixel of the answer the customer already asked for. */
/* No gradient panel and no rule: this is one line of text introducing the
   grid below it, not a banner. The collection hero keeps both. It also gets
   the page gutter directly, because unlike .plp-hero it has no __inner — which
   is why the old search heading sat hard against the left edge of the
   viewport. */
.plp-hero--search{
  max-width:var(--maxw);margin:0 auto;padding:26px 22px 2px;
  background:none;border-bottom:0;text-align:left;
}
.plp-hero--search .plp-hero__title{
  margin:0;font-size:clamp(1.5rem,1.1rem + 1.5vw,2.15rem);line-height:1.14;
  text-wrap:balance;overflow-wrap:anywhere;
}
.plp--search .plp-toolbar{padding-top:14px;}

@media(max-width:640px){
  .plp-hero--search{padding:16px 16px 0;}
  .plp-refine__btn{padding:0 11px;gap:6px;font-size:.72rem;}
  .plp-refine__panel{width:100%;}
  /* Refine and the count share the first row; sort and density take the
     second. Neither row can overflow, because the count wraps and the sort
     select is width-constrained rather than intrinsically sized. */
  .plp-toolbar{gap:10px;row-gap:10px;}
  .plp-count{font-size:.82rem;}
}

/* ===================================================================
   Touch targets and readable text on small screens
   ===================================================================

   Everything in this block came out of a measured sweep of every template at
   320/360/375/390/412/430/768px. The findings clustered on a handful of SHARED
   components — the header actions, the footer contact links, the drawer, the
   product card's icon buttons, the "View All" links — so they are fixed once,
   here, rather than per page.

   Scope: `(max-width:900px), (hover:none)`. A pointer that cannot hover is a
   finger whatever the width, and a window under 900px is where the mobile
   layout is in play. A desktop above 900px with a mouse sees none of this, so
   the approved desktop design is untouched.

   Technique: where a control's size IS the design — an icon circle, an
   underlined link — the hit area is extended with a transparent `::after`
   instead of by resizing the element. Nothing below moves a pixel of layout
   that a person can see; it only makes the box a thumb can hit as big as the
   box their eye is aiming at.

   44px is Apple's HIG minimum; Material asks 48. 44 is the floor here because
   it is what the smaller of the two guidelines requires and it fits the
   existing rhythm.
   =================================================================== */

@media (max-width: 900px), (hover: none) {

  /* ---- header ------------------------------------------------------ */

  /* The logo art stays 150x38; only the link's box grows around it. */
  .logo{min-height:44px;}

  /* 40x37 measured, and it was the WIDTH that was short — the mobile header
     already pins `.act` to `min-width:40px`. Both axes are stated so the fix
     does not depend on which one happens to be smaller at a given width. The
     icon and its optional label are already centred, so the row does not move:
     the bar's own padding is larger than the growth. */
  .act{min-width:44px;min-height:44px;justify-content:center;}

  .burger{min-width:44px;min-height:44px;}

  /* ---- mobile drawer ----------------------------------------------- */

  /* Had no rule at all and rendered at 34x24 — the control a customer uses to
     get OUT of the menu was the smallest thing in it. */
  #drawerClose{
    width:44px;height:44px;flex:none;
    display:inline-flex;align-items:center;justify-content:center;
    background:none;border:0;color:inherit;cursor:pointer;border-radius:10px;
  }

  /* The field, not just the box around it, so a tap anywhere on the row lands
     in the input. The wrapper's padding shrinks by the same amount the input
     grows, so the search row keeps its height. */
  .drawer-search{padding:0 16px;}
  .drawer-search input{min-height:44px;}

  /* ---- footer ------------------------------------------------------ */

  /* 23px links, stacked 13px apart: two lines of contact detail in a strip
     smaller than one fingertip. Centring inside 44px costs the footer nothing
     because the existing margin absorbs it. */
  .footer__contact{min-height:44px;margin-bottom:6px;}

  /* Accordion links sit 11px apart at 18px tall. Padding, not min-height, so
     the gap closes as the rows grow and the open panel keeps its height. */
  .footer__col ul{gap:0;}
  .footer__col ul a{display:flex;align-items:center;min-height:44px;}

  /* ---- "View All" links -------------------------------------------- */

  /*
   * These are 23px tall because the underline is drawn on the element itself,
   * so growing the box would move the rule away from the text. The hit area is
   * extended around them instead, which is invisible and leaves the design
   * exactly as approved.
   */
  .le__viewall,
  .wb__viewall,
  .shop-cats__viewall,
  .keys2__viewall{position:relative;}

  .le__viewall::after,
  .wb__viewall::after,
  .shop-cats__viewall::after,
  .keys2__viewall::after{content:"";position:absolute;inset:-11px -10px;}

  /* ---- carousel and section controls ------------------------------- */

  .gallery__nav,
  .keys2__nav{width:44px;height:44px;}

  .labgrown__toggle{width:44px;height:44px;}

  /* 11px block padding measured 41px tall. 13 lands it at 45. */
  .btn--sm{padding-block:13px;}

  /* ---- product card ------------------------------------------------ */

  /*
   * Wishlist and Quick View are 36px circles over the photograph. At 36 they
   * are under the floor and only 8px apart, which on a phone reads as one
   * ambiguous target rather than two. Both go to 44 and Quick View shifts
   * right so the gap between them is preserved.
   */
  .product-card__wish{width:44px;height:44px;}
  .product-card__qv{width:44px;height:44px;right:63px;}

  /* The list-view button; the grid view already uses a 44px circle from the
     `hover:none` block above. */
  .product-card__add2{height:44px;}

  /* ---- product page ------------------------------------------------ */

  /* "Share" is a 19px text button. Same reasoning as View All: extend, do not
     resize, because its baseline is aligned with the text beside it. */
  .pdp-sec{position:relative;}
  .pdp-sec::after{content:"";position:absolute;inset:-13px -10px;}

  /* 42px wide between its two steppers — two short of the floor, and the one
     control on the page a customer types into. */
  .pdp-qty__in{min-width:44px;}

  /*
   * The sticky bar's quantity steppers were 26px inside a 10px-padded bar.
   * Growing them to 44 would make the bar taller and eat more of the screen,
   * so they go to 40 — the largest that fits the bar as designed — and take
   * the remainder from a transparent extension.
   */
  .pdp-sticky__qbtn{width:40px;height:40px;position:relative;}
  .pdp-sticky__qbtn::after{content:"";position:absolute;inset:-2px;}

  /* ---- account and comment forms ----------------------------------- */

  .ac-eye{width:44px;height:44px;}
  .ac-tab{min-height:44px;}

  /* A 17px checkbox is unhittable; the label around it is the real target and
     has to be sized like one. */
  .ac-check{display:flex;align-items:center;min-height:44px;}

  /*
   * WordPress's comment form ships unstyled: 21px inputs and a 13px consent
   * checkbox. It is the only form a reader meets after an article, so it gets
   * the same floor as the rest. 16px type because anything smaller makes iOS
   * zoom the page on focus.
   */
  .comment-form input[type="text"],
  .comment-form input[type="email"],
  .comment-form input[type="url"],
  .comment-form textarea,
  .comment-form input[type="submit"]{
    min-height:44px;
    box-sizing:border-box;
    width:100%;
    max-width:100%;
    padding:11px 14px;
    font-size:16px;
    border:1px solid var(--line);
    border-radius:10px;
    background:#fff;
    color:var(--ink);
  }

  .comment-form textarea{min-height:120px;}

  .comment-form input[type="submit"]{
    background:linear-gradient(135deg,var(--gold),var(--gold-cta));
    border:none;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
    font-size:.82rem;cursor:pointer;
  }

  .comment-form .comment-form-cookies-consent{display:flex;align-items:center;gap:10px;min-height:44px;}
  .comment-form .comment-form-cookies-consent input{width:22px;height:22px;flex:none;}
}

/* ---- readable text --------------------------------------------------
   Two places rendered real sentences below 12px on a phone. Both are fixed at
   the source rather than with a blanket minimum, because in each case the
   cause is a rule that traded legibility for a layout effect. */

/*
 * The newsletter line was pinned to one line with
 * `white-space:nowrap; font-size:clamp(.56rem,2.45vw,1rem)`, which at 320–390px
 * resolves to 9–10.5px: a sentence a third smaller than the body copy around
 * it, in the one section on the page that asks for an email address.
 *
 * Wrapping is the honest trade. The single-line treatment is kept from 560px
 * up, where the sentence fits without shrinking below the base size.
 */
.newsletter__sub{white-space:normal;font-size:.95rem;max-width:34ch;}

@media(min-width:560px){
  .newsletter__sub{white-space:nowrap;max-width:none;font-size:clamp(.86rem,2.45vw,1rem);}
}

/*
 * Not changed: the breadcrumb trail (11.5px) and the PDP brand line (11.8px).
 *
 * The sweep first reported both as body copy under the 12px floor, because the
 * anchor inside each carries no class of its own and the test looked no further
 * than the element. They are neither — both are small tracked capitals, the
 * label treatment the approved design uses throughout, and raising them would
 * change the type system to satisfy a mis-read measurement. The instrument was
 * corrected instead; see the `tiny` classification in scratchpad/mobile/probe.js.
 */

/* ===================================================================
   Mobile layout requests
   ===================================================================
   Changes asked for while reviewing the site on a phone. Grouped here rather
   than edited into five different places so the set can be read, adjusted or
   reverted together. Everything is inside a mobile query; the desktop layout
   is untouched.
   =================================================================== */

@media (max-width: 860px) {

  /* ---- top bar: one message, not a rotator ------------------------- */

  /*
   * The bar cycled three promotional messages. On request it now carries only
   * the delivery offer — the one with a threshold in it — and the rotator and
   * the two left-hand promos are off.
   *
   * `height` becomes a minimum because the sentence is 58 characters and will
   * take two lines on a 320px screen; a fixed 38px would clip it.
   */
  .topbar .tb-rotator{display:none;}

  .topbar .promo{
    display:flex;align-items:center;justify-content:center;gap:8px;
    flex:1;min-width:0;text-align:center;
    font-size:.72rem;line-height:1.3;
  }

  .topbar .promo .icon{width:15px;height:15px;flex:none;}

  .topbar .row{height:auto;min-height:38px;padding:6px 14px;}

  /* ---- USP strip: off on mobile ------------------------------------ */

  /*
   * Five trust statements stacked into two columns took a full screen before
   * the first product. Hidden rather than deleted: the section, its content
   * and its desktop layout are untouched, and removing this rule brings it
   * straight back.
   */
  .usp{display:none;}

  /* ---- room between the brand grid and the rail below it ----------- */

  /*
   * The brands "View All" sat directly on top of the next section's eyebrow,
   * so the two blocks read as one. The gap belongs under the link rather than
   * over the heading, because the heading is a shared rail component used in
   * several places.
   */
  .wb__more{margin-bottom:46px;}

  /* ---- 21st Keys banner video: no crop ----------------------------- */

  /*
   * The banner is given a fixed aspect ratio on mobile and the video was
   * `object-fit:cover`, so a 16:10 box cropped the top off a wider clip and
   * took the headline with it. The still image in the same banner already uses
   * `contain`; the video now matches it, and letterboxes against the banner's
   * own dark ground exactly as the image does.
   */
  .keys2__banner video{object-fit:contain;}

  /* ---- Lab Grown banner video: no crop ----------------------------- */

  /* Same cause, same fix. `.labgrown__video` is `object-fit:cover` inside a
     media box whose ratio is set by the layout, so on a phone the clip was
     cropped to the box instead of the box following the clip. */
  .labgrown__video{object-fit:contain;}
}

/* The rail's two-up sizing lives in home-rail.css, which loads after this
   file and would otherwise override it. */


/* ---- footer, mobile ------------------------------------------------ */

@media (max-width: 520px) {

  /*
   * One rule between the social block and the accordion, not two.
   *
   * `.site-footer__top` ends with a `border-bottom`, and the first accordion
   * column opens with a `border-top` of its own. On a wide screen the grid's
   * 46px padding separates them; stacked into one column on a phone they land
   * two pixels apart and read as a double line. The column's own border is the
   * one that goes, because it belongs to a repeating pattern — every other
   * column needs its rule to divide it from the one above.
   */
  .footer__col--acc:first-of-type{border-top:0;}

  /*
   * The contact block centres on mobile, on request.
   *
   * Each row is an icon and a line of text in a flex row, so centring the text
   * alone would leave the icons hanging at the left. `justify-content` moves
   * the pair together, and the address — the one row that wraps to two lines —
   * gets its own centring so the second line sits under the first rather than
   * ragging left.
   */
  .footer__col--contact{text-align:center;}

  .footer__col--contact .footer__h{text-align:center;}

  .footer__contact{justify-content:center;}

  .footer__contact--addr{align-items:center;}

  .footer__contact--addr span{text-align:center;}

  .footer__hours{text-align:center;}
}

/* ---- swipe cue: progress bar + hint under every horizontal scroller ---- */

/*
 * The animation is redefined at the top level on purpose.
 *
 * `swipeHint` already exists, but inside `@media(max-width:600px)` — and
 * keyframes declared inside a media query only exist while that query matches.
 * The cue below runs to 860px, so between 601 and 860 it would have referenced
 * an animation that was not there and simply not moved. Same name, same values,
 * available everywhere.
 */
@keyframes swipeHint{0%,100%{transform:translateX(0);}50%{transform:translateX(5px);}}

/*
 * The affordance the Shop by Category rail already had, generalised.
 *
 * `initSwipeCues()` in main.js injects this after any element that actually
 * scrolls sideways, so a rail that happens to fit on a given screen shows
 * nothing and one that overflows always does. The visual language is copied
 * from `.shop-cats__progress` on purpose — one cue, one appearance, wherever
 * it appears.
 *
 * Mobile only: a desktop has visible arrows and a trackpad.
 */
.swipe-cue{display:none;}

@media (max-width: 860px) {
  .swipe-cue:not([hidden]){display:block;margin:16px 22px 0;}

  .swipe-cue__rail{
    display:block;position:relative;height:3px;
    background:var(--line);border-radius:3px;overflow:hidden;
  }

  .swipe-cue__bar{
    position:absolute;left:0;top:0;height:100%;
    background:var(--gold-cta);border-radius:3px;
    will-change:transform;
    transition:transform .18s var(--ease);
  }

  .swipe-cue__hint{
    display:flex;align-items:center;justify-content:center;gap:7px;
    margin-top:12px;
    font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
    font-size:.72rem;font-weight:500;letter-spacing:.08em;
    text-transform:uppercase;color:var(--muted);
  }

  .swipe-cue__hint svg{
    width:15px;height:15px;flex:none;
    fill:none;stroke:currentColor;stroke-width:1.8;
    stroke-linecap:round;stroke-linejoin:round;
    animation:swipeHint 1.8s var(--ease) infinite;
  }

  /* Inside a padded section the cue already sits within the gutter. */
  .plp-chips-wrap .swipe-cue,
  .plp-chips + .swipe-cue{margin-left:0;margin-right:0;}
}

@media (prefers-reduced-motion: reduce) {
  .swipe-cue__hint svg{animation:none;}
  .swipe-cue__bar{transition:none;}
}

/* ---- the sticky buy bar has to leave room for itself ---------------- */

/*
 * Now that the bar actually appears, it covers whatever is at the bottom of the
 * page — the audit found a footer link permanently under it. A fixed bar has to
 * reserve its own height, or the last thing on the page can never be reached.
 *
 * `:has()` scopes this to the state where the bar is showing, so a page without
 * one keeps its own spacing.
 */
/* 860px, matching where `.pdp-sticky` is actually displayed — scoped to 560 it
   left the tablet breakpoint with the same covered footer link. */
@media (max-width: 860px) {
  body:has(.pdp-sticky.is-on) .site-footer{padding-bottom:110px;}
}

/* ---- 21st Keys section, mobile ------------------------------------- */

@media (max-width: 860px) {

  /*
   * The banner box now follows the video instead of the other way round.
   *
   * The clip is 846x901 — portrait — and the box was pinned to 4:3, so
   * `object-fit:cover` cropped it and `contain` letterboxed it against the
   * banner's black ground. Neither is right: one loses the headline, the other
   * shows two black bars.
   *
   * Dropping the fixed ratio and taking the video out of absolute positioning
   * lets it set its own height, so nothing is cropped and no ground shows. The
   * width is capped so a portrait clip does not turn into a full screen of
   * banner, and the frame — the gold hairline and its inset second rule — is
   * kept exactly as designed.
   */
  .keys2__banner{
    aspect-ratio:auto;
    min-height:0;
    width:min(100%, 300px);
    margin-inline:auto;
    background:transparent;
  }

  .keys2__banner .keys2__ph{position:static;display:block;}

  .keys2__banner video{
    position:static;
    inset:auto;
    width:100%;
    height:auto;
    display:block;
    object-fit:contain;
  }

  /* The heading centres over the centred banner. */
  .keys2__head{flex-direction:column;align-items:center;text-align:center;}
  .keys2__titles{text-align:center;}
  .keys2__controls{justify-content:center;}

  /*
   * The prev/next arrows go.
   *
   * Every horizontal scroller now carries a progress bar and a "Swipe to
   * explore" line, which is the affordance a thumb actually uses; a pair of
   * 44px arrow buttons above it says the same thing twice and costs a row of
   * vertical space. They stay on desktop, where there is no swipe.
   */
  .keys2__nav{display:none;}
}

/* ---- gallery arrows: off on mobile too ------------------------------ */

/*
 * Same reasoning as the 21st Keys pair. The store gallery is a swipeable
 * track and now carries the shared progress bar and "Swipe to explore" line,
 * so the two 44px arrows sitting over the photographs repeat an affordance the
 * reader already has — and they cover part of the picture to do it. Desktop
 * keeps them, because a mouse has nothing else.
 */
@media (max-width: 860px) {
  .gallery__nav{display:none;}
}

/* ---- "View All" moves below the rail on mobile ---------------------- */

/*
 * Asked for: the link under the products rather than beside the heading, and
 * sitting just above the progress line.
 *
 * The link lives inside the heading block, so `order` alone cannot lift it out.
 * `display:contents` on the header promotes its two children — the heading and
 * the link — to be children of the section's own column, and from there the
 * three parts can be ordered: heading, rail, link, line. The markup is
 * unchanged, so the desktop layout (a grid row with the link at its right) is
 * exactly as approved.
 *
 * The cue is inserted after the viewport rather than after the track (see
 * initSwipeCues in main.js), which is what makes the link and the line
 * orderable siblings at all.
 */

@media (max-width: 640px) {

  /* ---- Limited Edition and any other `.limited-ed` rail ---- */

  .le__inner{display:flex;flex-direction:column;}

  .le__head{display:contents;}

  .le__heading{order:1;text-align:center;margin-bottom:22px;}

  .le__viewport{order:2;}

  .le__viewall{
    order:3;
    grid-column:auto;
    justify-self:auto;
    align-self:center;
    margin-top:20px;
  }

  /* The cue is last, so the link sits between the cards and the line. */
  .le__inner > .swipe-cue{order:4;margin-top:14px;}

  /* ---- 21st Keys rail, same arrangement ---- */

  .keys2__main{display:flex;flex-direction:column;}

  .keys2__head{display:contents;}

  .keys2__titles{order:1;text-align:center;margin-bottom:20px;}

  .keys2__viewport{order:2;}

  .keys2__controls{order:3;justify-content:center;margin-top:20px;}

  .keys2__main > .swipe-cue{order:4;margin-top:14px;}
}

/* ---- Shop by Category: a grid on mobile, not a carousel ------------- */

/*
 * Asked for: no carousel here on a phone.
 *
 * This section is a directory of nine categories, not a merchandising rail —
 * the point of it is to show what the shop is divided into, and a scroller hid
 * seven of the nine behind a gesture. As a two-column grid all nine are visible
 * in about a screen and a half, which is what a directory is for.
 *
 * The carousel's own furniture goes with it: the arrows, and the progress bar
 * and "Swipe to explore" line that this section carried before the shared cue
 * existed. There is nothing left to swipe, so a cue would be describing a
 * gesture that does nothing. `initSwipeCues()` already skips this track by
 * name, so no generic cue replaces them.
 *
 * "View All" moves under the grid, matching the two product rails.
 */

@media (max-width: 640px) {

  .shop-cats__inner{display:flex;flex-direction:column;}

  /* Promotes the heading and the link to siblings of the column, so the link
     can be ordered after the grid. Markup unchanged; desktop keeps its row. */
  .shop-cats__head{display:contents;}

  .shop-cats__heading{order:1;text-align:center;margin-bottom:24px;}

  .shop-cats__viewport{order:2;}

  .shop-cats__viewall{
    order:3;
    grid-column:auto;
    justify-self:auto;
    align-self:center;
    margin-top:24px;
  }

  .shop-cats__track{
    display:grid;
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:20px 12px;
    overflow:visible;
    scroll-snap-type:none;
    padding:0;
  }

  .shop-cats__item{flex:none;scroll-snap-align:none;}

  .shop-cats__nav,
  .shop-cats__progress,
  .shop-cats__hint{display:none;}
}

/* ---- product card and listing copy, mobile ------------------------- */

@media (max-width: 860px), (hover: none) {

  /*
   * Quick View is off on a phone, on request.
   *
   * It is a shortcut around a page a tap already reaches, and on a two-up card
   * it was one of two circles competing with the photograph. The wishlist stays
   * — it has no other entry point on a listing — and the panel itself is
   * untouched, so a wider screen keeps it.
   */
  .product-card__qv{display:none;}

  /*
   * Quick View is off on touch, so on a phone the heart is the only circle on
   * the card and takes the corner on its own. Desktop keeps the pair, and the
   * `right: 50px` offset that makes room for both — see the rail card block
   * further down, which is scoped to the same 640px query.
   */
  .le__track .product-card__wish{right:8px;}
}

@media (max-width: 640px) {

  /*
   * A shorter Add to Cart on the rail.
   *
   * 44px of gold across a 146px card was the heaviest thing on it. The button
   * is drawn at 38 and keeps a 44px touch area through the extension, so the
   * card lightens without the control getting harder to hit.
   */
  .le__track .product-card__add2{
    height:38px;
    position:relative;
  }

  .le__track .product-card__add2::after{
    content:"";
    position:absolute;
    inset:-3px 0;
  }
}

/*
 * Collection intro copy, sized for a phone.
 *
 * The lead paragraph under a collection heading was set at the desktop size and
 * ran four lines of large type before the first product. It steps down here and
 * keeps its measure, so the eye reaches the grid sooner.
 */
@media (max-width: 640px) {
  .plp-hero__lead,
  .plp-hero__lead p{
    font-size:.9rem;
    line-height:1.6;
  }

  .plp-hero__title{font-size:clamp(1.5rem,1.1rem + 2vw,2.1rem);}
}

/*
 * Tighter grid on a phone.
 *
 * 18px between two cards on a 390px screen is 18px taken out of the two
 * photographs beside it. 10px still reads as a gap and gives each card 4px
 * more of the width it is short of.
 */
@media (max-width: 640px) {
  .plp-grid{gap:10px;}
}

/* ---- listing views on a phone: list, two-up, one-up ----------------- */

/*
 * The third view was three columns on a phone — cards about 110px wide, with
 * their own smaller type and tighter padding to survive it. Asked for instead:
 * one large card. So on a phone the trio reads list / two-up / one-up, which
 * are three genuinely different ways to look at a catalogue rather than two
 * grids and a thumbnail sheet.
 *
 * `--n-4` is the number shop.js writes into the button's label ("%d per row"),
 * so setting it here keeps the label honest without touching the script.
 */
@media (max-width: 640px) {
  .plp-grid{--n-4:1;}

  .plp-grid.cols-4{grid-template-columns:1fr;gap:14px;}

  /* The compaction that made three-up survivable is wrong at full width. */
  .plp-grid.cols-4 .product-card__title{font-size:.84rem;-webkit-line-clamp:2;}
  .plp-grid.cols-4 .product-card__body{padding:14px 14px 16px;gap:5px;}
  .plp-grid.cols-4 .product-card__now{font-size:1rem;}
  .plp-grid.cols-4 .product-card__was{font-size:.82rem;}
  .plp-grid.cols-4 .product-card__wish{top:11px;right:11px;width:44px;height:44px;}

  /* The two-up gap, which `.cols-2` sets at 24px and therefore wins. */
  .plp-grid.cols-2{gap:10px;}
}


/* ---- the card's Add to Cart on a phone --------------------------------- */

/*
 * The grid card carried a floating dark disc over the bottom-right corner of
 * the photograph. Three things were wrong with it here:
 *
 *   · it is ink-on-white in a palette whose call to action is gold, so on a
 *     page of jewellery the heaviest mark on the screen was a black circle,
 *     repeated once per card;
 *   · it sits ON the product. At two-up the card is 174px and the disc landed
 *     on the corner of the ring it was meant to be selling;
 *   · it says nothing. The glyph is the same bag as the header's cart, which
 *     goes somewhere else entirely.
 *
 * The rails already answered this: `product-card__add2` is the same control
 * — same `data-stonex-add` wiring — with a label, in the body, under the
 * price. Switching it on here means the grid and the rails offer one Add to
 * Cart in one place rather than two in two, and the disc goes.
 *
 * It also squares the cards up. The button is pushed to the bottom of the body
 * by `margin-top:auto`, so a card with a one-line price and a card with a
 * struck-through second line end at the same height instead of leaving the
 * ragged gaps the grid had between rows.
 *
 * List view keeps its own button exactly as approved: there the card is a row,
 * the button belongs beside the text rather than stretched across it.
 */

@media (max-width: 860px), (hover: none) {

  .plp-grid .product-card__add { display: none; }

  /* The dots stay — they are the image indicator, not a control. */

  .plp-grid .product-card { height: 100%; }
  .plp-grid .product-card__body { flex: 1; }

  .plp-grid:not(.is-list) .product-card__add2 {
    display: inline-flex;
    align-self: stretch;
    margin-top: auto;
    position: relative;
    height: 38px;
    padding: 0 8px;
    border: 0;
    border-radius: 6px;
    background: var(--gold-cta);
    color: var(--ink);
    font-size: .62rem;
    letter-spacing: .04em;
    gap: 6px;
    white-space: nowrap;
  }

  /* 38px drawn, 46px to a thumb — the rail's trade, for the same reason,
     and the same four pixels of margin over the 44px guideline. */
  .plp-grid:not(.is-list) .product-card__add2::after {
    content: "";
    position: absolute;
    inset: -4px 0;
  }

  .plp-grid:not(.is-list) .product-card__add2 svg { width: 13px; height: 13px; }

  .plp-grid:not(.is-list) .product-card__add2:hover,
  .plp-grid:not(.is-list) .product-card__add2:focus-visible {
    background: var(--ink);
    color: var(--ivory);
  }
}

/*
 * Below 360px the label is what stops the button fitting.
 *
 * Two-up on a 320px screen leaves the card 139px and its body 119px of
 * content, and "ADD TO CART" with its icon needs 141px. The icon is the part
 * that carries no information the words do not, so it is the part that goes.
 */
@media (max-width: 359px) {
  .plp-grid.cols-2 .product-card__add2 svg,
  .plp-grid--default .product-card__add2 svg { display: none; }

  .plp-grid.cols-2 .product-card__add2,
  .plp-grid--default .product-card__add2 { font-size: .58rem; padding: 0 4px; }
}

/* ---- the third view's icon follows what the third view does ------------ */

/*
 * The switcher's third button is four-up on a desktop and one-up on a phone,
 * so one icon cannot be honest at both widths. The 2x2 grid stays where it
 * means four columns; below 640px — the same breakpoint that sets `--n-4:1` —
 * it is swapped for a single box, and the trio reads list / two / one.
 */
.plp-view__ic--one { display: none; }

@media (max-width: 640px) {
  .plp-view__ic--many { display: none; }
  .plp-view__ic--one { display: block; }
}


/* ---- Quick View is offered, not discovered ----------------------------- */

/*
 * The eye was `opacity: 0` until the card was hovered.
 *
 * Which means it is invisible to anyone who has not already put their pointer
 * on the card — so it cannot draw anyone in, and a shopper who has never
 * hovered a card does not know the shortcut exists. A control that only
 * appears once you are already committed to looking is not an entry point.
 *
 * It is shown at rest instead, at a weight that reads as available rather than
 * as an alert: the circle is white on the photograph, and hover still darkens
 * it, so the hover state now means "you are about to press this" rather than
 * "this exists".
 *
 * Desktop only. Quick View is off on touch, on request, and this whole block
 * is scoped above the width where that begins.
 */
@media (min-width: 861px) and (hover: hover) {
  .product-card__qv {
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }

  /* Slightly recessive until the card is engaged with, so a grid of twelve
     does not read as twelve buttons. */
  .product-card:not(:hover) .product-card__qv {
    opacity: .82;
  }

  .product-card__qv:focus-visible {
    opacity: 1;
  }
}


/* ---- the saved state on the product page ------------------------------- */

/*
 * `.pdp-sec` styled `.is-active`; the script sets `.is-on`.
 *
 * So the saved state had a rule and never met it — the fifth instance of this
 * exact drift in this theme (`.drawer.on`, `.dsub.is-open`,
 * `.footer__col--acc.open`, `.pdp-sticky.show`). Both class names are honoured
 * here rather than picking a winner, because `.is-active` is what the PDP's
 * share control uses and changing that is a separate job.
 *
 * The label changes too — see template-parts/product/info.php — so the state
 * is carried by the word, the fill and `aria-pressed` together, rather than by
 * colour alone.
 */
.pdp-sec.is-on { color: var(--garnet); }
.pdp-sec.is-on svg { fill: currentColor; }

/* ================= 404 / NOT FOUND ================= */
.nf{background:var(--ivory);padding:64px 0 76px;}
.nf__inner{max-width:860px;margin:0 auto;padding:0 22px;text-align:center;}
.nf__code{margin:0;font-family:var(--font-display,Georgia,serif);font-size:clamp(3.6rem,12vw,6rem);line-height:1;color:var(--gold,#c2a15b);opacity:.32;letter-spacing:.04em;}
.nf__title{margin:6px 0 12px;font-family:var(--font-display,Georgia,serif);font-size:clamp(1.7rem,5vw,2.5rem);line-height:1.2;font-weight:400;color:var(--ink,#1c1a17);}
.nf__lead{margin:0 auto 30px;max-width:56ch;font-size:.98rem;line-height:1.65;color:var(--muted,#6f685c);}
.nf__search{display:flex;gap:10px;max-width:520px;margin:0 auto 44px;}
.nf__search input[type=search]{flex:1 1 auto;min-width:0;padding:13px 16px;font-size:.95rem;border:1px solid var(--line,#e4dccd);border-radius:4px;background:#fff;color:var(--ink,#1c1a17);}
.nf__search input[type=search]:focus{outline:2px solid var(--gold,#c2a15b);outline-offset:1px;}
.nf__search button{flex:none;padding:13px 24px;border:1px solid var(--ink,#1c1a17);border-radius:4px;background:var(--ink,#1c1a17);color:#fff;font-size:.8rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;cursor:pointer;transition:.2s var(--ease);}
.nf__search button:hover{background:var(--gold-deep,#b68a3e);border-color:var(--gold-deep,#b68a3e);}
.nf__block{margin:0 0 38px;}
.nf__h{margin:0 0 16px;font-size:.78rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--muted,#6f685c);}
.nf__chips{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:10px;justify-content:center;}
.nf__chips a{display:inline-flex;align-items:center;gap:8px;padding:10px 17px;border:1px solid var(--line,#e4dccd);border-radius:999px;background:#fff;font-size:.9rem;color:var(--ink,#1c1a17);transition:.2s var(--ease);}
.nf__chips a:hover{border-color:var(--gold,#c2a15b);color:var(--gold-deep,#b68a3e);}
.nf__chips span{font-size:.76rem;color:var(--muted,#6f685c);}
.nf__grid{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,1fr);gap:18px;}
.nf__grid a{display:block;text-align:left;color:var(--ink,#1c1a17);}
.nf__grid img{display:block;width:100%;height:auto;border-radius:6px;background:#fff;border:1px solid var(--line,#e4dccd);}
.nf__name{display:block;margin-top:9px;font-size:.86rem;line-height:1.4;}
.nf__price{display:block;margin-top:3px;font-size:.86rem;font-weight:700;}
.nf__cta{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin:8px 0 26px;}
.nf__help{margin:0;font-size:.9rem;color:var(--muted,#6f685c);}
.nf__help a{color:var(--ink,#1c1a17);text-decoration:underline;}
@media(max-width:900px){
  .nf__grid{grid-template-columns:repeat(2,1fr);}
}
@media(max-width:560px){
  .nf{padding:44px 0 56px;}
  .nf__search{flex-direction:column;}
  .nf__search button{width:100%;}
  .nf__cta .btn-gold,.nf__cta .btn-ghost{flex:1 1 auto;justify-content:center;}
}

/* The variation price is injected as WooCommerce's own markup, so it needs the
   summary's typography applied to WooCommerce's classes rather than the
   theme's. Sale variations arrive as <del> + <ins>, matching the static
   treatment above. */
/*
 * The variation price must look identical to the static one.
 *
 * The static price is wrapped in `.pdp-price__now`, which carries the 2.2rem
 * weight-700 treatment. WooCommerce's `price_html` has no such wrapper — for a
 * single price it is a bare `.woocommerce-Price-amount` — so the injected value
 * inherited only the container's defaults and rendered small and washed out
 * beside the heading it replaced. The earlier rules targeted `.price` and
 * `ins`, neither of which WooCommerce emits for an unsale variation, so they
 * matched nothing at all.
 *
 * These declarations therefore restate the `.pdp-price__now` treatment rather
 * than inheriting it, and are keyed off the amount element WooCommerce does
 * emit. `del` keeps the smaller struck-through treatment of `.pdp-price__was`.
 */
.pdp-price--variation,
.pdp-price--variation .price,
.pdp-price--variation ins,
.pdp-price--variation > .woocommerce-Price-amount{
	font-family:"DM Sans","DM Sans Fallback",system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-size:2.2rem;
	font-weight:700;
	line-height:1.1;
	letter-spacing:-.01em;
	color:var(--ink);
	font-variant-numeric:tabular-nums;
	text-decoration:none;
}
.pdp-price--variation ins{background:none;}
.pdp-price--variation del,
.pdp-price--variation del .woocommerce-Price-amount{
	font-size:1rem;
	font-weight:500;
	color:var(--muted);
	text-decoration:line-through;
	text-decoration-thickness:1.5px;
	letter-spacing:normal;
}
.pdp-price--variation del{margin-left:10px;}
.pdp-price--variation .woocommerce-Price-amount{white-space:nowrap;}

/*
 * WooCommerce paints prices with its own `--wc-highlight`, which ships as
 * #958e09 — an olive that is nothing to do with this brand and beat the rule
 * above on specificity, so the variation price rendered olive beside a static
 * price in ink. Rebinding the variable inside this block fixes it at the
 * source, and the descendant rule catches any element WooCommerce colours
 * directly rather than through the variable.
 */
/*
 * The class is repeated to raise specificity above WooCommerce's own
 * `div.product p.price` rules without resorting to !important. A `:not()`
 * chain was tried first and lost — simpler and more specific beats clever here.
 */
.pdp-price.pdp-price--variation{--wc-highlight:var(--ink);}
/*
 * WooCommerce's rule is `.woocommerce:where(...) div.product p.price`. `:where()`
 * adds nothing, so it scores (0,3,2) — three classes AND two element selectors.
 * `.pdp-price.pdp-price--variation .price` is only (0,3,0) and loses on the
 * element count, which is why the price stayed olive through two attempts.
 * Repeating `.price` takes this to (0,4,0), which wins outright without
 * !important and without depending on stylesheet order.
 */
.pdp-price.pdp-price--variation,
.pdp-price.pdp-price--variation .price.price,
.pdp-price.pdp-price--variation ins.ins,
.pdp-price.pdp-price--variation .price.price .woocommerce-Price-amount,
.pdp-price.pdp-price--variation .price.price .woocommerce-Price-currencySymbol{color:var(--ink);}
.pdp-price.pdp-price--variation del.del,
.pdp-price.pdp-price--variation del.del .woocommerce-Price-amount,
.pdp-price.pdp-price--variation del.del .woocommerce-Price-currencySymbol{color:var(--muted);}

/* Buy Now is HIDDEN, not merely greyed, when the chosen variation cannot be
   bought. A disabled button still invites a click and still occupies the place
   the eye goes to; removing it leaves Notify Me as the obvious next step. */
.pdp-buynow[hidden]{display:none !important;}

/* The "You will be told when 55cm is back" line repeats what the button next to
   it already says, on a page that is already telling the customer the size is
   out of stock. Kept in the DOM for screen readers via aria-live, which is
   where it does its real work, but out of the visual layout. */
.pdp-buy__notify-for{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}

/* ---- variation swap: price and availability ---- */
/* The value is faded, never removed, so the block keeps its height and the
   page cannot shift while WooCommerce resolves the variation. Reserving the
   space is the difference between a transition and a jolt. */
.pdp-price,
.pdp-avail{transition:opacity .18s ease,transform .18s ease;}
.pdp-price.is-swapping,
.pdp-avail.is-swapping{opacity:.28;transform:translateY(2px);}

/* The size chips get the same easing as everything else in the theme, and a
   pressed state so a tap feels answered before the price catches up. */
.stonex-has-chips .pdp-chip{transition:background-color .18s var(--ease),border-color .18s var(--ease),color .18s var(--ease),transform .12s var(--ease);}
.stonex-has-chips .pdp-chip:active{transform:scale(.96);}

@media (prefers-reduced-motion: reduce){
  /* Honour the setting: no movement, and the fade is dropped to a bare hint so
     the change is still perceivable without animating. */
  .pdp-price,.pdp-avail,.stonex-has-chips .pdp-chip{transition:none;}
  .pdp-price.is-swapping,.pdp-avail.is-swapping{opacity:.6;transform:none;}
  .stonex-has-chips .pdp-chip:active{transform:none;}
}

/* "Choose a size" — the reason Add to Cart and Buy Now start disabled.
   Styled as guidance, not an error: a warning colour here would suggest the
   customer had done something wrong when they have only just arrived. */
.pdp-choose{display:flex;align-items:center;gap:9px;margin:0 0 14px;padding:11px 14px;background:var(--pearl,#f2ece1);border-left:3px solid var(--gold,#c2a15b);border-radius:3px;font-size:.88rem;line-height:1.45;color:var(--ink,#1c1a17);}
.pdp-choose svg{flex:none;width:17px;height:17px;color:var(--gold-deep,#b68a3e);}
.pdp-choose[hidden]{display:none;}

/* The whole buy row goes when the chosen variation cannot be bought, leaving
   Notify Me in its place rather than beneath a dead button. */
.woocommerce-variation-add-to-cart .pdp-buy[hidden]{display:none !important;}

/* ---- Add to Cart and Buy Now share the row ---- */
/*
 * The two are siblings in different wrappers — the stepper and Add to Cart sit
 * in `.pdp-buy`, Buy Now stands alone after it — so they stacked full width and
 * the page gave a secondary action the same weight as the primary one. Laying
 * the parent out as a wrapping flex row lets them take half each without moving
 * any markup, which keeps the form submission and the no-JS fallback intact.
 *
 * Anything else in the row (the "choose a size" prompt, backorder note, the
 * notify block) is pushed to its own full-width line.
 */
/*
 * `.variations_form .woocommerce-variation-add-to-cart{display:block}` further up
 * this file is two classes and beat a single-class rule, so the flex row never
 * applied and the two CTAs stayed stacked. Matched here at the same strength.
 */
.variations_form .woocommerce-variation-add-to-cart{display:flex;flex-wrap:wrap;align-items:stretch;gap:12px;}
.variations_form .woocommerce-variation-add-to-cart > *{flex:1 1 100%;}
/*
 * Basis 0, not calc(50% - 6px). With a percentage basis the border and padding
 * of the buy row pushed its outer width past half the container and the two
 * wrapped onto separate lines while still measuring 50% each. A zero basis lets
 * flex divide what is actually left after the gap, which is the thing that
 * needed halving.
 */
.variations_form .woocommerce-variation-add-to-cart > .pdp-buy,
.variations_form .woocommerce-variation-add-to-cart > .pdp-buynow{flex:1 1 0;min-width:0;margin:0;}
/* No fixed height: stretch makes the shorter button match the row containing
   the quantity stepper, so the two CTAs are the same size as well as width. */
.variations_form .woocommerce-variation-add-to-cart > .pdp-buynow{height:auto;min-height:52px;}

@media(max-width:560px){
  /* Below this each would be too narrow to read, so they stack. */
  .variations_form .woocommerce-variation-add-to-cart > .pdp-buy,
  .variations_form .woocommerce-variation-add-to-cart > .pdp-buynow{flex:1 1 100%;}
}

/* The out-of-stock line sat a full row away from the Notify Me button it
   belongs to, reading as two unrelated blocks. */
/*
 * "Out of stock" and the Notify Me button are one message and should sit
 * together. The 60px between them came from three places at once — the
 * availability line's own margin, the collapsed buy row's leftover gap, and
 * `.pdp-buy`'s shared margin, which the notify block inherits by carrying that
 * class. All three are set here rather than hunted one at a time.
 */
.pdp-buy--notify{margin-top:0;}
.woocommerce-variation-availability{margin-bottom:0;}
/* The buy row collapses to nothing when hidden, so its gap and margin must go
   with it or they stack up as dead space above the only usable control. */
.variations_form .woocommerce-variation-add-to-cart:has(> .pdp-buy[hidden]){gap:0;margin-bottom:0;}
.woocommerce-variation-add-to-cart .stock.out-of-stock,
.pdp-avail--out{margin-bottom:8px;}

/* ---- a size that is out of stock says so before it is clicked ---- */
/*
 * Struck through AND faded AND disabled: colour alone does not survive
 * greyscale or colour-blind vision, and "you cannot have this one" is not a
 * decorative distinction. The chip stays in place rather than being removed, so
 * the size run still reads as a continuous scale.
 */
.pdp-chip.is-soldout{
	position:relative;
	color:var(--muted,#6f685c);
	background:var(--ivory,#faf7f1);
	border-color:var(--line,#e4dccd);
	/* Still clickable — selecting it is how the customer reaches Notify Me —
	   so the cursor must not say otherwise. */
	cursor:pointer;
	opacity:.75;
}
.pdp-chip.is-soldout::after{
	content:"";
	position:absolute;
	left:8%;
	right:8%;
	top:50%;
	height:1px;
	background:currentColor;
	transform:rotate(-14deg);
	opacity:.75;
	pointer-events:none;
}
.pdp-chip.is-soldout:hover{border-color:var(--line,#e4dccd);color:var(--muted,#6f685c);}

/* ---- the buy row: stepper on its own line, then two equal CTAs ---- */
/*
 * Buy Now now sits inside `.pdp-buy` (see the variation template), so the row
 * lays itself out with no `display:contents` and no ordered spacer — both of
 * which worked but depended on the button being a sibling in another wrapper.
 * Moving one element was simpler than three CSS workarounds.
 */
.pdp-buy{display:flex;flex-wrap:wrap;gap:12px;align-items:stretch;}
/*
 * The stepper keeps its natural width. A zero-height full-width spacer after it
 * ends the line, so the bordered box stays compact instead of stretching the
 * column — `flex-basis:100%` on the stepper itself would have stretched it.
 */
.pdp-buy > .pdp-qty{flex:0 0 auto;justify-content:flex-start;order:1;}
.pdp-buy::after{content:"";order:2;flex:0 0 100%;height:0;}
.pdp-buy > .pdp-atc{order:3;}
.pdp-buy > .pdp-buynow{order:4;}
.pdp-buy > .pdp-atc,
.pdp-buy > .pdp-buynow{flex:0 0 calc(50% - 6px);width:calc(50% - 6px);min-width:0;height:52px;margin:0;}

@media(max-width:560px){
  /* Too narrow to read side by side below this. */
  .pdp-buy > .pdp-atc,
  .pdp-buy > .pdp-buynow{flex:0 0 100%;width:100%;}
}

/*
 * No sweep, no glow on the product page.
 *
 * `.btn-gold` carries a white diagonal sheen and a heavy gold shadow, which
 * suit a marketing button on the homepage. Beside a second call to action on a
 * product page they read as a stray overlay rather than a finish, and the shadow
 * bleeds over the image on narrow columns.
 */
.pdp-buy > .pdp-atc::before{display:none;}
.pdp-buy > .pdp-atc{box-shadow:none;}
.pdp-buy > .pdp-atc:hover{transform:none;box-shadow:none;}
.pdp-buy > .pdp-buynow:hover{transform:none;}

/*
 * Close the space between "Out of stock" and Notify Me.
 *
 * With the buy row collapsed, the 48px left between them came from the
 * variation form's own trailing margin plus the availability block's — neither
 * visible in the gap itself, which is why chasing the elements between the two
 * found nothing. Both are zeroed for the out-of-stock state only, so the
 * in-stock layout keeps its breathing room.
 */

/*
 * Out-of-stock selection: close the space between "Out of stock" and the
 * Notify Me button that answers it.
 *
 * The 60px came from two boxes, neither of them between the two elements —
 * which is why sweeping the DOM in the gap found nothing.
 *
 *   32px  WooCommerce's own `.woocommerce div.product form.cart{margin-bottom:2em}`.
 *         At specificity (0,3,2) it beat a (0,3,0) override — the same fight the
 *         price colour lost twice. `form.variations_form.cart` scores (0,4,1)
 *         and wins on class count.
 *
 *   16px  The `p.stock` margin inside the availability block. It never showed up
 *         on any element in the gap because it had COLLAPSED out through two
 *         ancestors and was being reported as `.woocommerce-variation` height.
 *         Forcing those ancestors to flex "fixed" the collapse and made the gap
 *         worse, which is how it was finally identified; zeroing the margin at
 *         its source is the actual fix.
 *
 * `:has()` was tried first and matched nothing here, so the scope is a class
 * that variations.js sets alongside the row's own `hidden` attribute.
 */
.pdp-info.is-unavailable-selection form.variations_form.cart{margin-bottom:0;}
.pdp-info.is-unavailable-selection .single_variation_wrap{margin-top:0;}
.pdp-info.is-unavailable-selection .woocommerce-variation{margin:0;}
.pdp-info.is-unavailable-selection .woocommerce-variation-availability{margin:0;}
.pdp-info.is-unavailable-selection .woocommerce-variation-availability .stock{margin:0;}
.pdp-info.is-unavailable-selection .woocommerce-variation-add-to-cart{margin:0;gap:0;min-height:0;}
.pdp-info.is-unavailable-selection .pdp-buy--notify{margin-top:0;}
