/*
Theme Name: Save Our Date — v4
Theme URI: https://saveourdate.studio
Author: Save Our Date
Description: Storefront for Save Our Date. Colourful, decorative and crafted — arches, ornament and a full palette on warm paper.
Requires at least: 6.5
Tested up to: 6.9
Requires PHP: 8.0
Version: 4.0.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: saveourdate-v4
*/

/* Palette, type and spacing live in theme.json. This is the component layer.

   The brief: fancy, colourful, creative — but composed rather than loud. This
   storefront IS the first impression of what the studio sells, so a restrained
   shop would undersell the product. The decoration therefore has to be real
   craft rather than noise.

   The organising idea is the ARCH — the shape of an invitation, a doorway, a
   mandap. It repeats at every scale: image frames, cards, dividers, the hero.
   Seven colours are used together but always in the same order, so richness
   reads as a palette rather than a pile.

   The audience is a mix of Indian and international families, so nothing here
   is culture-specific ornament. Eczar carries both Devanagari and Latin, which
   is what lets a bilingual invitation sit in one typeface. */

:root {
  color-scheme: light;
  /* 18px padding, the 50px button (the tallest child), 18px padding, 1px rule */
  --header-h: 87px;
}

* { box-sizing: border-box; }
/* clip, not hidden. overflow-x: hidden makes the element a scroll container,
   and a scroll container is what position: sticky measures against — so the
   header measured itself against the document rather than the window. clip
   stops the same sideways overflow without creating one. */
body { -webkit-font-smoothing: antialiased; overflow-x: clip; }
a { text-decoration: none; }

/* ── Labels & ornament ─────────────────────────────────────────────────── */

.fx-label {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--micro);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--wp--preset--color--terracotta);
  line-height: 1;
}
.fx-label::before {
  content: "";
  width: 26px;
  height: 1px;
  background: currentColor;
  flex: none;
}

/* A small repeating diamond band. Ornament that costs nothing and scales. */
.fx-band {
  height: 14px;
  background-image: radial-gradient(circle at 50% 50%, currentColor 1.6px, transparent 1.7px);
  background-size: 14px 14px;
  opacity: 0.35;
}

.fx-rule {
  border: 0;
  height: 1px;
  margin: 0;
  background: linear-gradient(90deg, transparent, currentColor 15%, currentColor 85%, transparent);
  opacity: 0.28;
}

/* ── Header ────────────────────────────────────────────────────────────── */

/* A block theme wraps every template part in its own element, and that wrapper
   is only as tall as the header inside it. A sticky element cannot travel
   further than its own containing block, so the header came unstuck the instant
   the page moved — on every page, not just the builder. display: contents takes
   the wrapper out of layout and hands .fx-header to .wp-site-blocks, whose box
   is the whole document. */
.wp-site-blocks > header.wp-block-template-part { display: contents; }

.fx-header {
  position: sticky;
  top: 0;
  z-index: 60;
  /* Opaque, not translucent: the logo file carries its own painted cream
     ground rather than transparency, so a see-through header would show it
     as a pale rectangle the moment dark content scrolled behind. */
  background: #FBF7F1;
  border-bottom: 1px solid rgba(59, 42, 58, 0.12);
  padding-inline: clamp(1.375rem, 5vw, 4.5rem);
  column-gap: clamp(1.25rem, 3vw, 3rem);
  row-gap: 0.875rem;
}
.fx-brand { display: flex; align-items: center; }
/* height drives it; width follows the 972:160 source so it can never distort */
.fx-brand img {
  display: block;
  height: clamp(30px, 4.6vw, 42px);
  width: auto;
  flex: none;
}

.fx-nav {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, 2.4vw, 2.25rem);
  font-size: var(--wp--preset--font-size--small);
  font-weight: 500;
}
.fx-nav a { color: var(--wp--preset--color--ink); }
.fx-nav a:hover { color: var(--wp--preset--color--terracotta); }

/* ── Buttons ───────────────────────────────────────────────────────────── */

.fx-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
  min-height: 50px;
  padding: 0 32px;
  border-radius: 999px;
  font-size: var(--wp--preset--font-size--small);
  font-weight: 700;
  letter-spacing: 0.02em;
  transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), background-color 0.3s, color 0.3s;
}
.fx-btn:hover { transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) { .fx-btn:hover { transform: none; } }
.fx-btn--solid { background: var(--wp--preset--color--terracotta); color: var(--wp--preset--color--paper); }
.fx-btn--solid:hover { background: var(--wp--preset--color--ink); color: var(--wp--preset--color--paper); }
.fx-btn--ghost { border: 1.5px solid currentColor; color: var(--wp--preset--color--ink); }
.fx-btn--ghost:hover { background: var(--wp--preset--color--ink); color: var(--wp--preset--color--paper); }

/* ── Sections ──────────────────────────────────────────────────────────── */

.fx-section {
  padding-block: var(--wp--preset--spacing--70);
  padding-inline: clamp(1.375rem, 5vw, 4.5rem);
  position: relative;
}
.fx-section--linen { background: var(--wp--preset--color--linen); }
.fx-section--ink   { background: var(--wp--preset--color--ink); color: var(--wp--preset--color--paper); }
.fx-section--ink a { color: var(--wp--preset--color--ochre); }

/* ── Hero ──────────────────────────────────────────────────────────────── */

.fx-hero {
  /* One screen exactly, so the designs below cannot show a sliver of arch at
     the fold. svh first, then dvh, so mobile browser chrome cannot clip it. */
  min-height: calc(100svh - var(--header-h));
  min-height: calc(100dvh - var(--header-h));
  display: grid;
  align-content: center;
  padding: var(--wp--preset--spacing--40) clamp(1.375rem, 5vw, 4.5rem) var(--wp--preset--spacing--50);
  position: relative;
  overflow: hidden;
}

/* Two soft blooms of colour, drawn from the palette, well under half opacity. */
.fx-hero::before,
.fx-hero::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  filter: blur(70px);
}
.fx-hero::before { top: -14%; right: -6%; width: min(46vw, 560px); aspect-ratio: 1; background: rgba(232, 196, 142, 0.30); }
.fx-hero::after  { bottom: -22%; left: -8%; width: min(38vw, 460px); aspect-ratio: 1; background: rgba(203, 182, 227, 0.26); }

.fx-hero__title {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 600;
  font-size: clamp(2.375rem, 5vw, 4.5rem);
  line-height: 1.06;
  letter-spacing: -0.02em;
  margin: 0;
  max-width: 17ch;
  text-wrap: balance;
}
.fx-hero__title em {
  font-style: italic;
  font-weight: 400;
  color: var(--wp--preset--color--terracotta);
}

.fx-arch {
  border-radius: 22px;
  overflow: hidden;
  position: relative;
}

/* ── Hero film ─────────────────────────────────────────────────────────── */
/* No frame, no keyline, no gradient behind. The arch was fighting the video:
   a 9:16 film inside a 3:4 card had to be letterboxed, and the bars were the
   first thing you saw. The column now takes the film's own proportions, so it
   fills edge to edge with nothing cropped and nothing padded. */

.fx-hero__film {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

@media (prefers-reduced-motion: reduce) { .fx-hero__film { display: none; } }

/* ── Occasion cards ────────────────────────────────────────────────────── */

.fx-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: clamp(1rem, 2vw, 1.75rem); }
.fx-card {
  border-radius: 22px;
  padding: clamp(1.75rem, 3vw, 2.5rem) clamp(1.5rem, 2.4vw, 2rem) clamp(1.5rem, 2.4vw, 2rem);
  min-height: 280px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.875rem;
  position: relative;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.fx-card:hover { transform: translateY(-7px); }
@media (prefers-reduced-motion: reduce) { .fx-card:hover { transform: none; } }
.fx-card::after {
  content: "";
  position: absolute;
  inset: 12px;
  border: 1px solid currentColor;
  border-radius: 22px;
  opacity: 0.28;
  pointer-events: none;
}
.fx-card h3 { margin: 0; font-size: var(--wp--preset--font-size--large); }
.fx-card p { margin: 0; font-size: var(--wp--preset--font-size--small); line-height: 1.55; }
.fx-card__no {
  position: absolute;
  top: clamp(1.25rem, 2vw, 1.75rem);
  left: clamp(1.5rem, 2.4vw, 2rem);
  font-family: var(--wp--preset--font-family--display);
  font-size: 0.875rem;
  letter-spacing: 0.1em;
  opacity: 0.6;
}

/* ── Steps ─────────────────────────────────────────────────────────────── */

.fx-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: clamp(2rem, 4vw, 3.5rem); }
.fx-step__no {
  width: 62px; height: 62px;
  display: grid; place-items: center;
  border-radius: 50%;
  font-family: var(--wp--preset--font-family--display);
  font-weight: 600;
  font-size: 1.375rem;
  margin-bottom: 1.25rem;
}

/* ── Gallery ───────────────────────────────────────────────────────────── */

.gal-count { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--stone); }

.gal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
  /* roomier than before: the tiles were butting into the filters and each other */
  gap: clamp(2.5rem, 4.5vw, 4.5rem) clamp(1.75rem, 3vw, 3rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
}
.gal-card { display: flex; flex-direction: column; gap: 1rem; }
.gal-card[hidden] { display: none; }
.gal-badge { display: inline-flex; align-items: center; gap: 8px; font-size: var(--wp--preset--font-size--micro); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }


.gal-name { font-family: var(--wp--preset--font-family--display); font-weight: 600; font-size: 1.625rem; line-height: 1; }
.gal-meta { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--stone); }
.gal-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.gal-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 20px; border-radius: 999px;
  font-size: var(--wp--preset--font-size--micro); font-weight: 700;
}
.gal-btn--ghost { border: 1.5px solid rgba(59, 42, 58, 0.25); color: var(--wp--preset--color--ink); }
.gal-btn--ghost:hover { border-color: var(--wp--preset--color--ink); }
.gal-btn--solid { background: var(--wp--preset--color--ink); color: var(--wp--preset--color--paper); }
.gal-btn--solid:hover { background: var(--wp--preset--color--terracotta); }
.gal-empty { padding: 3rem 0; color: var(--wp--preset--color--stone); }
.gal-empty[hidden] { display: none; }

/* ── Pricing ───────────────────────────────────────────────────────────── */

.fx-plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: clamp(1.25rem, 2.5vw, 2rem); }
.fx-plan {
  /* Not var(--arch): its vertical radius is a PERCENTAGE of height, so a tall
     pricing card grew a 141px dome where a 300px design tile gets 66px, and
     the inset keyline below drifted from 12px at the sides to 17px over the
     top. A fixed px vertical radius keeps the dome constant, and the ::after
     radius is exactly this minus the inset, so the curves stay parallel. */
  border-radius: 50% 50% 16px 16px / 56px 56px 16px 16px;
  /* The dome eats into the top corners, and the label and price start at
     padding-top on the LEFT edge — exactly where the curve is deepest. At a
     92px dome the curve needed up to 56px of clearance and padding-top gave
     32-48px, so the label rendered outside the card fill at five of six test
     widths. A 56px dome needs at most 34px; the top padding now starts at
     52px, which clears it everywhere from 320px up. */
  padding: clamp(3.25rem, 4.5vw, 4rem) clamp(1.75rem, 3vw, 2.5rem) clamp(2rem, 3.4vw, 3rem);
  display: flex; flex-direction: column; gap: 1.25rem;
  position: relative;
}
.fx-plan::after {
  content: ""; position: absolute; inset: 12px;
  border: 1px solid currentColor; opacity: 0.22;
  border-radius: 50% 50% 6px 6px / 44px 44px 6px 6px; /* 56 minus the 12px inset */
  pointer-events: none;
}
.fx-plan--standard { background: var(--wp--preset--color--linen); color: var(--wp--preset--color--ink); }
/* aubergine, not plum: the gold feature lines measured 3.4:1 on plum and
   6.32:1 here, and gold is what marks the two reasons to upgrade */
.fx-plan--premium  { background: var(--wp--preset--color--ink); color: var(--wp--preset--color--paper); }
.fx-plan__price { font-family: var(--wp--preset--font-family--display); font-weight: 600; font-size: clamp(2.75rem, 5.5vw, 4rem); line-height: 1; }
.fx-plan ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.8rem; }
.fx-plan li { display: flex; gap: 0.7rem; align-items: flex-start; font-size: var(--wp--preset--font-size--small); line-height: 1.5; }
.fx-plan li svg { flex: none; margin-top: 3px; }
.fx-plan li.is-off { opacity: 0.45; }

.fx-addons { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: clamp(0.875rem, 1.6vw, 1.25rem); }
.fx-addon {
  border: 1px solid rgba(59, 42, 58, 0.16);
  border-radius: 14px;
  padding: 1.375rem;
  background: var(--wp--preset--color--paper);
}
.fx-addon b { display: block; font-family: var(--wp--preset--font-family--display); font-weight: 600; font-size: 1.1875rem; margin-bottom: 0.4rem; }
.fx-addon span { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--terracotta); font-weight: 700; }

/* ── Forms ─────────────────────────────────────────────────────────────── */

.fx-field {
  width: 100%;
  min-height: 52px;
  border: 1.5px solid rgba(59, 42, 58, 0.22);
  border-radius: 12px;
  background: var(--wp--preset--color--paper);
  font-family: var(--wp--preset--font-family--body);
  font-size: max(16px, 1rem);
  color: var(--wp--preset--color--ink);
  padding: 0 16px;
  outline: none;
}
.fx-field:focus { border-color: var(--wp--preset--color--terracotta); }
textarea.fx-field { min-height: 120px; padding: 14px 16px; line-height: 1.6; resize: vertical; }

/* ── Footer ────────────────────────────────────────────────────────────── */

