/* ═══════════════════════════════════════════════════════════════════════
   WEDPAGE — SHARED DESIGN SYSTEM
   Loaded by index.html, catalog.html and template.html.

   Every section sits on the SAME rich black ground, and there are NO
   horizontal dividing lines — no
   rules between sections, rows, list items, nav or footer. Both are hard
   constraints from the client, and together they mean structure has to come
   from whitespace, type scale, a numbered rhythm and CLOSED SHAPES: a card
   with a border on all four sides is a box, not a divider, and is the only
   place a hairline is allowed. The only saturated colour on any page belongs
   to the PRODUCT — the invitation screens, the palette swatches, the 3px
   gradient hairline on a card. Nothing chrome-like is allowed to be
   coloured, so the eye reads colour as "this is a theme" every single time.
   The one exception to the no-rules rule is artwork INSIDE the invitation
   itself (its schedule band, its own site nav), which is the product being
   sold rather than page chrome.
   ═══════════════════════════════════════════════════════════════════════ */
:root{
  /* Native controls -- date pickers, scrollbars, form autofill -- follow this
     rather than staying light and punching holes in the page. */
  color-scheme:dark;
  /* Rich black, not grey-black: the ground carries a trace of warmth so the
     gold sits IN it rather than on top of it, and so the ivory type reads as
     candlelit rather than as pure #fff on #000 — which is what makes a dark
     page look cheap. --surface is the ground a card sits on, one step up from
     the page so a panel reads as lifted without a heavy border. */
  --paper:#0a0807;
  --surface:#141110;
  --surface-2:#1c1817;
  --ink:#f2ebe0;
  --ink-2:#b8ada0;
  --muted:#8a7f73;
  --line:rgba(242,235,224,.13);
  --line-2:rgba(242,235,224,.07);
  --gold:#c9a961;
  --gold-soft:#e2c88f;
  /* On a dark ground a drop shadow does nothing; depth has to come from a
     lifted surface plus a faint warm halo. */
  --shadow-card:0 1px 0 rgba(242,235,224,.05) inset, 0 30px 70px -40px rgba(0,0,0,.9);
  --shadow-lift:0 1px 0 rgba(242,235,224,.07) inset, 0 44px 90px -44px rgba(0,0,0,.95);
  --r-lg:30px; --r-md:20px; --r-sm:12px;
  --pad-x:clamp(1.15rem, 5vw, 4.5rem);
  --maxw:1180px;
  --nav-h:66px;
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:auto; -webkit-text-size-adjust:100%; scroll-padding-top:calc(var(--nav-h) + 18px)}
body{
  font-family:'Cormorant Garamond',Georgia,serif;
  color:var(--ink); background:var(--paper);
  overflow-x:hidden; -webkit-font-smoothing:antialiased;
}
img{max-width:100%; display:block}
a{color:inherit}
button{font:inherit; color:inherit; background:none; border:0}
:focus-visible{outline:2px solid var(--gold); outline-offset:3px; border-radius:4px}

.wrap{max-width:var(--maxw); margin:0 auto; padding-inline:var(--pad-x)}
.wide{max-width:1340px}

/* ---- the numbered section header, the main structural device ----
   Every header is CENTRED on the section's full width: label, title,
   description, and any CTA under them. The measures below are what stop a
   centred block from breaking badly — `balance` levels the lines of a
   display title so no single word is left alone on the last one, `pretty`
   keeps the lede off orphans, and the ch widths are wide enough that a
   sentence is not chopped into a narrow column in the middle of a 1180px
   page. Change the widths and you change where every headline wraps. */
/* Tightened. 7rem above every header plus a 4rem ornament above that put the
   best part of a screen of nothing between one section and the next, which on
   a laptop meant scrolling past emptiness to reach the next thing. */
.sec-head{display:grid; gap:.7rem; justify-items:center; text-align:center;
  padding-block:clamp(2.2rem,4.5vh,3.4rem) clamp(1.4rem,3vh,2.2rem)}
/* the section number and the eyebrow sit together with no rule between them
   — the gold number is the marker, the whitespace above is the separator */
.sec-meta{display:flex; align-items:center; justify-content:center; gap:1rem; flex-wrap:wrap}
.sec-eyebrow{font:600 .68rem/1 Montserrat; letter-spacing:.42em; text-transform:uppercase; color:var(--muted)}
/* Section titles follow the HERO exactly: Playfair 700, all caps, one colour
   the whole way through, balanced onto two lines. The gold italic run that
   used to close each headline is gone — the hero dropped it, so every header
   below drops it too, or the page reads as two different type systems. */
.sec-title{
  font-family:'Playfair Display',serif; font-weight:700;
  font-size:clamp(1.2rem,3.2vw,2rem); line-height:1.08; letter-spacing:-.025em;
  max-width:22ch; margin-inline:auto; text-align:center; text-wrap:balance;
  text-transform:uppercase;
}
/* kept as a selector so old markup still renders — it just no longer differs */
.sec-title em{font-style:normal; color:inherit}
/* the lede matches the hero's: no `balance`, a wide measure, so it settles on
   two full lines instead of a narrow three-line column */
.sec-lede{font-size:clamp(1.02rem,1.6vw,1.2rem); font-style:italic; color:var(--muted);
  max-width:76ch; margin-inline:auto; text-align:center; line-height:1.55}
/* a CTA row that belongs to the section header rather than to a card */
.sec-cta{display:flex; gap:.7rem; justify-content:center; flex-wrap:wrap; margin-top:.4rem}
/* `balance` levels the LINES of a title but will still break a phrase in the
   middle of it — "They / walk in." reads as two thoughts. Wrap the closing
   phrase of a headline in .nb to keep it whole. Only ever on SHORT phrases,
   and released below 420px where a locked phrase would overflow instead. */
.nb{white-space:nowrap}
@media (max-width:420px){ .nb{white-space:normal} }
/* an italic terminal ends further right than an upright one, so the word
   after a gold italic run reads as though it is touching it — buy back a hair */
