/* Engine · universal section styles.
   Header, hero, feature bar, product rail, category grid, FAQ, CTA,
   footer, cart drawer, and the sticky-chrome fixes.
 */

/* ===========================================================================
   Storefront sections — bespoke surfaces (header, hero, rails, editorial,
   FAQ, CTA, footer, side cart). Every value resolves from tokens.css; there
   is no brand literal in this file, so a primitive swap re-skins all of it.

   Type/rhythm mirrors the COFACTOR Next storefront: mono eyebrows at 0.12em,
   -0.02em display tracking, a 3-step radius rhythm, and dark used only where
   it carries the argument.
   =========================================================================== */

/* WordPress gives `main.wp-block-group` a flow block-gap, which inserted ~19px
   of canvas ABOVE and BELOW every storefront section — read as white space
   around the hero photograph. Our sections own their vertical rhythm. */
main.wp-block-group > * { margin-block: 0; }
main.wp-block-group { padding-top: 0; margin-top: 0; }

:root {
  --ms-axis: 80rem;          /* max-w-7xl */
  --ms-gutter: 1rem;
  --ms-header-h: var(--site-header-h, 4rem);
  --ms-ease: cubic-bezier(0.25, 1, 0.5, 1);
  /* was a second, identical definition of the drawer curve. A token family
     with a private copy alongside it is how two curves drift apart. */
  --ms-ease-drawer: var(--ease-drawer);
}
@media (min-width: 640px) { :root { --ms-gutter: 1.5rem; }}


.ms-wrap {
  box-sizing: border-box;   /* without this the gutters ADD to max-width and the page scrolls sideways */
  width: 100%;
  max-width: calc(var(--ms-axis) + var(--ms-gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--ms-gutter);
}
.ms-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: calc(var(--p-space-px) * -1);
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.ms-i { width: 1.25rem; height: 1.25rem; }
.ms-i--sm { width: 1rem; height: 1rem; }
.ms-i--xs { width: 0.875rem; height: 0.875rem; }

/* ---- reveal: one staggered page-load cascade, not scattered micro-motion ---
   Content is VISIBLE by default. The hidden start state is applied only when
   the `ms-js` class is present (set by an inline script before paint), so a
   JS failure, a blocked observer, or a no-JS client still renders the page
   instead of a blank canvas. The script also force-reveals after a timeout. */
/* The reveal runs as an ANIMATION, and moves with the independent `translate`
   property rather than `transform`.

   It used to be a transition on `opacity, transform`. Two silent failures came
   out of that, because `html.ms-js .ms-reveal.is-in` outranks any single-class
   component rule:
     1. `transition:` is a SHORTHAND, so it reset transition-property on every
        element carrying .ms-reveal — the category accordion's `flex-grow`
        transition was erased and the panels snapped open with no easing at all,
        and .ms-cat / .prodcard lost their hover transitions the same way.
     2. `transform: none` at the end erased any transform a component needed to
        hold (a centring translate, a decorative rotate).
   An animation touches neither `transition` nor `transform`, so components keep
   both. Keep it that way. */
.ms-reveal { opacity: 1; }
html.ms-js .ms-reveal { opacity: 0; }
html.ms-js .ms-reveal.is-in { animation: ms-reveal-in 500ms var(--ms-ease) var(--d, 0ms) both; }
@keyframes ms-reveal-in {
  from { opacity: 0; translate: 0 10px; }
  to   { opacity: 1; translate: none; }
}
@media (prefers-reduced-motion: reduce) {
  html.ms-js .ms-reveal, html.ms-js .ms-reveal.is-in { opacity: 1; animation: none; translate: none; }}


/* ---- shared type ---------------------------------------------------------- */
.ms-eyebrow {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-12); letter-spacing: var(--p-tracking-6); text-transform: uppercase;
  color: var(--ink-3); margin: 0;
}
.ms-eyebrow--onink { color: color-mix(in oklab, var(--brand-foreground) 62%, transparent); }
.ms-title {
  font-size: clamp(1.6rem, 3.2vw, 2.1rem); font-weight: 600; letter-spacing: var(--p-tracking-n20);
  line-height: 1.12; color: var(--foreground); margin: var(--p-space-2) 0 0; text-wrap: balance;
}
.ms-title--2xl { font-size: clamp(1.8rem, 3.6vw, 2.4rem); max-width: 22ch; }
.ms-muted { color: var(--ink-3); }
.ms-body { color: var(--ink-2); font-size: var(--p-text-16); line-height: 1.65; margin: var(--p-space-4) 0 0; max-width: 52ch; }
.ms-section { padding-block: clamp(3rem, 7vw, 5.5rem); }
.ms-sectionhead {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--p-space-6); margin-bottom: var(--p-space-7);
}
.ms-textlink {
  display: inline-flex; align-items: center; gap: var(--p-space-1-5); flex-shrink: 0;
  font-size: var(--p-text-14); font-weight: 600; color: var(--brand); text-decoration: none;
}
.ms-textlink:hover { text-decoration: underline; text-underline-offset: 4px; }
.ms-textlink svg { transition: transform 200ms var(--ms-ease); }
.ms-textlink:hover svg { transform: translateX(3px); }

/* ---- buttons -------------------------------------------------------------- */
.ms-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--p-space-2);
  border: 1px solid transparent; border-radius: var(--radius-md);
  font-family: inherit; font-size: var(--p-text-14); font-weight: 600; letter-spacing: 0.01em;
  padding: var(--p-space-2-5) var(--p-space-4); cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background-color 150ms var(--ms-ease), box-shadow 200ms var(--ms-ease),
              transform 160ms var(--ms-ease), border-color 150ms var(--ms-ease);
}
.ms-btn:active { transform: scale(0.97); }
.ms-btn--brand { background: var(--brand); color: var(--brand-foreground); }
.ms-btn--brand:hover { background: var(--brand-hover); box-shadow: var(--shadow-brand); color: var(--brand-foreground); }
.ms-btn--ghost {
  background: color-mix(in oklab, var(--background) 72%, transparent);
  border-color: var(--border-strong); color: var(--foreground);
  backdrop-filter: blur(8px);
}
.ms-btn--ghost:hover { background: var(--background); border-color: var(--foreground); }
.ms-btn--onink {
  background: color-mix(in oklab, var(--surface) 10%, transparent);
  border-color: color-mix(in oklab, var(--surface) 22%, transparent);
  color: var(--brand-foreground);
}
.ms-btn--onink:hover { background: color-mix(in oklab, var(--surface) 18%, transparent); color: var(--brand-foreground); }
.ms-btn--xl { padding: var(--p-space-3-5) var(--p-space-6); font-size: var(--p-text-15); }
.ms-btn--sm { padding: 0.4375rem var(--p-space-3); font-size: var(--p-text-13); }
.ms-btn--full { width: 100%; }

.ms-pill {
  display: inline-flex; align-items: center; gap: var(--p-space-2); width: fit-content;
  border: 1px solid var(--border-strong); border-radius: var(--p-radius-pill);
  background: color-mix(in oklab, var(--background) 70%, transparent);
  backdrop-filter: blur(6px);
  padding: var(--p-space-1-5) var(--p-space-3-5);
  font-size: var(--p-text-11); font-weight: 600; letter-spacing: var(--p-tracking-8); text-transform: uppercase;
  color: var(--ink-2); box-shadow: var(--elevation-whisper);
}

/* ===========================================================================
   HEADER
   =========================================================================== */
.ms-header {
  position: sticky; top: 0; z-index: 50;
  border-bottom: 1px solid transparent;
  background: color-mix(in oklab, var(--background) 85%, transparent);
  backdrop-filter: blur(16px) saturate(150%);
  transition: border-color 200ms var(--ms-ease), box-shadow 200ms var(--ms-ease);
}
/* The rule only appears once the page has moved under it — at rest the header
   should read as part of the canvas, not a bar bolted to the top of it. */
.ms-header.is-stuck { border-bottom-color: var(--border); box-shadow: var(--elevation-whisper); }

/* announcement bar */
.ms-topbar {
  background: var(--ink-band);
  padding-block: 0.4375rem;
}
.ms-topbar__text {
  margin: 0; text-align: center;
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-11); letter-spacing: var(--p-tracking-14); text-transform: uppercase;
  color: color-mix(in oklab, var(--brand-foreground) 80%, transparent);
}
.ms-header__bar {
  display: flex; align-items: center; gap: var(--p-space-6);
  height: var(--ms-header-h);
}
.ms-wordmark {
  display: inline-flex; align-items: center; gap: var(--p-space-2);
  font-weight: 700; font-size: var(--p-text-17); letter-spacing: var(--p-tracking-n10); text-transform: uppercase;
  color: var(--foreground); text-decoration: none; margin-right: auto;
}
.ms-wordmark__tick {
  width: 0.3125rem; height: 1.125rem; border-radius: var(--p-radius-pill); background: var(--brand);
}
.ms-nav { display: none; align-items: center; gap: var(--p-space-7); }
@media (min-width: 900px) { .ms-nav { display: flex; }}