.fx-footer {
  background: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--paper);
  padding-inline: clamp(1.375rem, 5vw, 4.5rem);
}
.fx-footer a { color: var(--wp--preset--color--paper); opacity: 0.72; }
.fx-footer a:hover { color: var(--wp--preset--color--ochre); opacity: 1; }

/* ── Accessibility ─────────────────────────────────────────────────────── */

:focus-visible { outline: 3px solid var(--wp--preset--color--terracotta); outline-offset: 3px; }
.fx-tap { min-height: 44px; min-width: 44px; }
.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; }

/* ── Homepage design strip ─────────────────────────────────────────────── */
/* A scroll-snapping row rather than a grid: the designs are the product, so
   they belong above the fold-and-a-bit, but a full grid there would push
   everything else off the page. Bleeds to the right edge so it is visibly
   scrollable rather than looking like a truncated row. */

.fx-strip {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(260px, 300px);
  gap: clamp(1rem, 2vw, 1.5rem);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: clamp(1.375rem, 5vw, 4.5rem);
  padding-bottom: 1rem;
  margin-inline: calc(clamp(1.375rem, 5vw, 4.5rem) * -1);
  padding-inline: clamp(1.375rem, 5vw, 4.5rem);
  scrollbar-width: thin;
  scrollbar-color: rgba(59, 42, 58, 0.28) transparent;
}
.fx-strip::-webkit-scrollbar { height: 6px; }
.fx-strip::-webkit-scrollbar-thumb { background: rgba(59, 42, 58, 0.28); border-radius: 999px; }
.fx-strip::-webkit-scrollbar-track { background: transparent; }

.fx-strip__card { scroll-snap-align: start; display: grid; gap: 0.875rem; }
.fx-strip__card .gal-swatch {
  min-height: 300px;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.fx-strip__card:hover .gal-swatch { transform: translateY(-6px); }
@media (prefers-reduced-motion: reduce) { .fx-strip__card:hover .gal-swatch { transform: none; } }
.fx-strip__meta { display: grid; gap: 0.3rem; }

/* ── Hero film ─────────────────────────────────────────────────────────── */


/* ── Homepage composition ──────────────────────────────────────────────── */
/* The page used to be five sections of one shape: a heading block, then a row
   of things, at the same width and the same rhythm every time. These give each
   section its own geometry — a left rail, a held-left split, a mosaic, a
   threaded track — so the eye has somewhere to go. */

/* A shared heading row: title left, supporting line right. */
.fx-head {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 2rem;
  align-items: end;
  justify-content: space-between;
  margin-bottom: clamp(2rem, 3.5vw, 3rem);
}
.fx-head > div { display: grid; gap: 1.25rem; }
.fx-head__title { margin: 0; max-width: 13ch; }
.fx-head__note {
  margin: 0;
  max-width: 38ch;
  color: var(--wp--preset--color--stone);
  line-height: 1.7;
}
.fx-head--ink .fx-head__note { color: rgba(251, 247, 241, 0.75); }

/* ── Left rail: a narrow sticky heading beside a full-bleed body ───────── */
.fx-rail {
  display: grid;
  grid-template-columns: minmax(200px, 260px) minmax(0, 1fr);
  gap: clamp(1.5rem, 3vw, 3rem);
  align-items: start;
  padding-right: 0;
}
.fx-rail__head {
  display: grid;
  gap: 1.25rem;
  align-content: start;
  position: sticky;
  top: calc(var(--header-h) + 1.25rem);
}
.fx-rail__title { margin: 0; font-size: var(--wp--preset--font-size--x-large); }
.fx-rail__note {
  margin: 0;
  color: var(--wp--preset--color--stone);
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.6;
}
.fx-rail__head .fx-btn { justify-self: start; }
.fx-rail__body { min-width: 0; }
/* the strip already bleeds; inside the rail the section's own missing right
   padding does that job, so its negative margins are cancelled */
.fx-rail .fx-strip {
  margin-inline: 0;
  padding-inline: 0;
  scroll-padding-left: 0;
}

/* ── Split: heading held left against a wider body ─────────────────────── */
.fx-split {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 2fr);
  gap: clamp(1.75rem, 3.5vw, 3.5rem);
  align-items: start;
}
.fx-split__head {
  display: grid;
  gap: 1.25rem;
  align-content: start;
  position: sticky;
  top: calc(var(--header-h) + 1.25rem);
}
.fx-split__title { margin: 0; font-size: var(--wp--preset--font-size--x-large); }
.fx-split__note { margin: 0; color: var(--wp--preset--color--stone); line-height: 1.7; }
.fx-split__fine {
  margin: 0;
  color: var(--wp--preset--color--stone);
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.6;
}
.fx-split__head .fx-btn { justify-self: start; }
.fx-split__body { min-width: 0; }
.fx-split .fx-plans { grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); }

/* ── Mosaic: six tiles that are not six identical tiles ────────────────── */
.fx-mosaic {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 1.75rem);
}
.fx-mosaic__a { grid-column: span 3; min-height: 360px; }
.fx-mosaic__b { grid-column: span 3; min-height: 360px; }
.fx-mosaic__c { grid-column: span 2; }
.fx-mosaic__d { grid-column: span 2; }
.fx-mosaic__e { grid-column: span 2; }
.fx-mosaic__f { grid-column: span 6; min-height: 220px; }

/* ── Track: the three steps strung along one rule ──────────────────────── */
.fx-track {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: clamp(2rem, 4vw, 3.5rem);
  position: relative;
}
/* threaded through the centre of the 62px number discs */
.fx-track::before {
  content: "";
  position: absolute;
  left: 31px;
  right: 31px;
  top: 31px;
  height: 1px;
  background: rgba(251, 247, 241, 0.22);
}
.fx-track__step { position: relative; }
.fx-track__step .fx-step__no { position: relative; z-index: 1; }

/* ── Closing ───────────────────────────────────────────────────────────── */
.fx-close {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: clamp(1.25rem, 2.5vw, 2rem);
}
.fx-close .fx-band { width: min(420px, 100%); margin-bottom: clamp(0.5rem, 1.5vw, 1rem); }
.fx-close__title { margin: 0; max-width: 17ch; }
.fx-close__note {
  margin: 0;
  max-width: 46ch;
  color: var(--wp--preset--color--stone);
  line-height: 1.7;
}

/* ── Composition, narrower ─────────────────────────────────────────────── */
@media (max-width: 1000px) {
  .fx-rail,
  .fx-split { grid-template-columns: minmax(0, 1fr); }
  .fx-rail__head,
  .fx-split__head { position: static; }
  .fx-rail { padding-right: 0; }
  /* stacked, the strip needs its own bleed back */
  .fx-rail .fx-strip {
    margin-inline: 0 calc(clamp(1.375rem, 5vw, 4.5rem) * -1);
  }
}
@media (max-width: 900px) {
  .fx-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fx-mosaic > * { grid-column: span 1; min-height: 280px; }
  .fx-mosaic__f { grid-column: span 2; min-height: 200px; }
}
@media (max-width: 780px) {
  .fx-track::before { display: none; }
}
@media (max-width: 560px) {
  .fx-mosaic { grid-template-columns: minmax(0, 1fr); }
  .fx-mosaic > * { grid-column: span 1; }
  .fx-mosaic__f { grid-column: span 1; }
}


/* ── Hero ──────────────────────────────────────────────────────────────── */
/* Two children, two columns, nothing placed by hand. The previous version
   assigned grid-column and grid-row explicitly, which is one more thing that
   can disagree with the markup; with exactly two boxes, auto-placement does
   the right thing on its own. */

.fx-hero__grid {
  position: relative;
  max-width: 1240px;
  margin-inline: auto;
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: center;
}

.fx-hero__copy {
  display: grid;
  gap: clamp(1rem, 2vw, 1.5rem);
  align-content: center;
}
.fx-hero__lede {
  margin: 0;
  max-width: 44ch;
  font-size: 1.1875rem;
  line-height: 1.62;
  color: var(--wp--preset--color--stone);
}
.fx-hero__url {
  margin: 0;
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.55rem 1rem;
  border: 1px solid rgba(59, 42, 58, 0.22);
  border-radius: 999px;
  background: var(--wp--preset--color--paper);
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--stone);
  white-space: nowrap;
  max-width: 100%;
  overflow-x: auto;
}
.fx-hero__url b { color: var(--wp--preset--color--ink); font-weight: 700; }
.fx-hero__cta { display: flex; flex-wrap: wrap; gap: 0.875rem; }

.fx-hero__filmwrap { display: grid; gap: 0.7rem; }
.fx-hero__film-col {
  position: relative;
  aspect-ratio: 9 / 16;
  height: min(58svh, 540px);
  width: auto;
  max-width: 100%;
  border-radius: 18px;
  overflow: hidden;
  background: var(--wp--preset--color--linen);
}
.fx-hero__filmcap { margin: 0; display: grid; gap: 0.15rem; max-width: 32ch; }
.fx-hero__filmcap b {
  font-size: var(--wp--preset--font-size--small);
  font-weight: 700;
  color: var(--wp--preset--color--ink);
}
.fx-hero__filmcap span {
  font-size: var(--wp--preset--font-size--micro);
  color: var(--wp--preset--color--stone);
}

@media (max-width: 920px) {
  .fx-hero__grid { grid-template-columns: minmax(0, 1fr); }
  .fx-hero__film-col { height: min(58svh, 460px); justify-self: center; }
  .fx-hero__filmcap { justify-items: center; text-align: center; max-width: none; }
}

/* ═══ Homepage v3 ═══════════════════════════════════════════════════════════
   From webdesign/Homepage v3.dc.html. That file is a single 1440px artboard in
   fixed pixels with no mobile state, so every size here is the fluid equivalent
   rather than the literal value, and the breakpoints are derived. Layout
   patterns for small screens come from the 390 board in the older Storefront
   file; colour and type come from v3 only. */

:root {
  --v3-gutter: clamp(1.25rem, 5vw, 4.5rem);
  --v3-section: clamp(4rem, 8vw, 7.5rem);
}

/* ── shared ─────────────────────────────────────────────────────────────── */

.v3-kicker {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  font-size: clamp(0.6875rem, 0.9vw, 0.75rem);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--wp--preset--color--terracotta);
  line-height: 1;
}
.v3-kicker::before {
  content: "";
  width: 28px;
  height: 1px;
  background: currentColor;
  flex: none;
}
.v3-kicker--gold { color: var(--wp--preset--color--ochre); }

