/* ============================================================================
   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 {
  --d-bg:#e8ecfb;                       /* was #050505 - the recovery-area wash */
  --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;
}

@media (max-width:760px) {
  /* stack rather than wrap into a ragged second row */
  .bogo-strip { padding:16px 18px; gap:14px; }
  .bogo-strip__pills { order:3; flex-basis:100%; }
  .bogo-strip__cta { order:4; }
}

/* ---- 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. */
.section .card.stack-promo,
.stack-promo {
  background:linear-gradient(115deg,#16225e 0%,#203191 32%,#3a4fc4 58%,#1b2a7a 100%);
  background-size:220% 220%;
  border:none;
  border-radius:20px;
  padding:clamp(28px,4vw,52px);
  box-shadow:0 18px 44px rgba(22,34,94,.28);
  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 */
.stack-promo::before {
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:
    radial-gradient(680px 320px at 12% 0%, rgba(147,197,253,.30), transparent 62%),
    radial-gradient(520px 300px at 96% 108%, rgba(134,239,172,.16), transparent 60%);
}
.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;
}

@media (prefers-reduced-motion:reduce) { .stack-promo { animation: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; }
.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);
}