h1 em, .spin-title em{padding-right:.07em}

/* ---- the section break ----
   The client's rule is no horizontal dividing lines, so this is deliberately
   NOT a rule: it is a centred ornament built only from CLOSED shapes — a gold
   diamond between two rings — with the real separation still coming from the
   whitespace around it. It carries the break where a hairline would have. */
.brk{
  display:flex; align-items:center; justify-content:center; gap:.85rem;
  padding-block:clamp(1rem,2.6vh,1.8rem); pointer-events:none;
}
.brk i{
  display:block; width:6px; height:6px; border-radius:50%;
  border:1px solid var(--gold); opacity:.55;
}
.brk b{
  display:block; width:11px; height:11px; transform:rotate(45deg);
  background:linear-gradient(135deg, var(--gold), rgba(200,169,97,.45));
  box-shadow:0 0 0 4px rgba(200,169,97,.1);
}
/* on paper-toned sections the ornament sits on the same ground, so nothing
   about it reads as a band across the page */
.brk.on-paper{background:var(--paper)}

/* ---- buttons ---- */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:.6rem;
  font:600 .78rem/1 Montserrat; letter-spacing:.09em; text-transform:uppercase;
  padding:1.02rem 1.9rem; border-radius:999px; text-decoration:none;
  border:1px solid transparent; cursor:pointer; white-space:nowrap;
  transition:transform .25s cubic-bezier(.2,.9,.3,1.1), box-shadow .25s ease, background .25s ease, color .25s ease, border-color .25s ease;
}
.btn-solid{background:var(--ink); color:var(--paper); box-shadow:0 14px 34px -14px rgba(0,0,0,.9)}
.btn-solid:hover{transform:translateY(-2px); box-shadow:0 22px 44px -16px rgba(20,17,14,.75)}
.btn-ghost{background:transparent; color:var(--ink); border-color:var(--line)}
.btn-ghost:hover{transform:translateY(-2px); border-color:rgba(242,235,224,.30); box-shadow:0 14px 30px -18px rgba(20,17,14,.5)}
.btn-sm{padding:.82rem 1.35rem; font-size:.7rem}
.btn-xs{padding:.62rem 1rem; font-size:.62rem; letter-spacing:.11em}
.btn[disabled]{opacity:.4; pointer-events:none}

/* ═══════════ NAV ═══════════ */
nav.site{
  position:fixed; inset:0 0 auto 0; z-index:120;
  display:flex; align-items:center; justify-content:space-between;
  gap:1rem; padding:.95rem var(--pad-x); min-height:var(--nav-h);
  background:var(--gold);
  backdrop-filter:saturate(180%) blur(16px); -webkit-backdrop-filter:saturate(180%) blur(16px);
  transition:box-shadow .35s ease;
}
/* a shadow, never a rule — the nav lifts off the page instead of being
   underlined by one */
nav.site.stuck{box-shadow:0 14px 34px -26px rgba(20,17,14,.75)}
/* The lockup ships as solid black on transparency, which is invisible on this
   ground. Rather than keeping a second gold PNG in step with the first, the
   artwork is used as a MASK and the gold is painted through it -- one file,
   any colour, and it stays crisp at every density. The img itself remains in
   the markup, carrying the alt text, and is only made transparent. */