.ms-nav__link {
  position: relative; font-size: var(--p-text-15); font-weight: 500; color: var(--ink-2);
  text-decoration: none; padding-block: var(--p-space-1); transition: color 150ms var(--ms-ease);
}
.ms-nav__link::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
  background: var(--brand); transform: scaleX(0); transform-origin: left;
  transition: transform 200ms var(--ms-ease);
}
.ms-nav__link:hover { color: var(--foreground); }
.ms-nav__link:hover::after { transform: scaleX(1); }
.ms-nav__link.is-current { color: var(--foreground); }
.ms-nav__link.is-current::after { transform: scaleX(1); }

/* ---- shop dropdown --------------------------------------------------------
   Categories are read from the taxonomy, so the panel is correct on any brand
   without editing the nav. */
.ms-navgroup { position: relative; display: flex; align-items: center; }
.ms-navgroup .ms-nav__link { display: inline-flex; align-items: center; gap: var(--p-space-1); }
.ms-nav__chev { transition: transform 200ms var(--ms-ease); }
.ms-navgroup.is-open .ms-nav__chev { transform: rotate(180deg); }
.ms-megapanel {
  position: absolute; top: calc(100% + 0.75rem); left: 50%;
  transform: translateX(-50%) translateY(-4px) scale(0.98);
  transform-origin: top center;
  min-width: 22rem; padding: var(--p-space-3);
  border: 1px solid var(--border); border-radius: var(--radius-xl);
  background: var(--background); box-shadow: var(--shadow-card);
  opacity: 0; pointer-events: none;
  transition: opacity 160ms var(--ms-ease), transform 160ms var(--ms-ease);
}
.ms-megapanel[hidden] { display: none; }
.ms-navgroup.is-open .ms-megapanel {
  opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0) scale(1);
}
@media (prefers-reduced-motion: reduce) { .ms-megapanel { transition: none; }}

.ms-megapanel__inner { padding: 0; max-width: none; }
.ms-megapanel__cats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--p-space-0-5); }
.ms-megacat {
  display: flex; align-items: center; justify-content: space-between; gap: var(--p-space-3);
  padding: 0.5625rem var(--p-space-3); border-radius: var(--radius-md);
  text-decoration: none; color: var(--foreground);
  transition: background-color 140ms var(--ms-ease);
}
.ms-megacat:hover { background: var(--surface-2); }
.ms-megacat__name { font-size: var(--p-text-14); font-weight: 500; }
.ms-megacat__n {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-11); font-variant-numeric: tabular-nums; color: var(--ink-4);
}
.ms-megapanel__all {
  display: flex; align-items: center; gap: var(--p-space-1-5); justify-content: center;
  margin-top: var(--p-space-2); padding-top: var(--p-space-3); border-top: 1px solid var(--border);
  font-size: var(--p-text-13); font-weight: 600; color: var(--brand); text-decoration: none;
}
.ms-megapanel__all:hover { gap: 0.5625rem; }

.ms-header__actions { display: flex; align-items: center; gap: var(--p-space-1); }
.ms-iconbtn {
  position: relative; display: grid; place-items: center;
  width: 2.5rem; height: 2.5rem; border: none; border-radius: var(--radius-md);
  background: transparent; color: var(--foreground); cursor: pointer;
  transition: background-color 150ms var(--ms-ease), transform 160ms var(--ms-ease);
}
.ms-iconbtn:hover { background: var(--surface-2); }
.ms-iconbtn:active { transform: scale(0.94); }
.ms-menu-toggle { display: grid; }
@media (min-width: 900px) { .ms-menu-toggle { display: none; }}

.ms-cart-count {
  position: absolute; top: 0.25rem; right: 0.1875rem;
  min-width: 1.0625rem; height: 1.0625rem; padding-inline: var(--p-space-1);
  display: grid; place-items: center; border-radius: var(--p-radius-pill);
  background: var(--brand); color: var(--brand-foreground);
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-10); font-weight: 600; font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--background);
}
.ms-mobilenav {
  display: flex; flex-direction: column; border-top: 1px solid var(--border);
  background: var(--background);
  max-height: calc(100dvh - var(--ms-header-h)); overflow-y: auto;
}
/* [hidden] must beat the display above, and the panel never exists on desktop */
.ms-mobilenav[hidden] { display: none; }
@media (min-width: 900px) { .ms-mobilenav { display: none; }}

.ms-mobilenav__inner { padding: 0.5rem var(--ms-gutter) 1.5rem; }
.ms-mobilenav__link {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.8125rem 0; font-size: var(--p-text-16); font-weight: 500;
  color: var(--foreground); text-decoration: none;
  border-bottom: 1px solid var(--border);
}
.ms-mobilenav__link svg { color: var(--ink-4); }
/* The drawer cannot reuse the desktop affordance: .ms-nav__link marks current
   by BRIGHTENING ink-2 to foreground plus the ::after bar, but these rows are
   already full foreground and carry a hairline rule, so a second line under one
   of them reads as a border, not a state. Colour alone carries it. --brand is
   per-brand, so every vertical gets its own. */
.ms-mobilenav__link.is-current { color: var(--brand); }
.ms-mobilenav__group {
  margin: var(--p-space-5) 0 var(--p-space-2-5);
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-10); letter-spacing: var(--p-tracking-14); text-transform: uppercase; color: var(--ink-3);
}
.ms-mobilenav__chips { display: flex; flex-wrap: wrap; gap: 0.4375rem; }
.ms-mobilenav__chip {
  display: inline-flex; align-items: center;
  padding: 0.4375rem var(--p-space-3); border-radius: var(--p-radius-pill);
  border: 1px solid var(--border-strong); background: var(--surface);
  font-size: var(--p-text-13); font-weight: 500; color: var(--foreground); text-decoration: none;
}
.ms-mobilenav__chip:active { transform: scale(0.97); }
.ms-mobilenav__cta { margin-top: var(--p-space-5); gap: 0.4375rem; }

/* The split-grid `.ms-hero` that lived here is gone (2026-08-21). It was
   ported from a live site that had diverged, and nothing ever emitted it —
   the engine hero renders `.ms-hero3`, and every vertical overrides that.
   It was 45 lines of CSS for a component with no markup, which is exactly
   what the catalogue gate is for. */

.ms-proofstrip {
  display: flex; align-items: center; gap: var(--p-space-7); width: fit-content;
  margin: var(--p-space-10) 0 0; padding-top: var(--p-space-5);
  border-top: 1px solid color-mix(in oklab, var(--border-strong) 60%, transparent);
}
@media (min-width: 640px) { .ms-proofstrip { gap: var(--p-space-10); }}

.ms-proofstrip dd { margin: 0; }
.ms-proofstrip__v {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-22); line-height: 1.75rem; font-weight: 600; font-variant-numeric: tabular-nums;
  color: var(--foreground);
}
.ms-proofstrip__l { display: block; margin-top: var(--p-space-0-5); font-size: var(--p-text-12); line-height: 1.35; color: var(--ink-2); }

/* ===========================================================================
   FEATURE BAR
   =========================================================================== */
.ms-featurebar {
  border-block: 1px solid var(--border);
  background: color-mix(in oklab, var(--surface-2) 55%, var(--background));
}
.ms-featurebar__grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--p-space-6) var(--p-space-2); padding-block: var(--p-space-8);
}
@media (min-width: 1024px) { .ms-featurebar__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }}

.ms-feature { display: flex; align-items: center; gap: var(--p-space-3); }
@media (min-width: 1024px) {
  .ms-feature + .ms-feature { border-left: 1px solid color-mix(in oklab, var(--border-strong) 45%, transparent); padding-left: var(--p-space-6); }}

.ms-feature__chip {
  display: grid; place-items: center; flex-shrink: 0;
  width: 2.5rem; height: 2.5rem; border-radius: var(--radius-lg);
  background: var(--brand-muted); color: var(--brand);
}
.ms-feature__title { margin: 0; font-size: var(--p-text-14); font-weight: 600; color: var(--foreground); }
.ms-feature__caption { margin: var(--p-space-0-5) 0 0; font-size: var(--p-text-12); color: var(--ink-3); }

