:root{
  --ink:#221D1A; --ink-soft:#6E655B; --line:#E7DCCB;
  --gold:#B9873E; --gold-text:#8A6429; --serif:'Fraunces', serif; --sans:'Manrope', sans-serif; --mono:'IBM Plex Mono', monospace;
  --ease-slow:cubic-bezier(.16,1,.3,1);
}
*{box-sizing:border-box; -webkit-tap-highlight-color:transparent;}
/* No overflow-x:hidden here — it was masking a real overflow bug
   (index.html's .blob) rather than fixing one; see .blob's own comment.
   Removing it on purpose means any future horizontal-overflow bug shows
   up as an actual scrollbar during testing instead of disappearing
   silently. */
body{
  margin:0; color:var(--ink); font-family:var(--sans); font-weight:300;
  background:
    radial-gradient(circle at 15% 20%, rgba(255,235,214,0.9), transparent 45%),
    radial-gradient(circle at 85% 15%, rgba(226,214,235,0.7), transparent 50%),
    radial-gradient(circle at 50% 90%, rgba(210,225,222,0.7), transparent 55%),
    #F6F1E8;
}
.wrap{max-width:1080px; margin:0 auto; padding:0 30px;}
:focus-visible{outline:1px solid var(--gold-text); outline-offset:3px;}
::selection{background:var(--gold); color:#fff;}
/* Scoped here, not on body — these are app-like interactive chrome (tap
   targets, the cart drawer, icon buttons), not reading content. Text
   pages (FAQ, Shipping & Returns, the ring-size table, etc.) need to stay
   selectable; a body-wide rule silently blocked that everywhere. */
.card, .medallion, .tray, .icon-btn, .whatsapp-float{
  -webkit-user-select:none; -moz-user-select:none; -ms-user-select:none; user-select:none;
}

/* Cross-document view transitions, site-wide. Pure progressive
   enhancement — browsers that don't support this at-rule simply ignore
   it and navigate normally, so there's no fallback branch to maintain.
   Root crossfade covers every navigation (kept brief so it never reads
   as latency on an unrelated page-to-page click); the named
   product-photo-<id> groups (set inline by products.js/product.html on
   matching grid-medallion and PDP-hero images) get the slower, authored
   duration below so a tapped photo visibly morphs into the PDP hero
   instead of two unrelated images crossfading. */
@view-transition{
  navigation:auto;
}
::view-transition-group(*){
  animation-duration:450ms;
  animation-timing-function:var(--ease-slow);
}
::view-transition-group(root){
  animation-duration:200ms;
}
@media (prefers-reduced-motion:reduce){
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*){
    animation:none !important;
  }
}

.grain{
  position:fixed; inset:0; pointer-events:none; z-index:3; opacity:0.035; mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-repeat:repeat;
}

header{padding:20px 0; text-align:center; position:relative;}
.logo{font-family:var(--serif); font-style:italic; font-weight:500; font-size:22px; text-decoration:none; color:inherit;}
.logo .mark{font-family:var(--sans); font-size:11px; color:var(--gold-text); vertical-align:super; margin-left:2px;}
.nav{display:flex; justify-content:center; gap:22px; margin-top:12px; flex-wrap:wrap;}
.nav a{
  font-family:var(--mono); font-size:10px; letter-spacing:0.08em; text-transform:uppercase;
  color:var(--ink-soft); text-decoration:none; padding-bottom:3px; border-bottom:1px solid transparent;
  transition:border-color 0.5s var(--ease-slow), color 0.5s var(--ease-slow);
}
@media (hover:hover) and (pointer:fine){
  .nav a:hover{color:var(--ink); border-color:var(--gold);}
}

/* Cart + Share sit as icons in the header's top-right corner, on the same
   line as the logo, rather than competing with Shop/Shipping/About for a
   slot in the text nav below — this is what actually decluttered the nav
   down to the essentials. right:30px/18px mirror .wrap's own horizontal
   padding at each breakpoint, so the icons line up with the page's edge. */
