/* THE HEADER OWNS ITS OWN LOOK (owner, 2026-09-14).
   Every value below used to be a token that each page family redefined on
   `.hdr`: the shop set --hdr-font to Inter and --hdr-accent to forest, the
   generated pages set them to Schibsted Grotesk and #7FA733 — so the wordmark
   changed colour, font and position as a shopper moved between families. A
   shared component cannot be shared if its callers own its variables, so the
   values live here and the families no longer declare them. Anything that must
   differ (the page background behind the bar) is still a token. */
.hdr {
  --hdr-bg: #fcfcf7;
  --hdr-border: rgba(28,58,19,0.32);
  --hdr-ink: #1c3a13;                 /* the wordmark, everywhere */
  --hdr-muted: #5d6a56;
  --hdr-accent: #1c3a13;              /* the brand mark, never the light lime */
  --hdr-accent-strong: #1c3a13;
  /* The light lime is a BADGE ground, not the brand mark. Kept as its own
     token so nobody reaches for --hdr-accent (dark forest) to fill a chip and
     lands dark-on-dark, which is exactly what happened to the "Request
     availability" tag in the search suggestions on 2026-09-15. */
  --hdr-tag-req-bg: #d3fa99;
  --hdr-tag-req-ink: #1c3a13;
  --hdr-on-accent: #fcfcf7;
  --hdr-hover: #eeeee9;
  --hdr-font: Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
  --hdr-font-mono: "IBM Plex Mono", ui-monospace, monospace;
}
/* ============================================================================
   Celio Health — unified sticky header shell (2026-09-12).
   ONE component, `.hdr`, used by both page families:
     - the ~31 hand-written shop pages (style.css design system)
     - the ~830 generated pSEO pages (celio-content.css design system)
   Colour/font never live here — each stylesheet defines the --hdr-* custom
   properties once (see the `.hdr{...}` token block near the old nav rules in
   style.css / celio-content.css) so this file stays a single shared layout,
   not a second palette to keep in sync.
   Loaded by EVERY page. On pages without server-rendered `#hdr` markup,
   app.js's injectChrome() builds it and replaces whatever static nav shipped
   (#site-header / nav.nav / .cg-navwrap). The pSEO generator's topNav() in
   pseo/lib/html.js renders the same structure directly for new builds.
   ============================================================================ */
.hdr { position: sticky; top: 0; z-index: 60; background: var(--hdr-bg);
  border-bottom: 1px solid var(--hdr-border); font-family: var(--hdr-font); }
.hdr__bar { max-width: 1280px; margin: 0 auto; display: flex; align-items: center;
  gap: 16px; padding: 10px 20px; }
.hdr__menu { display: none; flex: 0 0 auto; background: none; border: 0; font-size: 20px;
  line-height: 1; color: var(--hdr-ink); cursor: pointer; padding: 8px; border-radius: 999px; }
.hdr__menu:hover { background: var(--hdr-hover); }
.hdr__brand { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; font-weight: 700;
  font-size: 19px; color: var(--hdr-ink); white-space: nowrap; text-decoration: none; }
.hdr__brand:hover { color: var(--hdr-ink); }
.hdr__dot { color: var(--hdr-accent); }
.hdr__search { flex: 1 1 auto; min-width: 0; max-width: 560px; }
.hdr__search-input { width: 100%; min-width: 0; box-sizing: border-box; padding: 10px 16px;
  border: 1px solid var(--hdr-border); border-radius: 999px; background: var(--hdr-bg);
  color: var(--hdr-ink); font-family: var(--hdr-font); font-size: 14.5px; }
.hdr__search-input::placeholder { color: var(--hdr-muted); }
.hdr__search-input:focus-visible { outline: 2px solid var(--hdr-accent); outline-offset: 1px; }
.hdr__links { flex: 0 0 auto; display: flex; align-items: center; gap: 18px; list-style: none;
  margin: 0; padding: 0; font-size: 14.5px; white-space: nowrap; }
.hdr__links a, .hdr__dd-btn { color: var(--hdr-ink); text-decoration: none; background: none;
  border: 0; font: inherit; cursor: pointer; padding: 6px 2px; }