/* ===========================================================================
   PRODUCT RAIL CARD — ported from components/commerce/product-card.tsx (the
   home rail's card, which is NOT the PLP's ShopCard):
     rounded-2xl · shadow-whisper at rest → shadow-card on hover (no lift)
     aspect-square stage on the light radial gradient
     mono category eyebrow at 0.10em · title-md (20px) name
     price font-mono 20/28 + "/ unit" caption
     TWO size-lg buttons: outline "View Details" + INK "Add to Cart",
     stacked below 640 and side-by-side (flex-1 each) above it
   =========================================================================== */
.ms-prodgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--p-space-4); }
@media (min-width: 900px) { .ms-prodgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }}


.prodcard {
  display: flex; height: 100%; flex-direction: column; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--radius-2xl);
  background: var(--card); box-shadow: var(--elevation-whisper);
  transition: box-shadow 200ms var(--ease-out-quart);
}
@media (hover: hover) and (pointer: fine) {
  .prodcard:hover { box-shadow: var(--elevation-card); }
  .prodcard:hover .prodcard__img { transform: scale(1.04); }
  .prodcard:hover .prodcard__name { color: var(--brand); }}

.prodcard__link { display: flex; flex: 1; flex-direction: column; text-decoration: none; outline: none; }
.prodcard__link:focus-visible { box-shadow: inset 0 0 0 3px color-mix(in oklab, var(--ring) 30%, transparent); }

.prodcard__stage {
  position: relative; display: block; aspect-ratio: 1 / 1; overflow: hidden;
  border-bottom: 1px solid var(--border);
  background: radial-gradient(130% 100% at 50% 0%,
    var(--surface) 0%, var(--surface) 60%, var(--surface-warm) 100%);
}
.prodcard__img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 300ms var(--ease-out-quart);
}
.prodcard__body { display: flex; flex: 1; flex-direction: column; padding: var(--p-space-3) var(--p-space-3) 0; }
.prodcard__cat {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-12); line-height: 1rem; letter-spacing: var(--p-tracking-10); text-transform: uppercase;
  color: var(--ink-3);
}
.prodcard__name {
  margin-top: var(--p-space-1-5);
  font-size: var(--p-text-20); line-height: 1.3; letter-spacing: var(--p-tracking-n14); font-weight: 600;
  color: var(--foreground); transition: color 150ms var(--ease-out-quart);
}
.prodcard__price { display: flex; align-items: baseline; gap: var(--p-space-1-5); margin-top: var(--p-space-2); }
.prodcard__amount {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-20); line-height: 1.75rem; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--foreground);
}
.prodcard__amount .woocommerce-Price-amount { color: inherit; font: inherit; }
.prodcard__unit {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-13); line-height: 1.5; color: var(--ink-3);
}
.prodcard__unavail {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-13); font-weight: 600; letter-spacing: var(--p-tracking-6);
  text-transform: uppercase; color: var(--ink-3);
}
.prodcard__actions { display: flex; flex-direction: column; gap: var(--p-space-2); padding: var(--p-space-3); }
@media (min-width: 640px) {
  .prodcard__actions { flex-direction: row; }
  .prodcard__actions .btn { flex: 1; }}


/* ===========================================================================
   CATEGORY GRID
   =========================================================================== */
.ms-catgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--p-space-3); }
@media (min-width: 640px) { .ms-catgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }}

@media (min-width: 1024px) { .ms-catgrid { grid-template-columns: repeat(5, minmax(0, 1fr)); }}

.ms-cat {
  display: flex; flex-direction: column; gap: var(--p-space-3); padding: var(--p-space-3);
  border: 1px solid var(--border); border-radius: var(--radius-xl);
  background: var(--card); box-shadow: var(--elevation-whisper); text-decoration: none;
  transition: transform 200ms var(--ms-ease), box-shadow 200ms var(--ms-ease), border-color 200ms var(--ms-ease);
}
@media (hover: hover) and (pointer: fine) {
  .ms-cat:hover { transform: translateY(-2px); box-shadow: var(--elevation-card); border-color: color-mix(in oklab, var(--foreground) 14%, transparent); }
  .ms-cat:hover .ms-cat__chip { transform: scale(1.06); }}

.ms-cat__stage {
  display: grid; place-items: center; aspect-ratio: 5 / 4;
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: radial-gradient(120% 100% at 50% 0%, var(--surface) 0%, var(--surface) 60%, var(--surface-warm) 100%);
}
.ms-cat__chip {
  display: grid; place-items: center; width: 3rem; height: 3rem;
  border-radius: var(--radius-lg); background: var(--brand-muted); color: var(--brand);
  transition: transform 200ms var(--ms-ease);
}
.ms-cat__chip svg { width: 1.5rem; height: 1.5rem; }
.ms-cat__row { display: flex; align-items: center; justify-content: space-between; gap: var(--p-space-2); padding: 0 var(--p-space-1) var(--p-space-1); }
.ms-cat__label { font-size: var(--p-text-15); font-weight: 600; color: var(--foreground); }
.ms-cat__count {
  font-family: var(--font-mono), ui-monospace, monospace; font-size: var(--p-text-11);
  font-variant-numeric: tabular-nums; color: var(--ink-3);
}

/* ===========================================================================
   CATEGORY SLICES  —  `catslices`
   ===========================================================================
   THIS BLOCK EXISTED ONLY IN THE SUPPLEMENT VERTICAL, AND catslices IS AN
   ENGINE SECTION. `ms_catslices_sc()` is registered in engine/functions.php,
   so every brand can place it — but the only `.ms-slice` rules in the repo
   lived in verticals/supplement/assets/home.css. On the skincare build the
   section therefore rendered as bare anchors: `display: inline`, `position:
   static`, images at their intrinsic size, no frame, no grid. Measured on the
   live site before this commit, `.ms-slice` computed to 65x122px with a 0x0
   image inside it. It was not "broken styling", it was NO styling.

   The rule the boundary implies: a section registered by the engine must be
   dressed by the engine. A vertical may then refine it — the supplement's
   sheet loads after this one and still wins on every property it restates.

   The design is a rail of equal specimen columns, which is the same figure the
   ramp already draws with five products. Labels are NOT overlaid on the
   photographs: they sit in a solid footer under the image, where they keep
   full contrast against a token ground no matter what the category art is,
   and where the count is readable without hovering anything. Above ~1024px
   every category shares one row; below that the row becomes a snap rail
   rather than collapsing into orphaned grid rows. */