.icon-cluster{
  position:absolute; top:50%; right:30px; transform:translateY(-50%);
  display:flex; align-items:center; gap:16px;
}
.icon-btn{
  position:relative; display:inline-flex; align-items:center; justify-content:center;
  background:none; border:none; padding:0; margin:0; cursor:pointer; color:var(--ink-soft);
  text-decoration:none; -webkit-appearance:none; appearance:none;
  opacity:1; transition:opacity 0.3s var(--ease-slow), color 0.4s var(--ease-slow);
}
.icon-btn svg{width:20px; height:20px; display:block;}
.icon-btn.fading{opacity:0;}
.icon-btn.copied{color:var(--gold-text);}
@media (hover:hover) and (pointer:fine){
  .icon-btn:hover{color:var(--ink);}
}
.icon-badge{
  position:absolute; top:-6px; right:-9px; min-width:15px; height:15px; padding:0 3px;
  border-radius:50%; background:var(--gold); color:#fff; font-family:var(--mono); font-size:9px;
  display:flex; align-items:center; justify-content:center; line-height:1;
}
@media (max-width:760px){
  .icon-cluster{right:18px;}
}

.eyebrow{font-family:var(--mono); font-size:11px; letter-spacing:0.18em; color:var(--gold-text); text-transform:uppercase; margin:0 auto 15px;}
h2{font-family:var(--serif); font-style:italic; font-weight:400; font-size:20px; margin:36px 0 12px;}
p{color:var(--ink-soft); font-size:15px; line-height:1.8; margin:0 0 14px;}
.contact-line{
  font-family:var(--mono); font-size:11px; letter-spacing:0.05em; color:var(--ink);
  padding-bottom:4px; border-bottom:1px solid var(--line); display:inline-block;
}

/* ---------- PRODUCT GRID / CARD / MEDALLION (shop + product-detail "similar pieces") ---------- */
/* minmax(0,1fr), not bare 1fr: a bare 1fr track still won't shrink below its
   item's content-minimum width, so the fixed-width medallion + card padding
   was forcing each column wider than its real share of the row, pushing the
   second column off the right edge of the viewport on mobile (at the time,
   clipped by a since-removed body{overflow-x:hidden} — see that rule's own
   comment — so it wasn't even visible as a scrollbar, just silently cut
   off "Cascade Tennis Bracelet" etc.). padding-top/bottom only (not the
   shorthand): a shorthand here would reset padding-left/right to 0 on this
   element too, which fights any element that also carries .wrap for the
   horizontal inset (equal specificity, whichever rule is declared later in
   the file wins the whole property) — this class is used as .grid alone in
   some places and .grid.wrap in others, so it must never touch horizontal. */
.grid{display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:24px 32px; padding-top:36px; padding-bottom:48px;}
/* 4 columns needs more width than .wrap's usual 1080px to leave room for
   the fixed 190px medallion (4 cols at 1080px would squeeze each card's
   content area to ~171px, narrower than the medallion itself) — widen
   just the shop-grid sections, not every .wrap on the page. Scoped to
   .grid.wrap specifically so product.html's "You May Also Like" (plain
   .grid, always exactly 3 items) is untouched. */
.grid.wrap{max-width:1320px; grid-template-columns:repeat(4, minmax(0,1fr));}
.card{
  position:relative;
  min-width:0;
  background:rgba(255,255,255,0.55); backdrop-filter:blur(6px); border:1px solid var(--line);
  border-radius:58% 42% 51% 49% / 46% 55% 45% 54%;
  transition:border-radius 0.9s var(--ease-slow), transform 0.7s var(--ease-slow), box-shadow 0.6s var(--ease-slow);
  /* pan-y, not the default: lets a vertical finger-drag scroll the page
     normally while the swipe-to-wishlist gesture (products.js) reads
     horizontal movement itself — same division of labor as .pdp-hero's
     own swipe gallery. */
  touch-action:pan-y;
}
/* Swap to a live 1:1 drag-follow while a swipe is in progress — the
   default transition above would otherwise fight every intermediate
   transform update with its own 0.7s easing, turning finger-tracking
   into a laggy chase instead of an immediate follow. */
.card.dragging{transition:none;}
/* A sibling of .card-link, not nested inside it — a <button> inside an
   <a> is invalid HTML (interactive content can't nest) and would also
   need stopPropagation to avoid triggering the card's own navigation on
   every save/unsave tap. Sitting beside the link instead avoids both. */