.logo{display:flex; align-items:baseline; gap:.5rem; text-decoration:none; position:relative; color:#0d0b0a}
.logo img{opacity:0}
.logo::before{
  content:''; position:absolute; left:0; top:50%; transform:translateY(-50%);
  width:calc(26px * 1400 / 297); height:26px; background:#0d0b0a;
  -webkit-mask:url(wedpage-lockup-black.png) no-repeat left center/contain;
  mask:url(wedpage-lockup-black.png) no-repeat left center/contain;
}
.logo b{font-family:'Playfair Display',serif; font-weight:600; font-size:1.42rem; letter-spacing:-.01em; color:#0d0b0a}
.logo i{display:none}
/* The gap is the bar's shock absorber. Fixed at 2.1rem the six items needed
   1207px of nav and the links only stopped colliding at 1118px viewport, so
   every laptop narrower than that either wrapped or overflowed. Letting the
   gap shrink with the viewport keeps the real link bar down to ~1000px, which
   is where the burger takes over. */
.nav-links{display:flex; gap:clamp(.95rem, 2.1vw, 2.1rem); list-style:none}
/* nowrap, or a two-word item like "Make it yours" breaks onto a second line
   and the bar silently doubles in height while still claiming to fit — that is
   how the nav looked cramped at 1100px even though nothing was overflowing */
.nav-links a{font:500 .7rem/1 Montserrat; letter-spacing:.18em; text-transform:uppercase; color:rgba(13,11,10,.82); text-decoration:none; transition:color .25s; white-space:nowrap}
.nav-links a:hover, .nav-links a.on{color:#0d0b0a}
.nav-right{display:flex; align-items:center; gap:.55rem}

/* saved-templates counter — the wishlist lives in localStorage and is shared
   by every page, so the badge has to be rebuilt on each load */
.nav-save{
  position:relative; display:inline-flex; align-items:center; gap:.45rem;
  font:600 .64rem/1 Montserrat; letter-spacing:.14em; text-transform:uppercase; color:rgba(13,11,10,.9);
  border:1px solid rgba(13,11,10,.34); border-radius:999px; padding:.7rem .95rem; text-decoration:none;
  transition:border-color .25s ease, color .25s ease;
}
.nav-save:hover{border-color:rgba(13,11,10,.5); color:#0d0b0a}
.nav-save b{
  min-width:17px; height:17px; padding:0 4px; border-radius:999px; background:#0d0b0a; color:var(--gold);
  font:700 .58rem/17px Montserrat; text-align:center;
}
.nav-save[data-n="0"]{display:none}

/* Sign in / Dashboard — the way back into the product.
   Borrows .nav-save's shape so the two sit as a pair rather than as a button
   bolted on beside it. */
.nav-signin{
  display:inline-flex; align-items:center; gap:.45rem;
  font:600 .64rem/1 Montserrat; letter-spacing:.14em; text-transform:uppercase; color:rgba(13,11,10,.9);
  border:1px solid rgba(13,11,10,.34); border-radius:999px; padding:.7rem .95rem; text-decoration:none;
  transition:border-color .25s ease, color .25s ease;
}
.nav-signin:hover{border-color:rgba(13,11,10,.5); color:#0d0b0a}
/* The icon carries the meaning on a phone, where the CTA has already been
   dropped for space and the word would push the burger off the edge. */
.nav-signin-i{display:none; flex:none}
@media (max-width:640px){
  .nav-signin-i{display:block}
  .nav-signin span{
    position:absolute; width:1px; height:1px; margin:-1px; padding:0;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
  }
  .nav-signin{padding:.6rem .68rem}
}

/* hamburger */
.burger{display:none; width:42px; height:42px; border:0; border-radius:0; cursor:pointer; position:relative}
.burger span{position:absolute; left:12px; right:12px; height:1.5px; background:#0d0b0a; transition:transform .35s cubic-bezier(.2,.9,.3,1.1), opacity .2s}
.burger span:nth-child(1){top:16px} .burger span:nth-child(2){top:21px} .burger span:nth-child(3){top:26px}
body.menu .burger span:nth-child(1){transform:translateY(5px) rotate(45deg)}
body.menu .burger span:nth-child(2){opacity:0}
body.menu .burger span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}
.menu-sheet{
  position:fixed; inset:0; z-index:115; background:var(--paper);
  padding:calc(var(--nav-h) + 2.2rem) var(--pad-x) 2.4rem;
  display:flex; flex-direction:column; gap:.2rem;
  opacity:0; pointer-events:none; transform:translateY(-8px);
  transition:opacity .32s ease, transform .42s cubic-bezier(.2,.9,.3,1.1);
}
body.menu .menu-sheet{opacity:1; pointer-events:auto; transform:none}
.menu-sheet a{
  font-family:'Playfair Display',serif; font-size:clamp(1.7rem,7vw,2.4rem); font-weight:400;
  text-decoration:none; padding:.62rem 0;
  display:flex; justify-content:space-between; align-items:baseline;
}
.menu-sheet a span{font:600 .58rem/1 Montserrat; letter-spacing:.2em; color:var(--muted); text-transform:uppercase}
/* The CTA is a BUTTON, not another menu link.
   `.menu-sheet a` is (0,1,1) and out-specifies `.btn` at (0,1,0), so the
   button was inheriting the sheet's nav typography — Playfair Display at 27px,
   `justify-content:space-between` and the links' own vertical padding — which
   blew the label straight through both ends of its pill. Restate the button's
   own type here at (0,2,0) so it wins. */
.menu-sheet .btn{
  margin-top:1.6rem; align-self:flex-start;
  font:600 .78rem/1 Montserrat; letter-spacing:.09em; text-transform:uppercase;
  justify-content:center; padding:1.02rem 1.9rem;
}

/* ═══════════ DEVICE MOCKUPS ═══════════
   Three frames, one screen contract. Everything inside a frame is laid out
   against `.dv-screen`, which is a size container — so the invitation inside
   scales with the frame instead of needing a separate stylesheet per device.
   The `font-size:12px` line before the clamp is a deliberate fallback: if a
   browser does not understand cqw the clamp is dropped and 12px survives. */
.dv{position:relative; --dv-w:100%}
/* The screen wears the invitation's COVER, which is what the live preview
   behind it actually opens on — not the printed card's paper. See the note on
   `vars()` in site.js: painting these with --bg meant the handset showed an
   ivory card while the Live preview button an inch below opened the same
   template in deep crimson. */
.dv-screen{
  position:relative; overflow:hidden;
  background:linear-gradient(170deg,var(--cover,#2a1410),var(--cover2,#4a1c18));
  color:var(--cover-ink,#f0cd83);
  container-type:size;
}
.dv-screen::after{
  content:''; position:absolute; inset:0; z-index:9; pointer-events:none;
  background:linear-gradient(118deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,.05) 20%, transparent 36%);
  transition:opacity .5s ease;
}
.dv-screen.live::after{opacity:.35}

/* ---- iPhone ---- */
.dv-phone{width:var(--dv-w); aspect-ratio:9/19.2;
  background:linear-gradient(158deg,#26262d,#0a0a0d 42%,#17171c);
  border-radius:14.5%/6.9%; padding:2.4%;
  box-shadow:0 34px 70px -30px rgba(20,17,14,.75), inset 0 0 0 1.5px #34343d, 0 0 0 1px rgba(20,17,14,.10)}
.dv-phone .dv-screen{position:absolute; inset:2.4%; border-radius:12.5%/5.9%}
.dv-phone .island{position:absolute; top:3.1%; left:50%; transform:translateX(-50%);
  width:28%; height:3.2%; min-height:14px; background:#000; border-radius:14px; z-index:12}
.dv-phone .bt{position:absolute; width:2.5px; background:#3a3a44; border-radius:2px}
.dv-phone .bt.r{right:-2px; top:22%; height:10%}
.dv-phone .bt.l1{left:-2px; top:17%; height:6.4%}
.dv-phone .bt.l2{left:-2px; top:25.5%; height:6.4%}

/* ---- iPad ---- */
.dv-pad{width:var(--dv-w); aspect-ratio:3/4.1;
  background:linear-gradient(158deg,#2b2b31,#0d0d10 46%,#1b1b20);
  border-radius:4.6%/3.4%; padding:2.9%;
  box-shadow:0 34px 70px -30px rgba(20,17,14,.7), inset 0 0 0 1.5px #3a3a42, 0 0 0 1px rgba(20,17,14,.10)}
.dv-pad .dv-screen{position:absolute; inset:2.9%; border-radius:2.6%/1.9%}
.dv-pad .cam{position:absolute; top:1.35%; left:50%; transform:translateX(-50%);
  width:5px; height:5px; border-radius:50%; background:#05050a; box-shadow:inset 0 0 0 1px #2f2f38; z-index:12}

/* ---- MacBook ----
   The lid and the base are separate boxes so the hinge line stays crisp at
   any width; the base is a trapezoid drawn with a clip-path rather than a
   border trick, which keeps the bottom edge straight on high-dpi screens. */
.dv-mac{width:var(--dv-w); display:flex; flex-direction:column; align-items:center}
.dv-mac .lid{
  width:100%; aspect-ratio:16/10.4; position:relative; padding:1.15% 1.15% 2.6%;
  background:linear-gradient(160deg,#3a3a42,#141418 44%,#26262c);
  border-radius:1.9%/2.9%; box-shadow:0 26px 54px -28px rgba(20,17,14,.7), inset 0 0 0 1.2px #45454e;
}
.dv-mac .dv-screen{position:absolute; inset:1.15% 1.15% 2.6%; border-radius:.5%}
.dv-mac .cam{position:absolute; top:.5%; left:50%; transform:translateX(-50%);
  width:4px; height:4px; border-radius:50%; background:#0a0a0e; box-shadow:inset 0 0 0 1px #33333c; z-index:12}
.dv-mac .base{
  width:118%; height:clamp(9px,1.5vw,15px); position:relative; flex:0 0 auto;
  background:linear-gradient(180deg,#c9c9d1,#8f8f99 40%,#5c5c66);
  clip-path:polygon(1.4% 0, 98.6% 0, 96% 100%, 4% 100%);
  box-shadow:0 16px 26px -16px rgba(20,17,14,.6);
}
.dv-mac .base::after{content:''; position:absolute; left:50%; top:0; transform:translateX(-50%);
  width:13%; height:38%; background:#7d7d87; border-radius:0 0 8px 8px}

/* ---- what sits on the glass ---- */
.dv-status{position:absolute; top:2.4%; left:0; right:0; z-index:6; display:flex; justify-content:space-between;
  padding:0 8%; font:600 .58em/1 Montserrat; opacity:.7}
.dv-mac .dv-status, .dv-pad .dv-status{padding:0 4%; top:1.6%}

/* ═══════════ THE INVITATION POSTER ═══════════
   One markup contract, three layouts. `.pv` sets a font-size from the size
   of its container, and every measurement inside is in em — so the same
   nodes read correctly at 120px wide in a card and at 900px in a hero. */
.pv{
  position:absolute; inset:0; z-index:2; display:flex; flex-direction:column;
  align-items:center; justify-content:center; text-align:center; overflow:hidden;
  transition:opacity .45s ease;
  font-size:12px;                        /* fallback if cqw is unsupported */
  font-size:clamp(7px, 4.9cqw, 24px);
}
.dv-screen.live .pv{opacity:0; pointer-events:none}
.pv-in{display:flex; flex-direction:column; align-items:center; gap:.62em; width:100%; padding:0 8%}
.pv-kicker{font:600 .58em/1.4 Montserrat; letter-spacing:.24em; text-transform:uppercase; opacity:.62}
.pv-names{font-family:'Great Vibes',cursive; font-size:2.5em; line-height:1.06}
.pv-div{width:56%; display:flex; align-items:center; gap:.6em; font-size:.86em; color:var(--cover-ink,var(--metal))}
.pv-div::before,.pv-div::after{content:''; flex:1; height:1px; background:currentColor; opacity:.65}
.pv-line{font-size:.92em; font-style:italic; opacity:.82}
.pv-date{font:600 .78em/1 Montserrat; letter-spacing:.16em}
.pv-venue{font-size:.82em; opacity:.75}
.pv-rsvp{font:700 .62em/1 Montserrat; letter-spacing:.2em; padding:.72em 1.9em;
  border:1px solid var(--cover-ink,var(--metal)); border-radius:999px; margin-top:.2em}
.pv-note{font-size:.76em; opacity:.7}
.pv-sched{display:grid; gap:.4em; width:84%; margin-top:.1em;
  border-top:1px solid currentColor; border-bottom:1px solid currentColor; padding:.6em 0}
.pv-sched span{display:flex; justify-content:space-between; gap:1em; font:500 .72em/1 Montserrat; letter-spacing:.06em; opacity:.78}
.pv-tag{font-family:'Great Vibes',cursive; font-size:1.25em; opacity:.8}

/* phone: the tall stack */
.pv-phone .pv-in{gap:.66em; padding:0 9%}

/* tablet: same stack, more air, a wider rule */
.pv-pad{font-size:12px; font-size:clamp(7px, 3.4cqw, 26px)}
.pv-pad .pv-in{gap:.78em; padding:0 14%}
.pv-pad .pv-names{font-size:2.9em}

/* laptop: this is a WEBSITE, not a stretched card — a top bar with the
   monogram and section words, a centred masthead, and a row of photo
   plates, which is what the real thing looks like on a wide screen */
.pv-mac{font-size:12px; font-size:clamp(6px, 2.05cqw, 22px); justify-content:flex-start}
.pv-mac .pv-bar{
  width:100%; display:flex; align-items:center; justify-content:space-between;
  padding:1.15em 2.1em; border-bottom:1px solid currentColor; opacity:.9;
}
.pv-mac .pv-mono{font-family:'Great Vibes',cursive; font-size:1.5em; color:var(--cover-ink,var(--metal))}
.pv-mac .pv-nav{display:flex; gap:1.5em; font:600 .58em/1 Montserrat; letter-spacing:.2em; text-transform:uppercase; opacity:.66}
.pv-mac .pv-in{padding:2.4em 2.4em 0; gap:.66em}
.pv-mac .pv-names{font-size:3.5em}
.pv-mac .pv-div{width:34%}
.pv-plates{display:flex; gap:.7em; width:78%; margin-top:1.5em}
.pv-plates i{
  flex:1; aspect-ratio:4/3; border-radius:.35em; display:block;
  background:linear-gradient(150deg, color-mix(in oklab, var(--metal) 42%, transparent), color-mix(in oklab, var(--ink-c) 16%, transparent));
  box-shadow:inset 0 0 0 1px color-mix(in oklab, var(--metal) 55%, transparent);
}
.pv-plates i:nth-child(2){transform:translateY(-.5em)}

/* ═══════════ LIVE PREVIEW ═══════════
   Nothing streams until a visitor presses play, and exactly ONE preview runs
   anywhere on the site at a time — a page of simultaneous real-time 3D
   scenes is not a page, it is a space heater. */
/* The radius is on the FRAME ITSELF, not just on `.dv-screen`.
   An iframe is promoted to its own compositing layer, and a composited layer
   is not clipped by an ancestor's `overflow:hidden` + `border-radius` — so the
   running preview rendered as a hard SQUARE whose corners punched straight
   through the handset's rounded screen, while the still poster (a plain
   element, no layer of its own) curved correctly. `inherit` takes the exact
   percentage radius of whichever frame it is inside, so the phone, tablet and
   laptop each keep their own corner. `overflow:hidden` stays for the poster. */
.live-frame{position:absolute; inset:0; width:100%; height:100%; border:0; z-index:3;
  opacity:0; transition:opacity .55s ease; background:var(--cover,var(--bg2)); display:block;
  border-radius:inherit}
.dv-screen.ready .live-frame{opacity:1}
.live-start{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); z-index:8;
  display:inline-flex; align-items:center; gap:.5rem; cursor:pointer;
  font:700 8.5px/1 Montserrat; letter-spacing:.18em; text-transform:uppercase;
  /* deliberately NOT themed: half these screens are near-white and half are
     near-black, and a control tinted with --ink-c disappears into one or the
     other. Neutral dark glass reads on every palette. */
  color:#fff; background:rgba(14,11,9,.72); border:1px solid rgba(255,255,255,.34);
  backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
  border-radius:999px; padding:11px 17px; white-space:nowrap;
  box-shadow:0 14px 30px -14px rgba(0,0,0,.7);
  transition:transform .25s cubic-bezier(.2,.9,.3,1.1), box-shadow .25s ease;
  /* never wider than the screen it sits on, whatever the label says */
  max-width:88%;
}
/*
 * Below ~170px of screen the pill cannot hold its own words. Every measurement
 * in this control is a fixed px — fine on the 900px hero frame it was designed
 * against, but a catalog thumbnail's screen is 89–116px wide and the pill is
 * 142px, so "Live preview" was clipped at BOTH ends on all ten cards at every
 * viewport. `.dv-screen` is already a size container, so the frame itself can
 * say when the label no longer fits and the control becomes what it always was
 * underneath: a play button.
 */
@container (max-width:170px){
  .live-start{padding:9px; gap:0; border-width:1px}
  .live-start .live-lbl{display:none}
}
.live-start .pl{width:15px; height:15px; border-radius:50%; background:#fff; flex:0 0 auto;
  display:flex; align-items:center; justify-content:center}
.live-start .pl::before{content:''; border-style:solid; border-width:3.5px 0 3.5px 6px;
  border-color:transparent transparent transparent #14110e; margin-left:1.5px}
.live-start:hover{transform:translate(-50%,-50%) scale(1.05)}
.dv-screen.live .live-start{display:none}
.live-load{
  position:absolute; left:0; right:0; bottom:0; z-index:4; padding:14% 8% 7%;
  background:linear-gradient(to top, var(--bg2) 40%, transparent);
  text-align:center; font:600 8px/1.6 Montserrat; letter-spacing:.2em; text-transform:uppercase; opacity:0;
  transition:opacity .3s ease; pointer-events:none;
}
.dv-screen.live:not(.ready) .live-load{opacity:.72}
.live-x{
  position:absolute; z-index:13; top:6.5%; right:7%; display:none;
  width:26px; height:26px; border-radius:50%; cursor:pointer; align-items:center; justify-content:center;
  background:rgba(12,10,9,.55); color:#fff; border:1px solid rgba(255,255,255,.35);
  font:600 11px/1 Montserrat; backdrop-filter:blur(6px);
}
.dv-screen.live .live-x{display:flex}
.dv-mac .live-x, .dv-pad .live-x{top:4%; right:3.5%}

/* ---- device tab strip ---- */
.dv-tabs{display:inline-flex; gap:2px; padding:3px; border-radius:999px; background:rgba(242,235,224,.06); border:1px solid var(--line-2)}
.dv-tabs button{
  display:inline-flex; align-items:center; gap:.4rem; cursor:pointer;
  font:600 .58rem/1 Montserrat; letter-spacing:.14em; text-transform:uppercase; color:var(--muted);
  padding:.55rem .85rem; border-radius:999px; transition:background .25s ease, color .25s ease, box-shadow .25s ease;
}
.dv-tabs button svg{width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:1.5}
.dv-tabs button:hover{color:var(--ink)}
.dv-tabs button[aria-selected="true"]{background:var(--surface-2); color:var(--ink); box-shadow:0 1px 2px rgba(20,17,14,.1), 0 8px 18px -12px rgba(20,17,14,.5)}
.dv-tabs.mini button{padding:.42rem .6rem; font-size:.52rem; letter-spacing:.1em}
.dv-tabs.mini button svg{width:11px; height:11px}

/* the stage keeps the same footprint whichever device is selected, so the
   grid does not jump a hundred pixels when someone taps "Laptop" */
.dv-stage{display:flex; align-items:center; justify-content:center; position:relative}
.dv-stage .dv{transition:opacity .3s ease}
.dv-stage .dv[hidden]{display:none}

/* ═══════════ SWATCHES / CHIPS / BADGES ═══════════ */
.sw{display:flex; gap:.3rem; align-items:center}
.sw i{width:17px; height:17px; border-radius:50%; box-shadow:inset 0 0 0 1px rgba(242,235,224,.22)}
.sw.lg i{width:26px; height:26px}
.chip{
  display:inline-flex; align-items:center; gap:.45rem; cursor:pointer; white-space:nowrap;
  font:600 .66rem/1 Montserrat; letter-spacing:.1em; color:var(--ink-2);
  border:1px solid var(--line); background:var(--surface); border-radius:999px; padding:.78rem 1.15rem;
  transition:border-color .25s ease, color .25s ease, background .25s ease, transform .25s cubic-bezier(.2,.9,.3,1.1);
  box-shadow:none;
}
.chip:hover{transform:translateY(-1px); border-color:rgba(242,235,224,.28); color:var(--ink)}
.chip[aria-pressed="true"], .chip.on{background:var(--ink); color:var(--paper); border-color:var(--ink)}
.chip b{font-weight:600; opacity:.55}
.chip[aria-pressed="true"] b{opacity:.6}
.flag{
  font:700 .56rem/1 Montserrat; letter-spacing:.16em; text-transform:uppercase;
  padding:.42rem .68rem; border-radius:999px; background:var(--ink); color:var(--paper);
}
.flag.q{background:var(--surface); color:var(--ink); border:1px solid var(--line)}

/* price */
/* price
   Set in the serif the headlines use, not the UI sans: a price is the number a
   buyer weighs, and in a shop like this it should read as part of the editorial
   voice rather than as interface furniture.
   - tabular figures so a column of prices aligns on the decimal and does not
     jitter when the currency toggles;
   - the currency symbol a touch smaller and lifted, the way set prices are
     traditionally cut, so the digits carry the weight;
   - the struck-through compare-at price is quieter and set apart, so it reads
     as history rather than as a second price. */
.price{
  font:500 1.05rem/1 'Playfair Display', Georgia, serif;
  letter-spacing:0; color:var(--ink);
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
  white-space:nowrap;
}
.price .cur-sym{font-size:.78em; padding-right:.06em; vertical-align:.06em; opacity:.75}
.price s{
  margin-right:.5rem; font-size:.78em; font-weight:400;
  color:var(--muted); text-decoration-thickness:1px; text-underline-offset:2px;
}

/* currency switch */
.cur{display:inline-flex; padding:3px; border-radius:999px; background:rgba(242,235,224,.06); border:1px solid var(--line-2)}
.cur button{cursor:pointer; font:700 .66rem/1 Montserrat; padding:.5rem .78rem; border-radius:999px; color:var(--muted); transition:.25s}
.cur button[aria-pressed="true"]{background:var(--surface-2); color:var(--ink); box-shadow:none}

/* save / wishlist heart */
.save{
  width:34px; height:34px; border-radius:50%; cursor:pointer; display:grid; place-items:center;
  background:rgba(28,24,23,.86); border:1px solid var(--line); backdrop-filter:blur(6px);
  transition:transform .3s cubic-bezier(.2,.9,.3,1.3), border-color .25s ease;
}
.save svg{width:15px; height:15px; fill:none; stroke:var(--ink); stroke-width:1.6; transition:fill .25s ease, stroke .25s ease}
.save:hover{transform:scale(1.1)}
.save[aria-pressed="true"] svg{fill:#c2384c; stroke:#c2384c}
.save[aria-pressed="true"]{animation:pop .42s cubic-bezier(.2,.9,.3,1.4)}
@keyframes pop{0%{transform:scale(1)}45%{transform:scale(1.28)}100%{transform:scale(1)}}

/* ═══════════ TOAST ═══════════ */
.toast{
  position:fixed; left:50%; bottom:26px; z-index:300; transform:translate(-50%,24px);
  background:var(--ink); color:var(--paper); border-radius:999px; padding:.9rem 1.5rem;
  font:600 .68rem/1 Montserrat; letter-spacing:.12em; text-transform:uppercase;
  box-shadow:0 24px 50px -20px rgba(0,0,0,.6); opacity:0; pointer-events:none;
  transition:opacity .3s ease, transform .45s cubic-bezier(.2,.9,.3,1.1);
}
.toast.on{opacity:1; transform:translate(-50%,0)}

/* ═══════════ FOOTER ═══════════ */
footer.site{padding-block:clamp(2rem,5vh,3.4rem) 2rem; background:var(--paper)}
.foot-cta{
  border:1px solid var(--line); border-radius:var(--r-lg); padding:clamp(2rem,5vw,3.4rem);
  display:grid; gap:1.1rem; justify-items:center; text-align:center; position:relative; overflow:hidden;
  background:
    radial-gradient(120% 130% at 50% 0%, rgba(201,169,97,.16), rgba(10,8,7,0) 62%),
    var(--surface);
}
.foot-cta h3{font-family:'Playfair Display',serif; font-weight:400; font-size:clamp(1.7rem,4.4vw,2.7rem); line-height:1.1; max-width:22ch; text-wrap:balance}
.foot-cta h3 em{font-style:italic; color:var(--gold)}
.foot-cta p{font-style:italic; color:var(--muted); max-width:56ch; font-size:1.05rem; text-wrap:pretty}
/* Brand, Shop, The product, Help, Legal. It was declared with four columns
   while the storefront footer had five children, so Legal wrapped onto a row
   of its own. */
.foot-grid{display:grid; gap:clamp(1.6rem,4vw,3rem); grid-template-columns:1.5fr 1fr 1fr 1fr 1fr; margin-top:clamp(2.6rem,6vh,4rem)}
.foot-grid h4{font:600 .6rem/1 Montserrat; letter-spacing:.24em; text-transform:uppercase; color:var(--muted); margin-bottom:1.1rem}
.foot-grid ul{list-style:none; display:grid; gap:.62rem}
.foot-grid a{text-decoration:none; font-size:1.02rem; color:var(--ink-2); transition:color .25s}
.foot-grid a:hover{color:var(--gold)}
.foot-brand p{font-style:italic; color:var(--muted); margin-top:.8rem; max-width:30ch; line-height:1.5}
.foot-bottom{
  display:flex; flex-wrap:wrap; gap:1rem; justify-content:space-between; align-items:center;
  margin-top:clamp(2.8rem,7vh,4.2rem);
  font:500 .64rem/1.6 Montserrat; letter-spacing:.12em; text-transform:uppercase; color:var(--muted);
}

/* ═══════════ RESPONSIVE ═══════════ */
@media (max-width:900px){
  .foot-grid{grid-template-columns:1fr 1fr}
  .foot-brand{grid-column:1 / -1}
}
@media (max-width:640px){
  /* logo + saved + CTA + burger does not fit a phone: the CTA is the one that
     goes, because the menu sheet opens with the same button at the bottom */
  nav.site .nav-right > .btn{display:none}
}
/* The full nav bar needs ~970px and NOT 768.
   Measured: logo 117 + links 470 + right group 298 + 82 of padding = 967. The
   burger used to arrive only at 768px, so every width from 769 to 966 — the
   whole tablet range and a half-width laptop window — drew a nav that could
   not fit: the link list wrapped onto two lines and the "Choose a template"
   button was pushed clean off the right edge, half a word visible. The
   switch belongs at the width the bar actually stops fitting. */
@media (max-width:1000px){
  .nav-links{display:none}
  .burger{display:block}
  .nav-save span{display:none}
}
@media (max-width:768px){
  .sec-title{max-width:none}
  .foot-grid{grid-template-columns:1fr; gap:2rem}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important; animation-iteration-count:1 !important; transition-duration:.001ms !important}
}

/* ═══════════ LEGAL PAGES ═══════════
   Terms, privacy, refunds and the rest. Composed in
   src/storefront/legal/chrome.ts rather than authored as HTML files, because
   several pages sharing one shell beats one shell copied per page.

   Set narrow on purpose. These are the only pages on the site that are read
   rather than scanned, and a refund window nobody can find because the line
   runs 140 characters wide is a refund window that does not exist. */
.legal-page{background:var(--paper)}
.legal{
  /* ~74 characters at the body size. Deliberately not `74ch`: this element
     inherits the serif, where a ch is much narrower than in the sans the body
     sets, and the column came out at 420px. */
  max-width:calc(46rem + 2 * var(--pad-x));
  padding-top:calc(var(--nav-h) + clamp(1.5rem,6vh,3.6rem));
  padding-bottom:clamp(2.5rem,9vh,6rem);
}
.legal-head{border-bottom:1px solid var(--line); padding-bottom:1.6rem; margin-bottom:2.2rem}
.legal-eyebrow{
  font-family:Montserrat,system-ui,sans-serif; font-size:.7rem; font-weight:600;
  letter-spacing:.14em; text-transform:uppercase; color:var(--muted); margin:0 0 .9rem
}
.legal-eyebrow a{color:inherit; text-decoration:none}
.legal-eyebrow a:hover{color:var(--gold)}
.legal-head h1{
  font-family:'Playfair Display',Georgia,serif; font-weight:600;
  font-size:clamp(1.75rem,4.6vw,2.9rem); line-height:1.12; letter-spacing:-.01em;
  margin:0; text-wrap:balance; color:var(--ink)
}
/* On this page.
   These documents absorbed seven former pages each, so the reader who arrived
   for one clause needs a way in that is not the scrollbar. Generated from the
   h2 ids in chrome.ts -- if a section has no id it is deliberately not a
   destination and stays out of the list. */
.legal-toc{
  border:1px solid var(--line); border-radius:var(--r-sm); background:var(--line-2);
  padding:1.1rem 1.3rem; margin:0 0 2.4rem
}
.legal-toc h2{
  font-family:Montserrat,system-ui,sans-serif; font-weight:600; color:var(--muted);
  font-size:.7rem; letter-spacing:.14em; text-transform:uppercase; margin:0 0 .75rem
}
.legal-toc ol{
  margin:0; padding:0; list-style:none;
  display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.4rem 1.4rem;
  font-family:Montserrat,system-ui,sans-serif; font-size:.82rem
}
.legal-toc a{
  color:var(--ink-2); text-decoration:none; display:block; padding:.12rem 0;
  border-bottom:1px solid transparent
}
.legal-toc a:hover{color:var(--gold); border-bottom-color:var(--gold-soft)}

.legal-body{font-size:1rem; line-height:1.75; color:var(--ink-2)}
/* A sticky nav will otherwise land on top of whatever an anchor jumps to. */
.legal-body [id]{scroll-margin-top:calc(var(--nav-h) + 1rem)}
.legal-body h2{
  font-family:'Playfair Display',Georgia,serif; font-weight:600; color:var(--ink);
  font-size:clamp(1.25rem,3.4vw,1.4rem); line-height:1.25;
  margin:2.6rem 0 .8rem; text-wrap:balance
}
.legal-body h2:first-child{margin-top:0}
/* h3 is a real subheading now that the former pages are sections under an h2.
   The uppercase micro-label it used to be lives on as h4, inside cards. */
.legal-body h3{
  font-family:'Playfair Display',Georgia,serif; font-weight:600; color:var(--ink);
  font-size:1.06rem; line-height:1.3; margin:1.9rem 0 .55rem; text-wrap:balance
}
.legal-body h4,.legal-card h3{
  font-family:Montserrat,system-ui,sans-serif; font-weight:600; color:var(--ink);
  font-size:.82rem; letter-spacing:.1em; text-transform:uppercase; margin:0 0 .6rem
}
.legal-body p{margin:0 0 1.05rem}
.legal-body ul,.legal-body ol{margin:0 0 1.2rem; padding-left:1.35rem}
.legal-body li{margin-bottom:.5rem}
.legal-body li::marker{color:var(--gold-soft)}
.legal-body a{color:var(--ink); text-decoration:underline; text-underline-offset:3px;
  text-decoration-color:var(--gold-soft); text-decoration-thickness:1px;
  overflow-wrap:anywhere}
.legal-body a:hover{color:var(--gold)}
.legal-body b,.legal-body strong{color:var(--ink); font-weight:600}
.legal-body code{
  font-size:.86em; background:var(--line-2); padding:.12em .4em; border-radius:4px;
  overflow-wrap:anywhere
}

/* A pulled-out statement -- the refund summary, the address block. One per
   section at most; it stops working the moment everything is a card. */
.legal-card{
  background:var(--line-2); border:1px solid var(--line); border-radius:var(--r-sm);
  padding:1.15rem 1.3rem; margin:1.4rem 0 1.6rem
}
.legal-card p:last-child{margin-bottom:0}

/* Retention periods and refund windows are tables because they are tables.
   Own scroll container so a wide one never sends the page sideways. */
.legal-table{overflow-x:auto; margin:0 0 1.5rem; -webkit-overflow-scrolling:touch}
.legal-body table{
  width:100%; border-collapse:collapse; margin:0 0 1.5rem;
  font-family:Montserrat,system-ui,sans-serif; font-size:.86rem
}
.legal-table table{margin:0}
.legal-body thead th{
  text-align:left; font-weight:600; color:var(--ink); font-size:.72rem;
  letter-spacing:.09em; text-transform:uppercase;
  border-bottom:1px solid var(--line); padding:0 .8rem .6rem 0
}
.legal-body tbody td{
  vertical-align:top; padding:.72rem .8rem .72rem 0;
  border-bottom:1px solid var(--line-2); line-height:1.55
}
.legal-body tbody tr:last-child td{border-bottom:0}

/* Legal column in the footer: four documents, so it sits like the others. */
.foot-legal ul{line-height:1.9}
.foot-legal [aria-current="page"]{color:var(--gold); font-weight:600}

/* Desktop.

   The reading column stays capped at 74ch -- a legal document set 140
   characters wide is a legal document nobody finishes -- so on a wide screen
   the question is what to do with the room beside it, not how to fill it with
   longer lines. The contents list moves out of the flow and becomes a sticky
   rail: the space is used, the reader always knows where they are in a
   sixteen-section document, and the measure never changes. */
@media (min-width:1080px){
  .legal{
    display:grid;
    grid-template-columns:15rem minmax(0,46rem);
    column-gap:clamp(2.5rem,5vw,4.5rem);
    /* `.wrap` adds padding-inline inside this box, so it has to be in the
       sum or the reading column loses that much width. */
    max-width:calc(15rem + 4.5rem + 46rem + 2 * var(--pad-x));
    padding-bottom:7rem;
    align-items:start;
  }
  .legal-head{grid-column:1 / -1}
  .legal-toc{
    grid-column:1; grid-row:2;
    position:sticky; top:calc(var(--nav-h) + 1.75rem);
    margin:0; padding:0; background:none; border:0;
    max-height:calc(100vh - var(--nav-h) - 4rem);
    overflow-y:auto; overscroll-behavior:contain;
  }
  .legal-toc h2{margin-bottom:.9rem}
  .legal-toc ol{grid-template-columns:1fr; gap:0; font-size:.8rem}
  .legal-toc a{
    padding:.34rem 0 .34rem .8rem; border-bottom:0;
    border-left:1px solid var(--line); color:var(--muted)
  }
  .legal-toc a:hover{color:var(--gold); border-left-color:var(--gold-soft)}
  .legal-body{grid-column:2; grid-row:2}
  .legal-foot{grid-column:2; grid-row:3}
  /* Thin rail scrollbar, so a long contents list does not put a browser
     scrollbar through the middle of the layout. */
  .legal-toc::-webkit-scrollbar{width:3px}
  .legal-toc::-webkit-scrollbar-thumb{background:var(--line); border-radius:3px}
}

/* Very wide screens: nothing widens, it just centres with more air. */
@media (min-width:1600px){
  .legal{padding-top:calc(var(--nav-h) + 4.5rem)}
}

/* Tablet. */
@media (max-width:900px){
  .legal-toc ol{gap:.35rem 1rem}
}

/* Narrow tablet / large phone: two TOC columns stop earning their keep. */
@media (max-width:700px){
  .legal-toc{padding:1rem 1.1rem; margin-bottom:2rem}
  .legal-toc ol{grid-template-columns:1fr; gap:.3rem}
  .legal-toc a{padding:.3rem 0}
}

/* Phone. Tables scroll rather than wrap to two words per line, and every cell
   keeps a floor width so the columns stay readable while scrolling. */
@media (max-width:640px){
  .legal{padding-left:1.15rem; padding-right:1.15rem}
  .legal-body{font-size:.96rem; line-height:1.72}
  .legal-body h2{margin-top:2.1rem}
  .legal-body table{display:block; overflow-x:auto; white-space:nowrap;
    -webkit-overflow-scrolling:touch}
  .legal-body tbody td{white-space:normal; min-width:9rem}
  .legal-body thead th{min-width:9rem}
  .legal-card{padding:1rem 1.05rem}
  .legal-body ul,.legal-body ol{padding-left:1.15rem}
}

/* Small phone. */
@media (max-width:420px){
  .legal{padding-left:1rem; padding-right:1rem}
  .legal-head{padding-bottom:1.2rem; margin-bottom:1.8rem}
  .legal-body h3{font-size:1rem}
}

/* Closing block on every document: when it changed, which version, and the way
   to the other two. Quiet by design -- it is a reference, not a call to act. */
.legal-foot{
  margin-top:3rem; padding-top:1.4rem; border-top:1px solid var(--line);
  font-family:Montserrat,system-ui,sans-serif; font-size:.78rem; color:var(--muted);
  line-height:1.7
}
.legal-foot p{margin:0 0 .3rem}
.legal-foot b{color:var(--ink-2); font-weight:600}
.legal-foot a{color:var(--ink-2); text-decoration:none; border-bottom:1px solid var(--line)}
.legal-foot a:hover{color:var(--gold); border-bottom-color:var(--gold-soft)}

/* An aside inside a legal page: true, needed, and not the point of the
   paragraph it sits under. */
.legal-body .legal-quiet{color:var(--muted); font-size:.92em}
