/* =========================================================================
   Midnight Moon Nails — public + admin styles. Mobile-first.
   Midnight palette: a deeper indigo-violet primary + champagne-gold accent on
   cool moon-mist, with a richer near-black midnight sky in the chrome + home hero.
   The primary swatch
   keeps its historical `--pink*` variable names so one repoint re-themes the
   whole site (public + admin + every component) at once.
   ========================================================================= */
:root{
  color-scheme:light; /* the site is light-by-design (photos carry the page) — opt out of UA forced auto-darkening */
  --pink:#574a94; --pink-dark:#43386f; --pink-soft:#eae7f5;   /* names kept; values are deep indigo-violet */
  --bg:#f1f0f8; --card:#ffffff; --ink:#211d31; --ink-2:#4b4661; --muted:#6a6480;
  --line:#e2def0; --line-2:#d4cee8;
  --gold:#b08d4b; --gold-ink:#7a5e22; --gold-bg:#f6eeda;      /* champagne accent — moon & stars (unchanged) */
  --night:#1b1a3a; --night-2:#12122b;                         /* deeper midnight-indigo night sky */
  --serif:Georgia,"New York","Times New Roman",serif;
  --green:#2e7d52; --green-bg:#e6f4ec;
  --amber:#9a6a12; --amber-bg:#fdf1da;
  --blue:#2a5d8f;  --blue-bg:#e4eefb;
  --red:#b23b3b;   --red-bg:#fbe7e7;
  --gray:#5d5559;  --gray-bg:#eceaf1;
  --purple:#6b4a9a;--purple-bg:#efe7fa;
  --teal:#1f7a78;  --teal-bg:#dcf2f1;
  --radius:14px; --shadow:0 1px 3px rgba(40,34,66,.10),0 6px 18px rgba(40,34,66,.07);
  --maxw:1080px;
  /* Semantic tokens — components consume these by ROLE instead of reaching for the
     raw palette vars, so a theme can restyle one role (say, buttons) without moving
     every other purple on the page. The defaults below ARE the tuned Indigo roles
     (Indigo is the default theme, so its palette lives here rather than in an
     override block); every OTHER theme repoints these per-role the same way
     (Purple was first — Light Purple and Dark Purple now carry the same
     treatment). Hierarchy by value ladder: price + the CTA are the only
     saturated elements, headings/emphasis get a rich-but-neutral deep tier,
     legends/labels drop to the secondary --ink-2 tier, selected chips are a
     SOLID fill with white text (white = available, solid = chosen), and
     --focus-ring is a visible mid-value ring (the soft tint is invisible on a
     white field). All pairs AA-checked (≥4.5:1) against their theme's --bg /
     chip fill — see CLAUDE.md for the full ratio table. */
  --heading-ink:#2a2352;             /* deep aubergine-indigo — richer than --ink, not accent */
  --price-ink:var(--pink-dark);      /* card + PDP prices */
  --link-ink:#392f60;                /* darker than the price so links don't compete */
  --legend-ink:var(--ink-2);         /* fieldset legends + small form-group labels leave the accent tier */
  --emph-ink:#2a2352;                /* bold emphasis lines (PDP wear line, mm note, callout heading) join the heading tier */
  --btn-bg:#463b7e; --btn-bg-hover:#372e66; --btn-ghost-ink:var(--pink-dark);
  --callout-bar:#7a6cb4;             /* info-box left accent bar — quieter than the price */
  --chip-selected-bg:var(--pink); --chip-selected-ink:#fff; --chip-selected-note-ink:#e3e0f2;
  --focus-ring:#7a6cb4;              /* input/summary focus outlines */
  --success-ink:#27704a;             /* "✓ Free shipping" line — default --green is 4.45:1 here, just under AA */
}
/* Owner-selectable site themes (admin -> Settings, settings.site_theme) — each
   block REPOINTS the same --pink, --bg, --ink, --line, --night etc variables
   above (see the "--pink* trap" note up top and in CLAUDE.md); --card, --gold*, the
   status-badge pairs, --radius/--shadow/--maxw/--serif stay identical across
   themes on purpose. layout.php/admin_layout.php put the matching `theme-*`
   class on <body> (inherited by nearly everything) AND on <html> — <html> is
   body's ANCESTOR, not a descendant, so it can't inherit body's local custom
   property values and needs its own copy for the `html{background:var(--night-2)}`
   rule just below to repaint too. Four themes: Indigo (default, no override
   block — the :root values above ARE Indigo), Light Purple, Purple, Dark Purple.
   All four now carry the same semantic-token treatment (Purple was first) —
   each block repoints --heading-ink/--price-ink/--link-ink/--legend-ink/
   --emph-ink/--btn-bg(-hover)/--btn-ghost-ink/--callout-bar/--chip-selected-*/
   --focus-ring/--success-ink for its own field, all AA-checked (≥4.5:1)
   against that theme's --bg / chip fill — see CLAUDE.md for the ratio table. */
body.theme-light-purple,html.theme-light-purple{
  --pink:#7c5fb8; --pink-dark:#5f4699; --pink-soft:#f0e9fb;
  --bg:#f5effc;
  --ink:#251d3a; --ink-2:#4e4370; --muted:#71659a;
  --line:#e6dbf5; --line-2:#d6c7ec;
  --night:#2b2355; --night-2:#1e1840;
  --heading-ink:#322559;              /* deep aubergine-indigo — richer than ink, not accent */
  --emph-ink:#322559;                 /* wear line / callout heading join the heading tier */
  --price-ink:#5f4699;
  --link-ink:#4a3880;                 /* darker than the price so links don't compete */
  --legend-ink:var(--ink-2);          /* form labels leave the accent tier */
  --btn-bg:#563e91; --btn-bg-hover:#453175;  /* deeper than --pink: pops by value, not hue */
  --btn-ghost-ink:#5f4699;
  --callout-bar:#9c82d3;              /* quieter than the price */
  --focus-ring:#9c82d3;               /* pink-soft ring is invisible on this field */
  --chip-selected-bg:var(--pink);     /* #7c5fb8 — white text is 5.0:1, passes */
  --chip-selected-ink:#fff;
  --chip-selected-note-ink:#f6f2fd;   /* LIGHTENED, not darkened, from the Purple-tier tint (#ece4f9
                                         was only 4.07:1 against this lighter chip fill) — this field's
                                         --pink sits on the dark side of the black/white contrast
                                         crossover, so pushing the note text toward black tops out at
                                         ~4.19:1; only pushing it further toward white clears 4.5:1. */
  --success-ink:#27704a;              /* default --green is under AA here too */
}
body.theme-purple,html.theme-purple{
  /* Keeps the saturated lavender --bg on purpose (it's this theme's identity), and
     compensates through the semantic tokens: on a field this saturated everything
     purple reads equal, so saturation is spent on exactly two roles (price + CTA),
     darkness does structure (headings/labels), and near-white does surfaces.
     Neutrals: --muted is a near-gray so de-emphasized text doesn't read as a second
     accent; --pink-soft is lifted toward white so soft-filled panels/callouts
     separate from the saturated page. All values AA-checked against #eadef7. */
  --pink:#6b3fae; --pink-dark:#53308a; --pink-soft:#f7f2fd;
  --bg:#eadef7;
  --ink:#241833; --ink-2:#4a3a66; --muted:#615a77;
  --line:#ddcbf1; --line-2:#ccb4e7;
  --night:#251345; --night-2:#170b30;
  --heading-ink:#2e1c52;             /* deep aubergine — richer than ink, not accent */
  --link-ink:#4a2f85;                /* darker than the price so links don't compete */
  --legend-ink:var(--ink-2);         /* form labels leave the accent tier */
  --emph-ink:#2e1c52;                /* wear line / callout heading join the heading tier */
  --btn-bg:#4b2e8f; --btn-bg-hover:#3b2373;  /* deeper than --pink: pops by value, not hue */
  --callout-bar:#8a63c9;             /* quieter than the price */
  --chip-selected-bg:var(--pink);    /* SOLID mid-violet (the shop filter-chip pattern):
                                        white = available, solid = chosen — on this saturated
                                        field a tinted fill reads as the emptier chip. One
                                        value LIGHTER than --btn-bg so the CTA still outranks
                                        a selected chip by darkness + size. */
  --chip-selected-ink:#fff; --chip-selected-note-ink:#e8dcf8;
  --focus-ring:#8a63c9;              /* pink-soft ring is invisible on this field */
  --success-ink:#27704a;             /* default --green is 3.9:1 here — just under AA */
}
body.theme-dark-purple,html.theme-dark-purple{
  /* This theme's problem is compressed tiers — --pink-dark already sits near-black,
     so the old passthrough tokens (price/legend/etc riding --pink-dark) left
     everything reading the same near-black ink. The treatment restores chroma
     hierarchy the same way Purple does: price borrows --pink itself (still dark,
     but with real hue left in it) while headings/emphasis get their own deep tier. */
  --pink:#4b2e75; --pink-dark:#38215a; --pink-soft:#ebe3f6;
  --bg:#ede8f4;
  --ink:#201831; --ink-2:#463a60; --muted:#685e82;
  --line:#dcd1eb; --line-2:#c9bbdf;
  --night:#150f30; --night-2:#0d0820;
  --heading-ink:#2b1d4e;              /* deep aubergine-indigo — richer than ink, not accent */
  --emph-ink:#2b1d4e;                 /* wear line / callout heading join the heading tier */
  --price-ink:#4b2e75;                /* the --pink value — gives price chroma the near-black --pink-dark lacks */
  --link-ink:#38215a;                 /* darker than the price so links don't compete */
  --legend-ink:var(--ink-2);          /* form labels leave the accent tier */
  --btn-bg:#31205c; --btn-bg-hover:#241542;  /* deeper than --pink: pops by value, not hue */
  --btn-ghost-ink:#38215a;
  --callout-bar:#7d63ad;              /* quieter than the price */
  --focus-ring:#7d63ad;               /* pink-soft ring is invisible on this field */
  --chip-selected-bg:#4b2e75; --chip-selected-ink:#fff; --chip-selected-note-ink:#e7def4;
  --success-ink:#27704a;              /* default --green is under AA here too */
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;background:var(--night-2)}
body{margin:0;background:var(--bg);color:var(--ink);
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  line-height:1.55;font-size:16px}
img{max-width:100%;display:block}
a{color:var(--link-ink)}
.container{max-width:var(--maxw);margin:0 auto;padding:0 18px}
.muted{color:var(--muted)}
.req{color:var(--red)}
h1,h2,h3{line-height:1.25;color:var(--heading-ink)}
/* Celestial-elegant display face for public headings + wordmark (system serif,
   ships no font files). Admin headings stay sans for a utilitarian feel. */
body:not(.admin) h1,body:not(.admin) h2,.brand{font-family:var(--serif);letter-spacing:.2px}
.page{padding:24px 18px 56px}
/* The page's own top padding is the ONLY gap under the header — a first child's
   margin must not stack on top of it (margins don't collapse through padding).
   Without this the gap depended on whatever element a page happened to open
   with: 24px for a .prose or .hero, but 40px on product.php (a <p>), 45px on
   shop.php (an <h1>) and 58px on order-status.php (a .section).
   The second selector covers one level of nesting: `.section` has margin but no
   padding, so its own first child's margin COLLAPSES THROUGH it and escapes —
   zeroing only the section itself left order-status.php at 44px. Harmless for
   the other openers: `.prose` already zeroes its first heading, `.hero`'s first
   child is absolutely positioned, and an <h1>/<p> has no element children.
   Same rule at the bottom so a trailing margin doesn't add to the 56px. */
.page > :first-child,
.page > :first-child > :first-child{margin-top:0}
.page > :last-child{margin-bottom:0}

/* ---------- Header / nav ---------- */
/* Persistent night-sky header — the moon/stars live in the chrome on EVERY page,
   so the content below can stay light and let the product photos shine. */