.wishlist-btn{
  position:absolute; top:14px; right:14px; z-index:3;
  width:32px; height:32px; border-radius:50%; border:1px solid var(--line); background:rgba(255,255,255,0.7);
  color:var(--ink-soft); cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0;
  transition:border-color 0.4s var(--ease-slow), color 0.4s var(--ease-slow);
}
.wishlist-btn svg{width:16px; height:16px; display:block;}
.wishlist-btn.saved{color:var(--gold-text);}
.wishlist-btn.saved svg{fill:currentColor;}
.wishlist-btn svg.pop, .pdp-wishlist svg.pop{animation:squish 0.45s var(--ease-slow);}
@media (hover:hover) and (pointer:fine){
  .wishlist-btn:hover{border-color:var(--gold);}
}
/* A rounded/circular element's own clipped-off corners aren't hit-testable
   — a tap there falls straight through to whatever's behind. display:contents
   used to give .card-link no box of its own, so those dead corners (the
   medallion's circle, the card's organic blob) fell through past the link
   entirely, landing on the bare grid section: a real, silent no-op tap.
   Giving the link its own plain rectangular box (no border-radius, so no
   dead corners of its own) sitting behind the medallion/text makes every
   pixel inside the card's padding — including the medallion's clipped
   corners — resolve to this anchor. */
.card-link{
  display:flex; flex-direction:column;
  padding:32px 30px 26px; text-align:center;
  text-decoration:none; color:inherit;
}
@media (hover:hover) and (pointer:fine){
  .card:hover{
    border-radius:42% 58% 46% 54% / 58% 45% 55% 42%;
    transform:translateY(-5px);
    box-shadow:0 26px 44px -28px rgba(34,29,26,0.28);
  }
}
/* Touch has no hover to trigger the shape-shift above, so give it its own
   passive liquid motion instead of sitting static — and a tap squish in
   place of the hover-scale, using :active (safe on touch, unlike :hover).
   The ambient loop uses a plain ease-in-out, not --ease-slow: that curve
   front-loads all its motion into the first third of a duration, which
   reads as a snap-then-pause on every keyframe of a looping animation
   instead of a continuous drift. */
@media (hover:none){
  .card{ animation:cardMorph 16s ease-in-out infinite; transition:transform 0.35s var(--ease-slow); }
  .card:active{ transform:scale(0.98); }
  .medallion{ transition:transform 0.35s var(--ease-slow); }
  .medallion:active{ transform:scale(0.94); }
}
@keyframes cardMorph{
  0%,100%{border-radius:58% 42% 51% 49% / 46% 55% 45% 54%;}
  33%{border-radius:45% 55% 42% 58% / 58% 42% 55% 45%;}
  66%{border-radius:55% 45% 58% 42% / 42% 58% 45% 55%;}
}
.card h3{font-family:var(--serif); font-style:italic; font-weight:400; font-size:22px; margin:0 0 10px;}
.meta{display:flex; flex-wrap:wrap; gap:16px; justify-content:center; max-width:190px; margin:0 auto 14px;}
.meta span{
  font-family:var(--mono); font-size:9.5px; letter-spacing:0.08em; text-transform:uppercase;
  color:var(--ink-soft); padding-bottom:4px; border-bottom:1px solid var(--line);
}
.medallion{
  position:relative; width:190px; height:190px; margin:0 auto 14px; padding:0;
  border-radius:50%; border:none; cursor:pointer; background:rgba(255,255,255,0.4);
  display:flex; align-items:center; justify-content:center;
  transition:transform 0.6s var(--ease-slow);
}
.medallion img{width:76%; height:76%; object-fit:contain; filter:drop-shadow(0 14px 18px rgba(34,29,26,0.22));}
/* Applied by trackPhotoLoad() (products.js) to whichever container holds
   the <img> — .medallion or .pdp-hero — only when the photo isn't already
   cached/decoded, and removed the instant it loads or errors. If the
   script never runs, images just render exactly as before: nothing here
   depends on JS to become visible, only to add a loading sweep on top. */
.photo-shimmer::before{
  content:''; position:absolute; inset:0; border-radius:inherit; z-index:1; pointer-events:none;
  background:linear-gradient(100deg, transparent 35%, rgba(255,255,255,0.75) 50%, transparent 65%);
  background-size:250% 100%;
  animation:shimmerSweep 1.3s ease-in-out infinite;
}
@keyframes shimmerSweep{ 0%{background-position:250% 0;} 100%{background-position:-250% 0;} }
@media (prefers-reduced-motion:reduce){
  .photo-shimmer::before{animation:none; opacity:0.5;}
}
@media (hover:hover) and (pointer:fine){
  .medallion:hover{transform:scale(1.04);}
}
/* Scroll-in state, toggled by ScrollReveal() in motion.js. Classes only
   ever land on the element via JS, never as .medallion's own default —
   if the script fails to run, cards stay fully visible (progressive
   enhancement), never stuck invisible. The transition lives here rather
   than added to .medallion's own rule so hover/tap keep their original
   timing once .reveal-in finishes and both classes are removed. */