.v3-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 52px;
  padding: 0 clamp(1.375rem, 2.4vw, 1.875rem);
  border-radius: 999px;
  font-size: clamp(0.9375rem, 1.1vw, 0.9375rem);
  font-weight: 500;
  line-height: 1;
  transition: background-color 0.25s, color 0.25s, border-color 0.25s;
}
.v3-btn--solid   { background: var(--wp--preset--color--terracotta); color: var(--wp--preset--color--paper); }
.v3-btn--solid:hover   { background: #A93520; color: var(--wp--preset--color--paper); }
.v3-btn--outline { border: 1px solid var(--wp--preset--color--ink); color: var(--wp--preset--color--ink); }
.v3-btn--outline:hover { background: var(--wp--preset--color--ink); color: var(--wp--preset--color--paper); }
.v3-btn--gold    { background: var(--wp--preset--color--ochre); color: var(--wp--preset--color--ink); }
.v3-btn--gold:hover    { background: var(--wp--preset--color--paper); color: var(--wp--preset--color--ink); }

.v3-link {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 44px;
  font-size: 0.9375rem;
  font-weight: 500;
  border-bottom: 1px solid currentColor;
}

/* the subdomain, shown as an address rather than described */
.v3-url {
  margin: 0;
  justify-self: start;
  align-self: start;
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.75rem 1.25rem;
  border: 1px solid rgba(59, 42, 58, 0.25);
  border-radius: 999px;
  background: rgba(251, 247, 241, 0.7);
  font-size: 0.9375rem;
  color: var(--wp--preset--color--mute);
  max-width: 100%;
  overflow-x: auto;
  white-space: nowrap;
}
.v3-url strong { font-weight: 500; color: var(--wp--preset--color--ink); }

/* ── hero ───────────────────────────────────────────────────────────────── */

.v3-hero {
  position: relative;
  padding: clamp(3rem, 7vw, 7.5rem) var(--v3-gutter) clamp(3.5rem, 8vw, 8rem);
  overflow: hidden;
}
/* the single warm bloom from the artboard */
.v3-hero::before {
  content: "";
  position: absolute;
  top: -200px;
  right: -160px;
  width: min(820px, 90vw);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(232,196,142,.55) 0%, rgba(232,196,142,.18) 45%, rgba(251,247,241,0) 72%);
  pointer-events: none;
}
.v3-hero__grid {
  position: relative;
  max-width: 1296px;
  margin-inline: auto;
  display: grid;
  grid-template-columns: 7fr 5fr;
  gap: clamp(2.5rem, 5vw, 5rem);
  align-items: center;
}
.v3-hero__copy { display: grid; gap: clamp(1.25rem, 2.4vw, 2.25rem); align-content: center; }
.v3-hero__title {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-weight: 600;
  font-size: clamp(2.5rem, 7vw, 6.5rem);
  line-height: 0.98;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.v3-hero__title em {
  font-style: italic;
  font-weight: 500;
  color: var(--wp--preset--color--terracotta);
}
.v3-hero__lede {
  margin: 0;
  max-width: 34ch;
  font-size: clamp(1.0625rem, 1.4vw, 1.1875rem);
  line-height: 1.6;
  font-weight: 300;
  color: var(--wp--preset--color--mute);
  text-wrap: pretty;
}
.v3-hero__cta { display: flex; flex-wrap: wrap; gap: 0.875rem; }

.v3-hero__aside { display: grid; gap: 1.125rem; justify-items: start; justify-self: end; }
.v3-hero__film {
  position: relative;
  width: min(320px, 100%);
  aspect-ratio: 9 / 16;
  border-radius: 28px;
  overflow: hidden;
  background: var(--wp--preset--color--ink);
}
.v3-hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.v3-hero__filmtop {
  position: absolute;
  top: 20px;
  left: 24px;
  right: 24px;
  display: flex;
  justify-content: space-between;
  font-size: 0.625rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(251, 247, 241, 0.85);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}
.v3-hero__filmcap { margin: 0; display: grid; gap: 0.25rem; }
.v3-hero__filmcap b { font-size: 1rem; font-weight: 500; }
.v3-hero__filmcap span { font-size: 0.8125rem; color: var(--wp--preset--color--stone); }

/* ── 3D films ───────────────────────────────────────────────────────────── */

.v3-films {
  background: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--paper);
  padding: var(--v3-section) var(--v3-gutter);
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
}
.v3-films__head {
  max-width: 1296px;
  margin-inline: auto;
  width: 100%;
  display: grid;
  grid-template-columns: 7fr 5fr;
  gap: clamp(2rem, 5vw, 5rem);
  align-items: end;
}
.v3-films__head > div { display: grid; gap: 1.25rem; }
.v3-films__title {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-weight: 500;
  font-size: clamp(2rem, 4.4vw, 4rem);
  line-height: 1;
}
.v3-films__title em { font-style: italic; font-weight: 400; color: var(--wp--preset--color--ochre); }
.v3-films__note {
  margin: 0;
  font-size: clamp(1rem, 1.2vw, 1.0625rem);
  line-height: 1.65;
  font-weight: 300;
  color: rgba(251, 247, 241, 0.78);
}
.v3-films__grid {
  max-width: 1296px;
  margin-inline: auto;
  width: 100%;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 2.2vw, 2rem);
}
.v3-film { margin: 0; display: grid; gap: 1rem; }
.v3-film__frame {
  position: relative;
  aspect-ratio: 9 / 16;
  border-radius: 22px;
  overflow: hidden;
  display: grid;
  place-items: center;
}
.v3-film__frame--a { background: radial-gradient(circle at 50% 40%, #C7432B 0%, #6B2A22 55%, #3B2A3A 100%); }
.v3-film__frame--b { background: radial-gradient(circle at 50% 40%, #E8C48E 0%, #8A5A3A 55%, #3B2A3A 100%); }
.v3-film__frame--c { background: radial-gradient(circle at 50% 40%, #F5E6D6 0%, #B58A7A 55%, #3B2A3A 100%); }
/* No film has been delivered to a client. These are demonstrations and say so. */
.v3-film__sample {
  position: absolute;
  top: 14px;
  left: 14px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(59, 42, 58, 0.72);
  color: var(--wp--preset--color--paper);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.v3-film__play {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--wp--preset--color--paper);
  color: var(--wp--preset--color--terracotta);
  display: grid;
  place-items: center;
}
.v3-film__name {
  position: absolute;
  left: 20px;
  bottom: 20px;
  font-family: var(--wp--preset--font-family--display);
  font-style: italic;
  font-size: clamp(1.125rem, 1.9vw, 1.625rem);
  color: var(--wp--preset--color--paper);
}
.v3-film__meta { display: flex; justify-content: space-between; font-size: 0.875rem; }
.v3-film__meta span:last-child { color: rgba(251, 247, 241, 0.65); }
.v3-films__foot {
  max-width: 1296px;
  margin-inline: auto;
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.75rem;
  border-top: 1px solid rgba(251, 247, 241, 0.2);
  padding-top: 2rem;
}
.v3-films__foot > span { font-size: 0.875rem; color: rgba(251, 247, 241, 0.7); }

/* ── designs ────────────────────────────────────────────────────────────── */

.v3-designs { padding: var(--v3-section) var(--v3-gutter); display: grid; gap: clamp(2rem, 4vw, 3rem); }
.v3-designs__head,
.v3-designs__grid { max-width: 1296px; margin-inline: auto; width: 100%; }
.v3-designs__head {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 2rem;
  justify-content: space-between;
  align-items: end;
}
.v3-designs__head > div { display: grid; gap: 1.25rem; }
.v3-designs__title {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-weight: 500;
  font-size: clamp(1.875rem, 3.9vw, 3.5rem);
  line-height: 1.02;
}
.v3-designs__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1rem, 2.2vw, 2rem); }
.v3-design { display: grid; gap: 1.125rem; }
.v3-design__card {
  aspect-ratio: 4 / 5;
  border-radius: 22px;
  border: 1px solid rgba(59, 42, 58, 0.16);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1rem;
  padding: clamp(1.25rem, 2.8vw, 2.5rem);
  overflow: hidden;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.v3-design:hover .v3-design__card { transform: translateY(-6px); }
.v3-design__kicker { font-size: 0.625rem; letter-spacing: 0.3em; text-transform: uppercase; }
.v3-design__sample {
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(1.5rem, 3vw, 2.75rem);
  font-weight: 500;
  line-height: 1;
  white-space: pre-line;
}
.v3-design__rule { width: 40px; height: 1px; }
.v3-design__line { font-size: 0.6875rem; letter-spacing: 0.24em; text-transform: uppercase; opacity: 0.8; }
.v3-design__meta { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
.v3-design__name { font-family: var(--wp--preset--font-family--display); font-size: clamp(1.25rem, 2vw, 1.75rem); font-weight: 500; }
.v3-design__tier { font-size: 0.6875rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--wp--preset--color--stone); }

/* ── how it works ───────────────────────────────────────────────────────── */

.v3-steps {
  padding: 0 var(--v3-gutter) var(--v3-section);
  max-width: calc(1296px + 2 * 4.5rem);
  margin-inline: auto;
  display: grid;
  grid-template-columns: 4fr 8fr;
  gap: clamp(2rem, 5vw, 5rem);
}
.v3-steps__head { display: grid; gap: 1.25rem; align-content: start; }
.v3-steps__title {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-weight: 500;
  font-size: clamp(1.875rem, 3.9vw, 3.5rem);
  line-height: 1.02;
}
.v3-steps__note { margin: 0; font-size: 1rem; line-height: 1.65; font-weight: 300; color: var(--wp--preset--color--mute); }
.v3-steps__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid rgba(59, 42, 58, 0.2); }
.v3-step {
  display: grid;
  grid-template-columns: 96px 1fr 1fr;
  gap: clamp(1rem, 2.4vw, 2rem);
  padding: clamp(1.5rem, 2.8vw, 2.25rem) 0;
  border-bottom: 1px solid rgba(59, 42, 58, 0.2);
  align-items: start;
}
.v3-step__no {
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(2.25rem, 4.4vw, 4rem);
  line-height: 0.9;
  color: var(--wp--preset--color--ochre);
}
.v3-step__title { font-family: var(--wp--preset--font-family--display); font-size: clamp(1.25rem, 2.2vw, 2rem); font-weight: 500; line-height: 1.1; }
.v3-step__body { font-size: 1rem; line-height: 1.6; font-weight: 300; color: var(--wp--preset--color--mute); }

/* ── promise ────────────────────────────────────────────────────────────── */
/* This panel replaces the artboard's testimonial. The studio has no customers,
   so nothing here is attributed to a person; it states something true instead
   at the same visual weight. */

.v3-promise {
  position: relative;
  margin: 0 var(--v3-gutter) var(--v3-section);
  padding: clamp(2.5rem, 6vw, 6rem);
  background: var(--wp--preset--color--linen);
  border-radius: 32px;
  display: grid;
  grid-template-columns: 1fr 3fr;
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: center;
  overflow: hidden;
  max-width: calc(1296px + 2 * 4.5rem);
  margin-inline: auto;
}
.v3-promise__mark {
  position: absolute;
  top: clamp(-120px, -6vw, -60px);
  left: clamp(1rem, 3vw, 2.5rem);
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(12rem, 25vw, 22.5rem);
  line-height: 1;
  color: rgba(199, 67, 43, 0.12);
  pointer-events: none;
}
.v3-promise__side { position: relative; display: grid; gap: 0.75rem; align-content: center; }
.v3-promise__label { font-size: 0.75rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--wp--preset--color--stone); }
.v3-promise__who { font-family: var(--wp--preset--font-family--display); font-size: clamp(1.25rem, 2.2vw, 1.875rem); font-weight: 500; line-height: 1.1; }
.v3-promise__text {
  position: relative;
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(1.375rem, 3vw, 2.5rem);
  line-height: 1.25;
  text-wrap: pretty;
}
.v3-promise__text em { font-style: italic; color: var(--wp--preset--color--terracotta); }

/* ── narrower ───────────────────────────────────────────────────────────── */

@media (max-width: 1000px) {
  .v3-hero__grid,
  .v3-films__head { grid-template-columns: minmax(0, 1fr); }
  .v3-hero__aside { justify-self: start; }
  .v3-steps { grid-template-columns: minmax(0, 1fr); }
  .v3-promise { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 860px) {
  .v3-films__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .v3-films__grid .v3-film:last-child { display: none; }
  .v3-designs__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .v3-designs__grid .v3-design:last-child { display: none; }
  .v3-step { grid-template-columns: 64px 1fr; }
  .v3-step__body { grid-column: 2; }
}
@media (max-width: 560px) {
  .v3-films__grid,
  .v3-designs__grid { grid-template-columns: minmax(0, 1fr); }
  .v3-films__grid .v3-film:last-child,
  .v3-designs__grid .v3-design:last-child { display: grid; }
  .v3-hero__cta .v3-btn { width: 100%; justify-content: flex-start; }
  .v3-hero__film { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .v3-design:hover .v3-design__card { transform: none; }
  .v3-hero__video { display: none; }
}

/* ── Header: log in, basket, and a phone menu ───────────────────────────── */
/* The burger only exists once JS has confirmed it can drive it; without JS the
   links stay visible and nothing is hidden behind a control that cannot open. */

.v3-navend { display: flex; align-items: center; gap: 0.75rem; }
.v3-navend__login {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 1.375rem;
  border-radius: 999px;
  border: 1px solid rgba(59, 42, 58, 0.35);
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--wp--preset--color--ink);
  transition: background-color 0.25s, color 0.25s;
}
.v3-navend__login:hover { background: var(--wp--preset--color--ink); color: var(--wp--preset--color--paper); }
.v3-navend__cart {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid rgba(59, 42, 58, 0.35);
  color: var(--wp--preset--color--ink);
  transition: background-color 0.25s, color 0.25s;
}
.v3-navend__cart:hover { background: var(--wp--preset--color--ink); color: var(--wp--preset--color--paper); }

.v3-burger {
  display: none;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--wp--preset--color--ink);
  cursor: pointer;
}
.v3-mobilenav {
  position: fixed;
  inset: var(--header-h) 0 0;
  z-index: 70;
  background: var(--wp--preset--color--paper);
  padding: 1.5rem var(--v3-gutter) calc(2rem + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
.v3-mobilenav[hidden] { display: none; }
.v3-mobilenav a {
  display: flex;
  align-items: center;
  min-height: 64px;
  border-bottom: 1px solid rgba(59, 42, 58, 0.15);
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(1.75rem, 7vw, 2.125rem);
  color: var(--wp--preset--color--ink);
}
.v3-mobilenav__cta {
  margin-top: 2rem;
  justify-content: center;
  min-height: 56px;
  border: 1px solid var(--wp--preset--color--ink);
  border-bottom: 1px solid var(--wp--preset--color--ink);
  font-family: var(--wp--preset--font-family--body) !important;
  font-size: 0.8125rem !important;
  letter-spacing: 0.24em;
  text-transform: uppercase;
}

@media (max-width: 900px) {
  .v3-has-js .fx-nav { display: none; }
  .v3-has-js .v3-navend__login { display: none; }
  .v3-has-js .v3-burger { display: grid; place-items: center; }
}

/* ── Page heading (designs, and any plain page) ─────────────────────────── */

.v3-pagehead {
  padding: clamp(3rem, 6vw, 6rem) var(--v3-gutter) clamp(2rem, 4vw, 3.5rem);
  max-width: calc(1296px + 2 * 4.5rem);
  margin-inline: auto;
  display: grid;
  gap: clamp(1rem, 2vw, 1.75rem);
  justify-items: start;
}
.v3-pagehead__title {
  margin: 0;
  max-width: 15ch;
  font-family: var(--wp--preset--font-family--display);
  font-weight: 600;
  font-size: clamp(2.25rem, 5.5vw, 4.75rem);
  line-height: 1;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.v3-pagehead__note {
  margin: 0;
  max-width: 46ch;
  font-size: clamp(1rem, 1.3vw, 1.0625rem);
  line-height: 1.7;
  font-weight: 300;
  color: var(--wp--preset--color--mute);
}

/* ── Gallery filters as native selects ──────────────────────────────────── */

.v3-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: clamp(1rem, 2.5vw, 2rem);
  max-width: calc(1296px + 2 * 4.5rem);
  margin-inline: auto;
}
.v3-filter { margin: 0; display: grid; gap: 0.5rem; }
.v3-filter label {
  font-size: 0.6875rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}
.v3-select {
  appearance: none;
  -webkit-appearance: none;
  min-height: 52px;
  min-width: min(260px, 70vw);
  padding: 0 3rem 0 1.25rem;
  border: 1px solid rgba(59, 42, 58, 0.28);
  border-radius: 999px;
  background-color: var(--wp--preset--color--paper);
  /* the chevron, drawn rather than an image request */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.4rem) 55%, calc(100% - 1.05rem) 55%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  color: var(--wp--preset--color--ink);
  font-family: var(--wp--preset--font-family--body);
  font-size: max(16px, 1rem);
  line-height: 1;
  cursor: pointer;
}
.v3-select:hover { border-color: var(--wp--preset--color--ink); }
.v3-filters .gal-count { margin-left: auto; align-self: center; }

@media (max-width: 560px) {
  .v3-filters { gap: 1rem; }
  .v3-filter { width: 100%; }
  .v3-select { width: 100%; min-width: 0; }
  .v3-filters .gal-count { margin-left: 0; }
}

/* ── Dropdown, upgraded ─────────────────────────────────────────────────── */
/* The native <select> stays in the markup and keeps working with JS off; the
   script below swaps in this listbox when it can drive it. Native option
   panels cannot be styled — that blue highlight is the browser's, not ours. */

.v3-combo { position: relative; }
.v3-combo__btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  min-width: min(260px, 70vw);
  min-height: 52px;
  padding: 0 1.25rem;
  border: 1px solid rgba(59, 42, 58, 0.28);
  border-radius: 999px;
  background: var(--wp--preset--color--paper);
  color: var(--wp--preset--color--ink);
  font-family: var(--wp--preset--font-family--body);
  font-size: max(16px, 1rem);
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.2s;
}
.v3-combo__btn:hover { border-color: var(--wp--preset--color--ink); }
.v3-combo__btn svg { flex: none; transition: transform 0.2s; }
.v3-combo[data-open="true"] .v3-combo__btn { border-color: var(--wp--preset--color--ink); }
.v3-combo[data-open="true"] .v3-combo__btn svg { transform: rotate(180deg); }

.v3-combo__list {
  position: absolute;
  z-index: 40;
  top: calc(100% + 8px);
  left: 0;
  min-width: 100%;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--wp--preset--color--paper);
  border: 1px solid rgba(59, 42, 58, 0.18);
  border-radius: 18px;
  box-shadow: 0 18px 40px -18px rgba(59, 42, 58, 0.45);
  max-height: 320px;
  overflow-y: auto;
}
.v3-combo__list[hidden] { display: none; }
.v3-combo__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: 44px;
  padding: 0 0.875rem;
  border-radius: 12px;
  font-size: 0.9375rem;
  color: var(--wp--preset--color--ink);
  cursor: pointer;
}
.v3-combo__opt:hover,
.v3-combo__opt[data-active="true"] { background: var(--wp--preset--color--linen); }
.v3-combo__opt[aria-selected="true"] { font-weight: 500; }
.v3-combo__opt[aria-selected="true"]::after {
  content: "";
  width: 14px;
  height: 8px;
  border-left: 1.5px solid var(--wp--preset--color--terracotta);
  border-bottom: 1.5px solid var(--wp--preset--color--terracotta);
  transform: rotate(-45deg) translateY(-2px);
  flex: none;
}
/* the native control is only hidden once the listbox is actually in place */
.v3-filter[data-upgraded="true"] .v3-select {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

/* ── Design tile: a phone showing the design ────────────────────────────── */
/* The tiles used to be abstract colour swatches with three dots for the
   palette. A visitor had to click through to learn what a design looked
   like. The tile is now the invitation on a phone — address bar, names,
   date, functions — so the page answers the question it exists to ask. */

.gal-phone {
  position: relative;
  width: 100%;
  height: 100%;
  /* the screen's own radius, as a share of the 402x874 point screen */
  border-radius: 13.68% / 6.29%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* the black bezel between glass and rail, and the glass's own inner edge */
  box-shadow:
    0 0 0 0.8px #14100F,
    inset 0 0 0 0.5px rgba(255, 255, 255, 0.10);
}
.gal-phone__bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  border-bottom: 1px solid currentColor;
  font-size: 10px;
  letter-spacing: 0.04em;
  opacity: 0.72;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gal-phone__bar i { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.gal-phone__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: clamp(18px, 6%, 28px) clamp(16px, 6%, 24px);
}
.gal-phone__kicker { font-size: 9px; letter-spacing: 0.26em; text-transform: uppercase; }
.gal-phone__names { font-size: clamp(24px, 9cqw, 34px); line-height: 1.05; white-space: pre-line; }
.gal-phone__rule { width: 34px; height: 1px; }
.gal-phone__line { font-size: 9px; letter-spacing: 0.16em; text-transform: uppercase; opacity: 0.75; }
.gal-phone__events { margin-top: auto; display: flex; flex-direction: column; border-top: 1px solid currentColor; }
.gal-phone__events span {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid currentColor;
  font-size: 10px;
}
.gal-phone__events b { font-weight: 500; letter-spacing: 0.04em; }
.gal-phone__events i { font-style: normal; opacity: 0.7; }
/* the keylines are the design's own ink at low strength, not a grey */
.gal-phone__bar,
.gal-phone__events,
.gal-phone__events span { border-color: color-mix(in srgb, currentColor 22%, transparent); }

@media (prefers-reduced-motion: reduce) {
  .gal-card:hover .gal-device { transform: none; }
}

/* ── Dropdown: a highlight that travels ─────────────────────────────────── */
/* One pill moves and resizes to whichever option the pointer or the keyboard
   is on, instead of each row toggling its own background. */

.v3-combo__list { position: absolute; }
.v3-combo__hl {
  position: absolute;
  left: 6px;
  right: 6px;
  top: 0;
  height: 0;
  border-radius: 12px;
  background: var(--wp--preset--color--linen);
  transform: translateY(0);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
              height 0.22s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 0.18s ease;
  z-index: 0;
}
.v3-combo__opt { position: relative; z-index: 1; }
/* the travelling pill replaces the per-row hover fill */
.v3-combo__opt:hover,
.v3-combo__opt[data-active="true"] { background: transparent; }

@media (prefers-reduced-motion: reduce) {
  .v3-combo__hl { transition: opacity 0.18s ease; }
}

/* ── iPhone 17 Pro shell ────────────────────────────────────────────────── */
/* Proportions are the real device in logical points — 402 x 874 screen, a
   125 x 37 Dynamic Island 11 from the top, 55pt screen corners — written as
   percentages so the frame holds at any rendered width.

   The rail is CSS rather than a photograph on purpose: a real iPhone render is
   an Apple product image with licensing attached, and a drawn frame stays
   crisp at every size, costs no request, and cannot go stale. What makes metal
   read as metal is layering — a bright catch along both long edges, a darker
   body between them, a top-to-bottom light fall, and a genuinely dark ring
   where the glass meets the rail. One flat gradient reads as a printed stripe,
   which is what it looked like. */

.gal-device {
  position: relative;
  aspect-ratio: 402 / 874;
  padding: 2.6%;
  border-radius: 16.42% / 7.55%;
  background:
    /* light falling down the body */
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.30) 0%,
      rgba(255, 255, 255, 0.04) 14%,
      rgba(0, 0, 0, 0.10) 62%,
      rgba(0, 0, 0, 0.30) 100%),
    /* the rail itself: bright at both edges where it curves into the light */
    linear-gradient(90deg,
      #57534F 0%, #8F8983 2.5%, #D9D4CE 6%, #B4AEA8 10%,
      #7C7671 22%, #6A6560 50%, #7C7671 78%,
      #B4AEA8 90%, #D9D4CE 94%, #8F8983 97.5%, #57534F 100%);
  box-shadow:
    /* the outermost edge catches a hard highlight */
    inset 0 0 0 0.6px rgba(255, 255, 255, 0.55),
    /* and the frame sits on the page rather than floating on it */
    0 2px 3px -1px rgba(59, 42, 58, 0.30),
    0 26px 46px -24px rgba(59, 42, 58, 0.55);
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.gal-card:hover .gal-device { transform: translateY(-6px); }

/* Side buttons. One element per side, each drawn with stacked box-shadows:
   left carries the action button and the volume rocker, right the side button
   and the camera control. Without them the frame reads as a picture frame. */
.gal-device::before,
.gal-device::after {
  content: "";
  position: absolute;
  top: 0;
  width: 1.15%;
  height: 100%;
  pointer-events: none;
  background-repeat: no-repeat;
  /* box-shadow offsets cannot be percentages, but background-position can, so
     each button is a background layer placed as a share of the device height */
  background-image:
    linear-gradient(#726C67, #5E5954),
    linear-gradient(#726C67, #5E5954),
    linear-gradient(#726C67, #5E5954);
}
.gal-device::before {
  left: -0.45%;
  border-radius: 1px 0 0 1px;
  /* action button, then the volume rocker */
  background-size: 100% 3.1%, 100% 5.4%, 100% 5.4%;
  background-position: 0 12.8%, 0 19.4%, 0 26.6%;
}
.gal-device::after {
  right: -0.45%;
  border-radius: 0 1px 1px 0;
  /* side button, then camera control */
  background-size: 100% 8.2%, 100% 3.6%;
  background-position: 0 19.8%, 0 33.4%;
}

.gal-device__island {
  position: absolute;
  z-index: 3;
  top: 1.26%;
  left: 50%;
  transform: translateX(-50%);
  width: 31.1%;
  height: 4.23%;
  border-radius: 999px;
  background: #05070A;
  /* the sensor cluster is not a flat black pill */
  box-shadow: inset 0 0 0 0.5px rgba(255, 255, 255, 0.08);
}

@media (prefers-reduced-motion: reduce) {
  .gal-card:hover .gal-device { transform: none; }
}

/* ── iOS status bar ─────────────────────────────────────────────────────── */
/* 59pt of the 874pt screen. Its real job is to push the page content clear of
   the Dynamic Island; looking like iOS is the side effect that makes the
   preview read as a phone rather than a card with a black pill on it. */

.gal-phone__status {
  position: relative;
  z-index: 2;
  flex: none;
  height: 6.75%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 7%;
  /* the island sits in the gap between the two groups */
  font-size: clamp(7px, 3.4cqw, 11px);
  font-weight: 600;
  letter-spacing: 0.01em;
  opacity: 0.9;
}
.gal-phone__status b { font-weight: 600; }
.gal-phone__status i { display: flex; align-items: center; gap: 3px; }
.gal-phone__status svg { height: 0.75em; width: auto; display: block; }

/* container queries let the status bar and the names scale with the phone
   rather than with the viewport */
.gal-phone { container-type: inline-size; }

/* ═══ WooCommerce ═══════════════════════════════════════════════════════════
   Rewritten rather than extended, because the first version was written against
   markup that is not the markup this store emits.

   It skinned WooCommerce's CLASSIC shortcode output. Cart and Checkout here are
   the BLOCK versions, whose class names have nothing in common with it — so
   several hundred lines were styling elements that never render, while the two
   pages a customer actually pays on were unstyled.

   It also made .woocommerce a two-column grid on the account page. The sidebar
   column only exists once you are signed in, so a signed-out visitor's login
   form was laid into a 240px column inside a 720px page: a narrow strip with
   "USERNAME OR EMAIL ADDRESS" wrapping onto three lines.

   Which page uses which:

     /my-account/        classic   .woocommerce-form-login, #customer_login
     /cart/              BLOCK     .wc-block-cart, .wc-block-components-*
     /checkout/          BLOCK     .wc-block-checkout
     order received      classic   .woocommerce-order

   The block CSS ships its own layout and its own colours and loads after this
   file, but its selectors are mostly single-class, so a two-class selector here
   wins without !important. Where it does not, it is noted.                    */

/* ── Room to work ───────────────────────────────────────────────────────── */
/* page.html constrains content to 720px. That is right for a refund policy and
   much too narrow for a cart beside its totals. WordPress's own constraint is
   `.wp-container-… > :where(…)`, which is one class of specificity — these are
   two, so they win without !important and without knowing the hashed name. */

.woocommerce-account .entry-content > *,
.woocommerce-cart .entry-content > *,
.woocommerce-checkout .entry-content > *,
.woocommerce-order-received .entry-content > * {
  max-width: min(1100px, 100%);
}

.woocommerce-account .wp-block-post-title,
.woocommerce-cart .wp-block-post-title,
.woocommerce-checkout .wp-block-post-title,
.woocommerce-order-received .wp-block-post-title {
  max-width: min(1100px, 100%);
}

/* Two names WooCommerce reads that this palette does not define, so they were
   falling back to plain white and plain black. It is one use of each — a price
   colour in checkout.css, a border in cart.css — not a theming surface: nearly
   every colour WooCommerce draws is a literal in its own stylesheet and has to
   be overridden rule by rule below. Scoped to these pages so nothing else
   inherits names this theme does not otherwise use. */
.woocommerce-cart,
.woocommerce-checkout,
.woocommerce-order-received {
  --wp--preset--color--base: var(--wp--preset--color--paper);
  --wp--preset--color--black: var(--wp--preset--color--ink);
}

/* ── The clearfix trap ──────────────────────────────────────────────────── */
/* WooCommerce clearfixes half its containers with ::before and ::after set to
   display: table. Under floats those are invisible. In a GRID or a FLEX
   container they are children, and they take real cells.

   That is the whole reason the account page looked broken. .woocommerce became
   a two-column grid whose first cell was ::before, so the navigation sat in the
   right-hand column and the content dropped to the row below it; and the same
   thing put the login card in the right column with register below-left.

   Every container this file turns into a grid or a flex row has to reclaim
   those two cells. Forgetting one is invisible until it is not. */
.woocommerce-account .woocommerce::before,
.woocommerce-account .woocommerce::after,
.woocommerce-account #customer_login::before,
.woocommerce-account #customer_login::after,
.woocommerce form .form-row::before,
.woocommerce form .form-row::after,
.woocommerce-error::before,
.woocommerce-error::after,
.woocommerce-info::before,
.woocommerce-info::after,
.woocommerce-message::before,
.woocommerce-message::after,
.woocommerce-order ul.woocommerce-order-overview::before,
.woocommerce-order ul.woocommerce-order-overview::after {
  content: none;
}

/* ── Shared: type ───────────────────────────────────────────────────────── */

.woocommerce-page h1,
.woocommerce-page h2,
.woocommerce-page h3,
.woocommerce-page .wc-block-components-title {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.woocommerce h2,
.woocommerce-page .wc-block-components-title {
  font-size: clamp(1.375rem, 2.4vw, 1.875rem);
  margin: 0 0 1.25rem;
}

/* ── Shared: notices ────────────────────────────────────────────────────── */

.woocommerce-message,
.woocommerce-info,
.woocommerce-error,
.woocommerce-page .wc-block-components-notice-banner {
  list-style: none;
  margin: 0 0 1.75rem;
  padding: 1rem 1.25rem;
  border: 1px solid rgba(59, 42, 58, 0.18);
  border-radius: 14px;
  background: var(--wp--preset--color--linen);
  color: var(--wp--preset--color--ink);
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.6;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1rem;
}
.woocommerce-error,
.woocommerce-page .wc-block-components-notice-banner.is-error {
  border-color: rgba(199, 67, 43, 0.45);
  background: rgba(199, 67, 43, 0.07);
}
/* The other three states ship in WooCommerce's own green and blue, which are
   the only colours on these pages that belong to no palette. */
.woocommerce-page .wc-block-components-notice-banner.is-success {
  border-color: rgba(143, 179, 154, 0.55);
  background: rgba(143, 179, 154, 0.14);
}
.woocommerce-page .wc-block-components-notice-banner.is-info,
.woocommerce-page .wc-block-components-notice-banner.is-warning {
  border-color: rgba(232, 196, 142, 0.65);
  background: rgba(232, 196, 142, 0.18);
}
.woocommerce-message .button,
.woocommerce-info .button { margin-left: auto; }

/* A field that failed validation says so under itself; it must not look like a
   decorative red line. */
.woocommerce-page .wc-block-components-validation-error {
  margin-top: 0.375rem;
  color: var(--wp--preset--color--terracotta);
  font-size: var(--wp--preset--font-size--micro);
  line-height: 1.4;
}

/* ── Shared: fields ─────────────────────────────────────────────────────── */
/* Two shapes to serve. The classic form is label-above-input; the block form
   floats the label into the input and moves it on focus. Both end up with the
   same box so the account page and the checkout do not look like two sites. */

.woocommerce form .form-row {
  display: grid;
  gap: 0.5rem;
  margin: 0 0 1.25rem;
  padding: 0;
}
.woocommerce form .form-row label,
.woocommerce-account label {
  font-size: var(--wp--preset--font-size--micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}
.woocommerce form .form-row .required {
  color: var(--wp--preset--color--terracotta);
  text-decoration: none;
}

/* The classic form, where this theme owns the geometry. */
.woocommerce input[type="text"],
.woocommerce input[type="email"],
.woocommerce input[type="tel"],
.woocommerce input[type="password"],
.woocommerce input[type="number"],
.woocommerce textarea,
.woocommerce select,
.woocommerce .input-text {
  width: 100%;
  min-height: 52px;
  padding: 0 1.125rem;
  border: 1px solid rgba(59, 42, 58, 0.28);
  border-radius: 12px;
  background: var(--wp--preset--color--paper);
  color: var(--wp--preset--color--ink);
  font-family: var(--wp--preset--font-family--body);
  /* 16px minimum or iOS zooms the page on focus */
  font-size: max(16px, 1rem);
  line-height: 1.4;
  box-shadow: none;
  transition: border-color 0.25s, box-shadow 0.25s;
}
.woocommerce textarea,
.woocommerce-page .wc-block-components-textarea {
  min-height: 140px;
  padding: 0.875rem 1.125rem;
  line-height: 1.6;
  resize: vertical;
}
.woocommerce input:focus,
.woocommerce textarea:focus,
.woocommerce select:focus {
  outline: 2px solid var(--wp--preset--color--terracotta);
  outline-offset: 2px;
  border-color: transparent;
}

/* The block field, where WooCommerce owns the geometry and this changes only
   how it looks.

   Its label floats inside the input, and both the resting and the raised
   position are computed from WooCommerce's own padding — so changing that
   padding without moving both label positions leaves the label sitting on top
   of the typed text. Colour, border and focus change here; the height, the
   padding and the label stay exactly where WooCommerce puts them.

   Prefixed with the body element rather than another class. WooCommerce
   writes these as .wc-block-components-form .wc-block-components-text-input
   input[type=text] — three classes and an element — and its stylesheet loads
   after this one, so a class-for-class tie goes to WooCommerce. One element
   wins it, without !important and without a selector nobody can read. */
body.woocommerce-page .wc-block-components-text-input input[type="text"],
body.woocommerce-page .wc-block-components-text-input input[type="email"],
body.woocommerce-page .wc-block-components-text-input input[type="tel"],
body.woocommerce-page .wc-block-components-text-input input[type="number"],
body.woocommerce-page .wc-block-components-text-input input[type="password"],
body.woocommerce-page .wc-block-components-text-input input[type="url"],
body.woocommerce-page .wc-block-components-textarea,
body.woocommerce-page .wc-blocks-components-select .wc-blocks-components-select__select {
  border: 1px solid rgba(59, 42, 58, 0.28);
  border-radius: 12px;
  background-color: var(--wp--preset--color--paper);
  color: var(--wp--preset--color--ink);
  font-family: var(--wp--preset--font-family--body);
  transition: border-color 0.25s, box-shadow 0.25s;
}
body.woocommerce-page .wc-block-components-text-input input:focus,
body.woocommerce-page .wc-block-components-textarea:focus,
body.woocommerce-page .wc-blocks-components-select .wc-blocks-components-select__select:focus {
  border-color: var(--wp--preset--color--terracotta);
  outline: 2px solid var(--wp--preset--color--terracotta);
  outline-offset: 2px;
}
body.woocommerce-page .wc-block-components-text-input label {
  color: var(--wp--preset--color--stone);
  font-family: var(--wp--preset--font-family--body);
}
body.woocommerce-page .wc-block-components-text-input.has-error input {
  border-color: var(--wp--preset--color--terracotta);
}

/* The password reveal sits inside the field rather than beside it. */
.woocommerce .password-input,
.woocommerce .woocommerce-password-hint { display: block; position: relative; }
.woocommerce .show-password-input {
  position: absolute;
  inset-inline-end: 0.875rem;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.25s;
}
.woocommerce .show-password-input:hover { opacity: 1; }
.woocommerce .password-input input { padding-inline-end: 3.25rem; }

.woocommerce-form__label-for-checkbox,
.woocommerce-page .wc-block-components-checkbox__label {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 44px;
  font-size: var(--wp--preset--font-size--small);
  letter-spacing: 0;
  text-transform: none;
  color: var(--wp--preset--color--ink);
  cursor: pointer;
}

/* ── Shared: buttons ────────────────────────────────────────────────────── */

.woocommerce .button,
.woocommerce button.button,
.woocommerce input.button,
.woocommerce a.button,
.woocommerce #respond input#submit,
.woocommerce-page .wc-block-components-button:not(.is-link) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 52px;
  padding: 0 1.75rem;
  border: 0;
  border-radius: 999px;
  background: var(--wp--preset--color--terracotta);
  color: var(--wp--preset--color--paper);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--small);
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.25s, color 0.25s, transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.woocommerce .button:hover,
.woocommerce button.button:hover,
.woocommerce a.button:hover,
.woocommerce-page .wc-block-components-button:not(.is-link):hover {
  background: #A93520;
  color: var(--wp--preset--color--paper);
  transform: translateY(-2px);
}
.woocommerce .button.alt { background: var(--wp--preset--color--ink); }
.woocommerce .button.alt:hover { background: var(--wp--preset--color--terracotta); }

/* WooCommerce paints its own focus ring in its own blue. */
.woocommerce-page .wc-block-components-button:not(.is-link):focus {
  box-shadow: none;
  outline: 3px solid var(--wp--preset--color--terracotta);
  outline-offset: 3px;
}

.woocommerce-page .wc-block-components-button.is-link {
  color: var(--wp--preset--color--terracotta);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Mirroring the hover list exactly. A shorter list here is one specificity
   point short of the rules it is meant to cancel, so the lift survived
   reduced-motion on every classic button. */
@media (prefers-reduced-motion: reduce) {
  .woocommerce .button:hover,
  .woocommerce button.button:hover,
  .woocommerce a.button:hover,
  .woocommerce-page .wc-block-components-button:not(.is-link):hover { transform: none; }
}

/* ── The account page ───────────────────────────────────────────────────── */

.woocommerce-account .woocommerce { padding-block: clamp(1rem, 2vw, 2rem) clamp(3rem, 6vw, 5rem); }

/* Signed out: two cards, side by side when there is room. NOT a grid on
   .woocommerce itself — the navigation that would fill a first column does not
   exist yet, and laying the form into it is what broke this page. */
.woocommerce-account #customer_login {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 21rem), 1fr));
  gap: clamp(1.25rem, 3vw, 2.5rem);
  align-items: start;
}
.woocommerce-account #customer_login > div {
  width: auto;
  float: none;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  border: 1px solid rgba(59, 42, 58, 0.16);
  border-radius: 22px;
  background: var(--wp--preset--color--paper);
}
.woocommerce-account #customer_login > div > h2 {
  margin-top: 0;
  font-size: clamp(1.5rem, 2.6vw, 2rem);
}
.woocommerce-account #customer_login form { margin: 0; border: 0; padding: 0; }
.woocommerce-account #customer_login .button { width: 100%; margin-top: 0.5rem; }
.woocommerce-account .woocommerce-LostPassword {
  margin: 1rem 0 0;
  font-size: var(--wp--preset--font-size--small);
}
.woocommerce-account .woocommerce-privacy-policy-text p {
  font-size: var(--wp--preset--font-size--micro);
  line-height: 1.6;
  color: var(--wp--preset--color--stone);
}

/* Signed in: the navigation exists, so now there is a sidebar. Keyed off the
   navigation actually being in the page rather than off the logged-in body
   class — the class is what WordPress thinks, the element is what WooCommerce
   rendered, and the element is what this layout needs. */
.woocommerce-account .woocommerce:has(> .woocommerce-MyAccount-navigation) {
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
}
/* The notices render inside .woocommerce-MyAccount-content, not beside it, so
   there is no grid item here to span. */
.woocommerce-MyAccount-navigation ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.woocommerce-MyAccount-navigation li a {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0 1rem;
  border-radius: 12px;
  color: var(--wp--preset--color--ink);
  font-size: var(--wp--preset--font-size--small);
  text-decoration: none;
  transition: background-color 0.25s, color 0.25s;
}
.woocommerce-MyAccount-navigation li a:hover { background: var(--wp--preset--color--linen); }
.woocommerce-MyAccount-navigation li.is-active a {
  background: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--paper);
}
.woocommerce-MyAccount-content p { line-height: 1.7; color: var(--wp--preset--color--mute); }

/* ── Cart and checkout: the shell ───────────────────────────────────────── */
/* Both are a main column and a sidebar. WooCommerce's own widths are 65/35 in
   percentages with a percentage gutter; this keeps the proportion and gives the
   sidebar a card so the totals read as a thing you are about to agree to. */

/* WooCommerce lays these out as 65% and 35% with a percentage padding for the
   gutter. A gap on top of that adds up to more than 100% and pushes the sidebar
   onto its own line, so the widths go with the gutter. min-width: 0 because a
   long product name in a flex item will otherwise refuse to wrap. */
/* flex: none, or the column above would stretch a frame whose whole point is
   to be exactly 1280 wide before it is scaled. */
.bld__stage[data-view="desktop"] .bld__frame { flex: none; }

.woocommerce-page .wc-block-components-sidebar-layout {
  gap: clamp(1.5rem, 3.5vw, 3rem);
  align-items: start;
}
.woocommerce-page .wc-block-components-sidebar-layout .wc-block-components-main {
  width: auto;
  flex: 1 1 60%;
  min-width: 0;
  padding-right: 0;
}
.woocommerce-page .wc-block-components-sidebar-layout .wc-block-components-sidebar {
  width: auto;
  flex: 1 1 30%;
  min-width: 0;
  padding: clamp(1.25rem, 2.5vw, 2rem);
  border: 1px solid rgba(59, 42, 58, 0.16);
  border-radius: 22px;
  background: var(--wp--preset--color--linen);
}

/* On a phone the sidebar goes below, and the card would otherwise be a box
   inside a box. */
@media (max-width: 700px) {
  /* WooCommerce stacks these with a @container query, and a container query
     carries no specificity — so the flex widths set above went on winning and
     held the sidebar beside the cart on a phone. They are released here. */
  .woocommerce-page .wc-block-components-sidebar-layout .wc-block-components-main,
  .woocommerce-page .wc-block-components-sidebar-layout .wc-block-components-sidebar {
    flex: 1 1 100%;
    width: 100%;
  }
  .woocommerce-page .wc-block-components-sidebar-layout .wc-block-components-sidebar {
    padding-inline: 0;
    border: 0;
    border-top: 1px solid rgba(59, 42, 58, 0.16);
    border-radius: 0;
    background: none;
  }
}

.woocommerce-page .wc-block-cart .wc-block-cart__totals-title,
.woocommerce-page .wc-block-components-checkout-order-summary__title-text {
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--micro);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}

/* ── Cart: the line items ───────────────────────────────────────────────── */

table.wc-block-cart-items { border-collapse: collapse; }
.woocommerce-page table.wc-block-cart-items .wc-block-cart-items__header {
  font-size: var(--wp--preset--font-size--micro);
  letter-spacing: 0.16em;
  color: var(--wp--preset--color--stone);
  border-bottom: 1px solid rgba(59, 42, 58, 0.16);
}
/* the weight is set on the th, so it has to be unset there too */
.woocommerce-page table.wc-block-cart-items .wc-block-cart-items__header th { font-weight: 700; }
.woocommerce-page .wc-block-cart-items__row { border-bottom: 1px solid rgba(59, 42, 58, 0.16); }
.woocommerce-page .wc-block-cart-item__image img { border-radius: 12px; }
/* WooCommerce writes these as table.wc-block-cart-items .wc-block-cart-items__row
   .<thing> — three classes and an element. Matching that shape and adding the
   page class on top is what makes them land; one extra class was a tie, and a
   tie goes to whoever loads last, which is WooCommerce. */
.woocommerce-page table.wc-block-cart-items .wc-block-cart-items__row .wc-block-components-product-name {
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(1.0625rem, 1.6vw, 1.25rem);
  color: var(--wp--preset--color--ink);
  text-decoration: none;
}
.woocommerce-page table.wc-block-cart-items .wc-block-cart-items__row .wc-block-cart-item__quantity .wc-block-cart-item__remove-link,
.woocommerce-page table.wc-block-cart-items .wc-block-cart-items__row .wc-block-cart-item__save-for-later .wc-block-cart-item__save-for-later-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--wp--preset--font-size--micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}
.woocommerce-page table.wc-block-cart-items .wc-block-cart-items__row .wc-block-cart-item__quantity .wc-block-cart-item__remove-link:hover { color: var(--wp--preset--color--terracotta); }

.woocommerce-page .wc-block-components-quantity-selector {
  border: 1px solid rgba(59, 42, 58, 0.28);
  border-radius: 999px;
  background: var(--wp--preset--color--paper);
}
.woocommerce-page .wc-block-components-quantity-selector input.wc-block-components-quantity-selector__input {
  background: none;
  color: var(--wp--preset--color--ink);
  font-size: max(16px, 1rem);
}
/* WooCommerce draws these 30px wide at 0.6 opacity. Both were exact ties. */
.woocommerce-page .wc-block-components-quantity-selector .wc-block-components-quantity-selector__button {
  min-width: 44px;
  min-height: 44px;
  width: auto;
  color: var(--wp--preset--color--mute);
  opacity: 1;
  transition: color 0.25s;
}
.woocommerce-page .wc-block-components-quantity-selector .wc-block-components-quantity-selector__button:hover { color: var(--wp--preset--color--terracotta); }

/* ── Totals ─────────────────────────────────────────────────────────────── */

.woocommerce-page .wc-block-components-totals-wrapper { border-top: 1px solid rgba(59, 42, 58, 0.16); }
.woocommerce-page .wc-block-components-totals-item__label {
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--mute);
}
.woocommerce-page .wc-block-components-totals-item__value {
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(1rem, 1.5vw, 1.125rem);
  font-weight: 500;
  color: var(--wp--preset--color--ink);
}
.woocommerce-page .wc-block-components-totals-footer-item .wc-block-components-totals-item__label,
.woocommerce-page .wc-block-components-totals-footer-item .wc-block-components-totals-item__value {
  font-size: clamp(1.1875rem, 2vw, 1.5rem);
}
.woocommerce-page .wc-block-components-totals-footer-item .wc-block-components-totals-item__label {
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--ink);
}