.hdr__links a:hover, .hdr__dd-btn:hover { color: var(--hdr-accent); }
.hdr__dd { position: relative; }
.hdr__dd-menu { position: absolute; top: calc(100% + 10px); left: 0; min-width: 220px;
  background: var(--hdr-bg); border: 1px solid var(--hdr-border); border-radius: 14px;
  padding: 8px; display: none; flex-direction: column;
  box-shadow: 0 16px 40px -20px rgba(0,0,0,.35); }
.hdr__dd:hover .hdr__dd-menu, .hdr__dd:focus-within .hdr__dd-menu { display: flex; }
.hdr__dd-menu a { padding: 9px 12px; border-radius: 9px; white-space: nowrap; }
.hdr__dd-menu a:hover { background: var(--hdr-hover); }
/* margin-left:auto is what makes this a right-hand CLUSTER. On desktop the
   search field absorbs the slack, but it is display:none on mobile, so
   without this the controls packed against the wordmark and left ~93px of
   dead space to their right on a 390px phone. */
.hdr__right { flex: 0 0 auto; display: flex; align-items: center; gap: 10px; margin-left: auto; }
/* The cart is the rightmost control, always, whatever else is present. */
.hdr__right .hdr__state { order: 1; }
.hdr__right .hdr__account { order: 2; }
.hdr__right .hdr__cart { order: 3; }
.hdr__state { display: inline-flex; align-items: center; gap: 4px; background: none;
  border: 1px solid var(--hdr-border); border-radius: 999px; padding: 7px 12px;
  font: inherit; font-size: 13.5px; color: var(--hdr-ink); cursor: pointer; white-space: nowrap; }