.ms-slices {
  display: grid;
  grid-auto-flow: column;
  /* minmax(), so the row degrades honestly: N categories share the axis until
     they would go under 9.5rem, and after that the container scrolls instead
     of crushing every tile. A brand with 14 categories gets a rail, not 14
     unreadable slivers. */
  grid-auto-columns: minmax(9.5rem, 1fr);
  gap: var(--p-space-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  padding-bottom: var(--p-space-2);
  scrollbar-width: none;
}
.ms-slices::-webkit-scrollbar { display: none; }
/* A DEFINITE track width, not a minmax(). Inside a scroll port the grid's
   inline size is still definite, so `minmax(11rem, 68vw)` resolved every track
   to its MINIMUM — measured 176px at a 390px viewport, i.e. two cramped tiles
   on screen instead of the intended one-and-a-peek. A single value cannot be
   negotiated away, and the peek is what tells you the rail scrolls. */
@media (max-width: 767px) { .ms-slices { grid-auto-columns: min(68vw, 15rem); } }

.ms-slice {
  position: relative;
  display: grid;
  grid-template-rows: 1fr auto;   /* photograph, then the label bar */
  scroll-snap-align: start;
  min-width: 0;
  text-decoration: none;
  color: var(--foreground);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  /* Brand law §7.5: a card is a border and no shadow at rest — elevation is
     something an interaction earns. Motion timings are the law's 150ms
     hover/focus rung, not a bespoke duration. */
  transition: transform 150ms var(--ms-ease), box-shadow 150ms var(--ms-ease),
              border-color 150ms var(--ms-ease);
}
@media (hover: hover) and (pointer: fine) {
  .ms-slice:hover, .ms-slice.is-hover {
    transform: translateY(-3px);
    box-shadow: var(--elevation-lift);
    border-color: var(--border-hover);
  }
  .ms-slice:hover .ms-slice__img, .ms-slice.is-hover .ms-slice__img { transform: scale(1.04); }
  .ms-slice:hover .ms-slice__arrow, .ms-slice.is-hover .ms-slice__arrow { transform: translateX(3px); }
}
/* primitives.css strips the UA outline off .ms-slice — so this component owes
   the keyboard a ring of its own, and it is drawn OUTSIDE the tile so the
   overflow clip cannot eat it. */
.ms-slice:focus-visible, .ms-slice.is-focus {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-color: var(--ring);
}
.ms-slice:active { transform: translateY(-1px); }

/* The photograph. A packshot arrives on its own pale stage, so the well is
   tinted rather than white — a cream product shot on a white tile reads as a
   loading state. aspect-ratio, not a height, keeps every tile identical
   whatever the source image is. */
.ms-slice__img {
  grid-area: 1 / 1;
  width: 100%; height: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  display: block;
  background: var(--surface-2);
  transition: transform 250ms var(--ms-ease);
}
/* Brand law bans gradients in UI outright (§1.3, §7.2), and a scrim IS a
   gradient. It exists in this markup to make overlaid labels legible — and
   this tile does not overlay its labels, so there is nothing for it to do
   here. Hidden rather than removed from the PHP, for the same reason as
   `__side`: the supplement vertical does overlay, and does need it. */
.ms-slice__scrim { display: none; }

/* The line code, when a vertical supplies one through `ms_category_codes`.
   The skincare taxonomy has no codes, so on that brand this element renders
   EMPTY — hence :empty. Without it the tile carried a 22px blank chip whose
   only job was to look like something had failed to load. */
.ms-slice__chip {
  grid-area: 1 / 1;
  place-self: start start;
  margin: var(--p-space-3);
  padding: var(--p-space-1) var(--p-space-2);
  border-radius: var(--radius-chip);
  background: var(--brand-muted);
  color: var(--brand);
  font-family: var(--font-mono), ui-monospace, monospace;
  /* 12px is the brand's hard floor — "nothing ships smaller, anywhere" (§2). */
  font-size: var(--p-text-12);
  letter-spacing: var(--p-tracking-6);
  text-transform: uppercase;
  line-height: 1.5;
}
.ms-slice__chip:empty { display: none; }

/* The vertical spine label. The engine's tile carries its name in the footer
   bar, so a rotated second copy of the same word would be decoration dressed
   as information. Hidden here, NOT deleted from the markup: the supplement
   vertical's rail collapses its tiles and uses this as the label of a narrow
   tile, which is a real job. Engine hides, vertical may show. */
.ms-slice__side { display: none; }

.ms-slice__meta {
  grid-row: 2;
  display: grid;
  gap: var(--p-space-1);
  padding: var(--p-space-3) var(--p-space-3-5) var(--p-space-3-5);
  border-top: 1px solid var(--border);
  background: var(--card);
}
.ms-slice__name {
  font-size: var(--p-text-15);
  font-weight: 600;
  letter-spacing: var(--p-tracking-n10);
  color: var(--foreground);
  /* Categories are author-supplied strings. One long one must clip, not push
     the tile wider than its grid track. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ms-slice__row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--p-space-2); min-width: 0;
}
.ms-slice__count {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-12);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--p-tracking-4);
  color: var(--ink-3);
  white-space: nowrap;
}
.ms-slice__arrow {
  flex: none;
  color: var(--brand);
  transition: transform 200ms var(--ms-ease);
}

@media (prefers-reduced-motion: reduce) {
  .ms-slices { scroll-behavior: auto; }
  .ms-slice, .ms-slice__img, .ms-slice__arrow { transition: none; }
  .ms-slice:hover, .ms-slice.is-hover, .ms-slice:active { transform: none; }
  .ms-slice:hover .ms-slice__img, .ms-slice.is-hover .ms-slice__img { transform: none; }
}

/* ===========================================================================
   FAQ
   =========================================================================== */
/* The reference FAQ is a CENTRED max-w-3xl column with a centred header —
   not a left-aligned block inside the 80rem axis. */
.ms-faq-section {
  box-sizing: border-box; width: 100%; max-width: 48rem;
  margin-inline: auto; padding: var(--p-space-14) var(--p-space-3);
}
@media (min-width: 640px) { .ms-faq-section { padding-inline: var(--p-space-6); }}

@media (min-width: 1024px) { .ms-faq-section { padding-block: var(--p-space-24); }}

.ms-faq-section .ms-faq__head { text-align: center; margin-bottom: 0; }
.ms-faq-section .ms-eyebrow { margin-bottom: var(--p-space-3); }
.ms-faq__lede { margin: var(--p-space-3) auto 0; max-width: 28rem; color: var(--ink-2); }

.ms-faq__list { border-top: 1px solid var(--border); margin-top: var(--p-space-10); }
.ms-faqitem { border-bottom: 1px solid var(--border); }

/* The row is a GRID aligned to `start`, not a centred flex row. Centring put
   the toggle on the optical middle of a two-line question, so the column of
   toggles stopped being level the moment one question in a list wrapped and
   the rest did not. The 6px on the label is the optical correction that lands
   a single line's centre on the disc's: the disc's is 18px down, the line's is
   6 + 23/2. */
.ms-faqitem summary {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: start; gap: var(--p-space-5);
  padding-block: var(--p-space-4);
  cursor: pointer; list-style: none;
  font-size: var(--p-text-17); font-weight: 600; line-height: 1.35;
  letter-spacing: var(--p-tracking-n10); color: var(--foreground);
  transition: color 150ms var(--ms-ease);
}
.ms-faqitem summary::-webkit-details-marker { display: none; }
.ms-faqitem summary > span:first-child { padding-top: var(--p-space-1-5); text-wrap: pretty; }
.ms-faqitem summary:hover { color: var(--brand); }
.ms-faqitem summary:focus-visible {
  outline: 2px solid var(--ring); outline-offset: 3px; border-radius: var(--radius-control);
}

/* The toggle is a bordered disc rather than a naked chevron: on a hairline
   register the glyph is the only thing saying "this row does something", and a
   12px stroke floating in whitespace does not say it loudly enough. One `+`
   rotates 45deg into an `x` — a single animated property, and the open state
   is legible from across the page. */
.ms-faqitem__chev {
  flex-shrink: 0; display: grid; place-items: center;
  width: var(--p-space-9); height: var(--p-space-9);
  border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  background: var(--background); color: var(--ink-3);
  transition: transform 200ms var(--ms-ease), color 150ms var(--ms-ease),
              border-color 150ms var(--ms-ease), background-color 150ms var(--ms-ease);
}
.ms-faqitem summary:hover .ms-faqitem__chev {
  border-color: var(--border-hover); color: var(--foreground); background: var(--muted);
}
.ms-faqitem[open] .ms-faqitem__chev {
  transform: rotate(45deg);
  border-color: var(--brand); color: var(--brand);
  /* A mix rather than --brand-muted: in the dark appearance that role resolves
     to the same night as the page, and the open disc lost its fill entirely. */
  background: color-mix(in oklab, var(--brand) 12%, transparent);
}
.ms-faqitem[open] summary:hover .ms-faqitem__chev {
  background: color-mix(in oklab, var(--brand) 20%, transparent);
}

/* The answer sits in its own well behind a cupric edge. The brand's whole
   argument is that the number is printed on the page before the claim is made;
   marking the answer the way the site marks a concentration keeps the one
   accent doing one job — "this is the thing you asked for". */
.ms-faqitem__body { padding-bottom: var(--p-space-5); }
.ms-faqitem__answer {
  padding: var(--p-space-4) var(--p-space-5);
  border-left: 2px solid var(--brand);
  border-radius: 0 var(--radius-control) var(--radius-control) 0;
  /* Mixed off --card, not --surface-2: that role IS the page ground in the dark
     appearance, so the well would have had no fill there. */
  background: color-mix(in oklab, var(--brand) 5%, var(--card));
}
.ms-faqitem__body p {
  margin: 0; max-width: var(--prose-max); color: var(--ink-2);
  font-size: var(--p-text-16); line-height: 1.7; text-wrap: pretty;
}
.ms-faqitem__body p + p { margin-top: var(--p-space-3); }
.ms-faqitem__body .ms-textlink { margin-top: var(--p-space-4); }

/* JS opens the row by animating this wrapper's height. Without JS it is inert
   and <details> toggles natively — the answer is never hidden behind a rule the
   browser might not be running. */
.ms-faqitem__wrap { overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
  .ms-faqitem__chev { transition: color 150ms var(--ms-ease), border-color 150ms var(--ms-ease); }
}

/* ===========================================================================

/* ===========================================================================
   CLOSING CTA — the one dark band, and it carries the argument
   =========================================================================== */
.ms-cta {
  position: relative; overflow: hidden;
  display: grid; gap: var(--p-space-8); align-items: center;
  border-radius: var(--radius-3xl-plus, 1.75rem);
  background: var(--surface-ink);
  padding: clamp(2rem, 5vw, 3.5rem);
}
@media (min-width: 900px) { .ms-cta { grid-template-columns: 1.35fr 1fr; }}

.ms-cta__title {
  margin: var(--p-space-2-5) 0 0; font-size: clamp(1.75rem, 3.8vw, 2.5rem); font-weight: 600;
  letter-spacing: var(--p-tracking-n25); line-height: 1.08; color: var(--brand-foreground); text-wrap: balance;
}
.ms-cta__sub {
  margin: var(--p-space-3-5) 0 0; max-width: 44ch; line-height: 1.6;
  color: color-mix(in oklab, var(--brand-foreground) 72%, transparent);
}
.ms-cta__row { display: flex; flex-wrap: wrap; gap: var(--p-space-3); margin-top: var(--p-space-7); }
.ms-cta__media { justify-self: center; }
.ms-cta__media img { display: block; width: 100%; max-width: 20rem; height: auto; }
@media (max-width: 899px) { .ms-cta__media { display: none; }}


/* ===========================================================================
   FOOTER

   Three deliberate moves, in the order a reader meets them:

   1. THE RAMP replaces the 1px border. The dilution ramp is this brand's only
      sanctioned decorative device (law §8), and nine hard-edged steps do real
      work here: they carry the cream page down into the ink band instead of
      cutting it off. The ninth step IS --ink-band, so the ramp resolves into
      the ground it sits on rather than stopping on top of it.
   2. THE WIDE COLUMN earns its width. The site description moves from 14px
      body copy to Raleway at display scale — it is a heading, so type law puts
      it in the display face — and a mono receipt sits under it. That column
      used to be a blurb above ~300px of nothing.
   3. THE CLOSING MARK is the wordmark at 58% of the container, centred. Not a
      taste call: 58% centred is the label grid (law §4.2), the proportion the
      wordmark holds on every SKU's front panel. Vertical padding is one
      o-width, which is the clearspace rule (law §3) made literal.

   The three link columns now carry a rule under each head, so the lists start
   on one level. Three ragged lists with nothing aligning them read as an
   accident; the rule is what makes them read as a system.
   =========================================================================== */
.ms-footer {
  background: var(--ink-band);
  color: color-mix(in oklab, var(--brand-foreground) 78%, transparent);
}

/* The ramp. Steps are hard-edged and unlabelled — it is a dilution series,
   not a legend, and law §1 forbids resolving it into a gradient.

   EIGHT bands carry a NINE-step ramp because the ninth step is --ink-band,
   the footer's own ground. Drawing all nine painted two bands the same colour
   as the surface under them, and the strip read as a stripe that stopped 20%
   short of the right edge — a rendering bug, not a dissolve. Ending at step 8
   keeps every drawn band legible and still lands the ramp on its last step:
   the band the reader is already looking at. */
.ms-footer__ramp { display: grid; grid-template-columns: repeat(8, 1fr); height: var(--p-space-3); }
.ms-footer__ramp span { display: block; }
.ms-footer__ramp span:nth-child(1) { background: var(--ramp-1); }
.ms-footer__ramp span:nth-child(2) { background: var(--ramp-2); }
.ms-footer__ramp span:nth-child(3) { background: var(--ramp-3); }
.ms-footer__ramp span:nth-child(4) { background: var(--ramp-4); }
.ms-footer__ramp span:nth-child(5) { background: var(--ramp-5); }
.ms-footer__ramp span:nth-child(6) { background: var(--ramp-6); }
.ms-footer__ramp span:nth-child(7) { background: var(--ramp-7); }
.ms-footer__ramp span:nth-child(8) { background: var(--ramp-8); }

.ms-footer__grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--p-space-12) var(--p-space-8);
  padding-block: clamp(3.5rem, 7vw, 6rem) var(--p-space-12);
}
@media (min-width: 900px) {
  .ms-footer__grid { grid-template-columns: 2fr 1fr 1fr 1.1fr; gap: var(--p-space-12); }
}