.woocommerce-page .wc-block-components-totals-coupon__input input { min-height: 48px; }
/* These sit on the linen sidebar, where terracotta measures 4.03:1 — under AA
   for text this size. The darker red the buttons already use for hover is
   5.34:1 on the same ground. */
.woocommerce-page .wc-block-components-totals-coupon-link,
.woocommerce-page .wc-block-components-totals-shipping__change-address-button {
  color: #A93520;
  font-size: var(--wp--preset--font-size--small);
}

.woocommerce-page .wc-block-cart__submit-button,
.woocommerce-page .wc-block-components-checkout-place-order-button { width: 100%; }

/* ── Checkout: the steps ────────────────────────────────────────────────── */

.woocommerce-page .wc-block-components-checkout-step {
  padding-bottom: clamp(1.5rem, 3vw, 2.25rem);
  margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
  border-bottom: 1px solid rgba(59, 42, 58, 0.16);
}
.woocommerce-page .wc-block-components-checkout-step:last-of-type { border-bottom: 0; }
.woocommerce-page .wc-block-components-checkout-step__title {
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(1.25rem, 2.2vw, 1.625rem);
  font-weight: 500;
}
.woocommerce-page .wc-block-components-checkout-step__description {
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.6;
  color: var(--wp--preset--color--mute);
}
/* The step number is WooCommerce's, in WooCommerce's colour. */
.woocommerce-page .wc-block-components-checkout-step__heading-content { color: var(--wp--preset--color--terracotta); }

