/* ============================================================================
   PASTEL LAYER - On Top Labs
   ---------------------------------------------------------------------------
   Brandon, 2026-07-28: "every section must have this pastel esc. aesthetic" -
   the soft look of the product grid (pastel area washes, rounded cards, navy
   ink) extended to EVERY section of the site. This replaces the retired ruled
   identity experiment (identity.css, unlinked the same day; kept on disk only
   as a record).

   HOW IT WORKS
   Loads AFTER every other stylesheet on all 106 pages (tools/link-pastel.js).
   Two mechanisms, in order of coverage:

   1. THE DARK BANDS BECOME PASTEL BY TOKEN REMAP. `.band-dark` works by
      re-mapping the role tokens (--bg/--ink/--line...) to a --d-* dark set, and
      every band on every page - including the PDP flagship band product.js
      renders - reads those tokens. Redefining the --d-* set to a periwinkle
      wash with navy ink converts every dark band in one place, structure
      untouched.

   2. SECTION WASHES. Plain `.section` blocks rotate through four soft tints
      (mint / periwinkle / cream / blush) so the page reads as stacked pastel
      panels rather than one white sheet. The rotation is nth-of-type so it
      needs no markup changes and stays stable per page.

   Ink stays the engine's navy-black; buttons/pills/cards are untouched (they
   already carry the target aesthetic). Navy-on-tint measures >=12:1 on every
   wash below, so nothing here can create a contrast failure.

   Rollback: node tools/link-pastel.js --revert
   ========================================================================== */

:root {
  /* Section ground. --wash-peri is still live: the dark-band remap and the
     footer both use it. --wash-mint/-cream/-blush are retired with the section
     rotation (the cream one read as dirty yellow behind the catalog) and are
     kept only so any stray call site resolves to the neutral ground rather
     than to nothing. */
  --wash-mint:#fafafa;
  --wash-peri:#f0f4fe;
  --wash-cream:#fafafa;
  --wash-blush:#fafafa;
  /* deeper companion tints for chips/edges inside washes */
  --wash-line:rgba(13,20,36,.08);
}

/* ---- 1. dark bands -> periwinkle pastel ---------------------------------- */
/* The remap trick: .band-dark { --bg:var(--d-bg); ... } is already in
   styles.css, so ONLY the --d-* values need to change. Navy ink on the
   periwinkle wash: 14.9:1 body, links keep their underlines. */
:root {
  /* Lightened 2026-07-29 (Brandon): #e8ecfb read as a distinct slab against the
     #fafafa page ground. This sits much closer to the page while staying
     unmistakably tinted - cool enough to separate the band, light enough that
     the page reads as one surface rather than stacked panels.
     ⚠️ Navy ink (#0d1424) on this measures ~16:1, so the lightening cannot
     create a contrast failure. Do not push past ~#f6f8fe or the band stops
     being visible against #fafafa at all. */
  --d-bg:#f1f4fd;                       /* was #050505, then #e8ecfb */
  --d-surface:#ffffff;
  --d-surface-2:rgba(13,20,36,.05);
  --d-ink:#0d1424;
  --d-ink-2:#3a4358;
  --d-ink-3:#5c6579;
  --d-line:rgba(13,20,36,.12);
  --d-line-2:rgba(13,20,36,.18);
}
/* accent-on-dark was a pale sky (#93c5fd) chosen FOR black bands; on a pastel
   band it disappears (1.4:1). Some rules read --accent-ink, others reach for
   --accent-dark directly (the FDA band heading does), so both are remapped. */
.band-dark { --accent-ink:var(--accent); --accent-dark:var(--accent); --brand-sky:var(--accent); }
/* ^ --brand-sky included because the FDA band's heading is INLINE-styled
   color:var(--brand-sky) in the markup of every page - a custom property is the
   one thing that can reach through an inline style, so it is remapped per-band
   rather than chased across 106 files. */

/* the dot-grid overlay on dark bands was white dots + a royal glow tuned for
   black; re-tune for the pastel field */
.band-dark.grid-bg::before {
  background:
    radial-gradient(rgba(13,20,36,.10) 1px, transparent 1px),
    radial-gradient(640px 320px at 50% 0%, rgba(32,49,145,.10), transparent 70%);
  background-size:22px 22px, 100% 100%;
  opacity:1;
}

/* logos inside formerly-dark chrome were knocked out to white; undo */
.footer-brand .logo-mark { filter:none; }
.footer-brand .brand-logo__name { color:var(--ink); }

/* ---- hero headings on the pastel bands -----------------------------------
   bundles / featured / new-lots / stack each hardcode `style="color:#fff"` on
   their <h1> (and a couple of inline links) in the MARKUP - written when these
   bands were near-black. On the pastel band that is white-on-pale-lavender:
   effectively invisible, which is what "fix the white text" refers to.

   Inline styles beat stylesheets, so these are the one place `!important` is
   warranted - the alternative is hand-editing 5 spots across 4 files that a
   regeneration could reintroduce.

   ⚠️ Brand blue, NOT a pale blue. On the #e8ecfb band the brand royal measures
   9.9:1; a genuinely light blue such as #93c5fd measures about 1.9:1 and would
   be as unreadable as the white it replaces. "Lighter" here means lighter than
   the near-black body ink, which this is. */
.band-dark h1[style*="#fff"],
.band-dark h2[style*="#fff"],
.band-dark [style*="color:#fff"] {
  color:var(--accent) !important;
}
/* inline links inside those leads read as links, not as body copy */
.band-dark .lead a[style*="#fff"] {
  color:var(--accent) !important;
  text-decoration:underline; text-underline-offset:3px;
}

/* the DNA verify band hardcodes white for its headline stats (styles.css
   .proof-band/.proof-stat were written against the black band); on the pastel
   field they vanish, so they take the band ink instead */
.proof-band { color:var(--d-ink); }
.proof-stat b { color:var(--d-ink); }
.proof-pane b { color:var(--d-ink); }

/* ---- 2. section ground --------------------------------------------------- */
/* Brandon, 2026-07-28: "the entire catalog background should be FAFAFA, get rid
   of this ugly yellow."
   This was a four-way rotation of mint / periwinkle / cream / blush washes, and
   the cream one landed on the catalog band and read as dirty yellow behind the
   product grid. Replaced with one flat neutral for every section.
   The pastel identity is carried by the PRODUCT CARDS - each tinted by its own
   research area - which is what was actually approved. A neutral ground is what
   lets those tints read as colour-coding rather than as one more wash competing
   with the band behind them. */