.site-header{position:sticky;top:0;z-index:30;overflow:hidden;color:#fff;border-bottom:1px solid #2a2856;
  /* Faint champagne-gold hairline laid over the dark border-bottom, fading out at
     the edges — a whisper of gold echoing .section__head::after. Renders on the
     bottom edge only because the other three border sides have 0 width. */
  border-image:linear-gradient(90deg,transparent,rgba(176,141,75,.55),transparent) 1;
  background:linear-gradient(180deg,var(--night),var(--night-2))}
.site-header__inner{display:flex;align-items:center;justify-content:space-between;gap:14px;
  flex-wrap:wrap;padding-top:12px;padding-bottom:12px}
.brand{display:inline-flex;align-items:center;gap:9px;font-weight:800;font-size:20px;
  color:#fff;text-decoration:none;letter-spacing:.2px}
.brand__mark{width:26px;height:26px;flex:none}
.brand__mark .moon{fill:currentColor}   /* crescent glows white on the night header */
.brand__mark .spark{fill:var(--gold)}   /* champagne sparkle pops on dark */
.nav{display:flex;flex-wrap:wrap;gap:4px}
/* The overlay-only brand lockup is hidden everywhere except the mobile overlay; the
   links wrapper collapses (display:contents) so links stay direct flex children of the
   inline desktop/no-JS nav. Both are re-styled inside the max-width:699px overlay block. */
.nav__brand{display:none}
.nav__links{display:contents}
.nav__link{position:relative;padding:7px 11px;border-radius:8px;text-decoration:none;color:#e7e2f2;font-size:14px}
/* Animated gold underline on hover/active — a thin bar that scales in from the
   center. Reduced motion still shows the underline, just without the animation. */
.nav__link::after{content:"";position:absolute;left:11px;right:11px;bottom:3px;height:1.5px;
  background:var(--gold);transform:scaleX(0);transform-origin:center;pointer-events:none}
.nav__link:hover{background:rgba(255,255,255,.08)}
.nav__link:hover::after,.nav__link.is-active::after{transform:scaleX(1)}
@media (prefers-reduced-motion:no-preference){
  .nav__link::after{transition:transform .25s ease}
}
.nav__link.is-active{background:rgba(255,255,255,.16);color:#fff;font-weight:600}

/* ---------- Mobile nav: hamburger + full-screen night overlay (<700px, JS only) ----------
   Desktop (>=700px) and the no-JS path are UNCHANGED: the hamburger stays hidden and
   .nav renders inline exactly as it does today (the overlay rules below are scoped
   under html.js + the 699px media query). One <nav> element, restyled by CSS —
   no duplicated link markup. */
.nav-toggle{display:none;align-items:center;justify-content:center;width:44px;height:44px;
  flex:none;margin:0;padding:0;border:none;border-radius:8px;background:transparent;
  color:#fff;cursor:pointer}
.nav-toggle svg{width:24px;height:24px}
.nav-toggle:hover{background:rgba(255,255,255,.08)}
.nav__close{display:none}
.nav__star{display:none}
@media (max-width:699px){
  html.js .nav-toggle{display:inline-flex}
  html.js .site-header__inner{flex-wrap:nowrap}
  html.js .nav{position:fixed;inset:0;z-index:60;display:flex;flex-direction:column;
    align-items:center;justify-content:center;gap:8px;padding:90px 28px 50px;
    background:linear-gradient(165deg,var(--night),var(--night-2));
    visibility:hidden;opacity:0;pointer-events:none}
  html.js .nav.is-open{visibility:visible;opacity:1;pointer-events:auto}
  html.js .nav__links{display:flex;flex-direction:column;align-items:center;gap:8px}
  /* Brand lockup pinned to the top of the overlay (crescent + wordmark, like the header). */
  html.js .nav__brand{position:absolute;top:22px;left:0;right:0;display:flex;flex-direction:column;
    align-items:center;gap:7px;color:#fff;pointer-events:none}
  html.js .nav__brand-mark{width:34px;height:34px}
  html.js .nav__brand-mark .moon{fill:#fff}
  html.js .nav__brand-mark .spark{fill:var(--gold)}
  html.js .nav__brand-name{font-family:var(--serif);font-size:18px;letter-spacing:.3px}
  html.js .nav__link{font-family:var(--serif);font-size:clamp(22px,6vw,28px);color:#fff;
    padding:10px 6px;margin:0}
  html.js .nav__link::after{left:6px;right:6px;bottom:0}
  html.js .nav__link:hover{background:transparent}
  html.js .nav__link.is-active{background:transparent;color:#fff}
  html.js .nav__close{display:flex;align-items:center;justify-content:center;
    position:absolute;top:14px;right:14px;width:44px;height:44px;border:none;border-radius:50%;
    background:rgba(255,255,255,.08);color:#fff;font-size:24px;line-height:1;cursor:pointer}
  html.js .nav__close:hover{background:rgba(255,255,255,.16)}
  html.js .nav__star{display:block;position:absolute;pointer-events:none;opacity:.85}
  html.js .nav__star path,html.js .nav__star circle{fill:var(--gold)}
  html.js .nav__star--1{top:16%;left:14%;width:18px;height:18px}
  html.js .nav__star--2{bottom:20%;right:16%;width:15px;height:15px}
}
@media (max-width:699px) and (prefers-reduced-motion:no-preference){
  /* Panel open/close: fade + a gentle scale-settle. visibility is delayed on close so
     the fade-out actually plays before the overlay is hidden. */
  html.js .nav{transform:scale(1.04);
    transition:opacity .3s ease, transform .4s cubic-bezier(.2,.7,.2,1), visibility 0s .4s}
  html.js .nav.is-open{transform:scale(1);
    transition:opacity .3s ease, transform .4s cubic-bezier(.2,.7,.2,1), visibility 0s 0s}
  /* Brand eases down into place with the panel. */
  html.js .nav__brand{opacity:0;transform:translateY(-10px);
    transition:opacity .4s ease .05s, transform .4s ease .05s}
  html.js .nav.is-open .nav__brand{opacity:1;transform:none}
  /* Menu items slide up + fade in, staggered by the inline --i index. */
  html.js .nav__link{opacity:0;transform:translateY(18px);
    transition:opacity .45s ease, transform .55s cubic-bezier(.2,.7,.2,1);
    transition-delay:calc(var(--i,0) * 65ms + 130ms)}
  html.js .nav.is-open .nav__link{opacity:1;transform:none}
  html.js .nav__star{animation:sparkle 5.5s ease-in-out infinite}
  html.js .nav__star--2{animation-duration:6.4s;animation-delay:.6s}
}
/* Locks background scroll while the overlay menu is open (ui.js toggles this on <html>). */
html.nav-lock{overflow:hidden}

/* Slim site-wide free-shipping strip. Champagne/cream so it flows with the light
   content zone below the header (no dark band sandwiched between light rows); gold
   hairlines top+bottom read it as its own delicate layer. */
.promo-bar{background:var(--gold-bg);color:var(--gold-ink);text-align:center;
  font-size:11.5px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;padding:7px 14px;
  border-top:1px solid rgba(176,141,75,.28);border-bottom:1px solid rgba(176,141,75,.28)}
.promo-bar span{display:inline-block}
.promo-bar__spark{color:var(--gold)}
@media (max-width:480px){.promo-bar{font-size:10.5px;letter-spacing:.1em;padding:6px 12px}}

.status-banner{background:var(--pink-soft);color:var(--pink-dark);font-size:14px;
  border-bottom:1px solid var(--line-2)}
.status-banner .container{padding-top:10px;padding-bottom:10px;font-weight:600;text-align:center}
@media(max-width:640px){
  .status-banner{font-size:13px}
  .status-banner .container{padding-top:7px;padding-bottom:7px}
}

/* ---------- Footer — dark night-sky bookend to the header ---------- */
.site-footer{position:relative;overflow:hidden;color:#e7e2f2;border-top:1px solid #2a2856;margin-top:40px;
  /* Same faint gold hairline treatment as the header, for symmetry. */
  border-image:linear-gradient(90deg,transparent,rgba(176,141,75,.55),transparent) 1;
  background:linear-gradient(180deg,var(--night-2),var(--night));
  /* iOS home-indicator / safe-area inset — the dark footer should extend under it,
     not show the light page background peeking through. */
  padding-bottom:env(safe-area-inset-bottom)}
.site-footer__inner{display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;
  align-items:center;padding:26px 18px;max-width:var(--maxw);margin:0 auto;font-size:14px}
.site-footer strong{color:#fff}
.site-footer .muted{color:#b4adc8}   /* light-lavender muted, readable on the dark footer */
.footer-nav{display:flex;flex-wrap:wrap;gap:14px}
.footer-nav a{color:#e7e2f2;text-decoration:none}
.footer-nav a:hover{color:#fff;text-decoration:underline}
.social{display:flex;gap:10px;margin-top:12px}
.social__link{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;
  border-radius:50%;background:rgba(255,255,255,.08);color:#e7e2f2;transition:background .15s,color .15s}
.social__link:hover{background:var(--gold);color:var(--night)}
.social__link svg{width:19px;height:19px;fill:currentColor}

/* ---------- Twinkling starfield for the dark chrome (header + footer) ----------
   Two star layers pulse out of phase so they shimmer independently instead of
   all blinking together. Content sits above via z-index; motion honors reduced-motion. */
/* Twinkling starfield in the dark chrome (header + footer). On desktop it lives ONLY
   in the outer gutters beside the centered 1080px content — left of the logo, right
   of the menu — so it never sits behind text. The mask is px-anchored to the content
   edge (not %), so stars can't drift onto text as the window resizes; on narrow
   viewports the gutters vanish and the gutter-mask layers are replaced below 700px by
   a dim, full-bleed scatter (see the max-width:700px block) faint enough not to read
   as interference behind text. */
.site-header__inner,.site-footer__inner{position:relative;z-index:1}
.site-header::before,.site-header::after,
.site-footer::before,.site-footer::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  -webkit-mask:linear-gradient(90deg,#000,#000 calc(50% - 560px),transparent calc(50% - 535px),transparent calc(50% + 535px),#000 calc(50% + 560px),#000);
          mask:linear-gradient(90deg,#000,#000 calc(50% - 560px),transparent calc(50% - 535px),transparent calc(50% + 535px),#000 calc(50% + 560px),#000)}
/* Bright layer — larger stars, clustered near the edges so they fill the gutters. */
.site-header::before,.site-footer::before{
  background:
    radial-gradient(3.5px 3.5px at 3% 42%,  #ffffff 45%, transparent),
    radial-gradient(4px 4px     at 7% 66%,  #f6eeda 42%, transparent),
    radial-gradient(3px 3px     at 11% 24%, #ffffff 45%, transparent),
    radial-gradient(3.6px 3.6px at 89% 30%, #f6eeda 42%, transparent),
    radial-gradient(3px 3px     at 93% 62%, #ffffff 45%, transparent),
    radial-gradient(3.6px 3.6px at 97% 44%, #ffffff 45%, transparent);
  animation:twinkle 4.5s ease-in-out infinite}
/* Dim layer — smaller stars, out of phase, for depth. */
.site-header::after,.site-footer::after{
  background:
    radial-gradient(2px 2px     at 5% 55%, rgba(255,255,255,.90) 45%, transparent),
    radial-gradient(2px 2px     at 2% 30%, rgba(255,255,255,.80) 45%, transparent),
    radial-gradient(2.4px 2.4px at 9% 78%, rgba(246,238,218,.95) 42%, transparent),
    radial-gradient(2px 2px     at 95% 55%, rgba(255,255,255,.85) 45%, transparent),
    radial-gradient(2px 2px     at 98% 30%, rgba(255,255,255,.80) 45%, transparent),
    radial-gradient(2px 2px     at 91% 70%, rgba(255,255,255,.75) 45%, transparent);
  animation:twinkle 6s ease-in-out infinite 1.4s}
@keyframes twinkle{0%,100%{opacity:.45}50%{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .site-header::before,.site-header::after,
  .site-footer::before,.site-footer::after{animation:none;opacity:.9}}
/* Mobile (<700px): the gutters that hide the gutter-mask layers vanish, so the
   bright large-star layer (::before) and the gold .spark-star SVGs are just hidden
   outright — at header/footer width there's no room for them without sitting on
   text. The dim layer (::after) is kept but re-purposed as a full-bleed scatter of
   very small, very low-opacity dots (no mask needed — at this size/opacity the
   dots read as "night sky" texture, not interference, even sitting under text).
   Positions are spread out and avoid dead-center (~40–55%) where the wordmark sits. */
@media (max-width:700px){
  .site-header::before,.site-footer::before,.spark-star{display:none}
  .site-header::after,.site-footer::after{
    -webkit-mask:none;mask:none;
    background:
      radial-gradient(1.5px 1.5px at 6% 28%,  rgba(255,255,255,.55) 60%, transparent),
      radial-gradient(2px 2px     at 18% 68%, rgba(246,238,218,.5)  55%, transparent),
      radial-gradient(1.5px 1.5px at 30% 44%, rgba(255,255,255,.45) 60%, transparent),
      radial-gradient(2px 2px     at 62% 22%, rgba(255,255,255,.5)  55%, transparent),
      radial-gradient(1.5px 1.5px at 72% 62%, rgba(246,238,218,.45) 60%, transparent),
      radial-gradient(2px 2px     at 85% 34%, rgba(255,255,255,.45) 55%, transparent),
      radial-gradient(1.5px 1.5px at 94% 70%, rgba(255,255,255,.4)  60%, transparent);
    opacity:.35;
    animation:twinkle 7.5s ease-in-out infinite}
}
@media (max-width:700px) and (prefers-reduced-motion:reduce){
  .site-header::after,.site-footer::after{animation:none;opacity:.35}}

/* =========================================================================
   POLISH — reveal-on-scroll, hover motion, hero focal moon, type flourishes.
   All motion is gated on prefers-reduced-motion and never blocks content.
   ========================================================================= */

/* Smooth in-page (anchor) scrolling */
@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}

/* Offset anchor targets so a jump/scroll doesn't tuck the heading under the
   sticky site header (.site-header, position:sticky top:0). */
#how-it-works{scroll-margin-top:76px}

/* Reveal-on-scroll — the hidden start-state applies only when JS is active
   (html.js, set before first paint) AND motion is allowed. ui.js adds .is-in
   as each element scrolls into view. No JS or reduced-motion => always visible. */
@media (prefers-reduced-motion:no-preference){
  html.js .reveal{opacity:0;transform:translateY(18px);
    transition:opacity .6s ease,transform .55s cubic-bezier(.22,.7,.2,1),box-shadow .3s ease}
  html.js .reveal.is-in{opacity:1;transform:none}
  html.js .grid .reveal:nth-child(2){transition-delay:.06s}
  html.js .grid .reveal:nth-child(3){transition-delay:.12s}
  html.js .steps .reveal:nth-child(2){transition-delay:.06s}
  html.js .steps .reveal:nth-child(3){transition-delay:.12s}
  html.js .steps .reveal:nth-child(4){transition-delay:.18s}
}

/* Card hover — gentle lift + image zoom (photo stays clipped by .card__media) */
.card:hover{transform:translateY(-5px);
  box-shadow:0 8px 16px rgba(40,34,66,.10),0 22px 44px rgba(40,34,66,.13)}
.card__media img{transition:transform .55s cubic-bezier(.2,.7,.2,1)}
.card:hover .card__media img{transform:scale(1.06)}
@media (prefers-reduced-motion:reduce){
  .card:hover{transform:none}
  .card:hover .card__media img{transform:none}
}

/* Eyebrow label above a section heading, e.g. "✦ Handpicked" */
.eyebrow{display:block;margin:0 0 4px;font-size:11.5px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--gold-ink)}

/* Short gold hairline accent under each section heading */
.section__head::after{content:"";position:absolute;left:0;bottom:-1px;width:46px;height:2px;
  background:var(--gold);border-radius:2px}

/* Hero focal point — soft crescent moon + a couple of gold sparkles (behind the text) */
.hero .hero__deco{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.hero__moon{position:absolute;top:16px;right:36px;width:172px;height:172px;opacity:.92;
  filter:drop-shadow(0 8px 20px rgba(176,141,75,.22))}
.hero__spark{position:absolute}
.hero__spark .spark{fill:var(--gold)}
.hero__spark--1{top:122px;right:26px;width:16px;height:16px;opacity:.85}
.hero__spark--2{top:40px;right:196px;width:22px;height:22px;opacity:.7}
@media (max-width:640px){
  .hero__moon{width:104px;height:104px;top:10px;right:-6px;opacity:.72}
  .hero__spark--1{top:86px;right:8px}
  .hero__spark--2{top:14px;right:18px;width:16px;height:16px}
  /* Tighter mobile hero — products should appear sooner on a phone screen. */
  .hero{padding:28px 18px}
  .hero h1{font-size:clamp(26px,7.2vw,34px)}
}

/* =========================================================================
   CINEMATIC — hero entrance animation + scroll parallax on the hero crescent.
   All motion gated on prefers-reduced-motion.
   ========================================================================= */

/* Hero content entrance on load (staggered) */
@media (prefers-reduced-motion:no-preference){
  html.js .hero .eyebrow{animation:hero-in .7s 0s both cubic-bezier(.2,.7,.2,1)}
  html.js .hero h1{animation:hero-in .7s .05s both cubic-bezier(.2,.7,.2,1)}
  html.js .hero p{animation:hero-in .7s .16s both cubic-bezier(.2,.7,.2,1)}
  html.js .hero__cta{animation:hero-in .7s .27s both cubic-bezier(.2,.7,.2,1)}
}
@keyframes hero-in{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:no-preference){.hero__deco{will-change:transform,opacity}}
/* Safety net: if ui.js never runs (load/parse failure), the inline <head> script adds
   .reveal-all after a short timeout so .reveal content is never left permanently hidden. */
html.reveal-all .reveal{opacity:1!important;transform:none!important}

/* --- Gold sparkle stars in the dark chrome (borrowed from Golden Tide's nail art:
   thin gold 4-point sparkles + one 5-point, a few with a pale gem center) --------- */
.spark-star{position:absolute;z-index:0;pointer-events:none;transform-origin:center;opacity:.9}
.spark-star path{fill:var(--gold)}
.spark-star circle{fill:#fff7e8}
/* Positioned in the outer GUTTERS via calc anchored to the 1080px content edge
   (half = 540px): 'right:' = left of the logo, 'left:' = right of the menu. */
.spark-star--h1{right:calc(50% + 540px + 20px);top:22%;width:16px;height:16px}
.spark-star--h2{right:calc(50% + 540px + 84px);top:56%;width:12px;height:12px}
.spark-star--h3{left:calc(50% + 540px + 20px);top:26%;width:17px;height:17px}
.spark-star--h4{left:calc(50% + 540px + 90px);top:58%;width:12px;height:12px}
.spark-star--f1{right:calc(50% + 540px + 26px);top:26%;width:16px;height:16px}
.spark-star--f2{right:calc(50% + 540px + 96px);top:62%;width:12px;height:12px}
.spark-star--f3{left:calc(50% + 540px + 26px);top:28%;width:20px;height:20px}   /* the 5-point one */
.spark-star--f4{left:calc(50% + 540px + 98px);top:60%;width:15px;height:15px}
@media (prefers-reduced-motion:no-preference){
  .spark-star{animation:sparkle 5s ease-in-out infinite}
  .spark-star--h1{animation-duration:6s;animation-delay:.4s}
  .spark-star--h2{animation-duration:4.8s;animation-delay:1.3s}
  .spark-star--h3{animation-duration:5.6s;animation-delay:.9s}
  .spark-star--h4{animation-duration:6.6s;animation-delay:1.8s}
  .spark-star--f1{animation-duration:6s;animation-delay:.8s}
  .spark-star--f2{animation-duration:4.6s;animation-delay:1.6s}
  .spark-star--f3{animation-duration:7s;animation-delay:.3s}
  .spark-star--f4{animation-duration:5.4s;animation-delay:2s}
}
@keyframes sparkle{0%,100%{opacity:.5;transform:scale(.78) rotate(0deg)}
  50%{opacity:1;transform:scale(1.08) rotate(9deg)}}

/* --- Iridescent "opal" shimmer: a glint sweeps the gold CTA on hover, and the
   hero crescent shimmers like the opal cabochon on Golden Tide -------------------- */
.btn--gold{position:relative;overflow:hidden}
.btn--gold::after{content:"";position:absolute;top:0;left:-80%;width:55%;height:100%;
  transform:skewX(-20deg);pointer-events:none;
  background:linear-gradient(90deg,transparent,rgba(200,170,255,.35),
    rgba(255,255,255,.55),rgba(150,220,205,.35),transparent)}
@media (prefers-reduced-motion:no-preference){
  .btn--gold:hover::after{animation:sheen .85s ease}
  .hero__moon{animation:moon-opal 9s ease-in-out infinite}
}
@keyframes sheen{from{left:-80%}to{left:135%}}
@keyframes moon-opal{
  0%,100%{filter:hue-rotate(-10deg) saturate(1)    drop-shadow(0 8px 20px rgba(176,141,75,.22))}
  50%    {filter:hue-rotate(16deg)  saturate(1.15) drop-shadow(0 8px 22px rgba(150,140,215,.24))}}

/* --- Molten-gold filigree divider (flowing gold linework, à la Golden Tide /
   Crimson Lotus) — replaces the plain crescent between the hairlines ------------ */
.moon-divider .filigree{width:min(240px,72%);height:auto;flex:none}
.moon-divider .ln{fill:none;stroke:var(--gold);stroke-width:1.5;stroke-linecap:round;opacity:.92}
.moon-divider .bd{fill:var(--gold)}

/* ---------- Buttons ---------- */
.btn{display:inline-block;border:none;cursor:pointer;font:inherit;font-weight:600;
  padding:11px 18px;border-radius:10px;text-decoration:none;text-align:center;
  background:var(--btn-bg);color:#fff;transition:background .15s}
.btn:hover{background:var(--btn-bg-hover)}
/* Gentle press feedback. Disabled buttons already carry pointer-events:none, so
   :active never triggers for them — no extra guard needed. */
@media (prefers-reduced-motion:no-preference){
  .btn{transition:background .15s,transform .1s ease}
  .btn:active{transform:translateY(1px)}
}
.btn--block{display:block;width:100%}
.btn--ghost{background:#fff;color:var(--btn-ghost-ink);border:1px solid var(--line-2)}
.btn--ghost:hover{background:var(--pink-soft)}
.btn--muted{background:var(--gray-bg);color:var(--gray)}
.btn--danger{background:var(--red)}
.btn--danger:hover{background:#8f2f2f}
.btn--sm{padding:7px 12px;font-size:13px;border-radius:8px}
.btn:disabled,.btn.is-disabled{background:var(--gray-bg);color:var(--muted);cursor:not-allowed;pointer-events:none}

/* ---------- Hero (home) — light "moonrise" wash (the night now lives in the header) ---------- */
.hero{position:relative;overflow:hidden;border:1px solid var(--line);border-radius:var(--radius);
  padding:44px 24px;text-align:center;margin-bottom:26px;box-shadow:var(--shadow);
  background:linear-gradient(165deg,var(--pink-soft),#fff 72%)}
/* faint champagne crescent-glow, top-right — a subtle nod to the theme on the light hero */
.hero::after{content:"";position:absolute;top:-60px;right:-40px;width:180px;height:180px;
  border-radius:50%;pointer-events:none;
  background:radial-gradient(circle at 60% 45%, rgba(176,141,75,.14), rgba(176,141,75,0) 68%)}
.hero>*{position:relative;z-index:1}
.hero .eyebrow{margin:0 0 10px}
.hero h1{font-size:clamp(30px,5.2vw,42px);line-height:1.12;letter-spacing:.3px;margin:0 0 12px;color:var(--heading-ink)}
.hero p{max-width:620px;margin:0 auto 22px;color:var(--ink-2)}
.hero__cta{display:flex;gap:12px;justify-content:center;flex-wrap:wrap}

/* ---------- Section headings ---------- */
.section{margin:34px 0}
.section__head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin-bottom:14px;border-bottom:1px solid var(--line);padding-bottom:8px;position:relative}
.section__head h2{margin:0;font-size:22px}
.section__head .muted{font-size:14px}

/* ---------- Product grid + cards ---------- */
/* Mobile: a tight 2-up grid so more of the catalog shows above the fold. */
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
@media(min-width:560px){.grid{grid-template-columns:repeat(2,1fr);gap:16px}}
@media(min-width:860px){.grid{grid-template-columns:repeat(3,1fr)}}
@media(max-width:559px){
  /* Compact 2-up cards — tightens type/padding so two designs read comfortably
     side by side. Scoped to .grid only — shelves never compact. */
  .grid .card .card__body{padding:9px;gap:4px}
  .grid .card .card__title{font-size:14.5px}
  .grid .card .card__price{font-size:13px}
  .grid .card .card__meta{font-size:11.5px}
  .grid .card .card__foot{padding:0 9px 9px}
  .grid .card .card__foot .btn{padding:7px 12px;font-size:13px;border-radius:8px}
}

/* ---------- Horizontal shelves (Featured / On Sale rows) ----------
   A momentum-scrolling row of cards with scroll-snap, used on home ("Featured
   Sets", "On Sale") and the shop's "On Sale" row. The main shop catalog stays a
   .grid. Cards peek at the edge so the row reads as scrollable at a glance. */
.shelf{display:flex;gap:16px;overflow-x:auto;padding-bottom:8px;margin:0;
  scroll-snap-type:x proximity;scroll-padding-left:2px;-webkit-overflow-scrolling:touch;
  scrollbar-width:thin}
.shelf::-webkit-scrollbar{height:7px}
.shelf::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:4px}
.shelf::-webkit-scrollbar-track{background:transparent}
.shelf .card{flex:0 0 auto;width:76vw;scroll-snap-align:start}
@media(min-width:560px){.shelf .card{width:340px}}
@media(min-width:860px){.shelf .card{width:300px}}
/* render_product_card() marks every card .reveal for the scroll IntersectionObserver,
   which only ever sees vertical intersection — cards sitting off to the right inside
   a horizontally-scrolling shelf would otherwise stay invisible until swiped into
   view. Shelves opt out of reveal-on-scroll entirely. */
html.js .shelf .reveal{opacity:1;transform:none}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden;box-shadow:var(--shadow);display:flex;flex-direction:column;
  transition:transform .4s cubic-bezier(.22,.7,.2,1),box-shadow .3s ease}
.card__media{aspect-ratio:1/1;background:var(--pink-soft);position:relative;overflow:hidden}
.card__media img{width:100%;height:100%;object-fit:cover}
.card__media .placeholder{display:flex;align-items:center;justify-content:center;height:100%;
  color:var(--pink);font-size:40px}
.card__badges{position:absolute;top:10px;left:10px;display:flex;gap:6px;flex-wrap:wrap}
.card__body{padding:14px;display:flex;flex-direction:column;gap:6px;flex:1}
.card__title{font-weight:700;font-size:16px;margin:0}
.card__title a{color:var(--ink);text-decoration:none}
.card__price{font-weight:700;color:var(--price-ink)}
.card__meta{font-size:13px;color:var(--muted)}
.card__foot{padding:0 14px 14px}

/* ---------- Badges ---------- */
.badge{display:inline-block;font-size:11.5px;font-weight:700;letter-spacing:.2px;
  padding:3px 9px;border-radius:999px;line-height:1.6;white-space:nowrap}
.badge--green{background:var(--green-bg);color:var(--green)}
.badge--amber{background:var(--amber-bg);color:var(--amber)}
.badge--blue{background:var(--blue-bg);color:var(--blue)}
.badge--red{background:var(--red-bg);color:var(--red)}
.badge--gray{background:var(--gray-bg);color:var(--gray)}
.badge--purple{background:var(--purple-bg);color:var(--purple)}
.badge--teal{background:var(--teal-bg);color:var(--teal)}

/* ---------- Notices ---------- */
.notice{border-radius:10px;padding:12px 14px;font-size:14px;margin:14px 0;border:1px solid}
.notice__steps{margin:8px 0 0;padding-left:20px}
.notice__steps li{margin:2px 0}
.notice--info{background:var(--blue-bg);border-color:#cfe0f4;color:#214c73}
.notice--warn{background:var(--amber-bg);border-color:#f0dca8;color:#7a5410}
.notice--ship{background:var(--pink-soft);border-color:var(--line-2);color:var(--pink-dark)}

/* ---------- Kit callout (home) ---------- */
.kit-callout{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:32px 24px;text-align:center;max-width:560px;margin:0 auto}
.kit-callout h2{margin:0 0 10px;font-size:22px}
.kit-callout p{margin:0 0 18px;color:var(--ink-2);font-size:15px;line-height:1.6}
.flash{border-radius:10px;padding:11px 14px;margin:0 0 14px;font-size:14px;border:1px solid}
.flash--success{background:var(--green-bg);border-color:#bfe4cd;color:var(--green)}
.flash--error{background:var(--red-bg);border-color:#f0c4c4;color:var(--red)}
.flash--info{background:var(--blue-bg);border-color:#cfe0f4;color:#214c73}
.flash--warning{background:var(--amber-bg);border-color:#f0dca8;color:#7a5410}
/* render_flashes() prints directly inside <main class="container page">, ahead of
   any page-specific markup, so a flash normally spans the full .container width
   (1080px). On the handful of pages whose entire content IS a single narrow
   `.form` card (order.php, waitlist.php, invite.php) that card is capped at 680px
   and centered, so a full-width flash above it reads as unaligned — wider on both
   sides than the card it's describing. Scoped to exactly that layout (a flash
   immediately followed by `.form`, under the public <main>) so grid/shelf pages
   (shop/home/product), where full-width flashes already match the full-width
   content below them, are untouched. Adjacency (rather than matching on <main>)
   also keeps contact.php out — its `.form` is left-aligned at 760px, not the
   centered 680px card this rule aligns to. */
main.container.page > .flash:has(+ .form){max-width:680px;margin-left:auto;margin-right:auto}

/* ---------- Product detail ---------- */
.pd{display:grid;grid-template-columns:1fr;gap:24px}
@media(min-width:760px){.pd{grid-template-columns:1fr 1fr;align-items:start}}
/* Reading matter (description / included / how-to / notes) is a full-width band
   under BOTH columns. Source order is visual order, so the buy box is never
   separated from the photos by prose and nothing needs grid-lifting (the owner
   rejected the under-gallery placement that required it).
   One column by default, capped: full-width prose at --maxw:1080px runs ~128
   characters a line, roughly double the comfortable maximum.
   The cap goes on the CHILDREN, never on .pd__body itself — the band carries the
   section divider, and capping the band draws that hairline only as wide as the
   text, so it died 139px short on a tablet and 359px short on the kit page (no
   second column). A rule that stops three-fifths of the way across a page whose
   every other element is full-width reads as a rendering fault. */
@media(min-width:760px){
  .pd__body{grid-column:1/-1;border-top:1px solid var(--line);padding-top:22px}
}
.pd__body>.pdsec{max-width:68ch}
/* ONE column, at every width. Two attempts at filling the leftover space were
   tried and dropped (2026-08-04):
   - Reading matter left / how-to right. It only balances when a description is
     present; with `show_product_descriptions` off — which is where the shop is
     heading — the left column is two short blocks against seven steps, leaving a
     279px hole under it on every set page.
   - The seven steps flowing across two columns. It balances automatically and
     fills the width, but a numbered sequence read down one column and back up to
     the top of the next is harder to follow than a single list, and these are
     instructions someone reads mid-task with glue on their hands. Filling
     horizontal space is not worth that.
   The band reads as full-width because the DIVIDER spans it (see above); the
   prose staying at a comfortable measure underneath is typography, not a gap.
   If a how-to VIDEO is ever added, that is the case for two columns here —
   video beside the steps, an object next to the sequence rather than the
   sequence chopped up. */
.pd__body h2{font-size:1.17em}
.pdsec>h2:first-child{margin-top:22px}                       /* gap between sections */
.pd__body>.pdsec:first-child>h2:first-child{margin-top:0}    /* flush with the band's rule */
/* Gallery: large main stage + thumbnail rail (video plays inline in the stage) */
.pd__stage{aspect-ratio:1/1;border-radius:var(--radius);overflow:hidden;background:var(--pink-soft);
  display:flex;align-items:center;justify-content:center}
.pd__stage img{width:100%;height:100%;object-fit:cover}
.pd__stage .placeholder{color:var(--pink);font-size:64px}
.pd__stage.has-video{aspect-ratio:auto}
.pd__stage.has-video .video-embed{margin:0;border-radius:var(--radius)}
.pd__thumbs{display:flex;gap:8px;margin-top:8px;flex-wrap:wrap}
.pd__thumb{position:relative;width:64px;height:64px;padding:0;border:1px solid var(--line);
  border-radius:8px;overflow:hidden;cursor:pointer;background:var(--pink-soft);transition:border-color .15s,box-shadow .15s}
.pd__thumb img,.pd__thumb video{width:100%;height:100%;object-fit:cover;pointer-events:none}
.pd__thumb:hover{border-color:var(--line-2)}
.pd__thumb.is-active{border-color:var(--pink);box-shadow:0 0 0 2px var(--pink-soft)}
.pd__thumb-play{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  color:#fff;font-size:20px;background:rgba(0,0,0,.32);text-shadow:0 1px 3px rgba(0,0,0,.6)}
/* ---- Image lightbox (product.php's inline gallery script builds the DOM) ----
   A pure JS enhancement: the overlay is created and appended to <body> on first
   open, so with JS off nothing here exists, the stage image is not clickable and
   nothing is lost. The zoom cursor is gated on html.js for the same reason.
   Video is never lightboxed — the inline video embed keeps the stage to itself. */
html.js .pd__stage img{cursor:zoom-in}
html.js .pd__stage.has-video img{cursor:default}
.pdlb{position:fixed;inset:0;z-index:70;opacity:0;visibility:hidden;
  background:rgba(9,8,26,.96);                 /* the --night end of the palette, near-opaque */
  touch-action:none;                           /* the pinch/pan gestures are ours, not the browser's */
  -webkit-user-select:none;user-select:none}
.pdlb.is-open{opacity:1;visibility:visible}
.pdlb__stage{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;overflow:hidden}
/* auto width/height (not 100% + object-fit) so the element box IS the photo box —
   the pan clamp measures offsetWidth/offsetHeight and must not count letterboxing. */
.pdlb__img{display:block;width:auto;height:auto;max-width:100%;max-height:100%;object-fit:contain;
  transform-origin:center center;-webkit-user-drag:none;will-change:transform}
/* The magnifier IS the "there is more detail here" signal, so it appears only when
   the photo has real resolution left (see lbZoomCap() in product.php). */
.pdlb.is-zoomable:not(.is-zoomed) .pdlb__img{cursor:zoom-in}
.pdlb.is-zoomed .pdlb__img{cursor:grab}
.pdlb.is-zoomed.is-panning .pdlb__img{cursor:grabbing}
.pdlb__btn{position:absolute;display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;padding:0;border:none;border-radius:50%;
  background:rgba(255,255,255,.10);color:#fff;line-height:1;cursor:pointer}
.pdlb__btn:hover{background:rgba(255,255,255,.20)}
.pdlb__btn:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.pdlb__close{top:calc(14px + env(safe-area-inset-top));right:14px;font-size:26px}
.pdlb__nav{top:50%;margin-top:-22px;font-size:28px}
.pdlb__nav--prev{left:10px}
.pdlb__nav--next{right:10px}
.pdlb__count{position:absolute;left:0;right:0;bottom:calc(14px + env(safe-area-inset-bottom));
  margin:0;text-align:center;color:#fff;opacity:.72;font-size:13px;pointer-events:none}
@media (prefers-reduced-motion:no-preference){
  /* Fade in/out; visibility is transition-delayed on close so the fade actually
     plays (same pattern as the mobile nav overlay above). The scale-settle uses
     the standalone `scale` property, NOT transform — transform belongs to the
     zoom/pan state and the two must not fight over it. */
  .pdlb{transition:opacity .22s ease, visibility 0s .22s}
  .pdlb.is-open{transition:opacity .22s ease, visibility 0s 0s}
  .pdlb__img{scale:.97;transition:scale .28s cubic-bezier(.2,.7,.2,1)}
  .pdlb.is-open .pdlb__img{scale:1}
}
.pd__price{font-size:24px;font-weight:800;color:var(--price-ink);margin:6px 0}
.pd__price-why{display:inline-block;margin-left:10px;font-size:12.5px;font-weight:400;vertical-align:middle;text-decoration:none}
.pd__price-why:hover{text-decoration:underline}
.ship-free{margin:2px 0;font-size:14px;font-weight:600;color:var(--success-ink)}
.pd__trust{margin:12px 0 0;font-size:12.5px;color:var(--ink-2);border-top:1px solid var(--line);padding-top:10px}
.pd__wear{margin:8px 0 0;font-size:13.5px;font-weight:600;color:var(--emph-ink)}
.pd__variance{margin:6px 0 0;font-size:12.5px;color:var(--ink-2)}
/* "What's included" / "Important notes" were promoted from <h3> to <h2> for a
   correct heading order (the page's only other heading is the h1 title) — this
   keeps their browser-default size the same as the old <h3> (1.17em) instead of
   jumping to <h2>'s larger default (1.5em). */
.pd__details h2{font-size:1.17em}
/* The application instructions were briefly a collapsible <details> (folded on
   phones). They are a plain .pdsec now — always visible at every width, owner
   decision 2026-08-04 — so the summary/caret/toggle rules that lived here are
   gone rather than left dormant. Don't reintroduce a fold: a shopper reading
   these has glue on their hands mid-application, which is the worst moment to
   ask for a tap. */
/* Application instructions: numbered steps, one per stored line. Gold markers
   pick up the accent without competing with the price/CTA, which are the only
   two roles allowed the saturated tokens. */
.pd__steps{margin:10px 0 0;padding-left:0;list-style:none;counter-reset:pd-step}
.pd__steps li{position:relative;padding-left:30px;margin-bottom:9px;counter-increment:pd-step}
.pd__steps li:last-child{margin-bottom:0}
.pd__steps li::before{content:counter(pd-step);position:absolute;left:0;top:1px;
  width:21px;height:21px;border-radius:50%;background:var(--pink-soft);
  border:1px solid var(--line-2);color:var(--emph-ink);
  font-size:12px;font-weight:700;line-height:19px;text-align:center}
/* The optional "how to apply" link under the shared application instructions.
   Sits on the semantic link token like every other inline link, with the play
   glyph pulled into the same line so it reads as one target. */
.pd__apply-link{margin:10px 0 0}
.pd__apply-link a{display:inline-flex;align-items:center;gap:7px;
  color:var(--link-ink);font-weight:600;text-decoration:none}
.pd__apply-link a:hover{text-decoration:underline}
.pd__apply-link a span{font-size:12px;line-height:1}
.pd dl{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;font-size:14px;margin:14px 0}
.pd dt{color:var(--muted)}
.pd__callout{background:var(--pink-soft);border:1px solid var(--line-2);border-left:4px solid var(--callout-bar);
  border-radius:10px;padding:12px 16px;margin:14px 0}
.pd__callout strong{display:block;color:var(--emph-ink);font-size:15px;margin-bottom:4px}
.pd__callout p{margin:0;font-size:14px;color:var(--ink-2)}

/* ---------- Forms ---------- */
.form{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:20px;
  box-shadow:var(--shadow);max-width:680px;margin:0 auto}
/* The product editor carries tables (shape × length grid, stock rows), so it
   spans the full admin width like the Options/Materials panels do; customer-facing
   forms keep the narrow reading column. */
.form--wide{max-width:none}
.form h2{margin-top:0}
/* A form's save bar. Two buttons (save-and-stay + save-and-close) each stretch,
   so the pair still reads as the one full-width action a lone .btn--block was. */
.form-actions{display:flex;flex-wrap:wrap;gap:10px}
.form-actions .btn{flex:1 1 200px;text-align:center}
.form-actions__note{font-size:12.5px;color:var(--muted);text-align:center;margin:8px 0 0}
.fieldset{border:none;padding:0;margin:0 0 18px;min-inline-size:0}
.fieldset legend{font-weight:700;font-size:15px;margin-bottom:8px;color:var(--legend-ink)}
/* `.field` doubles as a plain <div> wrapper (single input) and, for grouped
   radio/checkbox controls, a <fieldset> (border:none/padding:0 is a no-op on a
   div but strips the browser's default fieldset chrome). `.field legend` mirrors
   `.field label`'s typography so a <legend> reads identically to the <label> it
   replaces; padding:0 clears the UA default legend inline padding. */
.field{margin-bottom:13px;border:none;padding:0}
.field label,.field legend{display:block;font-size:14px;font-weight:600;margin-bottom:5px;padding:0}
.field input,.field select,.field textarea{width:100%;padding:10px 12px;font:inherit;
  border:1px solid var(--line-2);border-radius:9px;background:#fff;color:var(--ink)}
.field textarea{resize:vertical;max-width:100%}
/* A native <select>'s UA chrome ignores most of our padding (WebKit/macOS clamps
   its height), so a dropdown rendered visibly SHORTER than the text input beside
   it — obvious on the address form's State/ZIP Code pair. Strip the native
   appearance and draw our own chevron so a select is exactly the same box as an
   input. The base rule above already set background:#fff, and this rule touches
   only the background LONGHANDS, so that color survives. */
.field select{-webkit-appearance:none;appearance:none;padding-right:34px;
  background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' stroke='%236b7280' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 12px center;background-size:12px 8px}
.field input[readonly]{background:var(--paper-2,#f4f2f7);color:var(--muted);cursor:default}
.field input[readonly]:focus{outline:none;border-color:var(--line-2)}
.field input:focus,.field select:focus,.field textarea:focus{outline:2px solid var(--focus-ring);border-color:var(--pink)}
.field .hint{font-size:12.5px;color:var(--muted);margin-top:4px}
.field-static{margin:0;padding:10px 12px;border:1px solid var(--line-2);border-radius:9px;
  background:var(--bg);font-weight:600;color:var(--ink)}
/* Static "label" for read-only display text that isn't paired with a form
   control (e.g. a chosen-options summary) — a <label> would mislead screen
   readers into expecting a focusable input, so callers use a plain element
   with this class instead. Matches `.field label` typography exactly. */
.field-label{display:block;font-size:14px;font-weight:600;margin-bottom:5px}
.row{display:grid;grid-template-columns:1fr;gap:0 14px}
@media(min-width:560px){.row--2{grid-template-columns:1fr 1fr}.row--3{grid-template-columns:2fr 1fr 1fr}}
.check{display:flex;gap:9px;align-items:flex-start;font-size:14px;margin-bottom:9px}
.check input{width:19px;height:19px;flex-shrink:0;margin-top:2px;accent-color:var(--pink);cursor:pointer}
.errors{background:var(--red-bg);border:1px solid #f0c4c4;color:var(--red);border-radius:10px;
  padding:12px 14px;margin-bottom:16px;font-size:14px}
.errors ul{margin:6px 0 0;padding-left:18px}

/* ---------- "Why made to order" night band (home) ----------
   The value props sit in a dark celestial panel so they read as the brand's
   signature moment — clearly different from the light "How it works" step
   cards below. Mobile: a horizontal scroll-snap carousel (the next card peeks
   at the edge); desktop (>=760px): four hairline-separated columns, no boxes. */
.why{position:relative;overflow:hidden;color:#fff;border-radius:var(--radius);
  padding:26px 18px 16px;box-shadow:var(--shadow);border:1px solid #2a2856;
  background:linear-gradient(165deg,var(--night),var(--night-2))}
.why::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.35;background:
  radial-gradient(1.5px 1.5px at 8% 22%,  rgba(255,255,255,.5)  60%, transparent),
  radial-gradient(2px 2px     at 22% 82%, rgba(246,238,218,.45) 55%, transparent),
  radial-gradient(1.5px 1.5px at 38% 30%, rgba(255,255,255,.4)  60%, transparent),
  radial-gradient(2px 2px     at 58% 14%, rgba(255,255,255,.45) 55%, transparent),
  radial-gradient(1.5px 1.5px at 72% 78%, rgba(246,238,218,.4)  60%, transparent),
  radial-gradient(2px 2px     at 90% 28%, rgba(255,255,255,.4)  55%, transparent)}
.why>*{position:relative}
.why__head{text-align:center;margin-bottom:20px}
.why__head .eyebrow{color:#c9a55e}   /* brighter champagne — --gold-ink is too dark on night */
.why__head h2{color:#fff;font-size:24px;margin:0}
.why__rule{width:46px;height:2px;background:var(--gold);border-radius:2px;margin:12px auto 0}
.why__track{display:flex;gap:12px;overflow-x:auto;padding:2px 2px 12px;
  scroll-snap-type:x proximity;-webkit-overflow-scrolling:touch;scrollbar-width:thin}
.why__track::-webkit-scrollbar{height:6px}
.why__track::-webkit-scrollbar-thumb{background:rgba(255,255,255,.22);border-radius:4px}
.why__track::-webkit-scrollbar-track{background:transparent}
.why__item{flex:0 0 auto;width:min(70vw,290px);scroll-snap-align:start;padding:16px;
  background:rgba(255,255,255,.055);border:1px solid rgba(176,141,75,.28);border-radius:12px}
.why__spark{width:20px;height:20px;margin-bottom:10px}
.why__spark path{fill:var(--gold)}
.why__item strong{display:block;font-family:var(--serif);font-weight:600;font-size:17.5px;
  letter-spacing:.2px;margin-bottom:6px}
.why__item p{margin:0;font-size:14.5px;line-height:1.55;color:#c9c3dd}
/* Inline links on the night panel. The global `a{color:var(--link-ink)}` is a dark
   indigo tuned for the LIGHT content area — on this dark card it all but vanishes.
   Every state is pinned (:link/:visited/:hover/:focus/:active) so a clicked link can
   never fall back to the UA's dark purple :visited color either. Champagne rather
   than white so it still reads as a link beside the #c9c3dd body text, and it holds
   AA on all four themes' --night fields (≥6:1 on the lightest, Light Purple). */
.why a,.why a:link,.why a:visited,.why a:active{color:#dcb978;text-decoration:underline;
  text-underline-offset:2px;text-decoration-thickness:1px;
  text-decoration-color:rgba(220,185,120,.6)}
.why a:hover,.why a:focus{color:#f2e0b4;text-decoration-color:currentColor}
.why a:focus-visible{outline:2px solid #dcb978;outline-offset:2px;border-radius:2px}
@media(min-width:760px){
  .why{padding:30px 26px 24px}
  .why__track{display:grid;grid-template-columns:repeat(4,1fr);gap:0;overflow:visible;padding:0}
  .why__item{width:auto;padding:6px 18px 4px;background:transparent;border:none;border-radius:0;
    border-left:1px solid rgba(176,141,75,.25)}
  .why__item:first-child{border-left:none;padding-left:2px}
  .why__item:last-child{padding-right:2px}
}

/* ---------- Process steps (home) ---------- */
/* Mobile: 2x2 (a single stacked column ran very long on a phone). Desktop: 4-across. */
.steps{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
@media(min-width:640px){.steps{grid-template-columns:repeat(4,1fr);gap:14px}}
.step{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:12px}
@media(min-width:640px){.step{padding:16px}}
.step .n{width:30px;height:30px;border-radius:50%;background:var(--pink);color:#fff;font-weight:700;
  display:flex;align-items:center;justify-content:center;margin-bottom:8px}

/* ---------- FAQ / policy prose ---------- */
.prose{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:24px;
  max-width:760px;box-shadow:var(--shadow)}
.prose h2{font-size:18px;margin:22px 0 6px}
.prose h2:first-child{margin-top:0}
.prose p,.prose li{color:var(--ink-2)}
details.faq{border-bottom:1px solid var(--line);padding:12px 0}
details.faq summary{font-weight:600;cursor:pointer}
details.faq p{margin:8px 0 0}

/* =========================================================================
   ADMIN
   ========================================================================= */
body.admin{background:#f2f1f8}
.admin-bar{background:var(--night);color:#fff;position:sticky;top:0;z-index:30}
.admin-bar__inner{max-width:1240px;margin:0 auto;display:flex;align-items:center;gap:14px;
  flex-wrap:wrap;padding:10px 18px}
.admin-brand{color:#fff;font-weight:700;text-decoration:none}
.admin-nav{display:flex;gap:4px;flex-wrap:wrap}
.admin-nav__link{color:#e7e2f2;text-decoration:none;padding:6px 11px;border-radius:8px;font-size:14px}
.admin-nav__link:hover{background:rgba(255,255,255,.12)}
.admin-nav__link.is-active{background:var(--pink);color:#fff}
.admin-bar__right{margin-left:auto;display:flex;align-items:center;gap:8px;font-size:13px;color:#c3bcd6}
.admin-link{color:#e4dcf3;text-decoration:none}
.admin-link:hover{text-decoration:underline}
.admin-main{padding:22px 0 60px}
.admin-container{max-width:1240px;margin:0 auto;padding:0 18px}
.admin-h1{font-size:24px;margin:6px 0 18px}

/* admin cards / stat tiles */
.stats{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;margin-bottom:22px}
@media(min-width:680px){.stats{grid-template-columns:repeat(3,1fr)}}
@media(min-width:1000px){.stats{grid-template-columns:repeat(4,1fr)}}
.stat{background:#fff;border:1px solid var(--line);border-radius:12px;padding:14px 16px;box-shadow:var(--shadow)}
.stat .label{font-size:12.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.4px}
.stat .value{font-size:26px;font-weight:800;margin-top:2px}
.stat a{text-decoration:none}
.panel{background:#fff;border:1px solid var(--line);border-radius:12px;padding:18px;
  box-shadow:var(--shadow);margin-bottom:18px}
.panel h2{margin:0 0 12px;font-size:17px}
.panel__head{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;margin-bottom:12px}
.panel__head h2{margin:0}

/* admin tables */
.table-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:12px;background:#fff}
table.data{width:100%;border-collapse:collapse;font-size:14px;min-width:680px}
table.data th,table.data td{padding:10px 12px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
table.data th{background:#f4f3fb;font-size:12.5px;text-transform:uppercase;letter-spacing:.3px;color:var(--muted)}
table.data tr:last-child td{border-bottom:none}
table.data tr:hover td{background:#faf9fe}
/* Sticky "Actions" column — the Open button stays visible without scrolling. */
table.data th:last-child,table.data td:last-child{position:sticky;right:0;z-index:1;box-shadow:-8px 0 8px -8px rgba(60,40,45,.18)}
table.data td:last-child{background:#fff}
table.data th:last-child{background:#f4f3fb}
table.data tr:hover td:last-child{background:#faf9fe}
/* Small glance tables (day/week breakdowns) — no column needs to freeze, so opt out
   of the sticky last-column treatment and the min-width meant for wide operational tables. */
table.data.compact{min-width:0}
table.data.compact th:last-child,table.data.compact td:last-child{position:static;box-shadow:none}
/* Truncate long emails so rows stay compact. */
.email-cell{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Click-to-sort column headers (JS reorders rows; see products.php). */
table.data th.sortable{cursor:pointer;user-select:none;white-space:nowrap}
table.data th.sortable:hover{color:var(--ink,#3a2f33)}
table.data th.sortable::after{content:"↕";opacity:.3;margin-left:5px;font-size:11px}
table.data th.sort-asc::after{content:"↑";opacity:.85}
table.data th.sort-desc::after{content:"↓";opacity:.85}
/* Fulfilment deadline badge, shown under the payment status. */
.due{display:block;margin-top:3px;font-size:12px;font-weight:600}
.due--ok{color:#2f855a}
.due--warn{color:#b7791f}
.due--late{color:#c53030}
.due--done{color:#2f855a}
/* Statistics: horizontal proportion bars + range tabs + hint notes. */
.statbar{height:8px;background:var(--line);border-radius:4px;overflow:hidden;min-width:90px;width:100%}
.statbar>span{display:block;height:100%;background:var(--pink)}
.statbar--warn>span{background:#e0a800}
.range-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
.range-tabs a{padding:5px 12px;border:1px solid var(--line);border-radius:999px;font-size:13px;color:var(--muted);text-decoration:none}
.range-tabs a.is-active{background:var(--pink);border-color:var(--pink);color:#fff}
.stat-note{font-size:12px;color:var(--muted);margin:8px 0 0}
.stat-flag{font-size:12px;font-weight:600;color:#b7791f}
.kv{display:grid;grid-template-columns:auto 1fr;gap:8px 16px;font-size:14px}
.kv dt{color:var(--muted)}
.kv dd{margin:0}

/* Order-view guided workflow card: step progress line + spaced hint lines. */
.wf-steps{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:0 0 10px;font-size:13px}
.wf-steps__sep{color:var(--muted)}
.wf-steps__step--done{color:var(--green)}
.wf-steps__step--current{color:var(--ink);font-weight:700}
.wf-steps__step--future{color:var(--muted)}
.wf-hint{margin:0 0 8px}
/* Pricing block the Workflow card inlines above the invoice form. */
.wf-pricing{margin:0 0 4px}
.wf-subhead{font-size:13px;font-weight:700;margin:0 0 8px;color:var(--ink)}
.wf-rule{border:0;border-top:1px solid var(--line);margin:14px 0}

/* (i) help icon next to an admin page's <h1>, linking to admin/help.php#<section>. */
.help-icon{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;
  margin-left:8px;border-radius:50%;background:var(--pink-soft,#ece8f9);color:var(--pink-dark,var(--pink));
  font-size:13px;font-weight:700;text-decoration:none;vertical-align:middle;line-height:1}
.help-icon:hover{background:var(--pink);color:#fff}

/* admin/help.php — the in-app admin guide */
.help-page{max-width:900px;margin:0 auto}
.help-page section{padding:32px 0;border-bottom:1px solid var(--line)}
.help-page section:last-child{border-bottom:none}
.help-page h2{font-size:1.5em;margin:0 0 6px;color:var(--pink-dark,var(--pink));
  border-bottom:3px solid var(--gold,#b8935a);display:inline-block;padding-bottom:4px}
.help-page h3{font-size:1.2em;margin:28px 0 8px;color:var(--pink-dark,var(--pink))}
.help-page h4{font-size:1.02em;margin:18px 0 6px}
.help-page .lede{color:var(--muted);font-size:1.02em;margin:8px 0 22px;max-width:640px}
.help-page .shot{display:block;max-width:100%;border:1px solid var(--line);border-radius:10px;
  box-shadow:var(--shadow);margin:16px 0 8px}
.help-page .cap{font-size:.88em;color:var(--muted);margin:0 0 18px;font-style:italic}
.help-page .steps{counter-reset:step}
.help-page .step{position:relative;padding:12px 14px 12px 50px;margin:8px 0;background:#fff;
  border:1px solid var(--line);border-radius:10px}
.help-page .step::before{counter-increment:step;content:counter(step);position:absolute;left:12px;
  top:50%;transform:translateY(-50%);width:24px;height:24px;border-radius:50%;
  background:var(--pink);color:#fff;display:flex;align-items:center;justify-content:center;
  font-weight:700;font-size:.82em}
.help-page .journey{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0}
.help-page .journey .node{background:var(--pink-soft,#ece8f9);color:var(--pink-dark,var(--pink));
  padding:7px 13px;border-radius:20px;font-size:.86em;font-weight:600}
.help-page .journey .arrow{color:var(--gold,#b8935a);font-weight:700;align-self:center}
.help-page .scenario{border-left:4px solid var(--gold,#b8935a);background:#fff;
  border-radius:0 10px 10px 0;padding:14px 20px;margin:16px 0}
.help-page .scenario h4{margin-top:0}
.help-page .toc{background:#fff;border:1px solid var(--line);border-radius:12px;padding:20px 24px;margin:16px 0}
.help-page .toc a{color:var(--pink-dark,var(--pink));text-decoration:none;font-weight:600}
.help-page .toc li{margin:6px 0}
.help-page .note{background:#fff8ea;border:1px solid #ecd9a6;border-radius:10px;padding:12px 16px;
  font-size:.94em;margin:14px 0}

/* Summary-first admin cards (Payment details / Tracking): saved values render
   read-only; the form expands behind a ghost-button Edit summary. */
.edit-toggle{margin-top:10px}
.edit-toggle>summary{list-style:none;cursor:pointer;user-select:none}
.edit-toggle>summary::-webkit-details-marker{display:none}
.edit-toggle>summary:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}
.edit-toggle[open]>summary{margin-bottom:12px}

/* admin -> FAQ: one compact row per question (question · badges · move · Edit).
   A GRID, not flex, with fixed widths on the three control columns — every card
   is its own container, so only equal fixed widths make the controls line up as
   columns down the list instead of drifting with each question's length. The
   editor is the last child: inline while closed, a full-width row once opened. */
.faq-row{padding:10px 18px}
.faq-row .panel__head{display:grid;grid-template-columns:minmax(0,1fr) 172px 82px 110px;
  align-items:center;gap:6px 10px;margin-bottom:0}
.faq-row .panel__head h2{font-size:15.5px;font-weight:600;line-height:1.35}
.faq-row__badges{display:flex;gap:6px;align-items:center;flex-wrap:wrap;justify-content:flex-end}
.faq-row__moves{display:flex;gap:6px;align-items:center;justify-content:flex-end}
.faq-row__moves .btn{min-width:38px}
/* A ghost move button at the end of the list: keep the outline, drop the fill —
   the default disabled grey block reads as "selected" next to its live sibling. */
.faq-row__moves .btn:disabled,.feat-order__moves .btn:disabled{background:#fff;border:1px solid var(--line);color:var(--muted);opacity:.5}
.faq-row .edit-toggle{margin-top:0;justify-self:end}
.faq-row .edit-toggle[open]{grid-column:1/-1;justify-self:stretch;margin-top:6px}

/* admin -> Products: the home "Featured Sets" shelf order — one compact row per
   featured design (position · thumb · title · ↑ ↓). Deliberately NOT a table:
   the products list's column-sort script binds to the first table.data on the
   page, and this panel sits above it. */
.feat-order{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.feat-order li{display:flex;align-items:center;gap:10px;padding:6px 10px;
  border:1px solid var(--line);border-radius:10px}
.feat-order__pos{min-width:20px;text-align:right;color:var(--muted);font-size:12px}
.feat-order__title{flex:1 1 auto;min-width:0}
/* Price · build hours · $-per-hr, the same figures as the products table below —
   a glance at what the shelf is actually promoting, never shown to customers. */
.feat-order__stats{display:flex;gap:12px;align-items:baseline;flex-shrink:0;
  color:var(--muted);font-size:12px;white-space:nowrap}
.feat-order__stats b{font-weight:600;color:var(--ink-2)}
/* The panel is folded by default; the summary is its heading, so it needs no top gap. */
.feat-order__fold{margin-top:0}
.feat-order__intro{display:flex;justify-content:space-between;align-items:center;
  flex-wrap:wrap;gap:10px;margin-bottom:12px}
.feat-order__intro p{margin:0}
.feat-order__moves{display:flex;gap:6px;flex-shrink:0}
.feat-order__moves .btn{min-width:38px}

/* Narrow admin windows: the question takes its own line, controls sit under it. */
@media (max-width:860px){
  .faq-row .panel__head{grid-template-columns:minmax(0,1fr) auto auto}
  .faq-row .panel__head h2{grid-column:1/-1}
  .faq-row__badges{justify-content:flex-start}
  .feat-order__stats{gap:8px;font-size:11px}
}

/* admin filters / toolbar */
.toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px}
.toolbar form{display:flex;gap:8px;flex-wrap:wrap}
.toolbar input[type=search],.toolbar input[type=text],.toolbar select{padding:8px 11px;border:1px solid var(--line-2);
  border-radius:8px;font:inherit;background:#fff}
.chip{display:inline-block;padding:6px 12px;border-radius:999px;background:#fff;border:1px solid var(--line-2);
  text-decoration:none;color:var(--ink);font-size:13px}
.chip.is-active{background:var(--pink);color:#fff;border-color:var(--pink)}

/* Shop shape/length filter chips, collapsed behind a <details> disclosure (no JS
   needed) — the chip rows otherwise fill the first screens on mobile. */
.filter-toggle{margin-bottom:16px}
.filter-toggle summary{display:inline-flex;align-items:center;gap:8px;list-style:none;
  padding:9px 16px;border-radius:999px;background:#fff;border:1px solid var(--line-2);
  font-size:14px;font-weight:600;color:var(--ink);cursor:pointer;user-select:none}
.filter-toggle summary::-webkit-details-marker{display:none}
.filter-toggle summary:hover{border-color:var(--pink)}
.filter-toggle summary:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}
.filter-toggle[open] summary{border-color:var(--pink);background:var(--pink-soft);color:var(--pink-dark)}
.filter-toggle__caret{font-size:11px}
@media (prefers-reduced-motion:no-preference){.filter-toggle__caret{transition:transform .15s ease}}
.filter-toggle[open] .filter-toggle__caret{transform:rotate(180deg)}
.filter-toggle .toolbar{margin:10px 0 0}
/* One labeled chip row per option group (Shape, Length) inside the disclosure. */
.filter-group{margin-top:12px}
.filter-group__label{display:block;margin-bottom:6px;font-size:12px;font-weight:600;
  letter-spacing:.04em;text-transform:uppercase;color:var(--legend-ink)}
.filter-toggle .filter-group .toolbar{margin:0}
/* A chip that would return no designs: still readable, but plainly not a link. */
.chip.is-off{opacity:.5;background:var(--bg);color:var(--muted);cursor:default}

/* Sort row — a sibling of the filter disclosure, not part of it. */
.sortbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:0 0 18px}
.sortbar__label{font-size:13px;font-weight:600;color:var(--legend-ink)}

/* admin two-column detail */
.cols{display:grid;grid-template-columns:1fr;gap:18px}
@media(min-width:920px){.cols{grid-template-columns:1.6fr 1fr}}

/* action button groups */
.actions{display:flex;flex-wrap:wrap;gap:8px}
.actions form{display:inline}

/* login */
.login-wrap{max-width:380px;margin:8vh auto;padding:0 18px}
.login-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:26px;box-shadow:var(--shadow)}
.login-card h1{font-size:20px;margin:0 0 16px;text-align:center}

/* misc */
.inline-form{display:inline}
.timeline{list-style:none;margin:0;padding:0;font-size:13.5px}
.timeline li{padding:8px 0;border-bottom:1px dashed var(--line)}
.timeline li:last-child{border-bottom:none}
.right{text-align:right}
.nowrap{white-space:nowrap}
.mt0{margin-top:0}.mb0{margin-bottom:0}
.stack>*+*{margin-top:12px}

/* =========================================================================
   OVERHAUL UI — sale badge, prices, video, fixed option, both-path CTAs
   ========================================================================= */

/* Pink sale badge — consistent with the .badge--* swatches */
.badge--pink{background:var(--pink-soft);color:var(--pink-dark)}

/* Sale pricing — used inside .card__price and .pd__price (render_price()) */
.price-was{color:var(--muted);font-weight:500;text-decoration:line-through;
  font-size:.82em;margin-right:6px}
.price-now{color:var(--price-ink);font-weight:800}
.card__price .price-was,.pd__price .price-was{display:inline}
.pd__price .price-now{font-size:1em}

/* On Sale section — reuses .section/.section__head; tiny accent only */
.section__head--sale{border-bottom-color:var(--pink)}

/* Responsive 16:9 video embed */
.video-embed{position:relative;width:100%;padding-top:56.25%;
  border-radius:var(--radius);overflow:hidden;background:#000;margin:14px 0}
.video-embed iframe,.video-embed video{position:absolute;top:0;left:0;width:100%;height:100%;border:0;
  border-radius:var(--radius)}
.video-embed video{object-fit:contain;background:#000}

/* Read-only single option value (shown when only one choice exists) — deliberately
   undecorated (no pill/border/accent color) so it doesn't look tappable like a button */
/* margin:0 because this is usually a <p>, and the UA's 1em top/bottom margin
   opened a gap far larger than any other label/value pair on the page (most
   visible under the address form's "Country"). `.field` already supplies the
   13px rhythm between fields, exactly as it does for .field-static. */
.opt-fixed{display:inline-block;margin:0;padding:2px 0;color:var(--ink);
  font-size:14px;font-weight:400}

/* Product page CTA (single adaptive button driven by the size picker) */
.pd__cta{margin:16px 0}
.pd__cta .btn{width:100%}
#pd-cta-note{margin:8px 0 0;text-align:center;font-size:13px}

/* Size-picker label row: "Choose a size" on the left, Size guide pill on the right.
   Scoped under .field to out-rank the base `.field label{display:block}` rule.
   Now rendered as a <legend> (the row groups the size radio chips) — width:100%
   overrides the browser's shrink-to-fit legend sizing so space-between still
   spans the full row like the <label> it replaces. */
.field .size-label{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%}

/* Size picker — chips for every configured size, regardless of availability.
   Chips flex to fill the row so it spans edge-to-edge with the CTA button below. */
.size-picker{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.size-chip{position:relative;flex:1 1 0;display:flex;flex-direction:column;align-items:center;justify-content:center;
  min-width:58px;padding:10px 8px;border:1px solid var(--line-2);border-radius:10px;background:#fff;
  cursor:pointer;text-align:center;transition:border-color .15s,background .15s}
.size-chip input{position:absolute;opacity:0;width:0;height:0;margin:0}
.size-chip__name{font-weight:700;font-size:15px;color:var(--ink)}
.size-chip__note{font-size:11px;color:var(--muted);margin-top:1px;white-space:nowrap}
.size-chip:hover{border-color:var(--pink)}
.size-chip:has(input:checked){border-color:var(--pink);background:var(--chip-selected-bg);box-shadow:0 0 0 1px var(--pink)}
.size-chip:has(input:checked) .size-chip__note{color:var(--chip-selected-note-ink)}
.size-chip:has(input:checked) .size-chip__name{color:var(--chip-selected-ink)}
.size-chip:has(input:focus-visible){outline:2px solid var(--pink);outline-offset:2px}
.size-chip.is-disabled{opacity:.55;cursor:not-allowed;background:var(--gray-bg)}
.size-chip.is-disabled .size-chip__name{text-decoration:line-through;color:var(--muted)}
/* Narrow phones: tighten the chips so all five (incl. the wider "Custom") fit the
   row without the last one overflowing the viewport edge. */
@media (max-width:560px){
  .size-picker{gap:6px}
  .size-chip{min-width:0;padding:9px 5px}
  .size-chip__name{font-size:14px}
}
/* Tap feedback + a tiny "settle" pop when a chip becomes checked. */
@media (prefers-reduced-motion:no-preference){
  .size-chip{transition:border-color .15s,background .15s,transform .12s ease}
  .size-chip:active{transform:scale(.96)}
  .size-chip.is-disabled:active{transform:none}
  .size-chip:has(input:checked){animation:chip-pop .18s ease}
}
/* Shared "settle" pop, reused by .size-chip / .opt-chip / .fit-method above/below. */
@keyframes chip-pop{0%{transform:scale(.94)}100%{transform:scale(1)}}

/* Shape / length / finish option chips (shown above the size picker on sets,
   and as the only configurator on the kit page). */
.pd__options{margin:14px 0}
/* Each row is a <fieldset> (groups that option's radio chips); border/padding
   reset strips the browser's default fieldset chrome. */
.opt-row{margin:10px 0;border:none;padding:0}
.opt-row__label{display:block;font-weight:600;font-size:13px;color:var(--ink);margin-bottom:6px;padding:0}
/* A group with only one value is a FACT, not a choice: "Shape: Coffin" on one
   line. A lone chip reads as a disabled button and invites a tap that does
   nothing, and it makes the groups that ARE choices harder to pick out. */
.opt-row--fixed{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;margin:6px 0}
.opt-row--fixed .opt-row__label{margin-bottom:0}
.opt-row--fixed .opt-fixed{font-size:13px}
.opt-row--fixed .pd__mm-note{flex-basis:100%;margin:2px 0 0}
.opt-chips{display:flex;flex-wrap:wrap;gap:8px}
.opt-chip{position:relative;display:inline-flex;flex-direction:column;align-items:center;
  padding:8px 14px;border:1px solid var(--line-2);border-radius:10px;background:#fff;cursor:pointer;
  transition:border-color .15s,background .15s}
.opt-chip input{position:absolute;opacity:0;width:0;height:0;margin:0}
.opt-chip__name{font-weight:600;font-size:14px;color:var(--ink)}
.opt-chip:hover{border-color:var(--pink)}
.opt-chip:has(input:checked){border-color:var(--pink);background:var(--chip-selected-bg);box-shadow:0 0 0 1px var(--pink)}
.opt-chip:has(input:checked) .opt-chip__name{color:var(--chip-selected-ink)}
.opt-chip:has(input:focus-visible){outline:2px solid var(--pink);outline-offset:2px}
/* A shape × length the catalog doesn't offer (e.g. Almond only in Medium/Long):
   the chip stays VISIBLE but disabled, so the row keeps its shape as the customer
   switches shapes instead of chips appearing and vanishing under their thumb.
   Same treatment as a dead size chip (.size-chip.is-disabled), plus a small note
   naming the counterpart that rules it out ("not in Almond"). */
.opt-chip.is-disabled{opacity:.55;cursor:not-allowed;background:var(--gray-bg)}
.opt-chip.is-disabled:hover{border-color:var(--line-2)}
.opt-chip.is-disabled .opt-chip__name{text-decoration:line-through;color:var(--muted)}
.opt-chip__note{font-size:11px;color:var(--muted);margin-top:1px;white-space:nowrap}
/* Shape chips only (product.php) — a small nail-tip silhouette, product-page-only
   (shape_tip_icon() in helpers.php). Sits above the label since .opt-chip is a
   column flex; color/selected/disabled states come for free by mirroring
   .opt-chip__name's own color rules onto the icon (stroke uses currentColor). */
.shape-ico{width:17px;height:auto;flex:none;display:block;margin:0 0 3px;color:var(--ink)}
.opt-chip:has(input:checked) .shape-ico{color:var(--chip-selected-ink)}
.opt-chip.is-disabled .shape-ico{color:var(--muted)}
.opt-fixed .shape-ico{display:inline-block;vertical-align:-3px;margin:0 6px 0 0}
@media (prefers-reduced-motion:no-preference){
  .opt-chip{transition:border-color .15s,background .15s,transform .12s ease}
  .opt-chip:active{transform:scale(.96)}
  .opt-chip.is-disabled:active{transform:none}
  .opt-chip:has(input:checked){animation:chip-pop .18s ease}
}
.opt-fixed--chip{padding:8px 0}
.pd__size-intro{margin:6px 0 0}
.pd__mm-note{margin:10px 0 0;font-weight:600;color:var(--emph-ink)}

/* Two-method custom-fit panel (sizing-kit numbers vs mm measurements) */
.fit-panel{margin-top:12px;padding:14px;border:1px solid var(--line-2);border-radius:12px;background:var(--pink-soft)}
.fit-panel__intro{margin:0 0 10px;font-size:14px}
.fit-methods{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:6px}
.fit-method{display:flex;align-items:center;gap:7px;padding:8px 12px;border:1px solid var(--line-2);
  border-radius:10px;background:#fff;cursor:pointer;font-size:14px}
.fit-method:has(input:checked){border-color:var(--pink);box-shadow:0 0 0 1px var(--pink)}
.fit-method:has(input:focus-visible){outline:2px solid var(--pink);outline-offset:2px}
@media (prefers-reduced-motion:no-preference){
  .fit-method{transition:border-color .15s,box-shadow .15s,transform .12s ease}
  .fit-method:active{transform:scale(.96)}
  .fit-method:has(input:checked){animation:chip-pop .18s ease}
}
.fit-group{margin-top:6px}
.fit-cross{margin-top:10px;padding:10px 12px;border:1px dashed var(--line-2);border-radius:10px;background:#fff}
/* "⇄ same as left" one-tap mirror next to the Right hand label */
.fit-mirror{display:inline-block;margin-left:8px;padding:2px 9px;font:600 11px/1.5 inherit;
  color:var(--pink-dark);background:var(--pink-soft);border:1px solid var(--line-2);border-radius:999px;
  cursor:pointer;vertical-align:middle;transition:background .12s,color .12s,border-color .12s}
.fit-mirror:hover{background:var(--pink);color:#fff;border-color:var(--pink)}
.fit-mirror.is-done{background:var(--pink);color:#fff;border-color:var(--pink)}
.fit-mirror.is-done::after{content:" ✓"}

/* Admin materials grid */
.materials-grid th,.materials-grid td{vertical-align:middle}
/* A shape × length pair the owner doesn't make at all: grayed so the offered
   pairs read at a glance. Stock is meaningless here, so the cell carries only the
   "Not offered" badge and the button that puts it back. */
.materials-cell--off{background:var(--gray-bg)}
.materials-cell--off .badge{opacity:.9}

/* Admin product-edit: the per-design shape × length grid (same table shape as the
   materials grid, one checkbox per pair instead of two buttons). A pair retired
   SHOP-WIDE is grayed and un-tickable here — a design narrows the shop's matrix,
   never widens it. */
table.data.pair-grid{min-width:0}
.pair-grid th,.pair-grid td{vertical-align:middle}
.pair-grid__cell{text-align:center}
.pair-grid__cell--off{background:var(--gray-bg)}
.pair-cell{display:inline-flex;align-items:center;justify-content:center;margin:0;padding:4px;cursor:pointer}
.pair-cell input{width:18px;height:18px;margin:0;cursor:pointer}
.pair-cell input:disabled{cursor:not-allowed}
.pair-cell input:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}
.pair-cell__off{display:block;font-size:11px;color:var(--muted)}

/* Custom nail widths (Left + Right, 5 each) */
.mm-fields .mm-hand{margin-top:8px}
.mm-hand__label{display:block;font-size:13px;font-weight:600;color:var(--legend-ink);margin-bottom:4px}
.mm-hand__row{display:grid;grid-template-columns:repeat(5,1fr);gap:6px}
.mm-input{width:100%;text-align:center;padding:8px 6px}

/* Size guide link in the picker label + the chart on /size-guide.php */
.size-guide-link{flex:none;display:inline-flex;align-items:center;gap:5px;font-size:13px;font-weight:700;
  color:var(--pink-dark);background:var(--pink-soft);border:1px solid var(--line-2);
  padding:4px 11px;border-radius:999px;text-decoration:none;line-height:1.2}
.size-guide-link:hover{background:var(--pink);color:#fff;border-color:var(--pink)}
.size-table{width:100%;border-collapse:collapse;font-size:14px;min-width:360px}
.size-table th,.size-table td{border:1px solid var(--line);padding:8px 12px;text-align:center}
.size-table thead th{background:var(--pink-soft);color:var(--pink-dark)}
.size-table tbody th{background:#f4f3fb;text-align:left}

/* Admin: product image reorder / make-primary manager */
.img-manager{display:flex;flex-wrap:wrap;gap:14px}
.img-item{position:relative;width:120px;border:1px solid var(--line);border-radius:10px;
  padding:8px;text-align:center;background:#fff}
.img-item.is-primary{border-color:var(--pink);box-shadow:0 0 0 1px var(--pink)}
.img-item img{width:104px;height:104px;object-fit:cover;border-radius:8px}
.img-item__badge{display:none;position:absolute;top:-9px;left:8px;font-size:10.5px;font-weight:700;
  background:var(--pink);color:#fff;padding:2px 8px;border-radius:999px;letter-spacing:.3px}
.img-item.is-primary .img-item__badge{display:inline-block}
.img-item__bar{display:flex;justify-content:center;gap:4px;margin-top:6px}
.img-item__bar .btn{padding:4px 8px;font-size:13px;line-height:1}
.img-item__remove{justify-content:center;font-size:13px;margin-top:6px}

/* Admin: ready-to-ship per-combo stock item rows */
#stock-items{display:flex;flex-direction:column;gap:8px;margin:10px 0}
.stock-item{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.stock-item select{flex:1 1 110px;min-width:90px}
.stock-item .stock-item__qty{flex:0 0 74px;width:74px}
.stock-item__remove{flex:0 0 auto;line-height:1;padding:6px 10px}
/* A stock row whose shape × length the design no longer offers. The row keeps its
   quantities and keeps selling (finished sets and the grid are separate things) —
   the note is the only difference, on its own line under the row. */
.stock-item__note{flex-basis:100%;font-size:12px;color:var(--muted)}
#stock-add-item{margin-top:2px}

/* =========================================================================
   MOONLIT THEME — gold CTA, night-sky brand marks, celestial dividers
   ========================================================================= */

/* Champagne-gold CTA (hero primary). Dark plum label keeps AA contrast on gold. */
.btn--gold{background:var(--gold);color:var(--night)}
.btn--gold:hover{background:#c39a52;color:var(--night)}   /* brighten (shimmer) on hover — darkening dropped label contrast below AA */

/* Scarcity signal: the ready-to-ship count is the one "moon-glow" gold accent
   (deep --gold-ink stays AA-legible as small text; prices remain violet). */
.stock-count{color:var(--gold-ink);font-weight:700}

/* Celestial section divider — a hairline rule with a centered crescent + sparkle. */
.moon-divider{display:flex;align-items:center;gap:16px;margin:4px 0 30px}
.moon-divider::before,.moon-divider::after{content:"";flex:1;height:1px;
  background:linear-gradient(90deg,transparent,var(--line-2),transparent)}
.moon-divider svg{display:block;width:30px;height:18px;flex:none}
.moon-divider .moon{fill:var(--pink)}
.moon-divider .spark{fill:var(--gold)}

/* Four-point sparkle bullets on policy/FAQ prose lists (SVG-free, uses the glyph). */
.prose ul{list-style:none;padding-left:1.4em;margin:10px 0}
.prose ul li{position:relative}
.prose ul li::before{content:"\2726";position:absolute;left:-1.4em;top:0;
  color:var(--gold-ink);font-size:.82em;line-height:1.7}

/* Champagne-tinted text selection, echoing the gold accent. */
::selection{background:var(--gold-bg);color:var(--gold-ink)}

/* ---------- Back-to-top button — night-sky disc, up chevron + gold spark ----------
   Rendered once by render_footer(). Hidden (opacity 0 + pointer-events none) until
   ui.js adds .is-visible past ~600px of scroll; with no JS it simply never appears
   (an enhancement, not content). The fade/slide transition is reduced-motion gated;
   the button stays fully functional (instant show/hide) either way. */
.to-top{position:fixed;right:14px;bottom:calc(18px + env(safe-area-inset-bottom));z-index:40;width:44px;height:44px;
  display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;
  cursor:pointer;padding:0;opacity:0;pointer-events:none;
  background:linear-gradient(165deg,var(--night),var(--night-2));
  box-shadow:0 2px 8px rgba(20,18,50,.25),0 8px 20px rgba(20,18,50,.22)}
.to-top svg{width:22px;height:22px;flex:none}
.to-top .chevron{stroke:#fff}
.to-top .spark{fill:var(--gold)}
.to-top.is-visible{opacity:1;pointer-events:auto}
@media (prefers-reduced-motion:no-preference){
  .to-top{transform:translateY(8px);transition:opacity .25s ease,transform .25s ease}
  .to-top.is-visible{transform:none}
}
.to-top:hover{background:linear-gradient(165deg,#242151,var(--night-2))}

/* ── Admin: Email Text editor (email-text.php) ───────────────────────────── */
.email-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:26px;align-items:start}
@media (max-width:900px){.email-grid{grid-template-columns:1fr}}
.email-editor__src{font:13px/1.55 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap}
.email-editor{min-height:280px;padding:12px 13px;border:1px solid var(--line-2);border-radius:9px;
  background:#fff;font-size:14px;line-height:1.55;color:var(--ink)}
.email-editor:focus{outline:2px solid var(--pink-soft);border-color:var(--pink)}
.email-editor>div{min-height:1.55em;margin:0 0 10px}
/* single-line chip editor for subject + heading (styled like a text input) */
.email-chipline{min-height:22px;padding:10px 12px;border:1px solid var(--line-2);border-radius:9px;
  background:#fff;font-size:14px;line-height:1.5;color:var(--ink)}
.email-chipline:focus{outline:2px solid var(--pink-soft);border-color:var(--pink)}
/* A field chip: gray, non-editable — moved/deleted as one unit, never typed into. */
.tok{display:inline-block;background:#ecebf3;border:1px solid #d3cfe4;color:#4a4560;border-radius:6px;
  padding:1px 7px;font-size:12.5px;line-height:1.5;white-space:nowrap;vertical-align:baseline;
  -webkit-user-select:none;user-select:none}
.tok--block{display:block;width:100%;padding:7px 10px;margin:2px 0;color:#5b556f;background:#f1f0f7;
  border-style:dashed;white-space:normal}
.tok--block::before{content:"▦ ";opacity:.55}
.email-tokbar{display:flex;flex-wrap:wrap;gap:6px}
.tok--btn{cursor:pointer;font:inherit;font-size:12.5px;padding:3px 9px}
.tok--btn:hover{background:#e0deee;border-color:var(--pink)}
.tok--btn.tok--block{width:auto;display:inline-block;padding:3px 9px;margin:0}
.tok--btn:disabled{opacity:.4;cursor:not-allowed}
.tok--btn:disabled:hover{background:#ecebf3;border-color:#d3cfe4}