.woocommerce-page .wc-block-components-radio-control__option {
  padding: 1rem 1.125rem 1rem 3rem;
  border: 1px solid rgba(59, 42, 58, 0.22);
  border-radius: 14px;
  transition: border-color 0.25s, background-color 0.25s;
}
.woocommerce-page .wc-block-components-radio-control__option + .wc-block-components-radio-control__option { margin-top: 0.625rem; }
.woocommerce-page .wc-block-components-radio-control__option:hover { border-color: rgba(59, 42, 58, 0.45); }
.woocommerce-page .wc-block-components-radio-control__option-checked {
  border-color: var(--wp--preset--color--ink);
  background: var(--wp--preset--color--linen);
}
.woocommerce-page .wc-block-components-radio-control__label { font-size: var(--wp--preset--font-size--small); }
.woocommerce-page .wc-block-components-radio-control__description {
  font-size: var(--wp--preset--font-size--micro);
  color: var(--wp--preset--color--stone);
}

.woocommerce-page .wc-block-components-order-summary-item__image img { border-radius: 10px; }
.woocommerce-page .wc-block-components-order-summary-item__description .wc-block-components-product-name {
  font-family: var(--wp--preset--font-family--display);
  font-size: 1.0625rem;
}
/* Colour only. WooCommerce fills this badge from currentColor, so setting a
   background here would paint it the same shade as its own number. */