.ms-footer__brand { grid-column: span 2; }
@media (min-width: 900px) { .ms-footer__brand { grid-column: span 1; padding-right: var(--p-space-10); }}

/* Fallback lockup — renders only for a brand that ships no cream wordmark
   SVG, in which case there is no closing mark and the footer needs a name. */
.ms-footer__lockup { display: flex; align-items: center; gap: var(--p-space-2-5); margin-bottom: var(--p-space-6); }
.ms-footer__tick { width: 0.375rem; height: 1.25rem; border-radius: var(--p-radius-pill); background: var(--brand); }
.ms-footer__wordmark {
  font-size: var(--p-text-22); font-weight: 700; letter-spacing: var(--p-tracking-n10);
  text-transform: uppercase; color: var(--brand-foreground); line-height: 1;
}

.ms-footer__line {
  margin: 0; max-width: 20ch;
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.5rem, 2.4vw, var(--p-text-30)); line-height: 1.2;
  letter-spacing: var(--p-tracking-n10);
  color: var(--brand-foreground);
}

/* The receipt — the brand's evidence layer, in the footer. Mono, hairline
   rules, square corners, no adjectives (label law §4.3). Every value in it is
   supplied by the vertical from a policy page, never composed here. */
.ms-footer__receipt { margin: var(--p-space-10) 0 0; max-width: 22rem; }
.ms-footer__row {
  display: grid; grid-template-columns: var(--p-space-20) 1fr; gap: var(--p-space-4);
  align-items: baseline; padding-block: var(--p-space-3);
  border-top: 1px solid color-mix(in oklab, var(--brand-foreground) 16%, transparent);
}
.ms-footer__row:last-child { border-bottom: 1px solid color-mix(in oklab, var(--brand-foreground) 16%, transparent); }
.ms-footer__row dt {
  margin: 0; font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-12); font-weight: 500;
  letter-spacing: var(--p-tracking-6); text-transform: uppercase;
  color: color-mix(in oklab, var(--brand-foreground) 48%, transparent);
}
.ms-footer__row dd {
  margin: 0; font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-12); line-height: 1.5;
  color: color-mix(in oklab, var(--brand-foreground) 88%, transparent);
}
.ms-footer__row dd a {
  color: inherit; text-decoration: none;
  border-bottom: 1px solid color-mix(in oklab, var(--brand-foreground) 30%, transparent);
  transition: border-color 150ms var(--ms-ease);
}
.ms-footer__row dd a:hover { border-bottom-color: var(--brand-foreground); }

/* 12px, not the 11px this head used to run at: law §2 sets a hard 0.75rem
   floor and nothing ships under it. The mono label tracking is documented as
   readable at both sizes, so the floor costs nothing here. */
.ms-footer__coltitle {
  margin: 0 0 var(--p-space-4); padding-bottom: var(--p-space-3);
  border-bottom: 1px solid color-mix(in oklab, var(--brand-foreground) 16%, transparent);
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-12); font-weight: 500;
  letter-spacing: var(--p-tracking-6); text-transform: uppercase;
  color: color-mix(in oklab, var(--brand-foreground) 52%, transparent);
}
.ms-footer__col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
/* min-height, not padding: the row has to clear the 44px touch floor whatever
   the label's line box happens to measure. */
.ms-footer__col a {
  display: flex; align-items: center; gap: var(--p-space-2);
  min-height: var(--p-space-11); padding-block: var(--p-space-1);
  font-size: var(--p-text-15); text-decoration: none;
  color: color-mix(in oklab, var(--brand-foreground) 70%, transparent);
  transition: color 150ms var(--ms-ease);
}
.ms-footer__col a:hover { color: var(--brand-foreground); }
.ms-footer__label { transition: translate 150ms var(--ms-ease); }
.ms-footer__col a:hover .ms-footer__label { translate: var(--p-space-1) 0; }
.ms-footer__arrow {
  opacity: 0; translate: calc(var(--p-space-1) * -1) 0;
  transition: opacity 150ms var(--ms-ease), translate 150ms var(--ms-ease);
}
.ms-footer__col a:hover .ms-footer__arrow { opacity: 1; translate: 0 0; }
@media (prefers-reduced-motion: reduce) {
  .ms-footer__label, .ms-footer__arrow { transition: none; translate: none; }
}