.medallion.reveal-init{
  opacity:0; transform:scale(0.7);
  transition:opacity 0.7s var(--ease-slow), transform 0.7s var(--ease-slow);
}
.medallion.reveal-init.reveal-in{ opacity:1; transform:scale(1); }
/* right/bottom are measured from the medallion's bounding square, but a
   circle never reaches its bounding square's corners — so the values below
   are chosen to land the badge roughly tangent to the visible circle
   (inset ≈ R·(1-1/√2)) rather than mostly hanging off past its edge. */
.amount-ball{
  position:absolute; right:22px; bottom:20px; z-index:2; width:62px; height:62px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--serif); font-style:italic; font-size:10px; letter-spacing:-0.02em; color:var(--ink);
  box-shadow:0 8px 16px -8px rgba(34,29,26,0.4);
  padding:0 4px; box-sizing:border-box; overflow:hidden;
}
.amount-ball.gold{background:var(--gold);}
.amount-ball.silver{background:#9CA09B;}
.medallion .ripple{position:absolute; inset:14%; border-radius:50%; border:1px solid var(--gold); opacity:0; pointer-events:none;}
.medallion.pulse{animation:squish 0.55s var(--ease-slow);}
.medallion.pulse .ripple{animation:rippleOut 1.1s var(--ease-slow);}
@keyframes rippleOut{ 0%{opacity:0.55; transform:scale(1);} 100%{opacity:0; transform:scale(1.65);} }
@keyframes squish{
  0%{transform:scale(1,1);} 30%{transform:scale(1.14,0.88);} 60%{transform:scale(0.92,1.07);} 100%{transform:scale(1,1);}
}

/* ---------- SEARCH OVERLAY ---------- */
/* Same frosted-glass full-screen treatment as the PDP zoom overlay
   (product.html's .zoom-overlay) — one shared visual language for
   "overlay takes over the screen," not two different modal styles. */
.search-overlay{
  position:fixed; inset:0; z-index:95; display:none; justify-content:center;
  background:rgba(246,241,232,0.97); backdrop-filter:blur(24px);
  opacity:0; transition:opacity 0.4s var(--ease-slow);
  padding:100px 24px 40px; overflow-y:auto;
}
.search-overlay.open{display:flex;}
.search-overlay.visible{opacity:1;}
.search-panel{width:100%; max-width:600px;}
.search-close{
  position:absolute; top:20px; right:20px; z-index:2;
  width:42px; height:42px; border-radius:50%; border:1px solid var(--line); background:rgba(255,255,255,0.7);
  color:var(--ink); cursor:pointer; font-size:20px; line-height:1;
  display:flex; align-items:center; justify-content:center;
  transition:border-color 0.4s var(--ease-slow);
}
@media (hover:hover) and (pointer:fine){
  .search-close:hover{border-color:var(--gold);}
}
.search-input{
  width:100%; font-family:var(--serif); font-style:italic; font-size:26px; color:var(--ink);
  border:none; border-bottom:1px solid var(--line); background:none; padding:8px 0 14px; outline:none;
}
.search-input::placeholder{color:var(--ink-soft);}
.search-results{margin-top:28px; display:flex; flex-direction:column; gap:2px;}
.search-result-item{
  display:flex; align-items:center; gap:16px; padding:10px 8px; text-decoration:none; color:inherit;
  border-radius:14px; transition:background 0.3s var(--ease-slow);
}
@media (hover:hover) and (pointer:fine){
  .search-result-item:hover{background:rgba(255,255,255,0.6);}
}
.search-result-item img{width:44px; height:44px; object-fit:contain; border-radius:50%; background:rgba(255,255,255,0.5); flex:0 0 auto;}
.search-result-name{font-family:var(--serif); font-style:italic; font-size:15px; display:block;}
.search-result-meta{font-family:var(--mono); font-size:9.5px; letter-spacing:0.04em; text-transform:uppercase; color:var(--ink-soft);}
.search-empty{font-family:var(--serif); font-style:italic; font-size:15px; color:var(--ink-soft); text-align:center; margin-top:36px;}

/* ---------- FLOATING WHATSAPP CONTACT ---------- */
/* Brand palette, not WhatsApp's own bright green + logo — a saturated
   green circle would clash hard with the pearlescent/gold/silver "Liquid
   Metal" direction. Same frosted-glass treatment as the zoom overlay's
   own circular controls, so it reads as part of this site, not a bolted-on
   third-party widget. */
.whatsapp-float{
  position:fixed; right:20px; bottom:calc(20px + env(safe-area-inset-bottom, 0px)); z-index:35;
  width:52px; height:52px; border-radius:50%;
  background:rgba(255,255,255,0.75); backdrop-filter:blur(10px);
  border:1px solid var(--line); color:var(--ink); cursor:pointer;
  display:flex; align-items:center; justify-content:center; padding:0;
  box-shadow:0 8px 20px -8px rgba(34,29,26,0.35);
  transition:bottom 0.6s var(--ease-slow), border-color 0.4s var(--ease-slow), background 0.4s var(--ease-slow);
}
.whatsapp-float svg{width:24px; height:24px; display:block;}
@media (hover:hover) and (pointer:fine){
  .whatsapp-float:hover{border-color:var(--gold); background:#fff;}
}
body.tray-active .whatsapp-float{
  bottom:calc(var(--tray-bar-h, 90px) + 20px + env(safe-area-inset-bottom, 0px));
}

/* ---------- SELECTION TRAY / CART DRAWER ---------- */
.tray{
  position:fixed; left:0; right:0; z-index:40;
  /* Tied to the same --tray-bar-h the rest of the file already measures
     the real tray bar with (set live by tray-ui.js), instead of a
     standalone guessed pixel value — that guess had no way to notice if
     the bar ever actually grew taller than it assumed. The extra 60px is
     margin so it's fully clear of the viewport, not just flush with it. */
  bottom:calc(-1 * var(--tray-bar-h, 90px) - 60px);
  background:rgba(246,241,232,0.9); backdrop-filter:blur(14px);
  border-top:1px solid var(--gold);
  transition:bottom 0.7s var(--ease-slow);
}
.tray.visible{bottom:0;}
.tray-inner{
  max-width:1080px; margin:0 auto; cursor:pointer;
  padding:14px 30px calc(18px + env(safe-area-inset-bottom, 0px));
  display:flex; flex-direction:column; gap:10px;
}
.tray-row{display:flex; align-items:center; gap:24px; flex-wrap:wrap;}
.cart-panel{
  /* minmax(0,0fr), not bare 0fr: a bare 0fr row still sizes to its item's
     content-minimum height, so the closed panel was never actually 0 —
     it sat at the empty-cart message's full height (~43px) year-round,
     which is exactly the sliver of "cart strip" that kept showing even
     with nothing in the cart. Same fix as the grid-column overflow bug,
     just in the row dimension this time. */
  display:grid; grid-template-rows:minmax(0,0fr); overflow:hidden;
  border-top:1px solid var(--line);
  transition:grid-template-rows 0.6s var(--ease-slow);
}
.tray.open .cart-panel{grid-template-rows:1fr; max-height:min(60vh, calc(var(--vvh, 100vh) - var(--tray-bar-h, 90px) - 20px));}
.cart-inner{
  min-height:0; max-height:min(60vh, calc(var(--vvh, 100vh) - var(--tray-bar-h, 90px) - 20px)); overflow-y:auto;
  max-width:1080px; margin:0 auto; display:flex; flex-direction:column; gap:16px;
  padding:18px 30px calc(24px + env(safe-area-inset-bottom, 0px));
  scrollbar-width:thin; scrollbar-color:var(--gold) transparent;
}
.cart-inner::-webkit-scrollbar{width:6px;}
.cart-inner::-webkit-scrollbar-track{background:transparent;}
.cart-inner::-webkit-scrollbar-thumb{background:var(--line); border-radius:3px;}
.cart-inner::-webkit-scrollbar-thumb:hover{background:var(--gold);}
.cart-line{display:flex; align-items:center; gap:14px;}
.cart-line-icon{width:34px; height:34px; flex:0 0 auto; overflow:hidden; border-radius:8px; background:rgba(0,0,0,0.03);}
.cart-line-icon img{width:100%; height:100%; object-fit:contain;}
.cart-line-info{flex:1; min-width:0;}
.cart-line-name{font-family:var(--serif); font-style:italic; font-size:15px;}
.cart-line-meta{font-family:var(--mono); font-size:9px; letter-spacing:0.06em; text-transform:uppercase; color:var(--ink-soft); margin-top:2px;}
.qty{display:flex; align-items:center; gap:10px;}
.qty button{
  width:24px; height:24px; border-radius:50%; border:1px solid var(--line); background:transparent;
  font-family:var(--mono); font-size:13px; color:var(--ink); cursor:pointer; line-height:1; padding:0;
  display:flex; align-items:center; justify-content:center;
  transition:border-color 0.4s var(--ease-slow), color 0.4s var(--ease-slow);
}
@media (hover:hover) and (pointer:fine){
  .qty button:hover{border-color:var(--gold); color:var(--gold-text);}
}
.qty span{font-family:var(--mono); font-size:12px; min-width:16px; text-align:center;}
.cart-line-price{font-family:var(--serif); font-style:italic; font-size:14px; white-space:nowrap;}
.cart-empty{font-family:var(--serif); font-style:italic; font-size:15px; color:var(--ink-soft); text-align:center; margin:10px 0;}
.checkout-note{font-family:var(--mono); font-size:10px; letter-spacing:0.02em; color:var(--ink-soft); text-align:center; margin:8px 0 0;}
.cart-discount-row{
  display:flex; justify-content:space-between; font-family:var(--mono); font-size:11px;
  letter-spacing:0.02em; color:var(--gold-text); padding-top:4px; border-top:1px dashed var(--line);
}
.bundle-nudge{font-family:var(--mono); font-size:10.5px; letter-spacing:0.02em; color:var(--gold-text); text-align:center; margin:0;}
.cart-checkout{
  margin-top:4px; align-self:center;
  padding:14px 30px; font-family:var(--mono); font-size:11.5px; letter-spacing:0.08em; text-transform:uppercase;
  color:#F6F1E8; background:var(--ink); border:none; border-radius:999px; cursor:pointer;
  transition:background 0.5s var(--ease-slow);
}
@media (hover:hover) and (pointer:fine){
  .cart-checkout:hover{background:#3a322b;}
}
.order-sent-title{font-family:var(--serif); font-style:italic; font-size:17px; color:var(--ink); text-align:center; margin:6px 0 0;}
.order-sent-note{font-family:var(--sans); font-size:13px; color:var(--ink-soft); text-align:center; max-width:44ch; margin:0 auto; line-height:1.7;}
.order-sent-back{
  align-self:center; font-family:var(--mono); font-size:10px; letter-spacing:0.06em; text-transform:uppercase;
  color:var(--ink-soft); background:none; border:none; cursor:pointer; padding:2px 0 4px;
  border-bottom:1px solid var(--line); margin:-4px 0 4px;
}
@media (hover:hover) and (pointer:fine){
  .order-sent-back:hover{color:var(--ink); border-color:var(--gold);}
}
.tray-thumbs{display:flex; align-items:center;}
.tray-thumbs .thumb{
  width:38px; height:38px; border-radius:50%; background:#fff; border:1px solid var(--line);
  display:flex; align-items:center; justify-content:center; margin-left:-11px; overflow:hidden;
  animation:settle 0.6s var(--ease-slow);
}
/* Animates FROM hidden, not TO visible — visible/full-size is this
   element's own real CSS state (nothing sets opacity/transform on it
   outside this keyframe), so the reduced-motion rule above (which
   deletes this animation entirely, not just skips to its end) simply
   leaves the thumb at that real state instead of stranding it at a
   `forwards`-held opacity:0 with no animation left to reach past it. */
@keyframes settle{ from{ opacity:0; transform:scale(0.85); } }
.tray-thumbs .thumb img{width:78%; height:78%; object-fit:contain;}
.tray-label{font-family:var(--mono); font-size:10.5px; letter-spacing:0.07em; text-transform:uppercase; color:var(--ink-soft);}
.tray-right{margin-left:auto; text-align:right;}
.tray-total{font-family:var(--serif); font-style:italic; font-size:19px;}
.tray-note{font-family:var(--mono); font-size:10px; color:var(--ink-soft); letter-spacing:0.09em; text-transform:uppercase; text-align:center;}

.travel-icon{
  position:fixed; border-radius:50%; background:#fff; border:1px solid var(--line);
  display:flex; align-items:center; justify-content:center; pointer-events:none; z-index:60; overflow:hidden;
}
.travel-icon img{width:100%; height:100%; object-fit:contain;}

/* ---------- TESTIMONIALS (shared: homepage + product page) ---------- */
.testimonials{padding-top:8px; padding-bottom:8px; text-align:center;}
.testimonial-grid{display:grid; grid-template-columns:repeat(auto-fit, minmax(240px, 1fr)); gap:26px; margin:0 0 26px; text-align:left;}
.testimonial-card{
  background:rgba(255,255,255,0.55); backdrop-filter:blur(6px); border:1px solid var(--line);
  padding:30px 26px; border-radius:42% 58% 46% 54% / 54% 42% 58% 46%;
}
.quote-mark{font-family:var(--serif); font-style:italic; font-size:36px; color:var(--gold-text); line-height:1; display:block; margin-bottom:6px;}
.testimonial-rating{margin:0 0 10px; letter-spacing:1px;}
.testimonial-rating .star{color:var(--line); font-size:13px;}
.testimonial-rating .star.filled{color:var(--gold);}
.testimonial-quote{font-family:var(--serif); font-style:italic; font-size:15px; color:var(--ink); line-height:1.6; margin:0 0 14px;}
.testimonial-name{font-family:var(--mono); font-size:10px; letter-spacing:0.06em; text-transform:uppercase; color:var(--ink-soft); margin:0;}
.testimonial-empty{font-family:var(--serif); font-style:italic; font-size:15px; color:var(--ink-soft); max-width:40ch; margin:0 auto 20px;}
.founder-note{font-family:var(--sans); font-size:13.5px; color:var(--ink-soft); max-width:46ch; margin:0 auto; line-height:1.7;}
.founder-note strong{color:var(--ink); font-weight:500;}

/* Base bottom padding is just breathing room, not tray clearance — the tray
   is fixed-position and off-screen (bottom:-140px) whenever the cart is
   empty and closed, which is the common case, so reserving ~100px for it
   unconditionally left that much dead space at the foot of every page for
   most visitors. Real clearance is added only while the tray is actually
   on-screen, via .tray-active (toggled in tray-ui.js) sized off the same
   --tray-bar-h the tray itself uses. */
footer{padding:34px 0 34px; text-align:center; font-family:var(--mono); font-size:10px; letter-spacing:0.08em; color:var(--ink-soft);}
body.tray-active footer{padding-bottom:calc(var(--tray-bar-h, 90px) + 24px + env(safe-area-inset-bottom, 0px));}
.footer-link{color:var(--ink-soft); text-decoration:none; border-bottom:1px solid transparent; padding-bottom:1px; transition:color 0.4s var(--ease-slow), border-color 0.4s var(--ease-slow);}
@media (hover:hover) and (pointer:fine){
  .footer-link:hover{color:var(--ink); border-color:var(--gold);}
}

@media (max-width:760px){
  header{padding:14px 0;}
  .nav{margin-top:8px; gap:16px;}
  /* Just Shop up top on mobile — Shipping & Returns/About live in the
     footer (present on every page regardless of device), and Cart/Share
     are icons in the header's own corner, not part of this list at all. */
  .nav-secondary{display:none;}
  .grid{grid-template-columns:repeat(2, minmax(0,1fr)); gap:14px; padding-top:20px; padding-bottom:32px;}
  /* .grid.wrap (0,2,0 specificity) needs its own mobile override — it'd
     otherwise beat this plain .grid rule (0,1,0) despite coming first in
     the file, since specificity outranks source order regardless of which
     media query matched. */
  .grid.wrap{grid-template-columns:repeat(2, minmax(0,1fr)); max-width:none;}
  .wrap{padding:0 18px;}
  .card-link{padding:22px 12px 16px;}
  .medallion{width:128px; height:128px; margin:0 auto 12px;}
  .amount-ball{width:46px; height:46px; font-size:8.5px; right:14px; bottom:13px;}
  .card h3{font-size:16px; margin:0 0 8px;}
  .meta{gap:6px; max-width:128px; margin:0 auto 14px;}
  .meta span{font-size:8px;}
  .tray-row{flex-direction:column; align-items:flex-start; gap:12px;}
  .tray-right{margin-left:0; text-align:left;}
  .cart-line{flex-wrap:wrap;}
  .cart-checkout{width:100%;}
}
@media (prefers-reduced-motion: reduce){ *{animation:none !important; transition-duration:0.001ms !important;} }