.woocommerce-page .wc-block-components-order-summary .wc-block-components-order-summary-item__quantity {
  color: var(--wp--preset--color--ink);
}

/* ── Order received ─────────────────────────────────────────────────────── */

.woocommerce-order-received .woocommerce-thankyou-order-received {
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(1.5rem, 3vw, 2.25rem);
  line-height: 1.15;
  color: var(--wp--preset--color--ink);
}
.woocommerce-order .woocommerce-order-overview {
  list-style: none;
  margin: clamp(1.5rem, 3vw, 2.5rem) 0;
  padding: clamp(1.25rem, 2.5vw, 2rem);
  border: 1px solid rgba(59, 42, 58, 0.16);
  border-radius: 22px;
  background: var(--wp--preset--color--linen);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
  gap: clamp(1rem, 2vw, 1.75rem);
}
.woocommerce-order .woocommerce-order-overview li {
  border: 0;
  padding: 0;
  margin: 0;
  font-size: var(--wp--preset--font-size--micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}
.woocommerce-order .woocommerce-order-overview li strong {
  display: block;
  margin-top: 0.5rem;
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(1.0625rem, 1.8vw, 1.375rem);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--wp--preset--color--ink);
}

/* ── Tables: order details, downloads, addresses ────────────────────────── */

.woocommerce table.shop_table {
  width: 100%;
  border: 0;
  border-collapse: collapse;
  border-radius: 0;
}
.woocommerce table.shop_table th,
.woocommerce table.shop_table td {
  padding: 1rem 0.75rem;
  border: 0;
  border-bottom: 1px solid rgba(59, 42, 58, 0.16);
  text-align: left;
  vertical-align: middle;
}
.woocommerce table.shop_table th {
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--micro);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}

.woocommerce .amount,
.woocommerce .price,
.woocommerce-page .wc-block-components-formatted-money-amount {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 500;
}
.woocommerce del { opacity: 0.5; }
.woocommerce ins { text-decoration: none; }

/* ── Motion ─────────────────────────────────────────────────────────────── */
/* Only on the parts that mount once. The cart's rows and the checkout's steps
   are React and are replaced whenever a quantity or an address changes — an
   entry animation on those would replay every keystroke, which is the opposite
   of calm. So the two columns rise once, and everything else animates only on
   hover and focus, which are events the customer caused. */

@keyframes sod-rise {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to { opacity: 1; transform: none; }
}

/* Only the pages WordPress renders whole. Cart and checkout mount a skeleton
   first and hydrate into it, so an entrance animation there runs on the
   placeholder and the real content simply appears — the opposite of the
   effect, and it collides with the layout jump hydration already causes. */
.woocommerce-account #customer_login > div,
.woocommerce-order .woocommerce-order-overview {
  animation: sod-rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.woocommerce-account #customer_login > div:nth-child(2) { animation-delay: 0.08s; }

/* The loading state before the block hydrates is several seconds on 4G, and a
   grey rectangle is a better answer than an empty page. */
/* Every cart update dims the content behind the spinner. WooCommerce takes it
   to 0.25, which is about 1.6:1 against the page — unreadable, and it is the
   text somebody is waiting to see change. */
.woocommerce-page .wc-block-components-loading-mask .wc-block-components-loading-mask__children { opacity: 0.5; }
/* The ring is a ::after border drawn in the element's own colour, so this is
   the one property that reaches it. */
.woocommerce-page .wc-block-components-spinner { color: var(--wp--preset--color--terracotta); }

@media (prefers-reduced-motion: reduce) {
  .woocommerce-account #customer_login > div,
  .woocommerce-order .woocommerce-order-overview { animation: none; }
}

/* 768, not 780: WooCommerce only prints the per-cell labels this layout needs
   below 768, and between the two the table has neither headers nor labels. */
@media (max-width: 768px) {
  .woocommerce-account .woocommerce:has(> .woocommerce-MyAccount-navigation) { grid-template-columns: minmax(0, 1fr); }
  .woocommerce table.shop_table thead { display: none; }
  .woocommerce table.shop_table tr { display: grid; gap: 0.25rem; padding: 0.75rem 0; border-bottom: 1px solid rgba(59, 42, 58, 0.16); }
  .woocommerce table.shop_table td { border: 0; padding: 0.25rem 0; }
  .woocommerce .button { width: 100%; }
}

/* ═══ The builder ═══════════════════════════════════════════════════════════
   /designs/ used to hand a visitor a flat picture of a design and a Buy
   button, which asked them to imagine their own names in it. The Preview
   button now opens the design here instead, with their names actually in it.

   Three columns on a desk — the tabs, the fields, and the invitation itself
   in a phone — collapsing to one on a phone, where the preview becomes a
   sheet the Preview pill pulls up. The invitation in the frame is the real
   built file served from /demo/<design>/, not a mock of it, so what the
   client is editing is the thing they will receive.

   Nothing here is behind a login. You can build the whole invitation before
   you pay; payment is what publishes it. */

/* ── Builder: shell ─────────────────────────────────────────────────────── */

.bld {
  --bld-rail: 190px;
  --bld-stage: 40%;
  --bld-bar: 76px;
  display: flex;
  flex-direction: column;
  min-height: calc(100svh - var(--header-h));
}

/* The bar carries the two things a client looks for — where their work went,
   and how to see it full size — so it stays put under the site header. */
.bld__bar {
  position: sticky;
  top: var(--header-h);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 2vw, 1.5rem);
  min-height: var(--bld-bar);
  padding: 0.75rem var(--v3-gutter);
  border-bottom: 1px solid rgba(59, 42, 58, 0.16);
  background: rgba(251, 247, 241, 0.85);
  backdrop-filter: blur(10px);
}

.bld__back {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  gap: 0.4rem;
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--mute);
  text-decoration: none;
  white-space: nowrap;
}
.bld__back:hover { color: var(--wp--preset--color--ink); }

.bld__title {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(1.1875rem, 2vw, 1.5rem);
  line-height: 1.1;
  min-width: 0;
}
/* The ellipsis has to go on an inline child: text-overflow applies to a block
   container overflowing with inline content, and .bld__title's children are
   flex items, so on the container it paints nothing and hard-clips instead. */
.bld__title [data-bld-name] {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bld__pkg {
  flex: none;
  padding: 0.25rem 0.625rem;
  border: 1px solid rgba(59, 42, 58, 0.22);
  border-radius: 999px;
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}

/* The save state is the quietest thing in the bar until it has bad news. */
.bld__saved {
  margin: 0 auto 0 0;
  /* Sentence case, not the small-caps the rest of the bar uses. Every other
     label here is a heading; this one is a sentence spoken to the client, and
     "PLEASE SIGN IN TO SAVE YOUR PROGRESS" reads as an instruction shouted at
     somebody rather than asked of them. */
  font-size: var(--wp--preset--font-size--small);
  letter-spacing: 0;
  color: var(--wp--preset--color--mute);
  /* "Not saved — this browser is blocking storage" is the longest line this
     ever carries, and it is the one the client must not miss. Let it wrap and
     let it shrink rather than clipping it or pushing Buy off the bar. */
  min-width: 0;
  line-height: 1.35;
}
.bld__saved[data-state="unsaved"] { color: var(--wp--preset--color--terracotta); }

/* The guest state is the one that asks something of the client, so it is a link
   rather than a sentence about one. Underlined, because a word in a status line
   does not otherwise read as clickable. */
.bld__saved[data-state="guest"] { color: var(--wp--preset--color--mute); }
.bld__signin {
  color: var(--wp--preset--color--terracotta);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.bld__signin:hover { color: var(--wp--preset--color--ink); }

.bld__acts { display: flex; align-items: center; gap: 0.625rem; flex: none; }
.bld__acts .v3-btn { min-height: 44px; }

.bld__body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: var(--bld-rail) minmax(0, 1fr) auto minmax(0, var(--bld-stage));
}

/* ── Builder: the divider ───────────────────────────────────────────────── */
/* Which of the two columns matters depends on what the client is doing.
   Writing the invitation wants a wide form; checking how the desktop layout
   falls wants a wide preview. No single split is right for both, so the
   boundary moves. Sticky and viewport-tall like the two columns it sits
   between, or the handle would sit halfway down a form three screens long
   and never be on screen at the moment it was wanted. */
.bld__grip {
  position: sticky;
  top: calc(var(--header-h) + var(--bld-bar));
  align-self: start;
  height: calc(100svh - var(--header-h) - var(--bld-bar));
  width: 11px;
  display: grid;
  place-items: center;
  cursor: col-resize;
  /* or a touch drag scrolls the page instead of moving the divider */
  touch-action: none;
}
.bld__grip span {
  width: 3px;
  height: 36px;
  border-radius: 999px;
  background: rgba(59, 42, 58, 0.22);
  transition: background-color 0.25s, transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.bld__grip:hover span,
.bld__grip:focus-visible span,
.bld[data-dragging="true"] .bld__grip span {
  background: var(--wp--preset--color--terracotta);
  transform: scaleY(1.4);
}
.bld__grip:focus-visible { outline: 2px solid var(--wp--preset--color--terracotta); outline-offset: -2px; }

/* An iframe swallows the pointer, so a drag that crossed the preview would
   stop dead the moment it got there. The form goes inert for the same
   reason: a drag passing over it must not land in a text field. */
.bld[data-dragging="true"] { cursor: col-resize; user-select: none; }
.bld[data-dragging="true"] .bld__frame,
.bld[data-dragging="true"] .bld__panel { pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .bld__grip span { transition: none; }
}

/* ── Builder: the tab rail ──────────────────────────────────────────────── */
/* Tabs are the "section" values the field schema already groups fields under
   (src/designs/_common/fields.json), so adding a field to a section adds it
   to a tab rather than to the builder. */

.bld__rail {
  border-inline-end: 1px solid rgba(59, 42, 58, 0.16);
  padding: clamp(1.25rem, 2.5vw, 2rem) 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: sticky;
  top: calc(var(--header-h) + var(--bld-bar));
  align-self: start;
  max-height: calc(100svh - var(--header-h) - var(--bld-bar));
  overflow-y: auto;
}

.bld__tab {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 44px;
  padding: 0 0.875rem;
  border: 0;
  border-radius: 12px;
  background: none;
  color: var(--wp--preset--color--mute);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--small);
  text-align: start;
  cursor: pointer;
  transition: background-color 0.25s, color 0.25s;
}
.bld__tab:hover { background: var(--wp--preset--color--linen); color: var(--wp--preset--color--ink); }
.bld__tab[aria-selected="true"] {
  background: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--paper);
}