.ms-footer__legal {
  display: flex; flex-direction: column; gap: var(--p-space-3);
  padding-block: var(--p-space-6);
  border-top: 1px solid color-mix(in oklab, var(--brand-foreground) 12%, transparent);
}
.ms-footer__legalrow {
  display: flex; flex-wrap: wrap; justify-content: space-between;
  gap: var(--p-space-2) var(--p-space-8);
}
.ms-footer__legal p { margin: 0; font-size: var(--p-text-12); line-height: 1.6; color: color-mix(in oklab, var(--brand-foreground) 48%, transparent); }
.ms-footer__dshea { max-width: 68ch; }

/* ---- the closing mark -----------------------------------------------------
   DELIBERATE DEVIATION FROM LOGO LAW §3, on an explicit founder call
   (2026-08-21). §3's misuse list names "outline" outright, and this draws the
   wordmark as a hairline with no fill. Do NOT quietly restore the solid cream
   mark on the grounds that the law says so — the deviation is the request.
   What the law still governs, and what is still honoured: the mark is not
   stretched, rotated, shadowed, gradient-filled, recoloured to a non-ramp
   hue, or re-typeset in live text. It remains the real SVG.

   WHY IT IS DRAWN RATHER THAN TINTED. Solid cream at any size read as a
   billboard — a large light mass that outweighed everything above it. An
   outline puts the same mark on the page as a line drawing, so it can run the
   full width and still sit behind the content rather than in front of it.

   The SVG carries fill as a PRESENTATION attribute (cream letterforms, and
   the counters as separate blue-800 shapes on top). A CSS `fill` beats a
   presentation attribute, so one rule flattens both groups — and the counters
   become drawn circles, which is what makes the letterforms read.

   non-scaling-stroke is load-bearing: without it the stroke scales with the
   viewBox, and a hairline on a 1200px desktop mark becomes a heavy rule on a
   360px phone. With it, 1.25px is 1.25px everywhere. */
.ms-footer__mark { padding-block: var(--p-space-12); }
@media (min-width: 900px) { .ms-footer__mark { padding-block: var(--p-space-16); }}
.ms-footer__mark .ms-wordmark__svg { display: block; height: auto; width: 100%; margin-inline: auto; }
.ms-footer__mark .ms-wordmark__svg path {
  fill: none;
  stroke: color-mix(in oklab, var(--ramp-3) 45%, transparent);
  stroke-width: 1.25;
  vector-effect: non-scaling-stroke;
}

/* ===========================================================================
   SIDE CART DRAWER
   =========================================================================== */
.ms-drawer { position: fixed; inset: 0; z-index: 100; }
.ms-drawer[hidden] { display: none; }
.ms-drawer__scrim {
  position: absolute; inset: 0;
  background: color-mix(in oklab, var(--scrim) 45%, transparent);
  opacity: 0; transition: opacity 260ms var(--ms-ease-drawer);
  backdrop-filter: blur(2px);
}
.ms-drawer__panel {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(26.5rem, 100%);
  display: flex; flex-direction: column;
  background: var(--background); border-left: 1px solid var(--border);
  box-shadow: var(--elevation-pop);
  transform: translateX(100%);
  transition: transform 320ms var(--ms-ease-drawer);
}
.ms-drawer.is-open .ms-drawer__scrim { opacity: 1; }
.ms-drawer.is-open .ms-drawer__panel { transform: none; }
@media (prefers-reduced-motion: reduce) {
  .ms-drawer__scrim, .ms-drawer__panel { transition: none; }}

.ms-drawer__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--p-space-4);
  padding: var(--p-space-4) var(--p-space-5); border-bottom: 1px solid var(--border);
}
.ms-drawer__title { margin: 0; font-size: var(--p-text-15); font-weight: 600; color: var(--foreground); }
.ms-drawer__title span {
  font-family: var(--font-mono), ui-monospace, monospace; font-size: var(--p-text-12);
  color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.ms-drawer__body { flex: 1; overflow-y: auto; padding: var(--p-space-5); }
.ms-drawer__empty { display: flex; flex-direction: column; align-items: center; gap: var(--p-space-4); padding: var(--p-space-12) var(--p-space-4); text-align: center; }
.ms-drawer__empty p { margin: 0; color: var(--ink-3); }

.ms-line { display: grid; grid-template-columns: 4.5rem 1fr; gap: var(--p-space-3-5); padding-block: var(--p-space-4); border-top: 1px solid var(--border); }
.ms-line:first-child { border-top: none; padding-top: 0; }
.ms-line__media { border-radius: var(--radius-md); overflow: hidden; background: var(--surface); aspect-ratio: 1; }
.ms-line__media img { width: 100%; height: 100%; object-fit: cover; }
.ms-line__name { margin: 0; font-size: var(--p-text-14); font-weight: 600; color: var(--foreground); line-height: 1.35; }
.ms-line__name a { color: inherit; text-decoration: none; }
.ms-line__sku {
  margin: 0.1875rem 0 0; font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-10); letter-spacing: var(--p-tracking-6); text-transform: uppercase; color: var(--ink-3);
}
.ms-line__row { display: flex; align-items: center; justify-content: space-between; gap: var(--p-space-3); margin-top: var(--p-space-2-5); }
.ms-qty { display: inline-flex; align-items: center; border: 1px solid var(--border-strong); border-radius: var(--radius-md); overflow: hidden; }
.ms-qty button {
  display: grid; place-items: center; width: 1.875rem; height: 1.875rem;
  border: none; background: transparent; color: var(--foreground); cursor: pointer;
  transition: background-color 150ms var(--ms-ease);
}
.ms-qty button:hover { background: var(--control); }
.ms-qty button svg { width: 0.875rem; height: 0.875rem; }
.ms-qty__v {
  min-width: 1.75rem; text-align: center;
  font-family: var(--font-mono), ui-monospace, monospace; font-size: var(--p-text-13);
  font-variant-numeric: tabular-nums; color: var(--foreground);
}
.ms-line__price { font-family: var(--font-mono), ui-monospace, monospace; font-size: var(--p-text-14); font-weight: 600; font-variant-numeric: tabular-nums; color: var(--foreground); }

.ms-drawer__foot { border-top: 1px solid var(--border); padding: var(--p-space-5); background: var(--surface); }
.ms-drawer__totals { display: flex; align-items: baseline; justify-content: space-between; font-size: var(--p-text-15); font-weight: 600; color: var(--foreground); }
.ms-drawer__total { font-family: var(--font-mono), ui-monospace, monospace; font-size: var(--p-text-18); font-variant-numeric: tabular-nums; }
.ms-drawer__note { margin: var(--p-space-1-5) 0 var(--p-space-4); font-size: var(--p-text-12); color: var(--ink-3); }
.ms-drawer__cartlink { display: block; margin-top: var(--p-space-3); text-align: center; font-size: var(--p-text-13); color: var(--ink-2); text-decoration: none; }
.ms-drawer__cartlink:hover { color: var(--foreground); text-decoration: underline; text-underline-offset: 3px; }

body.ms-locked { overflow: hidden; }

/* ---------------------------------------------------------------------------
   FREE SHIPPING PROGRESS — docked under the drawer head, above the items.

   Colour follows the palette law (palette-v2.css): blue acts, ochre measures.
   The fill is brand because the bar exists to pull the visitor toward an
   action; the money left to spend is mono ochre because it is a measured
   number. Green is NOT used at 100% — it is reserved for stock state, and an
   unlocked threshold is not a stock fact.
   --------------------------------------------------------------------------- */
.ms-ship { padding: var(--p-space-3-5) var(--p-space-5) var(--p-space-4); border-bottom: 1px solid var(--border); background: var(--surface); }
.ms-ship[hidden] { display: none; }
.ms-ship__msg {
  margin: 0 0 var(--p-space-2);
  font-size: var(--p-text-13); font-weight: 500; line-height: 1.4;
  color: var(--ink-2);
  /* The amount is the only part that moves. Tabular figures keep the text from
     re-flowing a pixel left and right on every quantity change. */
  font-variant-numeric: tabular-nums;
}
.ms-ship__track {
  position: relative; height: 0.375rem; overflow: hidden;
  border-radius: var(--p-radius-pill); background: var(--control);
}
.ms-ship__fill {
  display: block; width: 100%; height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--brand-soft), var(--brand));
  /* Was animated by transitioning `width` from 0. width is a LAYOUT property,
     so every frame re-ran layout for the whole drawer subtree — the one place
     in the engine breaking "only animate transform and opacity".

     clip-path rather than scaleX because the fill is a GRADIENT: scaleX would
     squash the gradient's colour ramp into whatever width it currently has, so
     the hue at the leading edge would shift as the bar filled. clip-path reveals
     the gradient at its true size. The element is now full-width and clipped;
     storefront.js sets clip-path, not width. */
  clip-path: inset(0 100% 0 0);
  transition: clip-path 420ms var(--ms-ease-drawer);
}
.ms-ship.is-done .ms-ship__msg { color: var(--foreground); font-weight: 600; }
.ms-ship.is-done .ms-ship__fill { background: var(--brand); }
@media (prefers-reduced-motion: reduce) { .ms-ship__fill { transition: none; } }