.section:not(.band-dark) { background:#fafafa; }

/* cards sitting on a wash need to stay paper-white so the grid keeps its
   layered read (tinted tile inside white card inside tinted section) */
.section .pcard, .section .card { background:var(--surface); }

/* hero: neutral ground into the faintest periwinkle lift, so it sits on the
   same #fafafa base as the catalog below rather than introducing a third tint */
.hero {
  background:linear-gradient(160deg,#fafafa,var(--wash-peri) 78%);
}

/* The ambient glow behind the hero animation. At its authored strength it was
   a hard lilac disc sitting directly behind a translucent ball-and-stick
   model, and the two together read as the "cloud" the old animation was
   rejected for. Softened to a barely-there lift so the helix reads as a
   structure on paper rather than as something glowing in fog. */
.hero__visual::before {
  background:radial-gradient(circle,rgba(147,197,253,.13),rgba(32,49,145,.05) 45%,transparent 68%);
}

/* the BOGO strip between hero and catalog: same neutral ground as the catalog
   it sits above (it was the other cream band, and the most visible one) */
.bogo-strip-wrap { background:#fafafa; padding-block:18px 10px; }

/* ---- BOGO strip, rebuilt 2026-07-28 -------------------------------------
   Three things made the inherited version read as a cheap banner:
     · a hard 2px royal gradient bar pinned across the top edge, which sits at
       a different radius to the card and reads as a stray rule rather than as
       part of it
     · a white -> lavender vertical gradient, so the plate got muddier toward
       the bottom exactly where the descriptive line sits
     · the two pills were hairline-outlined uppercase mono, which is the visual
       language this site uses for DISABLED controls - they looked like dead
       buttons sitting next to a live one
   Replaced with one flat periwinkle-tinted plate, a soft hairline, and filled
   pills. This is an offer, so it should read as calm and confident, not as a
   banner shouting from behind a rule. */
.bogo-strip {
  background:linear-gradient(180deg,#f4f7ff,#eef2fe);
  border:1px solid rgba(32,49,145,.14);
  box-shadow:none;
  border-radius:16px;
  padding:18px 22px;
  gap:20px;
}
/* the stray top bar */
.bogo-strip::before { content:none; }

.bogo-strip:hover {
  transform:none;                       /* no lift; it is a band, not a card */
  box-shadow:none;
  border-color:rgba(32,49,145,.30);
  background:linear-gradient(180deg,#f0f4ff,#e8eeff);
}

/* the sheen swept a pale blue diagonal across the copy on hover; on a plate
   this light it only ever muddied the text */
.bogo-strip__sheen { display:none; }

/* badge: solid navy tile, white glyph - the one high-contrast anchor */
.bogo-strip__badge {
  background:var(--accent);
  color:#fff;
  border-radius:12px;
  width:40px; height:40px;
}

/* pills: filled and soft, sentence case, so they read as attributes of the
   offer rather than as controls */
.bogo-strip__pill {
  background:rgba(255,255,255,.85);
  border:1px solid rgba(32,49,145,.10);
  border-radius:999px;
  padding:5px 12px;
  font-family:var(--font-sans);
  font-size:12px;
  font-weight:500;
  letter-spacing:0;
  text-transform:none;
  color:var(--ink-2);
  white-space:nowrap;
}

.bogo-strip__cta {
  font-weight:600;
  color:var(--accent);
  white-space:nowrap;
}

/* ---- mobile: condensed to two rows ---------------------------------------
   Brandon, 2026-07-28: "much smaller and more condensed on mobile".
   The flex version stacked into FOUR rows - badge, heading, description, pills,
   then the CTA - and ate 276px of the fold on a 375px screen for what is one
   promotional line. Now a 3-column grid: badge and CTA arrow pin to the sides
   of row 1, the copy occupies the middle, and the description tucks under it.

   ⚠️ The pills are HIDDEN here, not shrunk. "Mix & match any compounds" and
   "Unlimited" restate what the description already says, and they were the row
   that forced the full-width wrap. They still show from 761px up.

   ⚠️ The CTA is font-size:0 rather than display:none - that hides the words
   while leaving them in the accessibility tree, and the arrow is sized back up
   explicitly. The whole strip is a single <a> carrying its own aria-label, so
   nothing is lost. */
@media (max-width:760px) {
  .bogo-strip {
    display:grid;
    grid-template-columns:auto minmax(0,1fr) auto;
    align-items:center;
    gap:2px 12px;
    padding:12px 14px;
  }
  .bogo-strip__badge {
    grid-column:1; grid-row:1 / span 2;
    align-self:center;
    width:34px; height:34px; border-radius:10px;
  }
  .bogo-strip__badge svg { width:18px; height:18px; }

  .bogo-strip__text { grid-column:2; grid-row:1 / span 2; flex:initial; gap:1px; }
  .bogo-strip__text strong { font-size:var(--step-0); }
  .bogo-strip__text > span { font-size:11.5px; line-height:1.4; }

  .bogo-strip__cta { grid-column:3; grid-row:1 / span 2; font-size:0; }
  .bogo-strip__cta svg { width:18px; height:18px; }

  .bogo-strip__pills { display:none; }
}

/* ---- footer: pastel, not black ------------------------------------------- */
.site-footer {
  background:linear-gradient(180deg,var(--wash-peri),#e3e9fb);
  color:var(--ink-2);
}
.site-footer a { color:var(--ink); }
.site-footer .mono-label, .site-footer .eyebrow { color:var(--ink-3); }

/* Hover. styles.css hardcodes `color:#fff` on .footer-col a:hover and
   .footer-bottom a:hover - correct when the footer was a near-black band, but
   on the pastel footer it fades every link to invisible at the exact moment the
   pointer lands on it. Darken INTO the brand blue instead, which is the normal
   direction of travel for a link hover on light ground. */
.site-footer .footer-col a:hover,
.site-footer .footer-bottom a:hover,
.site-footer a:hover {
  color:var(--accent);
  text-decoration:underline;
  text-underline-offset:3px;
}
/* the brand wordmark was also knocked out to white for the dark footer */
.site-footer .footer-brand .brand-logo__name { color:var(--ink); }

/* ---- "Mix, match & save up to 50%" promo card ---------------------------
   🔴 This section rendered as a BLANK WHITE BOX with two floating buttons.
   Cause: its eyebrow, heading and paragraph are all inline `color:#fff` /
   rgba(255,255,255,.92) in index.html - written for a dark card - but nothing
   in the stylesheet ever gave `.stack-promo` a background. It inherited the
   white `.card` fill, so three elements of white text sat on white. The two
   buttons were visible only because they carry their own colours.

   Fixed by giving the card the dark brand ground it was always designed for,
   rather than by recolouring the copy: the white-on-dark treatment is what the
   markup, the `btn-on-dark` button and the inline white button all assume.

   The gradient is a slow three-stop royal->indigo drift. It animates
   background-position (compositor-friendly) rather than the gradient stops,
   which would force a repaint every frame. */
/* ⚠️ Selector must out-specify `.section .card` above (0,2,0), which sets the
   white paper fill and would otherwise reset background-image to none. */
/* Reskinned 2026-07-28: the ground was a navy/royal-blue sweep that belonged to
   no other surface on the site, and its second bloom was GREEN - the last of
   the old earthy palette, the same one the receptor orbit was carrying. It now
   walks the brand ramp (blue -> indigo -> violet, weighted blue) so the panel
   reads as the large-format expression of the cart-upsell gradient rather than
   a one-off.

   🔴 Every stop stays deep. This panel's copy is inline `color:#fff` in
   index.html, so a light stop anywhere in the sweep puts white text on a pale
   ground. The lightest stop here (#2f4fd0) is 5.9:1 against white. */
.section .card.stack-promo,
.stack-promo {
  background:linear-gradient(118deg,
    #152268 0%, #1d3fa8 24%, #2f4fd0 46%, #4a3fc0 68%, #5b2fa8 86%, #1a2160 100%);
  background-size:220% 220%;
  border:1px solid rgba(255,255,255,.10);
  border-radius:20px;
  /* 🔴 PADDING IS ZERO ON PURPOSE - do not restore it.
     .stack-promo__copy and .stack-promo__stats each carry their own padding, so
     a padding here was a SECOND inset. The visible symptom was the right-hand
     stats cell: its background wash stopped a full 52px short of the card on
     every side, so it read as a broken rectangle floating inside the panel
     rather than as one half of a split. With 0 here the two columns fill the
     card edge to edge and the divider runs the full height, which is what the
     split was always meant to look like. */
  padding:0;
  overflow:hidden;              /* so the column fills respect the 20px radius */
  /* tinted indigo, not neutral grey - a grey shadow under a violet panel reads
     as dirt against the pastel page ground */
  box-shadow:0 24px 60px rgba(23,31,92,.32), 0 4px 14px rgba(23,31,92,.16);
  animation:sp-drift 18s ease-in-out infinite alternate;
}
@keyframes sp-drift { from { background-position:0% 50%; } to { background-position:100% 50%; } }

/* a soft light bloom that keeps the top-left corner from going flat, plus the
   site's hex motif at very low opacity so the card belongs to the same family
   as the section headers. The top hairline is the "lit glass" edge the rest of
   the site's raised surfaces carry. */
.stack-promo::before {
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  border-radius:inherit;
  background:
    radial-gradient(680px 320px at 12% 0%, rgba(147,197,253,.28), transparent 62%),
    radial-gradient(560px 320px at 96% 108%, rgba(167,139,250,.22), transparent 60%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16);
}
.stack-promo::after {
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background-image:url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='56' height='96.99' viewBox='0 0 56 96.99'>\
<g fill='none' stroke='%23ffffff' stroke-width='1.1'>\
<path d='M14 0 L0 8.083 L0 24.248 L14 32.33 L28 24.248 L28 8.083 Z'/>\
<path d='M42 24.248 L28 32.33 L28 48.495 L42 56.577 L56 48.495 L56 32.33 Z'/>\
</g></svg>");
  background-size:150px 260px;
  opacity:.07;
  -webkit-mask-image:linear-gradient(200deg,#000 0%,transparent 70%);
  mask-image:linear-gradient(200deg,#000 0%,transparent 70%);
}
.stack-promo__in { position:relative; z-index:2; }

/* the outline button was invisible on the new dark ground */
.stack-promo .btn-ghost,
.stack-promo .btn-on-dark + .btn,
.stack-promo a.btn:not([style*="background"]) {
  background:transparent; color:#fff; border:1px solid rgba(255,255,255,.55);
}
.stack-promo a.btn:not([style*="background"]):hover {
  background:rgba(255,255,255,.12); border-color:#fff;
}

/* ---- newsletter opt-in --------------------------------------------------
   Was a centred mailto button ("Email us to subscribe") that dumped the user
   into their mail client and relied on someone adding them by hand. Now a real
   inline form: field + button on one line, copy on the left at wide widths.

   ⚠️ The field is a single rounded shell with the input and button INSIDE it,
   rather than two separate controls with a gap. That is what stops it reading
   like a generic bootstrap signup - but it means the focus ring has to go on
   the shell (:focus-within), not the input, or focus appears to land on a
   sub-rectangle floating inside the control. */
/* ⚠️ Must out-specify `.section .card:not(.stack-promo)` (0,3,0) further down
   this file, which sets the flat paper fill and a 16px radius. Plain `.nl`
   (0,1,0) loses to it and the gradient silently never appears - the same
   specificity trap documented on .stack-promo above. */
.section .card.nl,
.nl {
  position:relative; overflow:hidden;
  padding:clamp(28px,4vw,46px);
  /* 16px, matching every other card on the site. Not 20px: the generic card
     rule later in this file has equal specificity and would win anyway, so a
     20px here would be a dead declaration that reads as intent. */
  border-radius:16px;
  border:1px solid rgba(32,49,145,.10);
  background:linear-gradient(160deg,#ffffff 0%,#f6f8ff 62%,#f2f0ff 100%);
  box-shadow:0 1px 2px rgba(13,20,36,.03), 0 12px 34px rgba(13,20,36,.06);
}
/* a single soft bloom in the brand ramp, so the card is not flat white but is
   nowhere near strong enough to fight the dark stack panel above it */
.nl__glow {
  position:absolute; inset:auto -10% -60% auto; width:min(620px,80%); height:320px;
  background:radial-gradient(closest-side, rgba(99,102,241,.16), transparent 72%);
  pointer-events:none;
}
.nl__in {
  position:relative; z-index:1;
  display:grid; grid-template-columns:1.05fr 1fr; gap:clamp(22px,3vw,44px);
  align-items:center;
}
@media (max-width:820px){ .nl__in { grid-template-columns:1fr; gap:22px; } }

.nl__copy h2 { letter-spacing:-.02em; }
.nl__copy p { max-width:42ch; }

.nl__form { min-width:0; }
.nl__field {
  display:flex; align-items:center; gap:8px;
  padding:6px 6px 6px 8px;
  background:#fff;
  border:1px solid rgba(32,49,145,.16);
  border-radius:999px;
  box-shadow:0 1px 2px rgba(13,20,36,.04), 0 6px 16px rgba(13,20,36,.05);
  transition:border-color 160ms var(--ease-std), box-shadow 160ms var(--ease-std);
}
.nl__field:focus-within {
  border-color:var(--accent);
  box-shadow:0 0 0 3px rgba(99,102,241,.16), 0 6px 16px rgba(13,20,36,.06);
}
.nl__input {
  flex:1; min-width:0;
  border:0; outline:0; background:transparent;
  padding:11px 8px 11px 12px;
  font:inherit; font-size:var(--step-0); color:var(--ink);
}
.nl__input::placeholder { color:var(--ink-3); }
.nl__submit {
  flex:none; border-radius:999px; padding:11px 22px; white-space:nowrap;
  background:var(--bogo-grad, linear-gradient(135deg,#1d4ed8,#4f46e5));
  color:#fff; border:0;
  box-shadow:0 4px 12px rgba(29,78,216,.28);
  transition:transform 160ms var(--ease-std), box-shadow 160ms var(--ease-std), filter 160ms var(--ease-std);
}
.nl__submit:hover { transform:translateY(-1px); box-shadow:0 8px 20px rgba(29,78,216,.34); }
.nl__submit[disabled] { filter:grayscale(.35) opacity(.7); transform:none; cursor:default; }

/* visually hidden but still read out - the field has a placeholder, which is
   not a label */
.nl__sr {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.nl__hp { position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden; }

.nl__msg {
  margin:10px 2px 0; min-height:1.2em;
  font-size:var(--step--1); line-height:1.45; color:var(--ink-2);
}
.nl__msg.is-err { color:#b3261e; }
.nl__msg.is-ok  { color:#15803d; }
.nl__fine { margin:6px 2px 0; font-size:11.5px; color:var(--ink-3); }

/* ---- mobile hero: helix moves BEHIND the copy ----------------------------
   Brandon, 2026-07-28. On phones the hero grid collapses to one column, so the
   helix sat as its own 360px block UNDER the buttons and stats - the fold was
   all type, then a large detached graphic. It now fills the hero as a faint
   backdrop with the copy on top, which is how the desktop composition reads.

   ⚠️ Absolute positioning is what takes it out of flow; without that the block
   keeps its own row and the copy still sits above rather than over it. The
   parent .hero__in is already position:relative, so this anchors to it.

   Opacity is the whole point of the request - low enough that the headline and
   the 99.7% figures stay the subject. Verified after the change: nothing in the
   hero drops below AA against the pale ground. */
@media (max-width:900px){
  .hero__visual {
    position:absolute; inset:0; z-index:0;
    margin-top:0; min-height:0;
    opacity:.22;
    pointer-events:none;              /* never steal a tap from the CTAs */
  }
  /* the copy column is an unclassed first child of .hero__in */
  .hero__in > div:first-child { position:relative; z-index:1; }
  /* the visual's own radial bloom is redundant once it is a backdrop - it
     lightens the middle of the band and washes the lead paragraph out */
  .hero__visual::before { display:none; }
}

/* ---- affiliate application form -----------------------------------------
   The fields were unstyled `.calc__field` rows inherited from the calculator
   component: hairline boxes, every field full width including Name and Email,
   and a label sitting flush on the input above it, which made the form read as
   one undifferentiated column of empty rectangles.

   Styled to match the newsletter opt-in rather than as a one-off, so the site
   has ONE input treatment: white fill, indigo-tinted border, brand focus ring.
   ⚠️ If you restyle one of these two forms, restyle the other. */
.aff-form {
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:18px 20px;
}
@media (max-width:640px){ .aff-form { grid-template-columns:1fr; } }
.aff-form .calc__field { margin-top:0; }               /* the grid owns spacing */
.aff-form .calc__field--wide { grid-column:1 / -1; }

.aff-form .calc__label {
  display:block; margin-bottom:7px;
  font-family:var(--font-mono); font-size:10px; font-weight:600;
  text-transform:uppercase; letter-spacing:.07em; color:var(--ink-2);
}
/* the hint is a different KIND of information from the label, so it stops
   competing for the same emphasis - sentence case, lighter, not mono */
.aff-form .calc__label small {
  font-family:var(--font-sans); font-size:11.5px; font-weight:400;
  text-transform:none; letter-spacing:0; color:var(--ink-3); margin-left:7px;
}

.aff-form input,
.aff-form textarea {
  width:100%; display:block;
  padding:12px 14px;
  font:inherit; font-size:var(--step-0); color:var(--ink);
  background:#fff;
  border:1px solid rgba(32,49,145,.16);
  border-radius:12px;
  outline:0;
  transition:border-color 160ms var(--ease-std), box-shadow 160ms var(--ease-std);
}
.aff-form textarea { min-height:118px; resize:vertical; line-height:1.55; }
.aff-form input::placeholder,
.aff-form textarea::placeholder { color:var(--ink-3); }
.aff-form input:focus,
.aff-form textarea:focus {
  border-color:var(--accent);
  box-shadow:0 0 0 3px rgba(99,102,241,.16);
}
/* 🔴 :user-invalid, NOT :invalid, and NOT the :not(:placeholder-shown):invalid
   trick. Bare :invalid paints every required field red the moment the page
   loads, before anyone has typed. The placeholder-shown workaround silently
   does the same thing on any field WITHOUT a placeholder - which is what
   happened here: Name and Email have none, so `:not(:placeholder-shown)` was
   always true and both loaded pink. :user-invalid only matches after the user
   has actually interacted with the field, which is the real intent. */
.aff-form input:user-invalid,
.aff-form textarea:user-invalid { border-color:#e0a3a0; }

.aff-form__foot {
  display:flex; flex-wrap:wrap; align-items:center; gap:10px 18px;
  margin-top:4px;
}
.aff-form__submit {
  border-radius:999px; padding:13px 26px; border:0; color:#fff;
  background:var(--bogo-grad, linear-gradient(135deg,#1d4ed8,#4f46e5));
  box-shadow:0 4px 12px rgba(29,78,216,.28);
  transition:transform 160ms var(--ease-std), box-shadow 160ms var(--ease-std);
}
.aff-form__submit:hover { transform:translateY(-1px); box-shadow:0 8px 20px rgba(29,78,216,.34); }
.aff-form__note {
  margin:0; flex:1 1 260px; min-width:0;
  font-size:11.5px; line-height:1.55; color:var(--ink-3);
}

/* ---- promo interior: divider, numerals, note, buttons -------------------
   styles.css builds the right-hand panel with a hard `border-left:1px` in flat
   white. At this size a full-height rule reads as a table cell edge - it cuts
   the panel in two rather than separating two halves of one thing. Replaced
   with a hairline that fades out at both ends, which is the same device the
   section rules elsewhere on the site use. */
.stack-promo__stats {
  position:relative;
  border-left:none;
  /* ⚠️ A RADIAL that fades to transparent WELL INSIDE the element, not a linear
     wash across it. The linear version had colour still present at the element
     bounds, so it drew a hard rectangular edge - that edge is what read as a
     broken box. This one has nothing left to clip by the time it reaches the
     sides, so the glow sits behind the numbers with no boundary at all. */
  background:radial-gradient(58% 52% at 52% 46%, rgba(255,255,255,.11), transparent 72%);
}
/* the only edge in the panel: a hairline that fades out top and bottom, so the
   two halves are separated without a table-cell rule between them */
.stack-promo__stats::before {
  content:""; position:absolute; left:0; top:12%; bottom:12%; width:1px;
  background:linear-gradient(180deg,
    transparent 0%, rgba(255,255,255,.28) 20%, rgba(255,255,255,.28) 80%, transparent 100%);
}

/* ⚠️ Below 800px styles.css stacks the two halves and swaps the separator to a
   border-TOP. The vertical hairline above has to be turned into a horizontal
   one to match, or it keeps drawing down the left edge of the stacked block as
   a stray 1px line with nothing on the other side of it. The flat border-top is
   dropped at the same time so there are not two separators. */
@media (max-width:800px){
  .stack-promo__stats { border-top:none; }
  .stack-promo__stats::before {
    left:8%; right:8%; top:0; bottom:auto; width:auto; height:1px;
    background:linear-gradient(90deg,
      transparent 0%, rgba(255,255,255,.28) 18%, rgba(255,255,255,.28) 82%, transparent 100%);
  }
}

/* Numerals get a top-lit gradient rather than flat white - the same treatment
   as the .stat-cell figures, so the two biggest numbers on the site match.
   ⚠️ Gradient text has no fallback colour by definition; the @supports block
   below hands a real one back to anything that cannot clip text. */
.stack-promo__num b {
  background:linear-gradient(180deg,#ffffff 0%,#d8e4ff 78%,#c2d4ff 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
  letter-spacing:-.035em;
}
@supports not ((-webkit-background-clip:text) or (background-clip:text)) {
  .stack-promo__num b { -webkit-text-fill-color:#fff; color:#fff; }
}
.stack-promo__num span { opacity:1; color:rgba(255,255,255,.68); letter-spacing:.08em; }

/* the note was loose 12px mono floating under the numbers; a bordered pill
   gives it an edge to sit against and stops it reading as an orphan line */
.stack-promo__note {
  display:inline-block; justify-self:center;
  padding:7px 14px; border-radius:999px;
  border:1px solid rgba(255,255,255,.20);
  background:rgba(255,255,255,.07);
  color:rgba(255,255,255,.86); opacity:1;
  font-size:11.5px; letter-spacing:.02em;
}

/* The two CTAs were wrapping onto separate lines: the copy column is 1.15fr of
   the grid, and `.wrap-flex` let them break as soon as they were tight. Stacked
   buttons made the left column tall, which is what forced the whole card taller
   and left the large dead area under the numbers on the right.
   Only unwrapped where there is genuinely room - below 900px they stack again
   rather than shrinking into two cramped pills. */
@media (min-width:900px){
  .stack-promo__copy .wrap-flex { flex-wrap:nowrap; }
}

/* buttons: the solid one carries an inline `background:#fff` from index.html,
   so its fill cannot be restyled from here - depth has to come from shadow. */
.stack-promo .btn-lg { border-radius:999px; }
.stack-promo a.btn[style*="background"] {
  box-shadow:0 8px 22px rgba(8,15,60,.30), 0 2px 5px rgba(8,15,60,.18);
  transition:transform var(--dur) var(--ease-std), box-shadow var(--dur) var(--ease-std);
}
.stack-promo a.btn[style*="background"]:hover {
  transform:translateY(-2px);
  box-shadow:0 14px 32px rgba(8,15,60,.36), 0 3px 8px rgba(8,15,60,.20);
}
.stack-promo a.btn:not([style*="background"]) {
  border-color:rgba(255,255,255,.42);
  backdrop-filter:blur(2px);
  transition:transform var(--dur) var(--ease-std), background var(--dur) var(--ease-std), border-color var(--dur) var(--ease-std);
}
.stack-promo a.btn:not([style*="background"]):hover { transform:translateY(-2px); }

@media (prefers-reduced-motion:reduce) {
  .stack-promo { animation:none; }
  .stack-promo a.btn:hover { transform:none; }
}

/* ---- icon tiles: gradient instead of flat ------------------------------
   The pillar tiles took `background:var(--pillar)` - one flat pastel each. A
   gradient built from that SAME token keeps the five-colour coding intact (the
   tile still matches its card's top rule) while giving the chip depth.

   Built with color-mix off --pillar rather than five hand-written gradients, so
   adding or recolouring a pillar needs no new CSS and the two can never drift
   apart. The light stop is the pastel lifted toward white, the dark stop the
   same hue deepened - a tonal gradient, not a second hue, so it reads as
   dimensional rather than as a colour transition. */
.pillar__icon {
  background:linear-gradient(145deg,
    color-mix(in srgb, var(--pillar) 55%, #ffffff) 0%,
    var(--pillar) 52%,
    color-mix(in srgb, var(--pillar) 78%, var(--brand-ink)) 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55),
             0 2px 6px color-mix(in srgb, var(--pillar) 45%, transparent);
}
/* the feature tiles use the accent token rather than a per-card pastel */
.feature__icon {
  background:linear-gradient(145deg,
    color-mix(in srgb, var(--accent-tint) 70%, #ffffff) 0%,
    var(--accent-tint) 48%,
    color-mix(in srgb, var(--accent-tint) 62%, var(--accent)) 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.6),
             0 2px 6px rgba(32,49,145,.10);
}
/* Older engines without color-mix keep the flat fill they already had - a
   gradient is an enhancement here, never the thing that makes the icon legible. */
@supports not (background:color-mix(in srgb,#000 10%,#fff)) {
  .pillar__icon  { background:var(--pillar); box-shadow:none; }
  .feature__icon { background:var(--accent-tint); box-shadow:none; }
}

/* ---- verify band: vial pair in front of the DNA helix -------------------
   The helix canvas fills #dnaVisual; this layer sits above it. The two vials
   are tilted toward each other and overlapped so they read as a pair rather
   than as two separate images, matching the product-render style used on the
   cards. */
.verify-visual { position:relative; display:grid; place-items:center; min-height:min(520px,60vh); }
/* the helix host is stretched under the whole area; the vial pair overlays it */
.verify-visual > #dnaVisual { position:absolute; inset:0; width:100%; height:100%; }

.vial-pair {
  position:relative; z-index:2;
  display:flex; align-items:flex-end; justify-content:center;
  width:min(360px,78%);
  /* lift off the helix so the glass reads as being in front of it */
  filter:drop-shadow(0 18px 30px rgba(13,20,36,.18));
}
/* Positioned to match the supplied reference exactly: the two vials LEAN INTO
   each other so their caps meet near the centre and the bodies splay outward
   at the base, with KLOW (right) overlapping IN FRONT at the neck.
   · GLP-3 RT sits left and rotates CLOCKWISE, so its top tips toward centre
   · KLOW sits right and rotates COUNTER-CLOCKWISE, tipping toward centre
   · the pair overlaps ~26%, and KLOW rides slightly higher, as in the render */
.vial-pair__v { width:62%; height:auto; display:block; }
.vial-pair__v--a { transform:rotate(9deg)  translateX(13%); z-index:1; }
.vial-pair__v--b { transform:rotate(-7deg) translateX(-13%) translateY(-3%); z-index:2; }

/* a gentle settle on entry, matched to the rest of the page's motion.
   Each vial needs its OWN keyframes: a shared one would animate `transform`
   back to none and silently throw away the rotation above. */
.vial-pair__v { animation:700ms cubic-bezier(.22,.7,.2,1) both; }
.vial-pair__v--a { animation-name:vp-in-a; animation-delay:120ms; }
.vial-pair__v--b { animation-name:vp-in-b; animation-delay:200ms; }
@keyframes vp-in-a { from { opacity:0; transform:rotate(9deg) translateX(13%) translateY(16px) scale(.97); }
                     to   { opacity:1; transform:rotate(9deg) translateX(13%); } }
@keyframes vp-in-b { from { opacity:0; transform:rotate(-7deg) translateX(-13%) translateY(-3%) translateY(16px) scale(.97); }
                     to   { opacity:1; transform:rotate(-7deg) translateX(-13%) translateY(-3%); } }

@media (max-width:760px) {
  .verify-visual { min-height:340px; }
  .vial-pair { width:min(280px,70%); }
}
@media (prefers-reduced-motion:reduce) { .vial-pair__v { animation:none; } }

/* ---- PDP research-direction bars (section 02) --------------------------
   Replaces the fabricated "72%" label that used to sit here. `.mag__pct` is
   dead markup now; `.mag__tag` carries the qualitative tag instead, and every
   fill reaches 100% so nothing encodes a value. */
.mag__pct { display:none; }
.mag__tag {
  font-family:var(--font-mono); font-size:10px; text-transform:uppercase;
  letter-spacing:.1em; color:var(--accent);
  background:var(--accent-tint); border-radius:999px; padding:3px 10px;
  white-space:nowrap;
}
.mag__fill { background:var(--bogo-grad, linear-gradient(90deg,#1d4ed8,#7c3aed)); }

/* ---- research pages polish (COA library, Lab results, Test methods,
       COA explained, Glossary) -------------------------------------------
   These five are the site's evidence pages and were the plainest in the build:
   flat white cards on flat white, no accent anywhere, so the pages that carry
   the proof looked the least considered. Styled through the SHARED components
   they already use rather than per page, so nothing needs re-authoring and the
   five stay consistent with each other. */

/* stat tiles (COA library) - gradient numerals + a lifted card
 *
 * 🔴 THE .stat-row RESET BELOW IS LOAD-BEARING - do not drop it.
 * styles.css builds this component as a HAIRLINE-DIVIDER grid: `gap:1px` over
 * `background:var(--line)`, so the grey parent showing through the 1px gaps IS
 * the divider. Restyling the cells as separate rounded cards widened that gap
 * to 14px, which turned the divider trick into a grey slab behind and between
 * every card - visible around each rounded corner and down every gutter.
 *
 * So the parent has to stop being a visual element at all: no background, no
 * shadow, no radius. Each card now carries its own edge.
 *
 * ⚠️ `overflow:visible` is the non-obvious half. The base rule sets
 * `overflow:hidden` (needed when the parent had the radius); leave it and the
 * parent clips each card's drop shadow flat against the container edge, so the
 * outer two cards lose the shadow on their outer sides and the row looks
 * lopsided for no visible reason. */
.stat-row {
  gap:14px;
  background:none;
  box-shadow:none;
  border-radius:0;
  overflow:visible;
}
.stat-cell {
  background:#fff;
  border:1px solid rgba(32,49,145,.07);
  border-radius:16px; padding:20px 18px;
  box-shadow:0 1px 2px rgba(13,20,36,.04), 0 6px 16px rgba(13,20,36,.05);
  transition:transform var(--dur) var(--ease-std), box-shadow var(--dur) var(--ease-std);
}
.stat-cell:hover { transform:translateY(-2px); box-shadow:0 2px 6px rgba(13,20,36,.05), 0 14px 30px rgba(13,20,36,.08); }
/* dark bands: the base rule repaints the cell surface, which would undo the
   white card above and bring the grey slab back on those sections */
.band-dark .stat-row { background:none; box-shadow:none; }

/* PDP overview stat tiles sit on the periwinkle band as of 2026-07-28 (the
   generic product layout now opens on the same band the bespoke one does). The
   tile's own rgba(13,20,36,.05) tint over that band drags --ink-3 to 4.49:1 -
   just under AA, and on 11px uppercase mono, which is where it matters most.
   --ink-2 takes it to 8.4:1. */
.band-dark .stat-tile__l { color:var(--ink-2); }
.stat-cell__v {
  background:linear-gradient(135deg,#1d4ed8 0%,#3b82f6 45%,#7c3aed 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
  font-weight:800; letter-spacing:-.03em;
}
/* the gradient-text trick has no fallback colour by definition, so anything
   that cannot clip text must be given a real one back */
@supports not ((-webkit-background-clip:text) or (background-clip:text)) {
  .stat-cell__v { -webkit-text-fill-color:currentColor; color:var(--accent); }
}

/* Assay-methods comparison table + tick lists.
   Both classes were authored in the markup but never defined, so the table that
   is the whole point of that page rendered at browser defaults - 1px cell
   padding, no rules, centred headers. Caught by tools/verify-site.js section 4,
   which is exactly what that check is for. */
.lr-table { border-collapse:collapse; font-size:var(--step--1); }
.lr-table th {
  text-align:left; padding:11px 14px;
  font-family:var(--font-mono); font-size:10.5px; font-weight:600;
  text-transform:uppercase; letter-spacing:.07em; color:var(--ink-3);
  border-bottom:1px solid rgba(32,49,145,.14); white-space:nowrap;
}
.lr-table td {
  padding:12px 14px; border-bottom:1px solid var(--line);
  color:var(--ink-2); line-height:1.55; vertical-align:top;
}
.lr-table td:first-child { color:var(--ink); font-weight:600; }
.lr-table tbody tr:nth-child(even) { background:rgba(99,102,241,.028); }
.lr-table tbody tr:last-child td { border-bottom:none; }

.tick-list { list-style:none; padding-left:0; margin:0; display:grid; gap:9px; }
.tick-list li { position:relative; padding-left:25px; line-height:1.55; color:var(--ink-2); }
/* check drawn from two borders rather than a glyph, so it inherits the accent
   colour and needs no font or SVG */
.tick-list li::before {
  content:""; position:absolute; left:3px; top:.34em;
  width:5px; height:9px; border:solid var(--accent);
  border-width:0 2px 2px 0; transform:rotate(45deg);
}

/* section headings across all five */
.sec-head .eyebrow { color:var(--accent); }
.sec-head h2 { letter-spacing:-.025em; }

/* feature tiles (Test methods) */
.feature {
  background:linear-gradient(165deg,#ffffff,#f6f8ff);
  border:1px solid rgba(32,49,145,.09);
  border-radius:16px; padding:22px;
  box-shadow:0 1px 2px rgba(13,20,36,.03), 0 8px 22px rgba(13,20,36,.045);
  transition:transform var(--dur) var(--ease-std), box-shadow var(--dur) var(--ease-std);
}
.feature:hover { transform:translateY(-2px); box-shadow:0 2px 4px rgba(13,20,36,.04), 0 14px 30px rgba(13,20,36,.085); }

/* generic cards on these pages (Glossary, COA library, methods) */
.mol-section ~ .section .card,
.section .card:not(.stack-promo) {
  border-radius:16px;
  border:1px solid rgba(32,49,145,.09);
  box-shadow:0 1px 2px rgba(13,20,36,.03), 0 8px 22px rgba(13,20,36,.045);
}

/* lab list rows */
.lr-labs > * { border-radius:12px; }

@media (prefers-reduced-motion:reduce) {
  .stat-cell:hover, .feature:hover { transform:none; }
}

/* ---- side cart: free-unit marking + one-click BOGO upsell ---------------
   Ported from the sibling storefront 2026-07-28 and reskinned to this brand.
   Two separate things:
     1. free units are now MARKED on the line they belong to (chip on the
        thumbnail, "n paid · n free" split, struck-through gross price). The
        cart previously showed every line at full price with a single "BOGO"
        row in the totals, which reads as though nothing is actually free.
     2. a one-click upsell button under any line where adding 1-2 more would
        land a free vial. The offer text comes from bogoOfferFor(), which
        recomputes the real pairing against the live cart - so it never
        promises "free" when the maths would not give it. */

/* ONE gradient for every BOGO surface - the upsell card, the "1 FREE" pill and
   the thumbnail outline - so they read as one system rather than three
   unrelated highlights. Weighted BLUE: the brand blue holds the first ~60% and
   violet only appears in the tail, so it reads as blue with a purple lift
   rather than as a purple gradient. */
:root {
  --bogo-grad:linear-gradient(135deg,#1d4ed8 0%,#3b82f6 30%,#4f46e5 66%,#7c3aed 100%);
  --bogo-wash:linear-gradient(115deg,#e9f0ff 0%,#edf1ff 58%,#f3efff 100%);
  --bogo-wash-hi:linear-gradient(115deg,#dde8ff 0%,#e3e8ff 58%,#ebe2ff 100%);
  --bogo-ink:#1d4ed8;              /* 7.6:1 on the palest stop of --bogo-wash */
  --bogo-line:rgba(37,99,235,.24);
  --bogo-line-hi:rgba(37,99,235,.46);
}

/* --- 1. free-unit marking --- */
/* the free chip, inline with the line's meta text */
.cart-line__freeflag {
  display:inline-block;
  background:var(--bogo-grad); color:#fff;
  font-family:var(--font-mono); font-size:9px; font-weight:600;
  text-transform:uppercase; letter-spacing:.06em; line-height:1.5;
  padding:2px 8px; border-radius:999px; white-space:nowrap;
  box-shadow:0 1px 3px rgba(29,78,216,.28);
}
.cart-line__paid {
  font-family:var(--font-mono); font-size:10px; color:var(--ink-3);
  margin-left:7px;
}
/* The vial renders are clear glass on a transparent background, so at 42px on
   a white tile they are effectively invisible - in the product grid they read
   only because they sit on a tinted area wash. Give the cart thumb the same
   kind of ground. */
.cart-line__thumb {
  position:relative;
  background:linear-gradient(160deg,#f4f7ff,#eef2fb);
  border-radius:11px;
}
/* A gradient OUTLINE: `outline` only takes a solid colour, and `border-image`
   ignores border-radius and would square the corners off. The two-layer
   background trick paints the fill to the padding box and the gradient to the
   border box, which is the only way to get a rounded gradient edge. */
.cart-line.has-free .cart-line__thumb {
  border:2px solid transparent;
  border-radius:11px;
  background-image:linear-gradient(#fff,#fff), var(--bogo-grad);
  background-origin:border-box;
  background-clip:padding-box, border-box;
}
.cart-line__split { margin-top:5px; display:flex; align-items:center; }
.cart-line__amt { font-size:12px; margin-top:6px; }
.cart-line__amt.is-disc { color:var(--bogo-ink); font-weight:600; }
.cart-line__amt s { color:var(--ink-faint); font-weight:400; margin-right:3px; }

/* --- 2. the upsell button --- */
/* Brand gradient: light blue -> purple (sky #93c5fd into lavender #c4b5fd),
   replacing the green it was ported with. Green is kept for the "n free" chips
   on the cart lines themselves - those are a STATUS signal and stay in the
   verified-green family; this button is a brand surface, not a status. */
.cart-upsell {
  display:flex; align-items:center; gap:10px; width:100%;
  margin:-2px 0 14px; padding:10px 12px;
  background:var(--bogo-wash);
  border:1px solid var(--bogo-line); border-radius:12px;
  cursor:pointer; text-align:left; color:var(--ink);
  font-family:var(--font-sans); font-size:12.5px; line-height:1.35;
  /* the strip slides down out of the line above it, so it reads as belonging
     to that product rather than as a banner dropped into the cart */
  animation:cu-in 420ms cubic-bezier(.22,.7,.2,1) both;
  transition:border-color 140ms, background 140ms, transform 140ms;
}
@keyframes cu-in { from { opacity:0; transform:translateY(-6px); } to { opacity:1; transform:none; } }
.cart-upsell:hover {
  border-color:var(--bogo-line-hi);
  background:var(--bogo-wash-hi);
}
.cart-upsell:active { transform:scale(.988); }
/* the deep blue, not the pastel - the pale stop of --bogo-wash would measure
   about 1.7:1 and be unreadable */
.cart-upsell em { font-style:normal; font-weight:700; color:var(--bogo-ink); }
.cart-upsell b { font-weight:600; }
.cart-upsell i { font-style:normal; color:var(--ink-3); font-family:var(--font-mono); font-size:11px; }
.cart-upsell__txt { flex:1 1 auto; min-width:0; }
.cart-upsell__spark, .cart-upsell__go {
  flex:0 0 auto; display:grid; place-items:center;
  width:24px; height:24px; border-radius:8px;
}
/* the spark chip carries the gradient at full saturation, so the blue->purple
   move is legible even at 24px */
.cart-upsell__spark { background:var(--bogo-grad); color:#fff; }
.cart-upsell__spark svg { width:13px; height:13px; }
/* pulses twice then stops - enough to pull the eye to a live offer, not a
   permanently animating element sitting next to a price */
.cart-upsell__spark { animation:cu-pulse 1.5s ease-in-out 2 420ms; }
@keyframes cu-pulse {
  0%,100% { transform:scale(1); }
  50%     { transform:scale(1.16); box-shadow:0 0 0 5px rgba(37,99,235,.18); }
}
.cart-upsell__go { background:rgba(37,99,235,.12); color:var(--bogo-ink); }
.cart-upsell__go svg { width:14px; height:14px; }
.cart-upsell:hover .cart-upsell__go { background:var(--bogo-grad); color:#fff; }
.cart-upsell:focus-visible { outline:2px solid var(--bogo-ink); outline-offset:2px; }
/* brief confirm state after a click, before the cart re-renders */
.cart-upsell.is-adding {
  background:var(--bogo-grad);
  border-color:transparent; color:#fff;
}
.cart-upsell.is-adding em, .cart-upsell.is-adding i { color:#fff; }
.cart-upsell.is-adding .cart-upsell__go { background:rgba(255,255,255,.24); color:#fff; }
.cart-upsell.is-adding .cart-upsell__spark { background:rgba(255,255,255,.22); }

@media (prefers-reduced-motion:reduce) {
  .cart-upsell, .cart-upsell__spark { animation:none; }
  .cart-upsell:active { transform:none; }
}

/* ---- Build a Stack: empty state ----------------------------------------
   Replaces the generic grey "layers" glyph. The art shows two paid vials and a
   dashed third, so the empty panel demonstrates Buy-1-Get-1 instead of just
   reporting that nothing is here yet. */
.stack-empty { padding:34px 20px 30px; }
.stack-empty__art {
  width:min(200px,72%); height:auto; display:block; margin:0 auto 14px;
  color:var(--ink);
}
.stack-empty__msg {
  font-size:var(--step--1); line-height:1.6; color:var(--ink-3); margin:0;
}
/* vials settle in one after another, left to right - the same order a stack is
   actually built in. Purely decorative, so it is fully disabled below. */
.stack-empty__art .se-v {
  animation:se-rise 620ms cubic-bezier(.22,.7,.2,1) both;
  animation-delay:calc(var(--se-i, 0) * 110ms + 80ms);
  transform-box:fill-box; transform-origin:bottom center;
}
@keyframes se-rise { from { opacity:0; transform:translateY(7px) scaleY(.93); } to { opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce) { .stack-empty__art .se-v { animation:none; } }

/* ---- research use-case bars (product.js, under the signature strip) ------
   Sits directly beneath the FORMULA / MOL.WEIGHT / CAS strip, which on the 20
   compounds with no published formula was three cells reading "See COA".
   Colour is the compound's own research-area accent, so it matches its card
   tint and area landing page. */
.ucase { margin-top:18px; }
.ucase__head { display:flex; align-items:baseline; gap:10px; margin-bottom:10px; }
.ucase__eyebrow {
  font-family:var(--font-mono); font-size:10.5px; text-transform:uppercase;
  letter-spacing:.16em; color:var(--ink-3);
}
.ucase__row { margin-bottom:12px; }
.ucase__top { display:flex; align-items:baseline; gap:10px; margin-bottom:5px; }
.ucase__title { font-size:var(--step--1); font-weight:600; color:var(--ink); }
.ucase__tag {
  font-family:var(--font-mono); font-size:10px; text-transform:uppercase;
  letter-spacing:.1em; color:var(--ucase-ink);
  background:color-mix(in srgb, var(--ucase-accent) 26%, #fff);
  border-radius:999px; padding:2px 9px; white-space:nowrap;
}
@supports not (background:color-mix(in srgb,#000 10%,#fff)) {
  .ucase__tag { background:var(--accent-tint); }
}
.ucase__track {
  height:6px; border-radius:999px; overflow:hidden;
  background:rgba(13,20,36,.07);
}
.ucase__fill {
  display:block; height:100%; width:100%; border-radius:999px;
  background:linear-gradient(90deg, var(--ucase-ink), var(--ucase-accent));
  /* EVERY bar fills to 100%. The animation is a staggered reveal, never a
     value - see the note in product.js. --ucase-i staggers the start so the
     rows cascade rather than firing as one block. */
  transform-origin:left center;
  animation:ucase-sweep 820ms cubic-bezier(.22,.7,.2,1) both;
  animation-delay:calc(var(--ucase-i, 0) * 130ms + 120ms);
}
@keyframes ucase-sweep { from { transform:scaleX(0); opacity:.25; } to { transform:scaleX(1); opacity:1; } }
.ucase__note {
  font-size:11.5px; color:var(--ink-3); line-height:1.55; margin-top:10px;
}
.ucase__note a { color:var(--accent); }

@media (prefers-reduced-motion:reduce) {
  .ucase__fill { animation:none; transform:none; opacity:1; }
}

/* ---- styled dropdowns (assets/js/fancy-select.js) ------------------------ */
/* The native <select> stays in the DOM as the value's source of truth; it is
   taken out of the visual flow but deliberately NOT display:none, so it keeps
   participating in form semantics. */
.fsel { position:relative; display:block; }
.fsel__native {
  position:absolute; width:1px; height:1px; margin:-1px;
  padding:0; border:0; clip:rect(0 0 0 0); clip-path:inset(50%); overflow:hidden;
}

.fsel__btn {
  display:flex; align-items:center; gap:10px; width:100%;
  padding:0; background:none; border:0; cursor:pointer;
  font-family:var(--font-sans); font-size:var(--step-0); font-weight:600;
  color:var(--ink); text-align:left; line-height:1.25;
}
.fsel__val { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.fsel__caret { flex:0 0 auto; color:var(--ink-3); display:grid; place-items:center; }
.fsel__caret svg { width:15px; height:15px; }
.fsel.is-open .fsel__caret { transform:rotate(180deg); }
.fsel__caret { transition:transform var(--dur) var(--ease-std); }

/* the swatch/glyph chip that carries the colour coding */
.fsel__mark {
  flex:0 0 auto; width:22px; height:22px; border-radius:7px;
  display:grid; place-items:center;
  background:var(--accent-tint); color:var(--accent);
  box-shadow:inset 0 0 0 1px rgba(13,20,36,.07);
}
.fsel__mark svg { width:13px; height:13px; }

/* the popup */
.fsel__list {
  position:absolute; z-index:60; top:calc(100% + 10px); left:-14px; min-width:calc(100% + 28px);
  max-height:min(58vh,360px); overflow-y:auto; padding:6px;
  background:#fff; border:1px solid rgba(13,20,36,.10); border-radius:14px;
  box-shadow:0 1px 2px rgba(13,20,36,.04), 0 12px 34px rgba(13,20,36,.13);
}
.fsel__list[hidden] { display:none; }

.fsel__opt {
  display:flex; align-items:center; gap:10px;
  padding:9px 10px; border-radius:10px; cursor:pointer;
  font-size:var(--step--1); color:var(--ink-2); white-space:nowrap;
}
.fsel__txt { flex:1 1 auto; }
.fsel__tick { flex:0 0 auto; width:15px; height:15px; opacity:0; color:var(--accent); }
.fsel__tick svg { width:15px; height:15px; }

/* hover/keyboard focus share one treatment so pointer and keyboard users see
   the same thing - and it is a tint, never the OS's hard blue bar */
.fsel__opt.is-active { background:var(--accent-tint); color:var(--ink); }
/* selected carries a CHECK as well as the tint, so the state does not depend
   on colour alone */
.fsel__opt.is-on { color:var(--ink); font-weight:600; }
.fsel__opt.is-on .fsel__tick { opacity:1; }

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

/* ---- washes must never flatten the layered read --------------------------- */
/* pale chips/inputs that were tuned for pure white get a hairline so they
   still register as objects on a tinted field */
.section:not(.band-dark) .pill, .section:not(.band-dark) .chip {
  box-shadow:0 0 0 1px var(--wash-line);
}

/* ---- shop toolbar: dropdowns must clear the product grid ------------------
   🔴 THE BUG THIS FIXES, so nobody "simplifies" it away.
   Every product card carries `.reveal`, which sets transform:translateY(16px).
   A non-none transform CREATES A STACKING CONTEXT, and the cards sit after the
   toolbar in the DOM - so they painted on top of the open dropdown no matter
   what z-index .fsel__list carried. Its z-index:60 only ranked it inside the
   toolbar's own context, which had none.

   .reveal.in sets transform:none and releases the context, but the transition
   is .6s, and cards below the fold never reach .in at all - which is why this
   was permanent on mobile and intermittent on desktop.

   Fix is to give the TOOLBAR a context that outranks the grid, not to escalate
   the dropdown's z-index (that can never win across sibling contexts).
   ⚠️ Do not add overflow:hidden/auto to .shop-toolbar or .tool-selects - it
   would clip the panel instead of layering it. */
.shop-toolbar {
  position: relative;
  z-index: 200;          /* above .reveal cards, below the cart drawer (z 500+) */
}
/* while a select is open, lift that control above its neighbour so two open
   panels can never fight */
.shop-toolbar .fsel.is-open,
.shop-toolbar .tool-select:focus-within { position: relative; z-index: 3; }

/* the panel itself: high within the toolbar's context, and allowed to overhang */
.fsel__list { z-index: 240; }

/* mobile - the toolbar goes full width and the panel must not be clipped by it */
@media (max-width: 640px) {
  .shop-toolbar { z-index: 200; }
  .tool-selects { overflow: visible; }
  .fsel__list { max-height: min(52vh, 320px); }
}

/* ==== BEGIN: PDP overview band de-tint · 2026-08-02 (Brandon) ================
   REVERT: delete this whole block, from the BEGIN line to the END line.
   Nothing else in the codebase was touched, so removing it restores the
   periwinkle opening band exactly as it was.

   WHY
   `01 · Overview` is the ONLY section on a product page with a painted
   background. It carries `.band-dark`, which the remap at the top of this file
   turns into the periwinkle wash (--d-bg #f1f4fd). Every other PDP section -
   dosing, compare, profile, kinetics, molecule, timeline, literature,
   documentation, reference, faq - computes to `rgba(0,0,0,0)` over the white
   body. So the page opened on a tinted slab and then went white for the rest of
   the scroll, which is the break Brandon flagged.

   WHAT THIS DOES - and deliberately does not do
   Only the FILL is neutralised. The `.band-dark` token remap stays in force, so
   the ink stays exactly as authored: the flagship hero inline-styles its <h1>
   `color:var(--d-ink)` and its lead `var(--d-ink-2)`, and those are the navy
   #0d1424 / #3a4358 pair, which measure BETTER on white (18.6:1) than on the
   periwinkle they were checked against. Undoing the remap instead of the fill
   would have re-armed the `--accent-ink` / `--brand-sky` rules above and is the
   wrong lever.

   Covers BOTH product-page templates, which are separate code paths that happen
   to emit the same id:
     · flagship.js orbitHero()  -> the 4 bespoke SKUs (GLP-1, GLP-2, GLP-3 RT,
       BPC-157): heading + prose + 99%+/USA/3rd-party proof stats + the atom
       composition field. Verified: not one descendant has a background, so
       nothing in it relied on the band for contrast.
     · product.js  (~line 577) -> the other 26: signature strip, "Studied for"
       bars, stat tiles. `.csig` resolves to --d-surface (#ffffff) and would go
       white-on-white, but it carries a 1px #e5e5e5 box-shadow ring and stays
       legible. `.stat-tile` (5% navy) and `.ucase__track` (7% navy) are tints,
       not solids, so they read on either ground.

   Scoped to `#sec-overview`, which exists on product pages only. `.band-dark`
   elsewhere - home, bundles, featured, new-lots, stack, the footer - is
   untouched and keeps the periwinkle.
   ------------------------------------------------------------------------- */
#sec-overview.band-dark { background:transparent; }

/* The dot grid has to be re-stated, not just inherited. `.band-dark.grid-bg`
   (line ~79) darkens the dots to rgba(13,20,36,.10) because they were being
   read against the periwinkle; on white that same value reads about twice as
   heavy as the plain `.grid-bg` siblings two sections down. These are the
   values from `.grid-bg::before` in styles.css, restated here because the
   two-class selector above outranks the one-class base rule. */
#sec-overview.band-dark.grid-bg::before {
  background:
    radial-gradient(rgba(20,20,20,.055) 1px, transparent 1px),
    radial-gradient(640px 320px at 50% 0%, rgba(32,49,145,.10), transparent 70%);
  background-size:22px 22px, 100% 100%;
  opacity:1;
}
/* ==== END: PDP overview band de-tint ===================================== */