/* A filled dot means the client has changed something on that tab. It is a
   progress mark, not a validity mark — nothing here can be wrong yet. */
.bld__tab i {
  margin-inline-start: auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--wp--preset--color--ochre);
  opacity: 0;
}
.bld__tab[data-touched="true"] i { opacity: 1; }

/* ── Builder: the fields ────────────────────────────────────────────────── */

.bld__panel {
  padding: clamp(1.5rem, 3vw, 2.75rem) clamp(1.25rem, 3vw, 2.75rem) clamp(4rem, 8vw, 7rem);
  overflow-y: auto;
  max-height: calc(100svh - var(--header-h) - var(--bld-bar));
}

.bld__panelhead { margin: 0 0 clamp(1.25rem, 2.5vw, 2rem); max-width: 34rem; }
.bld__panelhead h2 {
  margin: 0 0 0.5rem;
  font-family: var(--wp--preset--font-family--display);
  font-size: clamp(1.5rem, 2.8vw, 2.125rem);
  line-height: 1.1;
}
.bld__panelhead p {
  margin: 0;
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.6;
  color: var(--wp--preset--color--mute);
}

.bld__group { max-width: 34rem; }
.bld__group + .bld__group {
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
  padding-top: clamp(1.75rem, 3vw, 2.5rem);
  border-top: 1px solid rgba(59, 42, 58, 0.16);
}

.bld__grouphead {
  margin: 0 0 1rem;
  font-size: var(--wp--preset--font-size--micro);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}

.bld__field { display: grid; gap: 0.5rem; margin-bottom: 1.125rem; }
.bld__field:last-child { margin-bottom: 0; }

.bld__label {
  font-size: var(--wp--preset--font-size--micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}

.bld__input {
  width: 100%;
  min-height: 52px;
  padding: 0 1.125rem;
  border: 1px solid rgba(59, 42, 58, 0.28);
  border-radius: 12px;
  background: var(--wp--preset--color--paper);
  color: var(--wp--preset--color--ink);
  font-family: var(--wp--preset--font-family--body);
  /* 16px minimum or iOS zooms the page on focus */
  font-size: max(16px, 1rem);
  line-height: 1.4;
}
textarea.bld__input {
  min-height: 118px;
  padding: 0.875rem 1.125rem;
  line-height: 1.7;
  resize: vertical;
}
.bld__input:focus-visible { outline: 3px solid var(--wp--preset--color--terracotta); outline-offset: 2px; }

/* Devanagari in a field wants the face it will be set in, not the UI face —
   otherwise a client types Hindi and sees a system fallback while the preview
   beside them shows the real one. */
.bld__input[lang="hi"] { font-family: 'Tiro Devanagari Hindi', 'Noto Serif Devanagari', serif; }

.bld__hint {
  margin: 0;
  font-size: var(--wp--preset--font-size--micro);
  line-height: 1.5;
  color: var(--wp--preset--color--stone);
}

.bld__row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 1rem; }
.bld__row .bld__field { margin-bottom: 0; }

/* The second-language switch. Off by default for a client who does not need
   it; on, every field grows its twin rather than opening a second form. */
.bld__switch {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 44px;
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--ink);
  cursor: pointer;
}
.bld__switch input { position: absolute; opacity: 0; pointer-events: none; }
.bld__switch span:first-of-type {
  flex: none;
  width: 46px;
  height: 26px;
  border-radius: 999px;
  background: rgba(59, 42, 58, 0.22);
  position: relative;
  transition: background-color 0.25s;
}
.bld__switch span:first-of-type::after {
  content: "";
  position: absolute;
  top: 3px;
  inset-inline-start: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--wp--preset--color--paper);
  transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.bld__switch input:checked + span { background: var(--wp--preset--color--olive); }
.bld__switch input:checked + span::after { transform: translateX(20px); }
.bld__switch input:focus-visible + span { outline: 3px solid var(--wp--preset--color--terracotta); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .bld__switch span:first-of-type::after { transition: none; }
}

/* ── Builder: type and colour ───────────────────────────────────────────── */
/* Size is a multiplier on the invitation's own clamp() scale, never a
   replacement for it — see the --*-scale properties in src/core/base.css.
   Three steps rather than a slider, because a slider invites a client to
   spend an afternoon on 3% and still end up somewhere the design did not
   anticipate. */

/* Two columns, not three: colour and size are on separate tabs now, so a row
   is a name and one control. The ratio line still spans the row beneath. */
.bld__style {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.75rem 1rem;
  padding: 0.875rem 0;
  border-bottom: 1px solid rgba(59, 42, 58, 0.16);
}
.bld__style:last-of-type { border-bottom: 0; }
.bld__style > span:first-child { font-size: var(--wp--preset--font-size--small); }

.bld__sizes { display: flex; gap: 2px; padding: 3px; border-radius: 999px; background: var(--wp--preset--color--linen); }
.bld__size {
  min-width: 44px;
  min-height: 44px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--wp--preset--color--mute);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--micro);
  letter-spacing: 0.08em;
  cursor: pointer;
  transition: background-color 0.25s, color 0.25s;
}
.bld__size[aria-pressed="true"] {
  background: var(--wp--preset--color--paper);
  color: var(--wp--preset--color--ink);
  box-shadow: 0 1px 2px -1px rgba(59, 42, 58, 0.30);
}

.bld__swatch { position: relative; display: inline-flex; }
.bld__swatch input {
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid rgba(59, 42, 58, 0.28);
  border-radius: 8px;
  background: none;
  cursor: pointer;
}
.bld__swatch input::-webkit-color-swatch-wrapper { padding: 3px; }
.bld__swatch input::-webkit-color-swatch { border: 0; border-radius: 5px; }

/* The ratio is shown, never enforced. A hard block on 4.5:1 would generate
   support mail and take a choice away from a family; the number sitting
   beside the swatch is what actually stops a bad one. */
.bld__ratio {
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--wp--preset--font-size--micro);
  color: var(--wp--preset--color--stone);
}
.bld__ratio[data-fail="true"] { color: var(--wp--preset--color--terracotta); }

/* Reset undoes work and the only way back is to do it again, so it is quiet
   — outlined, not filled — and it greys out while there is nothing to undo.
   Disabled rather than hidden: a control that appears and vanishes is harder
   to find the second time than one that is simply grey. */
.bld__reset {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 1.125rem;
  border: 1px solid rgba(59, 42, 58, 0.22);
  border-radius: 999px;
  background: none;
  color: var(--wp--preset--color--mute);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--small);
  cursor: pointer;
  transition: background-color 0.25s, color 0.25s, border-color 0.25s;
}
.bld__reset:hover:not(:disabled) {
  border-color: var(--wp--preset--color--terracotta);
  color: var(--wp--preset--color--terracotta);
}
.bld__reset:disabled { opacity: 0.45; cursor: default; }

@media (prefers-reduced-motion: reduce) {
  .bld__reset { transition: none; }
}

.bld__pairs { display: grid; gap: 0.625rem; }
.bld__pair {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  min-height: 56px;
  padding: 0.625rem 1.125rem;
  border: 1px solid rgba(59, 42, 58, 0.22);
  border-radius: 12px;
  background: var(--wp--preset--color--paper);
  color: var(--wp--preset--color--ink);
  text-align: start;
  cursor: pointer;
  transition: border-color 0.25s, background-color 0.25s;
}
.bld__pair:hover { border-color: rgba(59, 42, 58, 0.45); }
.bld__pair[aria-pressed="true"] { border-color: var(--wp--preset--color--ink); background: var(--wp--preset--color--linen); }
.bld__pair b { font-size: clamp(1.1875rem, 1.8vw, 1.375rem); font-weight: 400; }
.bld__pair i { font-style: normal; font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--mute); }

/* ── Builder: the live preview ──────────────────────────────────────────── */
/* The frame holds /demo/<design>/?edit=1 — the real built invitation with the
   editor bridge awake in it. Edits arrive by postMessage; nothing is rebuilt
   on a server and nothing round-trips, so a name appears as it is typed. */

.bld__stage {
  border-inline-start: 1px solid rgba(59, 42, 58, 0.16);
  background: var(--wp--preset--color--linen);
  padding: clamp(1.25rem, 2.5vw, 2rem);
  display: flex;
  flex-direction: column;
  gap: 1rem;
  position: sticky;
  top: calc(var(--header-h) + var(--bld-bar));
  align-self: start;
  /* A definite height, not a max-height. max-height leaves the box auto-sized,
     so flex: 1 on the wrap below resolves to auto and the phone's own height
     has nothing to measure against — which is how it ended up taller than the
     window with no way to shrink. */
  height: calc(100svh - var(--header-h) - var(--bld-bar));
}

.bld__stagebar { display: flex; align-items: center; gap: 0.5rem; }
.bld__views { display: flex; gap: 2px; padding: 3px; border-radius: 999px; background: rgba(59, 42, 58, 0.08); }
.bld__view {
  min-height: 44px;
  padding: 0 0.875rem;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--wp--preset--color--mute);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 0.25s, color 0.25s;
}
.bld__view[aria-pressed="true"] { background: var(--wp--preset--color--paper); color: var(--wp--preset--color--ink); }

.bld__open {
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 44px;
  font-size: var(--wp--preset--font-size--micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--wp--preset--color--mute);
  text-decoration: none;
}
.bld__open:hover { color: var(--wp--preset--color--ink); }

/* A grid rather than a flex row, so the device's height: 100% resolves
     against a definite area: sized from its width alone, a 340px phone is
     738px tall and overflowed a laptop-height stage. */
.bld__devicewrap { flex: 1; min-height: 0; display: grid; place-items: center; }
/* The desktop frame is the widest thing in this column and the toolbar above it
   is aligned left, so centring it left a gap on one side and none on the other.
   It sits to the left, under its own controls. */
.bld__stage[data-view="desktop"] .bld__devicewrap { justify-items: start; }
.bld__stage[data-view="desktop"] .bld__device { margin-inline: 0; }

/* .gal-device supplies the rail, the buttons and the 402x874 proportion; this
   only decides how wide it is allowed to be and stops the gallery's hover
   lift, which has no meaning around something you are editing. */
.bld__device { margin-inline: auto; }
/* Sized in ONE expression rather than through a chain of percentage heights:
   the narrowest of a comfortable 340px, the column it sits in, and whatever
   width makes an 874-tall phone fit the height left after the toolbar, the
   caption and the padding. On a laptop that lands around 260px — a phone you
   can read, not one that runs off the screen. */
.bld__stage[data-view="phone"] .bld__device {
  aspect-ratio: 402 / 874;
  height: auto;
  width: min(
    340px,
    100%,
    calc((100svh - var(--header-h) - var(--bld-bar) - 7rem) * 402 / 874)
  );
}

.bld__screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 13.68% / 6.29%;
  overflow: hidden;
  background: #FBF7F2;
  /* a column, so the status bar can take its height off the top and the
     iframe gets what is left */
  display: flex;
  flex-direction: column;
  /* the status bar sizes itself against the phone, not the viewport */
  container-type: inline-size;
  box-shadow:
    0 0 0 0.8px #14100F,
    inset 0 0 0 0.5px rgba(255, 255, 255, 0.10);
}
.bld__frame { display: block; flex: 1; min-height: 0; width: 100%; border: 0; }

/* ── The status bar ─────────────────────────────────────────────────────── */
/* 59pt of the 874pt screen, the same proportion the gallery uses. Copied
   rather than shared because the gallery draws a mock invitation inside
   .gal-phone and this holds a real one in an iframe — the two shells cannot
   be the same element, but they must look like the same phone. */