/* ---------------------------------------------------------------------------
   ORDER BUMP — one offer, between the items and the checkout button.

   THE WHOLE CARD IS THE LABEL. A 16px checkbox is a poor tap target on a
   phone, and the bump lives exactly where the thumb already is on the way to
   Checkout; making the image, the title and the price all part of the same
   control is the difference between a decoration and an offer.

   It sits OUTSIDE .ms-drawer__body on purpose: the body scrolls, and an offer
   that only exists below the fold of a long cart is not an offer.
   --------------------------------------------------------------------------- */
.ms-bump { padding: var(--p-space-3-5) var(--p-space-5); border-top: 1px solid var(--border); background: var(--surface); }
.ms-bump[hidden] { display: none; }
.ms-bump[data-loading] { opacity: 0.6; pointer-events: none; }

.ms-bump__eyebrow {
  margin: 0 0 var(--p-space-2);
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-10); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-3);
}

.ms-bump__card {
  display: block; position: relative; cursor: pointer;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-md);
  background: var(--card);
  transition: border-color 180ms var(--ms-ease), background-color 180ms var(--ms-ease), box-shadow 180ms var(--ms-ease);
}
.ms-bump__card:hover { border-color: var(--primary); }
/* Solid border once accepted: the dashed edge is the "not yet decided" signal,
   so it must not survive the decision. */
.ms-bump.is-on .ms-bump__card {
  border-style: solid; border-color: var(--primary);
  background: color-mix(in oklab, var(--primary) 6%, var(--card));
  box-shadow: var(--elevation-whisper);
}

/* Visually replaced, never removed — a display:none input is unfocusable, and
   the keyboard path is the whole reason this is a real checkbox. */
.ms-bump__box {
  position: absolute; width: 1px; height: 1px;
  margin: calc(var(--p-space-px) * -1); padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden;
}
.ms-bump__box:focus-visible + .ms-bump__offer { outline: 2px solid var(--primary); outline-offset: 3px; border-radius: var(--radius-md); }

.ms-bump__offer { display: grid; grid-template-columns: 3.25rem 1fr; gap: var(--p-space-3); padding: var(--p-space-3); }
.ms-bump__media {
  border-radius: var(--radius-sm); overflow: hidden;
  background: var(--surface); aspect-ratio: 1;
}
.ms-bump__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ms-bump__body { display: flex; flex-direction: column; gap: 0.1875rem; min-width: 0; }
.ms-bump__title { font-size: var(--p-text-13); font-weight: 600; line-height: 1.3; color: var(--foreground); }
.ms-bump__pitch {
  font-size: var(--p-text-12); line-height: 1.4; color: var(--ink-3);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ms-bump__prices { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--p-space-1-5); margin-top: var(--p-space-0-5); }
.ms-bump__price {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-13); font-weight: 600; font-variant-numeric: tabular-nums;
  color: var(--foreground);
}
.ms-bump__was {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-11); font-variant-numeric: tabular-nums; color: var(--ink-3);
}
.ms-bump__save {
  font-size: var(--p-text-10); font-weight: 600; letter-spacing: 0.02em;
  padding: var(--p-space-0-5) var(--p-space-1-5); border-radius: var(--p-radius-pill);
  color: var(--data-accent);
  background: var(--data-accent-fill);
}

.ms-bump__cta {
  display: flex; align-items: center; gap: var(--p-space-2);
  padding: var(--p-space-2-5) var(--p-space-3);
  border-top: 1px dashed var(--border-strong);
  font-size: var(--p-text-13); font-weight: 600; color: var(--foreground);
}
.ms-bump.is-on .ms-bump__cta { border-top-style: solid; border-top-color: color-mix(in oklab, var(--primary) 30%, transparent); }
.ms-bump__check {
  display: grid; place-items: center; flex: none;
  width: 1.125rem; height: 1.125rem;
  border: 1.5px solid var(--border-strong); border-radius: var(--p-radius-4);
  background: var(--background); color: var(--primary-foreground);
  transition: background-color 160ms var(--ms-ease), border-color 160ms var(--ms-ease);
}
.ms-bump__check svg { width: 0.75rem; height: 0.75rem; opacity: 0; transform: scale(0.6); transition: opacity 160ms var(--ms-ease), transform 160ms var(--ms-ease); }
.ms-bump.is-on .ms-bump__check { background: var(--primary); border-color: var(--primary); }
.ms-bump.is-on .ms-bump__check svg { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .ms-bump__card, .ms-bump__check, .ms-bump__check svg { transition: none; }}

/* --- notify capture (founding-lot) --- */
.ms-notify__row { display: flex; gap: var(--p-space-2); flex-wrap: wrap; }
.ms-notify__input {
  flex: 1 1 14rem;
  min-width: 0;
  font-size: var(--p-text-16);              /* >=16px: below this iOS zooms the viewport */
  padding: var(--p-space-3) .9rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  /* Was `var(--ink-1)`, which is defined NOWHERE — the ramp is --ink-2/3/4 and
     the strongest ink is --foreground. An undefined custom property with no
     fallback makes the declaration invalid at computed-value time, so `color`
     fell through to inherit.
     It went unnoticed because the vertical's form baseline styled this input as
     `input[type="email"]` — (0,1,1), which outranked `.ms-notify__input` (0,1,0)
     and supplied a working colour on top. Putting that baseline in :where()
     (specificity 0, so a component always wins) is what exposed it. */
  color: var(--foreground);
}
.ms-notify__input:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.ms-notify__msg { margin: .6rem 0 0; font-size: var(--p-text-15); min-height: 1.25rem; }
[data-state="ok"]  .ms-notify__msg { color: var(--success); }
[data-state="err"] .ms-notify__msg { color: var(--destructive); }
.ms-notify__submit[data-loading] { opacity: .6; pointer-events: none; }

.pdp-atc--notify { display: block; }
.pdp-notify__lead { margin: 0 0 .9rem; color: var(--ink-2); text-wrap: pretty; }

.ms-notify__toast {
  position: fixed; inset-inline: 1rem; bottom: 1rem; z-index: 60;
  margin-inline: auto; max-width: 32rem;
  padding: .8rem var(--p-space-4); border-radius: var(--radius);
  /* --band-ink is #10131A. The label therefore has to be an ON-DARK role, and
     `var(--ink-1)` was not one — it was not anything, so the toast inherited
     body ink and put near-black text on a near-black ground. Nothing masked
     this one; the toast is simply unreachable until a SKU is out of stock or
     its lot is unproduced, which is why it survived.
     --on-dark-warm (cream) rather than --brand-foreground (white): it is what
     .btn--ink already uses on this exact ground. */
  background: var(--band-ink); color: var(--on-dark-warm);
  box-shadow: var(--elevation-pop);
}
.ms-notify__toast.is-err { border-left: 3px solid var(--destructive); }
.ms-notify__toast.is-ok  { border-left: 3px solid var(--success-tint); }

/* ==========================================================================
   FIXES — sticky chrome, hover bridge  (2026-08-16)
   ========================================================================== */

/* #2 STICKY HEADER, EVERY PAGE.
   `.ms-header` already declared position:sticky and it did nothing, because
   the block template wraps it in <header class="wp-block-template-part">.
   A sticky element sticks within its CONTAINING BLOCK, and that wrapper is
   only as tall as the header itself — so there was never any room to travel.
   Moving sticky up to the wrapper gives it the whole page to stick against.
   Scoped to the direct child of .wp-site-blocks so the footer part is
   untouched. */
.wp-site-blocks > header.wp-block-template-part {
  position: sticky;
  top: var(--ms-chrome-top, 0px);
  z-index: 50;
}
.ms-header { position: static; }

/* #2b THE ADMIN BAR EATS THE TOP OF THE STUCK HEADER.
   `#wpadminbar` is `position: fixed; top: 0` at z-index 99999, so a sticky
   header pinned at 0 mounts UNDERNEATH it and loses its first 32px — the
   wordmark reads as clipped and the header never looks like it lands flush.
   Only logged-in users ever see this, but the mount is wrong, not the admin
   bar. WP core publishes its own height (32px, 46px under 783px), so read that
   rather than restating it — the literal is a fallback for a WP that doesn't.
   Below 601px core switches the bar to position:absolute so it scrolls away
   with the page, and the offset has to go back to zero or the header floats
   32px clear of the top. Logged out, the property is undefined and this whole
   block resolves to the 0px it always was. */