.hdr__state:hover { border-color: var(--hdr-accent); }
.hdr__state--warn { border-color: #b5642f; color: #b5642f; }
/* Fixed box so the digit count never reflows the bar around it. */
.hdr__cart { position: relative; flex: 0 0 auto; width: 40px; height: 40px; display: inline-flex;
  align-items: center; justify-content: center; border-radius: 999px; font-size: 18px;
  color: var(--hdr-ink); text-decoration: none; }
.hdr__cart:hover { background: var(--hdr-hover); }
.hdr__cart-badge { position: absolute; top: 1px; right: 1px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 999px; background: var(--hdr-accent-strong); color: var(--hdr-on-accent);
  font-family: var(--hdr-font-mono); font-size: 10px; line-height: 16px; text-align: center;
  box-sizing: border-box; }
.hdr__cart-badge[data-empty="1"] { display: none; }
.hdr__account { flex: 0 0 auto; font-size: 14px; white-space: nowrap; }
.hdr__account a { color: var(--hdr-ink); }
.hdr__mobile-search { display: none; }
.hdr__mobilepanel { display: none; }

@media (max-width: 900px) {
  .hdr__links, .hdr__account, .hdr__search { display: none; }
  .hdr__menu { display: inline-flex; align-items: center; justify-content: center; }
  .hdr__bar { padding: 8px 12px; gap: 8px; }
  .hdr__mobile-search { display: block; padding: 0 12px 10px; }
  .hdr__mobile-search .hdr__search-input { width: 100%; }
  .hdr__state { padding: 6px 9px; font-size: 12.5px; }
  .hdr.is-open .hdr__mobilepanel { display: flex; }
  .hdr__mobilepanel { position: absolute; top: 100%; left: 0; right: 0; background: var(--hdr-bg);
    border-bottom: 1px solid var(--hdr-border); flex-direction: column; padding: 8px 12px 14px;
    max-height: calc(100vh - 56px); overflow-y: auto; }
  .hdr__mobilepanel a, .hdr__mobilepanel button { display: block; width: 100%; text-align: left;
    padding: 12px 10px; border-radius: 10px; color: var(--hdr-ink); text-decoration: none;
    background: none; border: 0; font: inherit; font-size: 15px; }
  .hdr__mobilepanel a:hover, .hdr__mobilepanel button:hover { background: var(--hdr-hover); }
  .hdr__mp-divider { border-top: 1px solid var(--hdr-border); margin: 6px 0; }
}
@media (max-width: 360px) {
  .hdr__brand { font-size: 16px; }
  .hdr__bar { gap: 6px; padding: 8px 10px; }
}

/* Below-header notice for a platform-unsupported collection state (NJ/NY/RI
   only — product-level restrictions stay on the product/cart line). */





/* Glyph-independent marks (2026-09-13). The brand flower and the cart were a
   ❋ character and a 🛒 emoji: both lay out fine and paint nothing where the
   font lacks them, which left an empty 40px box where the cart should be. The
   site already draws the flower as a CSS dot elsewhere; the cart is now an
   inline SVG that inherits currentColor. */
.hdr__dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  background: var(--color-lime, #d3fa99); font-size: 0; line-height: 0; }
.hdr__cart-ico { display: block; pointer-events: none; }


/* THE ONE definition of the unsupported-state blocker. It lived in three files
   — here, style.css and celio-content.css — and this one, loaded last, quietly
   won: the alert kept a 20px gutter while the bar switched to 12px on a phone,
   so the two never lined up anywhere the max-width was not the constraint. */
.hdr-notice { background: #fbeceb; border-bottom: 1px solid #d9a29b; color: #8f2f21;
  font-size: 13.5px; padding: 0; }
.hdr-notice__inner { max-width: 1280px; margin: 0 auto; display: flex; align-items: baseline;
  gap: 10px; flex-wrap: wrap; padding: 9px 20px; }
.hdr-notice__icon { flex: 0 0 auto; }
.hdr-notice__text { flex: 1 1 260px; min-width: 0; line-height: 1.45; }
.hdr-notice__change { background: none; border: 0; padding: 0; font: inherit; color: inherit;
  font-weight: 500; text-decoration: underline; cursor: pointer; flex: 0 0 auto; }
.hdr-notice__change:hover { text-decoration: none; }
/* Mirror EVERY step the bar takes, not just the first: the bar narrows again
   at 360px, and an alert that did not follow sat 2px inside it there. */
@media (max-width: 900px) { .hdr-notice__inner { padding: 8px 12px; } }
@media (max-width: 360px) { .hdr-notice__inner { padding: 8px 10px; } }


/* One geometry and one type scale, stated absolutely so a page's own base
   font-size cannot resize the bar underneath the shared component. */
.hdr__bar { min-height: 60px; box-sizing: border-box; }
.hdr__brand { font-size: 19px; line-height: 1.2; font-weight: 700;
  font-family: var(--hdr-font); letter-spacing: -0.01em; color: var(--hdr-ink); }
.hdr__dot { background: #d3fa99; }
.hdr, .hdr input, .hdr button, .hdr a { font-family: var(--hdr-font); }
.hdr__links a, .hdr__dd-btn, .hdr__state, .hdr__search-input { font-size: 14.5px; line-height: 1.3; }


/* A page family styling "every anchor" must not reach inside the shared header:
   `.cg a { color: var(--cg-lime-deep) }` outranks `.hdr__brand` on specificity
   alone, which is why the wordmark turned light green on generated pages. The
   header's own rules are scoped through `.hdr` so they win on specificity
   rather than on !important, and a family can still theme what it owns. */
.hdr .hdr__brand, .hdr .hdr__brand:hover, .hdr .hdr__brand:visited { color: var(--hdr-ink); }
.hdr .hdr__links a, .hdr .hdr__dd-menu a, .hdr .hdr__mobilepanel a,
.hdr .hdr__account a, .hdr .hdr__cart { color: var(--hdr-ink); }
.hdr .hdr__links a:hover, .hdr .hdr__dd-menu a:hover { color: var(--hdr-accent-strong); }
.hdr .hdr-notice__change { color: inherit; }

/* SEARCH IS ALWAYS VISIBLE ON MOBILE (owner, 2026-09-14).
   It briefly lived inside the hamburger menu, which produced two search
   experiences and hid the one people actually reach for. There is now exactly
   one mobile entry point: the persistent second row below the top bar. The
   homepage hero search stays, deliberately -- that one is a discovery CTA,
   not navigation. */
.hdr__mp-search{display:none !important}


/* ---------------------------------------------------------------- mobile
   The active/pressed treatment was the browser default -- Android Chrome
   paints rgba(51,181,229,.4) over any tapped link, which is a bright cyan
   that exists nowhere in the Celio palette. Suppressed, and replaced with the
   cream/green press and selected states the rest of the product uses. */
.hdr a, .hdr button { -webkit-tap-highlight-color: transparent; }
.hdr__mobilepanel a:active, .hdr__mobilepanel button:active,
.hdr__menu:active, .hdr__state:active, .hdr__cart:active { background: var(--hdr-hover); }
/* The page you are on, said in the brand's own colours rather than a browser
   default: cream ground, forest text, and a forest rule down the leading edge. */
.hdr__mobilepanel a[aria-current="page"],
.hdr__links a[aria-current="page"] {
  background: var(--hdr-hover);
  color: var(--hdr-ink);
  font-weight: 700;
}
.hdr__mobilepanel a[aria-current="page"] {
  box-shadow: inset 3px 0 0 var(--hdr-accent-strong);
}
.hdr__mobilepanel a:focus-visible, .hdr__mobilepanel button:focus-visible {
  outline: 2px solid var(--hdr-accent-strong); outline-offset: -2px;
}
/* Account actions in the menu read as one group, under the divider. */
#nav-account-m { display: flex; flex-direction: column; }

@media (max-width: 900px) {
  /* The second row: one full-width search field, always present. */
  .hdr__mobile-search { display: block; padding: 0 12px 10px; }
  .hdr__mobile-search .hdr__search-input { width: 100%; }
  /* The wordmark yields before the action cluster does. */
  .hdr__brand { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  /* A state button whose label grows ("State" -> "NY ⚠") must not push the
     cart off the edge; it truncates instead. */
  .hdr__state { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; }
}
@media (max-width: 400px) {
  .hdr__bar { gap: 6px; padding: 8px 10px; }
  .hdr__mobile-search { padding: 0 10px 9px; }
  .hdr__state { padding: 6px 8px; font-size: 12px; }
  .hdr__menu { padding: 6px; }
}
@media (max-width: 340px) {
  .hdr__brand { font-size: 15px; }
  .hdr__state { max-width: 34vw; }
}

/* The cart and menu controls are inline SVG, not emoji. The emoji versions
   were invisible anywhere an emoji font is missing -- which is how a cart
   icon came to be missing from the header in the first place. Sized here so
   the button box never depends on glyph metrics. */
.hdr__menu svg, .hdr__cart-ico { display: block; }


/* ============================== SEARCH SUGGESTIONS ==========================
   These rules lived in style.css, which only the homepage and the shop pages
   load. The header -- and therefore the search box and its dropdown -- is on
   EVERY page, so on authored product pages the suggestion list rendered with
   no styling at all: transparent, position:static, no z-index, metadata run
   together. It was not an intermittent glitch; those pages simply never had
   the stylesheet. header.css is the one file every page loads, so the
   presentation rules for a global component belong here.
   ========================================================================== */
/* Self-contained on the HEADER's own tokens. The rules previously leaned on
   --color-hairline / --color-stone / --color-lime, which are declared in
   style.css -- so simply moving them here would have left the dropdown
   borderless and background-less on every page that does not load that file.
   A global component cannot depend on a page-family stylesheet, for colour any
   more than for layout. */
.cs-wrap { position: relative; display: block; flex: 1 1 auto; min-width: 0; }
.toolbar .cs-wrap { flex: 1; }
.toolbar .cs-wrap .toolbar__search { width: 100%; }
.vhero__search .cs-wrap { flex: 1 1 auto; }
.vhero__search .cs-wrap .vhero__searchinput { width: 100%; }
.cs-suggest { position: absolute; z-index: 900; left: 0; right: 0; top: calc(100% + 6px);
  background: var(--hdr-bg, #fcfcf7); border: 1px solid var(--hdr-border, #e3e2d3);
  border-radius: 16px; overflow: hidden auto; max-height: min(60vh, 420px);
  box-shadow: 0 12px 32px rgba(20,30,20,.12); text-align: left;
  font-family: var(--hdr-font, inherit); color: var(--hdr-ink, #22381e); }
.cs-item, .cs-all { display: flex; align-items: baseline; gap: 12px; justify-content: space-between;
  padding: 10px 14px; cursor: pointer; font-size: 15px; line-height: 1.35;
  color: var(--hdr-ink, #22381e); text-decoration: none; }
.cs-item + .cs-item, .cs-all { border-top: 1px solid var(--hdr-border, #e3e2d3); }
.cs-item.is-active, .cs-all.is-active, .cs-item:hover, .cs-all:hover {
  background: var(--hdr-hover, #eeeee9); }
.cs-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The metadata is a row of separate facts, not one run-on string. */
.cs-meta { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.cs-kind { font-size: 12px; opacity: .6; }
.cs-price { font-size: 14px; font-weight: 600; }
.cs-tag { font-size: 12px; padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--hdr-border, #e3e2d3); white-space: nowrap; }
.cs-tag--req { background: var(--hdr-tag-req-bg, #d3fa99);
  color: var(--hdr-tag-req-ink, #1c3a13); border-color: transparent; }
.cs-tag--off { opacity: .65; }
.cs-all { font-size: 13.5px; opacity: .85; }
/* Non-interactive message row for a no-match search: never active/hoverable,
   never bordered like the option rows above it. */
.cs-empty { padding: 10px 14px; font-size: 14px; color: var(--hdr-ink, #22381e);
  opacity: .8; cursor: default; }
/* The dropdown must clear the header and the sticky page chrome everywhere. */
.hdr .cs-suggest { z-index: 200; }

/* ========================== DIALOGS (state + request) =======================
   ONE definition, in the one stylesheet every page loads. There were two --
   16 rules in style.css and 21 in celio-content.css -- so the same dialog had
   two designs depending on which page family opened it.

   The crowding was structural, not decorative: the title, the product name,
   the collection state and the email label were four different typographic
   treatments stacked with no rhythm, and the email label sat INLINE beside
   its input so the field was squeezed into what was left of the row. This
   gives the dialog one vertical rhythm, a labelled row pattern that matches
   the purchase card, and a stacked field.
   ========================================================================== */
.celio-modal-open { overflow: hidden; }
.celio-statemodal { position: fixed; inset: 0; z-index: 1000;
  background: rgba(18, 28, 16, .52);
  display: flex; align-items: center; justify-content: center; padding: 24px; }
.celio-statemodal__box { background: #fff; color: var(--hdr-ink, #22381e);
  border-radius: 20px; padding: 28px 28px 24px; width: 100%; max-width: 420px;
  box-shadow: 0 24px 60px -16px rgba(12, 24, 10, .45);
  font-family: var(--hdr-font, inherit); text-align: left; }
.celio-statemodal__box h3 { margin: 0; font-size: 20px; line-height: 1.25;
  font-weight: 700; letter-spacing: -.01em; }
/* The product is context for the title, not a second heading. */
.celio-reqmodal__product { margin: 6px 0 0; font-size: 14px; line-height: 1.45;
  color: var(--hdr-muted, #5E6656); }

/* The state reads as a fact with a quiet action, the same shape as a row in
   the purchase card, instead of a sentence with a button wedged into it. */
.celio-reqmodal__state { margin: 20px 0 0; border-top: 1px solid var(--hdr-border, #e3e2d3);
  border-bottom: 1px solid var(--hdr-border, #e3e2d3); }
.celio-reqmodal__known { display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; padding: 12px 0; font-size: 14px; }
.celio-reqmodal__known b { font-weight: 600; }
.celio-reqmodal__state .btn, .celio-reqmodal__change { background: none; border: 0; padding: 0;
  font: inherit; font-size: 13.5px; font-weight: 600; color: var(--hdr-accent-strong, #1c3a13);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer; flex: none; }

/* Stacked label, full-width control. */
.celio-statemodal__field { display: block; margin: 18px 0 0; }
.celio-reqmodal__label { display: block; margin: 0 0 6px; font-size: 13px; font-weight: 600;
  color: var(--hdr-muted, #5E6656); }
.celio-statemodal__field input, .celio-statemodal__field select,
.celio-statemodal select { width: 100%; box-sizing: border-box; padding: 11px 13px;
  border: 1px solid var(--hdr-border, #e3e2d3); border-radius: 12px;
  font: inherit; font-size: 15px; background: #fff; color: var(--hdr-ink, #22381e); }
/* The dropdown arrow is one of the surfaces we did not draw. The platform one
   is grey and square in a dialog built out of forest and pills, so it gets the
   same chevron the purchase card uses. The control stays a native <select>:
   only its arrow is ours. */
.celio-statemodal select { appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 40px; border-radius: 12px;
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%231c3a13' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 16px 16px; }
.celio-statemodal__field input:focus-visible, .celio-statemodal select:focus-visible {
  outline: 2px solid var(--hdr-accent-strong, #1c3a13); outline-offset: 1px; border-color: transparent; }
.celio-reqmodal__msg { margin: 12px 0 0; font-size: 13.5px; line-height: 1.5;
  color: var(--cg-alert-head, #7A3D18); }

/* ---- what the dialog needs, beside the dialog -----------------------------
   The markup reached for .h-sm, .form-note and .alert, which exist only in
   style.css. The hand-written pages load that file; the 1128 generated
   product pages load celio-content.css instead -- so on the pages where a
   shopper is most likely to open this dialog, the helper line and the refusal
   rendered as plain unstyled body text and the refusal lost its box entirely.
   A dialog that app.js injects into either page family has to carry its own
   typography, so every class it uses is defined here now, scoped to it.
   Specificity is two classes deep on purpose: it must win over style.css
   wherever that file happens to load later. */
.celio-statemodal .form-note { margin: 6px 0 0; font-size: 14px; line-height: 1.5;
  color: var(--hdr-muted, #5d6a56); }
.celio-statemodal .celio-statemodal__alert { margin: 14px 0 0; padding: 12px 14px;
  border-radius: 12px; font-size: 14px; line-height: 1.5;
  background: var(--cg-alert-bg, #FBEEE6); border: 1px solid var(--cg-alert-border, #E7C9B4);
  color: var(--cg-alert-body, #6E4223); }
.celio-statemodal .celio-statemodal__alert[hidden] { display: none; }
.celio-statemodal__alert b { display: block; margin: 0 0 3px; font-weight: 700;
  color: var(--cg-alert-head, #7A3D18); }

/* Clearing the state is a way back out, not a third decision, so it stays off
   the action row -- three controls abreast made "Clear collection state" and
   "Continue browsing" both wrap inside a 420px card -- and reads as the quiet
   action it is: the same underlined link the request dialog uses, not the
   browser-default button it used to be. */
.celio-statemodal__clearrow { margin: 14px 0 0; text-align: left; }
.celio-statemodal__clear { background: none; border: 0; padding: 0;
  font: inherit; font-size: 14px; font-weight: 600; color: var(--hdr-muted, #5d6a56);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.celio-statemodal__clear:hover { color: var(--hdr-ink, #22381e); }
.celio-statemodal__clear:focus-visible { outline: 2px solid var(--hdr-accent-strong, #1c3a13);
  outline-offset: 3px; border-radius: 4px; }

.celio-statemodal__actions { display: flex; align-items: center; gap: 10px;
  justify-content: flex-end; margin: 24px 0 0; }
.celio-statemodal .btn { display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 20px; border-radius: 999px; border: 1px solid transparent;
  font: inherit; font-size: 15px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.celio-statemodal .btn--primary { background: var(--hdr-accent-strong, #1c3a13); color: #fff; }
.celio-statemodal .btn--primary:hover { filter: brightness(1.15); }
.celio-statemodal .btn--ghost { background: transparent; color: var(--hdr-ink, #22381e);
  border-color: var(--hdr-border, #e3e2d3); }
.celio-statemodal .btn--ghost:hover { background: var(--hdr-hover, #eeeee9); }
.celio-statemodal .btn:focus-visible { outline: 2px solid var(--hdr-accent-strong, #1c3a13);
  outline-offset: 2px; }

/* One authored moment: the card arrives, it does not blink into place. The
   sheet comes up from the edge it is attached to. Both start from a state that
   is already legible, so a reader who prefers no motion loses nothing. */
@keyframes celio-modal-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes celio-modal-rise { from { opacity: 0; transform: translateY(10px) scale(.985); }
  to { opacity: 1; transform: none; } }
@keyframes celio-modal-sheet { from { transform: translateY(100%); } to { transform: none; } }
.celio-statemodal { animation: celio-modal-fade 140ms ease-out both; }
.celio-statemodal__box { animation: celio-modal-rise 260ms cubic-bezier(.16, 1, .3, 1) both; }

@media (max-width: 560px) {
  .celio-statemodal { padding: 0; align-items: flex-end; }
  .celio-statemodal__box { max-width: none; border-radius: 20px 20px 0 0;
    padding: 24px 20px max(20px, env(safe-area-inset-bottom));
    animation-name: celio-modal-sheet; }
  /* Full-width on a sheet, because two pills sharing a 390px row made
     "Continue browsing" wrap to two lines beside a one-line "Cancel".
     column-reverse puts the primary action at the thumb, over Cancel, with
     the quiet Clear link last. */
  .celio-statemodal__actions { flex-direction: column-reverse; align-items: stretch;
    gap: 8px; margin-top: 20px; }
  .celio-statemodal__actions .btn { width: 100%; }
  .celio-statemodal__clearrow { margin-top: 16px; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  .celio-statemodal, .celio-statemodal__box { animation: none; }
}


/* ---- Toast: the confirmation after Add to cart ----------------------------
   This lives here, beside the autocomplete and the state dialog, because it is
   the same kind of thing: markup that app.js injects into whatever page is on
   screen. There are two page families -- the hand-written pages load
   style.css, the generated product and category pages load celio-content.css
   and pdp.css -- and header.css is the only stylesheet both of them load.

   It was defined only in style.css. So "Vitamin D 25-Hydroxy added to cart"
   appeared on the homepage and the test menu, and on a product detail page the
   same element was created with the same text and the same .show class, got no
   styling at all, and rendered as plain text at the end of the document, 2,149px
   down and below the footer. The shopper saw nothing -- on the page where they
   are most likely to be adding to the cart.

   Tokens are --hdr-* with literal fallbacks, like everything else here, so the
   rule does not depend on a variable the other family happens to define. */
.toast {
  position: fixed; bottom: 24px; left: 50%;
  transform: translateX(-50%) translateY(120%);
  background: var(--hdr-ink, #1c3a13); color: var(--hdr-bg, #fcfcf7);
  padding: 12px 22px; border-radius: 999px;
  font-family: var(--hdr-font-mono, "IBM Plex Mono", ui-monospace, monospace);
  font-size: 12px; letter-spacing: .015em;
  z-index: 1100;                 /* above the state dialog's 1000 */
  transition: transform .3s ease;
  max-width: calc(100vw - 32px); text-align: center;
}
.toast.show { transform: translateX(-50%) translateY(0); }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }

/* ---- `hidden` must actually hide -----------------------------------------
   The UA rule is [hidden]{display:none} at specificity (0,1,0), so ANY author
   rule that sets display on a class outranks it. .pcard{display:flex} did
   exactly that: four blog cards carried the hidden attribute and were painted
   anyway, so the whole archive was on screen and Load More appeared to do
   nothing but remove itself. Toggling el.hidden is the pattern this codebase
   uses for the condition tabs and the blog batches; this is the rule that
   makes the attribute mean what it says, whatever display its element has. */
[hidden] { display: none !important; }

/* ---- Add to cart: the cart catches, the badge answers ---------------------
   The click happens down the page; the badge lives in the header, usually
   outside where the shopper is looking. So this is two coordinated movements,
   not one pulse: the CART GLYPH dips as though it caught something, and the
   BADGE answers ~90ms later. Sequencing them reads as cause and effect --
   "your item went in HERE" -- where a single pulse only says "something
   changed somewhere".

   The badge overshoots and settles (1 -> 1.38 -> 0.94 -> 1.06 -> 1) rather
   than easing symmetrically. A symmetric pulse reads as a notification badge
   nagging for attention; an overshoot with a small undershoot reads as
   something physical arriving and coming to rest.

   The digit is swapped at the PEAK of that pop, in JS, not before it. If the
   number changes first, the eye arrives after the information has already
   settled; changing it at peak scale means it flips at its largest and most
   legible, exactly as the eye lands.

   0 -> 1 is a different event and gets a different animation. The badge does
   not exist yet -- it is display:none -- so it should ARRIVE (scale up from
   nothing with a slight downward drift) rather than react. Popping an element
   that was never on screen looks like a glitch.

   The halo is what carries to the corner of the eye: scale alone is too small
   a signal at 16px. It dies in 420ms so it never nags. */

.hdr__cart-badge { transform-origin: 50% 50%; }
.hdr__cart-badge::after {
  content: ""; position: absolute; inset: -1px; border-radius: 999px;
  border: 1.5px solid var(--hdr-accent-strong, #1c3a13);
  opacity: 0; pointer-events: none;
}

@keyframes hdr-cart-bump {
  0%   { transform: scale(1); }
  28%  { transform: scale(1.38); }
  55%  { transform: scale(.94); }
  78%  { transform: scale(1.06); }
  100% { transform: scale(1); }
}
@keyframes hdr-cart-arrive {
  0%   { transform: scale(.2) translateY(-5px); opacity: 0; }
  55%  { transform: scale(1.26) translateY(0);  opacity: 1; }
  80%  { transform: scale(.96); }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes hdr-cart-halo {
  0%   { opacity: .5;  transform: scale(1); }
  100% { opacity: 0;   transform: scale(2.7); }
}
/* The cart tips as it catches, then rights itself. Small: 6 degrees and 2px.
   Anything larger stops reading as weight and starts reading as a wobble. */
@keyframes hdr-cart-catch {
  0%   { transform: translateY(0)    rotate(0deg); }
  30%  { transform: translateY(2px)  rotate(-6deg); }
  62%  { transform: translateY(-1px) rotate(3deg); }
  100% { transform: translateY(0)    rotate(0deg); }
}

.hdr__cart-badge.is-bumping  { animation: hdr-cart-bump 420ms cubic-bezier(.34,1.56,.64,1) both; }
.hdr__cart-badge.is-arriving { animation: hdr-cart-arrive 460ms cubic-bezier(.34,1.56,.64,1) both; }
.hdr__cart-badge.is-bumping::after,
.hdr__cart-badge.is-arriving::after { animation: hdr-cart-halo 420ms ease-out both; }
.hdr__cart.is-catching .hdr__cart-ico { animation: hdr-cart-catch 360ms cubic-bezier(.36,.07,.19,.97) both; }

/* Motion is the whole mechanism here, so there is nothing to soften -- it is
   removed. The count still updates and the toast still announces it, which is
   the information the animation was carrying. */
@media (prefers-reduced-motion: reduce) {
  .hdr__cart-badge.is-bumping,
  .hdr__cart-badge.is-arriving,
  .hdr__cart-badge.is-bumping::after,
  .hdr__cart-badge.is-arriving::after,
  .hdr__cart.is-catching .hdr__cart-ico { animation: none !important; }
}

/* ============================================================================
   TOUCH TARGETS >= 44x44 (WCAG 2.5.5), header height unchanged.
   Each control gets real padding so its OWN measured box (getBoundingClientRect,
   border-box, no margin) clears 44px; an equal negative margin removes that
   same padding from the flex row's own sizing, so `.hdr__bar`'s min-height
   is never asked to grow. Only `.hdr__cart`'s fixed box grows by 4px (40->44)
   and is pulled back with -2px vertical margin for the same reason. */
.hdr__links a, .hdr__dd-btn { padding: 13px 8px; margin: -7px -6px; }
.hdr__account { display: inline-flex; }
.hdr__account a { display: inline-flex; align-items: center; padding: 13px 8px; margin: -13px -4px; }
.hdr__cart { width: 44px; height: 44px; margin: -2px -2px; } /* 44px hit area, 40px in flow: at 320px the extra 4px pushed the bar past the edge */
.hdr__state { min-height: 44px; margin: -4.5px 0; }

@media (max-width: 900px) {
  .hdr__menu { padding: 12px; margin: -6px; }
  .hdr__state { min-height: 44px; margin: -7px 0; }
}

/* At 320px the wider hit areas left the right cluster 1px past the edge and
   the cart's negative margin 2px more. Tighter gaps there; the targets keep
   their 44px. */
@media (max-width: 360px) {
  .hdr__right { gap: 6px; }
  .hdr__cart { margin-right: 0; }
}

/* Two targets the audit pass left a pixel short: the account link on the
   generated pages at 320px (43px) and the wordmark (18px tall). Hit area
   only; nothing visible grows. */
.hdr__account a { display: inline-flex; align-items: center; min-height: 44px; }
.hdr__brand { padding: 13px 0; margin: -13px 0; display: inline-flex; align-items: center; }