.bld__status {
  position: relative;
  z-index: 2;
  flex: none;
  height: 6.75%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 7%;
  /* the island sits in the gap between the two groups */
  font-size: clamp(7px, 3.4cqw, 11px);
  font-weight: 600;
  letter-spacing: 0.01em;
  opacity: 0.9;
}
.bld__status b { font-weight: 600; }
.bld__status i { display: flex; align-items: center; gap: 3px; }
.bld__status svg { height: 0.75em; width: auto; display: block; }

/* Desktop view: the same iframe, a plainer surround. The phone rail and its
   island would read as a lie around a 16:10 window. */
/* Sized from the room it has, the same way the phone is. Width alone made it
   as wide as the column and 16:10 tall, which on a laptop is taller than the
   column — so the preview ran off the bottom and had to be scrolled to, which
   is the opposite of a preview. The narrowest of: the column, and whatever
   width makes a 16:10 screen fit the height left after the toolbar. */
.bld__stage[data-view="desktop"] .bld__device {
  aspect-ratio: 16 / 10;
  width: min(
    100%,
    calc((100svh - var(--header-h) - var(--bld-bar) - 7rem) * 16 / 10)
  );
  padding: 0.7%;
  border-radius: 14px;
  background: linear-gradient(180deg, #7C7671, #4A4642);
  box-shadow: 0 22px 44px -26px rgba(59, 42, 58, 0.55);
}
.bld__stage[data-view="desktop"] .bld__device::before,
.bld__stage[data-view="desktop"] .bld__device::after,
.bld__stage[data-view="desktop"] .bld__status,
.bld__stage[data-view="desktop"] .gal-device__island { display: none; }
.bld__stage[data-view="desktop"] .bld__screen { border-radius: 6px; }

/* And give it the room. A 1280px layout shown inside a 40% column scales to
   about a third, which is a thumbnail rather than a preview — you cannot read
   it, which is the only reason to look at it. Selecting Desktop widens the
   column; the fields keep the rest. */
.bld:has(.bld__stage[data-view="desktop"]) { --bld-stage: 58%; }

/* An iframe is its own viewport, so a 400px-wide frame renders the phone
   layout however wide the bezel around it is drawn. To show the desktop
   layout the frame has to BE 1280px and then be scaled down; --bld-zoom is
   set from the real width in page-builder.html. Transform only, so this stays
   inside the house rule about what may be animated. */
.bld__stage[data-view="desktop"] .bld__frame {
  width: 1280px;
  height: 800px;
  transform: scale(var(--bld-zoom, 0.3));
  transform-origin: top left;
}

/* Both views above size themselves from the viewport minus 7rem — a guess at
   the chrome above them. The real figure is the stage’s own padding (4rem at
   this width), its toolbar (2.75rem) and the gap between the two (1rem):
   7.75rem. So the desktop preview was about twelve pixels too tall, and its
   bottom edge sat below the fold — the one thing a preview may not do. Making
   the wrap a size container puts its real height in reach, and then there is
   nothing left to estimate.

   Only above the sheet breakpoint. Below it the stage is height: auto, and
   container-type: size means contain: size — a box that sizes itself as if it
   were empty, which against an indefinite parent height collapses to nothing
   and would take the preview down with it. */
@media (min-width: 1001px) {
  @supports (container-type: size) {
    .bld__devicewrap { container-type: size; }

    .bld__stage[data-view="phone"] .bld__device {
      width: min(340px, 100cqw, calc(100cqh * 402 / 874));
    }
    .bld__stage[data-view="desktop"] .bld__device {
      width: min(100cqw, calc(100cqh * 16 / 10));
    }
  }
}

/* ── Builder: the changes are shown, not swapped ────────────────────────── */
/* Switching tab replaces everything in the panel in one frame, which read as
   a flicker rather than as a change. Opacity and a 6px rise only, per the
   house rule about what may be animated, and the groups arrive a beat apart
   so the eye follows the change instead of being handed a new page. */
@keyframes bld-enter {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.bld__panel.is-entering > * { animation: bld-enter 0.28s cubic-bezier(0.22, 1, 0.36, 1) both; }
.bld__panel.is-entering > :nth-child(2) { animation-delay: 0.04s; }
.bld__panel.is-entering > :nth-child(3) { animation-delay: 0.08s; }
.bld__panel.is-entering > :nth-child(4) { animation-delay: 0.12s; }
.bld__panel.is-entering > :nth-child(n + 5) { animation-delay: 0.16s; }

/* Phone and desktop are the same iframe in a different shell. Cutting from
   one to the other read as a jump; a fade and a hair of scale make it a
   change of view. */
@keyframes bld-swap {
  from { opacity: 0; transform: scale(0.985); }
  to   { opacity: 1; transform: none; }
}
.bld__devicewrap.is-swapping .bld__device { animation: bld-swap 0.32s cubic-bezier(0.22, 1, 0.36, 1) both; }

@media (prefers-reduced-motion: reduce) {
  .bld__panel.is-entering > *,
  .bld__devicewrap.is-swapping .bld__device { animation: none; }
}


/* The pill is the only way back to the preview on a phone, so it never
   scrolls away and it clears the home indicator. */
.bld__peek {
  display: none;
  position: fixed;
  z-index: 45;
  inset-inline: 0;
  bottom: calc(1rem + env(safe-area-inset-bottom));
  margin-inline: auto;
  width: max-content;
  align-items: center;
  gap: 0.5rem;
  min-height: 48px;
  padding: 0 1.5rem;
  border: 0;
  border-radius: 999px;
  background: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--paper);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--small);
  font-weight: 500;
  cursor: pointer;
  box-shadow: 0 12px 28px -12px rgba(59, 42, 58, 0.55);
}
.bld[data-sheet="open"] .bld__peek { background: var(--wp--preset--color--terracotta); }

/* ── Builder: phone layout ──────────────────────────────────────────────── */
/* Below 1000px the preview cannot sit beside the fields and still be worth
   looking at, so it becomes a sheet the Preview pill pulls up over them.
   Same iframe, same messages — only the box around it moves. */

@media (max-width: 1000px) {
  /* Block, not a one-column grid: a sticky item is confined to its grid area,
     and as its own auto-height row the rail would have nowhere to travel — it
     would simply sit at the top of the page and scroll away. */
  .bld__body { display: block; }

  .bld__rail {
    position: sticky;
    top: calc(var(--header-h) + var(--bld-bar));
    z-index: 20;
    flex-direction: row;
    max-height: none;
    overflow-x: auto;
    overflow-y: hidden;
    gap: 0.375rem;
    padding: 0.625rem var(--v3-gutter);
    border-inline-end: 0;
    border-bottom: 1px solid rgba(59, 42, 58, 0.16);
    background: var(--wp--preset--color--paper);
    scrollbar-width: none;
  }
  .bld__rail::-webkit-scrollbar { display: none; }
  .bld__tab { flex: none; white-space: nowrap; }

  .bld__panel { max-height: none; overflow: visible; padding-inline: var(--v3-gutter); }

  /* There are no two columns to divide here — the preview is a sheet that
     covers the form, and its width is the window’s. */
  .bld__grip { display: none; }

  /* Translated off-screen it would still be focusable, and the pill's
     aria-expanded="false" would be a lie to a screen reader. visibility takes
     it out of the tab order; the delay lets the slide finish first. */
  .bld:not([data-sheet="open"]) .bld__stage {
    visibility: hidden;
    transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s 0.3s;
  }
  .bld__stage {
    position: fixed;
    inset: auto 0 0;
    z-index: 40;
    max-height: 88svh;
    border-inline-start: 0;
    border-top: 1px solid rgba(59, 42, 58, 0.16);
    border-radius: 22px 22px 0 0;
    box-shadow: 0 -18px 40px -18px rgba(59, 42, 58, 0.45);
    transform: translateY(100%);
    transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .bld[data-sheet="open"] .bld__stage { transform: none; }

  /* The pill is fixed 16-64px above the viewport bottom and the sheet's own
     bottom edge is the viewport bottom, so the sheet has to reserve that band
     — and the safe-area inset belongs on the sheet, not on a child of it. */
  .bld__stage {
    height: auto;
    padding-bottom: calc(4.5rem + env(safe-area-inset-bottom));
  }
  .bld__stage[data-view="phone"] .bld__device {
    width: min(280px, 72vw, calc((70svh - 7rem) * 402 / 874));
  }
  .bld__stage[data-view="desktop"] .bld__device {
    width: min(100%, calc((70svh - 7rem) * 16 / 10));
  }

  .bld__peek { display: inline-flex; }
}

/* Matched to the specificity of the closed-sheet rule above, which is (0,2,0);
   a bare .bld__stage here would lose to it and the sheet would still slide. */
@media (prefers-reduced-motion: reduce) {
  .bld__stage,
  .bld:not([data-sheet="open"]) .bld__stage { transition: none; }
}

@media (max-width: 560px) {
  /* the bar wraps to two rows here, and the rail sticks below it */
  .bld { --bld-bar: 118px; }
  .bld__bar { flex-wrap: wrap; row-gap: 0.5rem; }
  .bld__saved { order: 3; width: 100%; margin: 0; }
  .bld__acts { margin-inline-start: auto; }
  .bld__acts .v3-btn { padding: 0 1.125rem; }
  .bld__title { font-size: 1.1875rem; }
}

/* Without scripting there is no builder — say so and send them somewhere
   that still works, rather than leaving three empty columns. */
.bld__nojs {
  margin: 0 auto;
  padding: clamp(3rem, 8vw, 6rem) var(--v3-gutter);
  max-width: 34rem;
  text-align: center;
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.7;
  color: var(--wp--preset--color--mute);
}

/* ═══ The basket ════════════════════════════════════════════════════════════
   Adding a package from a design tile without leaving the page: the button
   becomes a stepper, a small panel says what is in the basket, and the header
   basket carries a count. Drawn by assets/cart.js against WooCommerce's Store
   API — the same API the block Cart and Checkout read, so there is one idea of
   what the basket holds.                                                     */

/* ── The count on the header basket ─────────────────────────────────────── */

.v3-navend__cart { position: relative; }
.v3-navend__count {
  position: absolute;
  top: -2px;
  inset-inline-end: -2px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--wp--preset--color--terracotta);
  color: var(--wp--preset--color--paper);
  font-size: var(--wp--preset--font-size--micro);
  font-weight: 500;
  line-height: 18px;
  text-align: center;
}

/* ── The stepper ────────────────────────────────────────────────────────── */

.sod-qty {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid rgba(59, 42, 58, 0.28);
  border-radius: 999px;
  background: var(--wp--preset--color--paper);
}
.sod-qty__btn {
  min-width: 38px;
  min-height: 38px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--wp--preset--color--mute);
  font-family: var(--wp--preset--font-family--body);
  font-size: 1.0625rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.25s, color 0.25s;
}
.sod-qty__btn:hover { background: var(--wp--preset--color--linen); color: var(--wp--preset--color--ink); }
.sod-qty__n {
  min-width: 1.75rem;
  text-align: center;
  font-size: var(--wp--preset--font-size--small);
  font-variant-numeric: tabular-nums;
}

/* On a tile it stands where the button stood, so the row does not reflow when
   one becomes the other. */
.sod-qty--tile { min-height: 52px; }

/* The tile sells nothing now — building is the only way in — so its single
   action takes the whole row rather than sitting half-width beside nothing. */
.gal-btn--only { flex: 1; justify-content: center; }

/* ── The panel ──────────────────────────────────────────────────────────── */

.sod-mini {
  position: fixed;
  z-index: 70;
  top: calc(var(--header-h) + 0.75rem);
  inset-inline-end: clamp(0.75rem, 3vw, 1.75rem);
  width: min(23rem, calc(100vw - 1.5rem));
  padding: clamp(1rem, 2vw, 1.375rem);
  border: 1px solid rgba(59, 42, 58, 0.16);
  border-radius: 22px;
  background: var(--wp--preset--color--paper);
  box-shadow: 0 18px 40px -18px rgba(59, 42, 58, 0.45);
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 0.25s, transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.sod-mini[data-open="true"] { opacity: 1; transform: none; }

.sod-mini__head { display: flex; align-items: center; gap: 1rem; }
.sod-mini__title {
  margin: 0 auto 0 0;
  font-size: var(--wp--preset--font-size--micro);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--wp--preset--color--stone);
}
.sod-mini__close {
  flex: none;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--wp--preset--color--mute);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}
.sod-mini__close:hover { background: var(--wp--preset--color--linen); }

.sod-mini__items { margin: 1rem 0 0; display: grid; gap: 0.875rem; }
.sod-mini__item {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding-bottom: 0.875rem;
  border-bottom: 1px solid rgba(59, 42, 58, 0.16);
}
.sod-mini__itemtext { min-width: 0; margin-inline-end: auto; }
.sod-mini__name {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: 1.0625rem;
  line-height: 1.2;
  color: var(--wp--preset--color--ink);
}
.sod-mini__meta {
  margin: 0.25rem 0 0;
  font-size: var(--wp--preset--font-size--micro);
  color: var(--wp--preset--color--stone);
}
.sod-mini__empty {
  margin: 0;
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--mute);
}

.sod-mini__foot { margin-top: 1rem; display: grid; gap: 0.75rem; }
.sod-mini__total {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: 1.25rem;
  color: var(--wp--preset--color--ink);
}
.sod-mini__go { justify-content: center; width: 100%; }

@media (max-width: 560px) {
  /* Bottom of the screen on a phone, where a thumb already is. */
  .sod-mini {
    top: auto;
    bottom: 0;
    inset-inline: 0;
    width: auto;
    border-radius: 22px 22px 0 0;
    padding-bottom: calc(1.375rem + env(safe-area-inset-bottom));
    transform: translateY(12px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .sod-mini { transition: opacity 0.2s; transform: none; }
  .sod-mini[data-open="true"] { transform: none; }
  .sod-qty__btn { transition: none; }
}