body.admin-bar { --ms-chrome-top: var(--wp-admin--admin-bar--height, 32px); }
@media screen and (max-width: 600px) { body.admin-bar { --ms-chrome-top: 0px; } }

/* #1 falls out of the above: the shop toolbar is sticky at
   top: var(--ms-header-h) and was leaving that gap empty because nothing was
   actually occupying it. With the header genuinely stuck, the toolbar now
   docks directly beneath it. */

/* #5 HOVER BRIDGE FOR THE MEGA-MENU.
   The panel sits at `top: calc(100% + 0.75rem)`, so travelling from the Shop
   link to the panel crossed 12px of dead space, left .ms-navgroup, and fired
   mouseleave. This spans the gap with an invisible bridge that only exists
   while the menu is open, so it can never block clicks when closed. */
.ms-navgroup::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  width: 24rem;
  height: 1rem;
  transform: translateX(-50%);
  display: none;
}
.ms-navgroup.is-open::after { display: block; }

/* The marquee is now the first element on the page, above the sticky header.
   Both variants: the home ticker (.ms-marquee) and the PDP one (.pdp-marquee). */
.ms-marquee,
.pdp-marquee { position: relative; z-index: 51; }

/* ===========================================================================
   ENGINE-OWNED LAYOUT FOR ENGINE-RENDERED SURFACES
   ---------------------------------------------------------------------------
   ms_hero_sc(), ms_marquee_sc() and ms_cta_sc() live in the ENGINE, but until
   2026-08-19 the CSS that laid them out lived only in
   verticals/supplement/assets/home.css. The engine was rendering markup it
   did not style. The first non-supplement vertical shipped and the hero
   copy fell out from under its photograph, the marquee printed as two lines
   of raw text above the header, and its nowrap track pushed the whole page
   ~90px wider than the viewport.

   Rule restored: if the engine renders it, the engine lays it out. Verticals
   may retheme these (colours, motion, whether a glow exists at all) but must
   never be REQUIRED for the structure to hold. Everything here resolves from
   tokens; the only literals are ratios.
   =========================================================================== */

/* ---- hero: full-bleed photograph, copy on the page axis ---- */
.ms-hero3 {
  position: relative; overflow: clip; isolation: isolate;
  display: flex; align-items: center;
  min-height: min(88dvh, 46rem);                 /* dvh, never vh */
  background: var(--background);
}
.ms-hero3__bg { position: absolute; inset: -8% 0; z-index: 0; }
.ms-hero3__bg picture, .ms-hero3__bg img {
  display: block; width: 100%; height: 100%; object-fit: cover; object-position: 68% 50%;
}
.ms-hero3__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(100deg,
      var(--background) 0%,
      color-mix(in oklab, var(--background) 92%, transparent) 26%,
      color-mix(in oklab, var(--background) 55%, transparent) 44%,
      transparent 62%),
    linear-gradient(to top,
      color-mix(in oklab, var(--background) 55%, transparent) 0%, transparent 30%);
}
.ms-hero3__inner { position: relative; z-index: 3; width: 100%; }
.ms-hero3__copy {
  display: flex; flex-direction: column; align-items: flex-start;
  max-width: 34rem; padding-block: clamp(3.5rem, 9vh, 6rem);
}
.ms-hero3__h1 {
  margin: var(--p-space-5) 0 0;
  font-size: clamp(2.4rem, 4.6vw, 4rem);
  line-height: 1.03; letter-spacing: -0.035em; font-weight: 600;
  color: var(--foreground); text-wrap: balance;
}
.ms-hero3__accent { color: var(--brand); }
.ms-hero3__sub { margin: var(--p-space-5) 0 0; max-width: 30rem; color: var(--ink-2); }
.ms-hero3__cta { display: flex; flex-wrap: wrap; gap: var(--p-space-3); margin-top: var(--p-space-8); }
.ms-hero3__notes { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.ms-chipcard--n0 { right: max(4%, calc(50% - 30rem)); top: 26%; transform: rotate(-1.5deg); }
.ms-chipcard--n1 { right: max(6%, calc(50% - 26rem)); bottom: 24%; transform: rotate(1.25deg); }
@media (max-width: 1023px) { .ms-hero3__notes { display: none; } }
@media (max-width: 767px) {
  .ms-hero3 { min-height: auto; align-items: flex-start; }
  .ms-hero3__bg { inset: 0; }
  .ms-hero3__bg img { object-position: 50% 62%; }
  .ms-hero3__scrim {
    background: linear-gradient(to bottom,
      var(--background) 0%,
      color-mix(in oklab, var(--background) 88%, transparent) 34%,
      color-mix(in oklab, var(--background) 40%, transparent) 56%,
      transparent 78%);
  }
  .ms-hero3__copy { max-width: none; padding-block: var(--p-space-10) 22rem; }
  .ms-hero3__cta { align-self: stretch; flex-direction: column; }
  .ms-hero3__cta .btn { width: 100%; }
}

/* chip card — annotates the photographed product. Solid by default; a
   vertical that wants glass opts in (the skincare law bans blur outright). */
.ms-chipcard {
  position: absolute; display: flex; flex-direction: column; gap: 0.1875rem;
  padding: var(--p-space-2-5) var(--p-space-3-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 0.3rem);
  background: var(--background);
  box-shadow: var(--elevation-card);
}
.ms-chipcard__k {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--p-text-11); font-weight: 500; letter-spacing: var(--p-tracking-6); text-transform: uppercase;
  color: var(--ink-3);
}
.ms-chipcard__v { font-size: var(--p-text-14); font-weight: 600; letter-spacing: var(--p-tracking-n10); color: var(--foreground); white-space: nowrap; }
.ms-proofstrip--hero {
  justify-content: flex-start; width: auto; margin: var(--p-space-9) 0 0; padding: var(--p-space-5) 0 0;
  border-top: 1px solid var(--border-strong);
}

/* ---- marquee: thin band, seamless -50% loop ---- */
.ms-marquee {
  overflow: hidden; background: var(--ink-band);
  border-block: 1px solid color-mix(in oklab, var(--surface) 8%, transparent);
  padding-block: var(--p-space-3-5);
}
.ms-marquee__track { display: flex; width: max-content; animation: ms-marquee-scroll 30s linear infinite; }
.ms-marquee__seq { display: flex; align-items: center; flex-shrink: 0; }
.ms-marquee__item {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--p-text-12); font-weight: 500; letter-spacing: var(--p-tracking-6); text-transform: uppercase;
  color: color-mix(in oklab, var(--brand-foreground) 78%, transparent);
  white-space: nowrap;
}
.ms-marquee__dot {
  width: 0.25rem; height: 0.25rem; border-radius: var(--p-radius-pill); flex-shrink: 0;
  margin-inline: var(--p-space-6); background: var(--brand);
}
@keyframes ms-marquee-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .ms-marquee__track { animation: none; } }
@media (hover: hover) { .ms-marquee:hover .ms-marquee__track { animation-play-state: paused; } }

/* ---- CTA: the closing band carries its own photograph ---- */
.ms-cta--photo { overflow: hidden; }
.ms-cta__photo { position: absolute; inset: 0; z-index: 0; }
.ms-cta__photo::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(95deg,
    var(--surface-ink) 0%,
    color-mix(in oklab, var(--surface-ink) 88%, transparent) 34%,
    color-mix(in oklab, var(--surface-ink) 42%, transparent) 56%,
    transparent 78%);
}
.ms-cta__photo img { width: 100%; height: 100%; object-fit: cover; object-position: 72% 50%; display: block; }
.ms-cta--photo .ms-cta__copy { position: relative; z-index: 2; max-width: 34rem; }
.ms-cta--photo { grid-template-columns: 1fr; }
@media (min-width: 900px) { .ms-cta--photo { grid-template-columns: 1fr; min-height: 24rem; align-content: center; } }
@media (max-width: 767px) {
  .ms-cta__photo::after {
    background: linear-gradient(to bottom,
      var(--surface-ink) 0%,
      color-mix(in oklab, var(--surface-ink) 82%, transparent) 55%,
      color-mix(in oklab, var(--surface-ink) 55%, transparent) 100%);
  }
}
/* .ms-cta__glow is a vertical decision (a colored glow is banned for
   skincare); the engine gives it no paint, so it is inert unless themed. */
.ms-cta__glow { position: absolute; pointer-events: none; }
.ms-cta__copy { position: relative; }
