/* ===================== Glow Research — styles ===================== */

/* ---------- fonts, self-hosted ----------
   Used to load from Google's CDN (fonts.googleapis.com / fonts.gstatic.com).
   That is one more network request that has to succeed, on a connection this
   site does not control, before a single custom letterform can render — and
   on a weak mobile signal it sometimes does not, which silently falls back
   through the stack to a plain system font with no error anywhere to catch.
   Shipping the files ourselves removes that dependency entirely: the fonts
   are just part of the site now, cached and served the same way any other
   asset here is.

   Each family below is one variable-weight woff2 (Google serves the same
   file for every weight requested from a variable source; verified by
   downloading the CSS this stylesheet used to import and comparing URLs
   across weights), so five files cover every weight this site uses. Latin
   subset only — the one Google subset that covers plain English copy,
   matching what fonts.googleapis.com/css2's own "latin" block served.
   Re-fetch tools/README's font-refresh note (below) if a new weight or
   family is ever needed. */
@font-face{
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../assets/fonts/space-grotesk.woff2') format('woff2');
}
@font-face{
  font-family: 'Sora';
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url('../assets/fonts/sora.woff2') format('woff2');
}
@font-face{
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 500 600;
  font-display: swap;
  src: url('../assets/fonts/fraunces-normal.woff2') format('woff2');
}
@font-face{
  font-family: 'Fraunces';
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url('../assets/fonts/fraunces-italic-500.woff2') format('woff2');
}
@font-face{
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../assets/fonts/jetbrains-mono.woff2') format('woff2');
}

:root{
  --bg: #0a0a0a;
  --bg-2: #111111;
  --surface: #141414;
  --surface-2: #1a1a1a;
  --border: rgba(255,255,255,0.1);
  --text: #f7f7f5;
  --text-dim: #a3a3a1;
  --text-dimmer: #6b6b69;

  /* monochrome palette — all "accent" variables map to white/gray tones */
  --pink: #ffffff;
  --pink-soft: #e4e4e2;
  --orange: #cfcfcd;
  --purple: #ffffff;
  --teal: #e4e4e2;
  --yellow: #ffffff;

  --grad-main: linear-gradient(120deg, #ffffff 0%, #cfcfcd 50%, #9c9c99 100%);
  --grad-alt: linear-gradient(120deg, #ffffff, #8a8a87);

  /* The display face: every page title, price, stat numeral and badge.
     Named once so changing it means changing one line, not the thirty-odd
     places across the stylesheet and page-level <style> blocks that set it
     directly. Sora is the fallback: it is already being downloaded for body
     copy, so degrading to it before a system sans costs no extra request. */
  --font-display: 'Space Grotesk', 'Sora', Arial, sans-serif;

  --radius: 20px;
  --radius-sm: 12px;
  --container: 1180px;

  --shadow-glow: 0 0 60px rgba(255, 255, 255, 0.12);
}

*,*::before,*::after{ box-sizing: border-box; }
html{ scroll-behavior: smooth; }
body{
  margin:0;
  background: var(--bg);
  color: var(--text);
  font-family: 'Sora', system-ui, sans-serif;
  /* Sora Medium: the weight for navigation and body copy sitewide. Anything
     that wants a different weight (a button, a badge, a numeral) sets its
     own font-weight; this is the default the rest of the page inherits. */
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
  /* No overflow-x here, deliberately. It sat on this rule to clip the
     announcement marquee, whose track is deliberately wider than the screen,
     but .marquee-bar has always clipped its own track, so the rule was
     clipping nothing.
     
     What it did do is make <body> a scroll container: overflow-x on its own
     computes overflow-y to auto, and the page then has the document scrolling
     and the body scrolling inside it. On iOS that is the "you reach the
     bottom and can keep going" bug, an empty screen of background under the
     footer. tools/check-claims.js holds the marquee to clipping itself, which
     is what keeps this removable. */
}
img{ max-width:100%; display:block; }
a{ color: inherit; text-decoration:none; }
h1,h2,h3,h4,h5{
  font-family: var(--font-display);
  /* Space Grotesk SemiBold: the weight for every title and heading sitewide. */
  font-weight: 600;
  line-height: 1.08;
  margin: 0 0 .5em;
  letter-spacing: -0.02em;
}
p{ margin:0 0 1em; color: var(--text-dim); line-height:1.65; }
.container{ max-width: var(--container); margin: 0 auto; padding: 0 24px; }

.grad-text{
  background: var(--grad-main);
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: shimmer 6s ease-in-out infinite;
}
@keyframes shimmer{
  0%,100%{ background-position: 0% 50%; }
  50%{ background-position: 100% 50%; }
}

.mono{ font-family:'JetBrains Mono', monospace; letter-spacing: 0.08em; }

/* ---------- buttons ---------- */
.btn{
  display: inline-flex; align-items:center; justify-content:center; gap:10px;
  padding: 16px 28px;
  border-radius: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  border: 1.5px solid transparent;
  cursor: pointer;
  transition: transform .25s ease, box-shadow .25s ease, background .25s ease, color .25s ease, border-color .25s ease;
  white-space: nowrap;
}
.btn-primary{
  background: #ececea;
  color: #0a0a0a;
  /* a shade darker than the fill, so the chip has a defined edge instead of
     dissolving into whatever sits behind it */
  border-color: #d8d8d5;
  /* background/border settle faster than the transform so the hover reads as
     an immediate response rather than a slide */
  transition: background .15s ease, border-color .15s ease,
              transform .25s ease, box-shadow .25s ease, color .25s ease;
}
.btn-primary:hover{
  background: #f0f0f0;
  border-color: #cfcfcb;
}
/* btn-primary is a pale chip built for dark sections. On the white overlay
   surfaces (cart drawer, checkout, quick-add) it reads as a disabled button,
   so invert it there. */
.cart-drawer .btn-primary,
.co-page .btn-primary,
.qa-sheet .btn-primary,
.search-modal .btn-primary{
  background:#0a0a0a; color:#fff;
  /* the light edge that defines the chip on dark sections would ring a black
     button here, so match the border to the inverted fill */
  border-color:#0a0a0a;
}
.cart-drawer .btn-primary:hover,
.co-page .btn-primary:hover,
.qa-sheet .btn-primary:hover,
.search-modal .btn-primary:hover{
  background:#2a2a28; color:#fff;
  border-color:#2a2a28;
}
.btn-outline{
  background: transparent;
  border-color: rgba(255,255,255,0.55);
  color: var(--text);
}
.btn-outline:hover{
  border-color: #fff;
  background: rgba(255,255,255,0.08);
  color: var(--text);
  box-shadow: 0 0 32px 2px rgba(255,255,255,.3);
}
/* the hero's second CTA is deliberately the quiet option, so its hover is a
   plain lighten, not the glow the outline button gets elsewhere */
.hero-cta .btn-outline:hover{
  background: rgba(255,255,255,0.14);
  box-shadow: none;
}
.btn-ghost{
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 10px;
  border-radius: 999px;
}
.btn-ghost:hover{ background: rgba(255,255,255,0.12); }
.btn-lg{ padding: 15.5px 29px; font-size: 1rem; }

/* ---------- async button state ----------
   Shared across any form submit that hits the network (sign in, forgot
   password, reset password, wholesale apply): the label crossfades to a
   spinner instead of a bare disabled button, so a request that takes a
   moment reads as "working" rather than "stuck". Same ring-of-eight-ticks
   mark iOS/macOS use, not a spinning arc. */
.btn-label{ display:inline-flex; align-items:center; gap:9px; transition: opacity .12s ease; }
.btn[disabled],
button[disabled].si-forgot{ cursor: progress; }
.btn-spin{ position:relative; width:15px; height:15px; flex-shrink:0; }
.btn-spin i{
  position:absolute; top:50%; left:50%; width:2px; height:5px;
  margin:-2.5px 0 0 -1px; border-radius:1px; background:currentColor;
  animation: btnTick .8s linear infinite;
}
@keyframes btnTick{ 0%{ opacity:1; } 100%{ opacity:.15; } }
@media (prefers-reduced-motion: reduce){
  .btn-spin i{ animation: none; opacity:.55; }
}

/* press feedback — short duration so the push reads as a click, not a slide */
.btn:active{ transform: translateY(1px) scale(.988); transition-duration: .06s; }

/* keyboard focus: white ring on the dark hero, ink ring on the light header */
.btn:focus-visible,
.chip:focus-visible,
.add-btn:focus-visible{
  outline: 2px solid #fff; outline-offset: 3px;
}
.site-header :focus-visible,
.section-light :focus-visible{
  outline: 2px solid #0a0a0a; outline-offset: 3px;
}
/* The white ring above is for the dark sections. These surfaces are white, so
   it lands white-on-white and a keyboard user sees nothing at all. Declared
   after the .btn rule so it wins the specificity tie. */
.pd-page :focus-visible,
.co-page :focus-visible,
.cart-drawer :focus-visible,
.qa-sheet :focus-visible,
.search-modal :focus-visible{
  outline: 2px solid #0a0a0a; outline-offset: 3px;
}

/* ---------- marquee ---------- */
.marquee-bar{
  background: linear-gradient(90deg, #0a0a0a, #1c1c1c);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
  padding: 8px 0;
}
.marquee-track{
  display:flex;
  gap: 48px;
  white-space: nowrap;
  animation: marquee 28s linear infinite;
  width: max-content;
}
.marquee-track span{
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  font-weight: 600;
  color: #d4d4d2;
  text-transform: uppercase;
  font-family: 'JetBrains Mono', monospace;
}
.star-bullet{
  display:inline-block; font-style:normal; color:#6b6b69; font-size: 0.6rem;
  margin: 0 40px 0 -8px; flex-shrink:0;
  animation: twinkle 2.4s ease-in-out infinite;
}
@keyframes twinkle{
  0%,100%{ opacity:.35; transform: scale(.85) rotate(0deg); }
  50%{ opacity:1; transform: scale(1.15) rotate(20deg); }
}
@keyframes marquee{
  from{ transform: translateX(0); }
  to{ transform: translateX(-50%); }
}
.marquee-bar:hover .marquee-track{ animation-play-state: paused; }

/* ---------- header ---------- */
.site-header{
  position: sticky; top:0; z-index: 100;
  background: #fff;
  border-bottom: 1px solid rgba(0,0,0,0.1);
  transition: box-shadow .3s ease;
  color: #0a0a0a;
}
.nav-inner{
  display:flex; align-items:center; justify-content:space-between;
  height: 70px;
}
.logo{
  font-family: 'Fraunces', Georgia, serif;
  font-size: 1.65rem;
  font-weight: 600;
  display:inline-flex; align-items:center; gap:3px;
  color: #0a0a0a;
}
.spark{
  color: #0a0a0a; font-size: 0.7em; display:inline-block;
  transition: transform .5s cubic-bezier(.34,1.56,.64,1);
}
.logo:hover .spark{ transform: rotate(90deg) scale(1.25); }
.main-nav{ display:flex; gap: 34px; }
.main-nav a{
  font-size: 0.88rem; font-weight: 500; color: #45453f;
  position: relative; padding: 4px 0;
  transition: color .2s ease;
}
.main-nav a::after{
  content:''; position:absolute; left:0; bottom:-2px; width:0; height:2px;
  background: #0a0a0a; transition: width .25s ease;
}
.main-nav a:hover{ color: #0a0a0a; }
.main-nav a:hover::after{ width: 100%; }
.main-nav a.active{ color: #0a0a0a; }
.main-nav a.active::after{ width: 100%; }
.nav-actions{ display:flex; align-items:center; gap: 10px; }
.nav-icon-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  height: 42px; padding: 0 14px;
  background: transparent; border: 1.5px solid rgba(0,0,0,0.18); color:#0a0a0a;
  border-radius: 0; cursor:pointer;
  font-family: inherit; font-weight: 600; font-size: 0.9rem;
  transition: border-color .2s ease, background .2s ease;
}
.nav-icon-btn:hover{ border-color:#0a0a0a; background: rgba(0,0,0,0.05); }
.nav-icon-btn:active{ transform: translateY(1px); transition-duration: .06s; }
#searchToggle, .nav-cart{ width: 42px; padding:0; }
.nav-cart{ position: relative; }
.nav-cart-badge{
  position:absolute; top:-8px; right:-8px;
  min-width: 18px; height:18px; padding: 0 4px;
  display:flex; align-items:center; justify-content:center;
  background:#0a0a0a; color:#fff;
  font-size: 0.65rem; font-weight:700;
  border-radius: 0;
}
/* an empty cart shouldn't advertise itself — badge appears on first add */
.nav-cart-badge[data-count="0"]{ display:none; }
.nav-cart-badge.bump{ animation: badgeBump .32s ease; }
@keyframes badgeBump{
  0%{ transform: scale(1); }
  40%{ transform: scale(1.45); }
  100%{ transform: scale(1); }
}
.hamburger{
  display:none; flex-direction:column; gap:5px; background:none; border:none; cursor:pointer; padding: 6px;
}
.hamburger span{ width: 22px; height:2px; background: #0a0a0a; border-radius: 2px; transition: .3s; }


/* ---------- hero ---------- */
.hero{
  position: relative;
  padding: 62px 0 68px;
  overflow: hidden;
  isolation: isolate;
  background: #000000;
}

/* AI-generated nebula backdrop, used as atmosphere only — masked so its own
   (mistyped) vial render fades to black and our real product stays the only
   readable vial in the frame, and darkened on the left so the copy stays legible */
.hero-nebula-bg{
  position:absolute; inset:0; z-index:-4;
  background: url('../assets/hero-nebula-bg.jpg') center 38% / cover no-repeat;
  opacity:.4;
}
.hero-nebula-bg::after{
  content:'';
  position:absolute; inset:0;
  background:
    radial-gradient(ellipse 62% 78% at 56% 50%, #000 0%, #000 32%, rgba(0,0,0,.85) 48%, transparent 72%),
    linear-gradient(90deg, #000 0%, rgba(0,0,0,.96) 34%, rgba(0,0,0,.5) 60%, rgba(0,0,0,.62) 100%);
}
.hero-glow{
  position:absolute; border-radius:50%; filter: blur(110px); z-index:-1; opacity:.09;
  animation: float 12s ease-in-out infinite;
}
.glow-1{ width:480px;height:480px; background: #3a3a38; top:-180px; left:-120px; }
.glow-2{ width:420px;height:420px; background: #2a2a28; top:60px; right:-160px; animation-delay: -4s; }
.glow-3{ width:360px;height:360px; background: #222220; bottom:-200px; left:35%; animation-delay: -8s; }
@keyframes float{
  0%,100%{ transform: translate(0,0) scale(1); }
  50%{ transform: translate(30px,-20px) scale(1.08); }
}
#particleCanvas{ position:absolute; inset:0; z-index:-1; opacity:.6; }

/* two-column stage: copy left, showpiece right */
.hero-grid{
  position:relative; z-index:1;
  display:grid; grid-template-columns: 1fr; align-items:center;
  min-height: min(60vh, 540px);
}
.hero-copy{ text-align:left; max-width: 700px; transition: transform .35s cubic-bezier(.2,.8,.2,1); will-change: transform; }
.hero-eyebrow{
  display:inline-flex; align-items:center; gap: 8px; font-family:'JetBrains Mono', monospace;
  font-size: 0.74rem; letter-spacing:.18em; text-transform:uppercase; font-weight:600;
  color: #ffffff; margin-bottom: 16px; position: relative; z-index: 1;
}
@keyframes pulse{ 0%,100%{opacity:1;} 50%{opacity:.35;} }

/* the hero line — solid, semibold, full white, largest size on the page */
.hero-title{
  font-size: clamp(2.4rem, 5vw, 4.1rem);
  font-weight: 600;
  color: var(--text);
  margin-bottom: 24px;
  letter-spacing: -0.03em;
}
/* Pin the headline to "Trusted" / "Research Peptides." at every width,
   phone included. Left to wrap on its own it breaks after "Research"
   instead, orphaning "Peptides." on its own line. */
.hero-br{ display:inline; }
/* welcome.html's own headline runs bigger than index.html's shared default
   above: it is the whole reason a visitor lands here, with no catalog grid
   or nav competing for attention below it, so it can afford to be the
   biggest thing on the page. The wider column gives the second line
   ("Research Peptides.") the room the bigger size needs without wrapping to
   a third; the hero photograph's own fade (see .hero-visual-img) already
   tolerates copy running further right than the base 700px column does. */
.wl-hero .hero-copy{ max-width: 860px; }
.wl-hero .hero-title{ font-size: clamp(2.4rem, 6.4vw, 5.4rem); }
/* Outline treatment shared with 404.html's stub-hero heading: same idea,
   thin stroke instead of a fill, for a headline word that should read as
   a second weight rather than a second color. */
/* The one word set in stroke rather than fill. Both heroes take it: it is the
   most recognisable thing the type does anywhere on the site, and the welcome
   headline reading as flat white was most of why that page looked like it
   belonged to a different brand. */
.hero-title .outline,
.hero-title .outline{ color: transparent; -webkit-text-stroke: 1.4px rgba(255,255,255,.82); }
/* Back to a flowing paragraph, but not at the plain-paragraph default
   (400 weight, --text-dim): semibold at a shade lighter than the headline's
   full white reads as confident supporting copy rather than body text.
   Line-height opened up a touch over the site's 1.65 default, since bold
   text at a tight leading looks cramped next to the headline above it. */
.hero-sub{
  font-size: 1.05rem; font-weight: 600; color: #c7c7c4; line-height: 1.62;
  max-width: 500px;
  margin: 0 0 32px;
}
@media (max-width: 560px){
  .hero-sub{ font-size: 0.83rem; }
}
/* hero rhythm runs on an 8px scale end to end: eyebrow 16 → title 24 →
   sub 32 → cta 24 → rule → stats 24. The gaps were previously 20/20/30/30/22,
   close enough to look almost-systematic, which is what read as sloppy. */
.hero-cta{ display:flex; gap:16px; flex-wrap:wrap; margin-bottom: 24px; }

/* welcome.html's hero leads with one dominant button, not two side by side
   like index.html, so it does not inherit that sizing: it fills the same
   column hero-copy already sets a max-width on, at every width the mobile
   rules below don't already cover. Its full width is also what pushes
   .wl-cta-coa onto its own line below via flex-wrap, with no flex-direction
   override needed at any width. */
.wl-cta-shop,
.wl-cta-coa{ width:100%; text-align:center; }
/* Still the quiet option next to Shop Peptides: an outline button, not a
   filled one, so the hierarchy between them reads the same way it did when
   they sat side by side, just stacked instead of side by side now. */
.wl-cta-coa{ margin-top: 4px; }

/* the rule seals the stats to the block above rather than letting them float
   as a separate stack — equal 24px breathing room on both sides of it */
.hero-stats{
  display:flex; gap: 40px; flex-wrap:wrap;
  padding-top: 24px; border-top: 1px solid var(--border);
}
.stat{ text-align:left; }
.stat-num, .stat-suffix{
  font-family:var(--font-display); font-size: 1.9rem; font-weight:500;
  background: var(--grad-main); -webkit-background-clip:text; background-clip:text; color:transparent;
}
.stat p{ margin:4px 0 0; font-size: 0.76rem; color: var(--text-dimmer); text-transform: uppercase; letter-spacing:.06em; }
/* Escape hatch for a stat whose headline is a word rather than a number. Keeps
   the gradient and weight but drops a size, because a whole word set at the
   numeral size crowds the other two out of the row. js/script.js skips it when
   it runs the count-up: there is nothing to count. */
.stat-num.stat-text{ font-size: 1.5rem; letter-spacing:-.01em; }

/* Below 640px the three figures have to hold one row: wrapped, a lone stat on
   a second line reads as an afterthought rather than part of the set. Three
   equal columns instead of content-width flex, so they divide the space rather
   than overflow it, and the label drops to a size that keeps each one on a
   single line at 360px. Labels stay short for the same reason. */
@media (max-width: 640px){
  .hero-stats{
    display:grid; grid-template-columns: repeat(3, 1fr);
    gap: 0 12px; flex-wrap:nowrap;
  }
  .stat-num, .stat-suffix{ font-size: 1.6rem; letter-spacing:-.02em; }
  .stat p{ font-size: 0.62rem; letter-spacing:.04em; white-space:nowrap; }
}
@media (max-width: 380px){
  .stat-num, .stat-suffix{ font-size: 1.42rem; }
  .stat p{ font-size: 0.58rem; letter-spacing:.02em; }
}

/* ---- hero showpiece: oversized product visual, bleeding into the frame ---- */
/* wide trio shot, anchored bottom-right and oversized so it bleeds off the
   right edge and gets cropped clean by the hero's own overflow:hidden at
   the bottom — feels like the image continues past the section rather than
   sitting boxed inside it. Left edge fades to black so the copy stays legible. */
.hero-visual-img{
  position:absolute; z-index:0;
  /* Held to the right half and bled off the right edge, which is the treatment
     this shot was composed for and the one that was here before the clip
     briefly took the slot. The hero grid is left-set, so the copy owns the
     left and this owns what is left over: anything that creeps under the
     headline is fighting it rather than filling the page.

     Two masks, intersected, and between them they are the whole blend. The
     linear one is the important one: transparent to 6%, fully solid only by
     46%, so the left side of the photograph does not end anywhere, it
     dissolves into the section long before it reaches the copy. The radial
     is a vignette over the vials themselves, pulling the light into the
     middle of the shot and taking the corners down to nothing.

     Note the angle. 100deg rather than 90deg tilts the fade off vertical so it
     runs with the vials, which stand at a slight lean of their own. */
  right: -6%; bottom: -9%;
  width: min(1240px, 88vw); height:auto; max-width:none;
  border-radius: 0;
  -webkit-mask-image: linear-gradient(100deg, transparent 0%, transparent 6%, #000 46%, #000 100%),
                       radial-gradient(ellipse 95% 90% at 62% 45%, #000 55%, transparent 98%);
  -webkit-mask-composite: source-in;
          mask-image: linear-gradient(100deg, transparent 0%, transparent 6%, #000 46%, #000 100%),
                       radial-gradient(ellipse 95% 90% at 62% 45%, #000 55%, transparent 98%);
          mask-composite: intersect;
  opacity: .82;
}
/* scrim behind the copy column so the oversized image never fights legibility */
.hero-copy-scrim{
  position:absolute; z-index:0; inset:0;
  background: linear-gradient(90deg, #000 0%, #000 34%, rgba(0,0,0,.75) 52%, transparent 78%);
}


.scroll-cue{
  position:absolute; bottom: 24px; left:50%; transform:translateX(-50%);
  width: 26px; height: 42px; border: 2px solid var(--border); border-radius: 20px;
}
.scroll-cue span{
  display:block; width:4px; height:8px; background: var(--pink); border-radius:2px;
  margin: 6px auto; animation: scrollcue 1.6s infinite;
}
@keyframes scrollcue{ 0%{ transform:translateY(0); opacity:1;} 70%{opacity:0;} 100%{ transform:translateY(14px); opacity:0;} }

/* ---------- sections ---------- */
.section{ padding: 110px 0; }
/* Half the sitewide band at both ends. The grid is dense and reads as its own
   block, so the section does not need the full 110 to separate from the hero
   above it or the standard cards below. */
#peptides{ padding-top: 39px; padding-bottom: 55px; }
.section-dark{ background: var(--bg-2); }
.section-light{ background:#fff; color:#0a0a0a; }
.section-light p{ color:#55554f; }
.section-head{ max-width: 640px; margin-bottom: 56px; }
.section-head.center{ margin-left:auto; margin-right:auto; text-align:center; }
.eyebrow{
  display:inline-block; font-size: 0.75rem; font-weight:600; letter-spacing:.14em;
  text-transform: uppercase; color: var(--text-dimmer); margin-bottom: 14px;
  font-family:'JetBrains Mono', monospace;
}
.section-head h2{ font-size: clamp(1.9rem, 3.6vw, 2.8rem); }
.section-head.light h2{ color: var(--text); }
.section-light .eyebrow{ color:#8a8a86; }
.section-light .section-head h2{ color:#0a0a0a; }
.section-light .grad-text{
  background: linear-gradient(120deg, #0a0a0a 0%, #4a4a48 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* ---------- catalog page ----------
   The catalog has no hero above it, so the stock 110px section padding left a
   dead band of white between the header and the first chip. This page opens
   straight into the goods instead. Bottom padding leaves room for the fixed
   search bar so the last row of cards isn't sitting under it. */
.catalog-page{ padding: 34px 0 90px; }
.catalog-page .section-head{ margin-bottom: 20px; }
.catalog-page .filter-row{ margin-bottom: 26px; }

.product-grid-empty{
  grid-column: 1 / -1; margin: 0; padding: 44px 0;
  text-align:center; color:#55554f;
}

/* ---------- filter chips ---------- */
/* filters left, sort right; the sort drops onto its own line before the chips
   are forced to wrap awkwardly against it */
.catalog-controls{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap: 16px; flex-wrap:wrap; margin-bottom: 26px;
}
.catalog-controls .filter-row{ margin-bottom: 0; flex: 1 1 auto; }

.sort-field{ position:relative; display:inline-flex; align-items:center; flex-shrink:0; }
.sort-select{
  appearance:none; -webkit-appearance:none;
  padding: 12px 40px 12px 16px; border-radius: 0;
  border:1px solid var(--border); background: transparent; color: var(--text);
  font-family:'Sora', sans-serif; font-size:0.9rem; font-weight:600;
  letter-spacing:-0.005em; cursor:pointer;
  transition: border-color .2s ease, background .2s ease;
}
.sort-caret{
  position:absolute; right:14px; pointer-events:none; color: var(--text-dim);
}
.section-light .sort-select{ border-color: rgba(0,0,0,0.22); color:#45453f; }
.section-light .sort-select:hover{ border-color:#0a0a0a; color:#0a0a0a; background: rgba(0,0,0,.04); }
.section-light .sort-caret{ color:#45453f; }
/* the native dropdown list is drawn by the OS, so it needs its own colors —
   without this the options render white-on-white on some platforms */
.sort-select option{ background:#fff; color:#0a0a0a; }

@media (max-width: 640px){
  .catalog-controls{ flex-direction:column; align-items:stretch; }
  .sort-field{ width:100%; }
  .sort-select{ width:100%; }
}

.catalog-search{ position:relative; flex:1 1 auto; display:flex; align-items:center; }
.catalog-search-icon{ position:absolute; left:14px; color: var(--text-dim); pointer-events:none; }
.catalog-search-input{
  width:100%; padding: 12px 16px 12px 40px; border-radius: 0;
  border:1px solid var(--border); background: transparent; color: var(--text);
  font-family:'Sora', sans-serif; font-size:0.9rem; font-weight:500;
  transition: border-color .2s ease, background .2s ease;
}
.catalog-search-input::placeholder{ color: var(--text-dimmer); }
.catalog-search-input:hover{ border-color: rgba(255,255,255,.32); }
.catalog-search-input:focus{ outline:none; border-color:#fff; background: var(--surface-2); }
.catalog-search-input:focus-visible{ outline: 2px solid #fff; outline-offset: 2px; }
.section-light .catalog-search-icon{ color:#45453f; }
.section-light .catalog-search-input{ border-color: rgba(0,0,0,0.22); color:#0a0a0a; }
.section-light .catalog-search-input::placeholder{ color:#7a7a76; }
.section-light .catalog-search-input:hover{ border-color:#0a0a0a; }
.section-light .catalog-search-input:focus{ outline:none; border-color:#0a0a0a; background: rgba(0,0,0,.03); }

.filter-row{ display:flex; gap: 10px; flex-wrap:wrap; margin-bottom: 36px; }
/* squared boxes rather than pills — the site's buttons and product tiles are
   all sharp-cornered, so a rounded pill was the one soft shape left. The
   active state inverts to solid, the same flip every button here does. */
.chip{
  padding: 12px 22px; border-radius: 0; border:1px solid var(--border);
  background: transparent; color: var(--text-dim);
  font-family:'Sora', sans-serif; font-size:0.9rem; font-weight:600;
  letter-spacing:-0.005em; cursor:pointer;
  transition: background .2s ease, color .2s ease, border-color .2s ease;
}
.chip:hover{ border-color: var(--pink); color: var(--text); background: rgba(255,255,255,.06); }
.chip.active{ background: var(--text); color:#0a0a0a; border-color: var(--text); }
.section-light .chip{ background:transparent; border-color: rgba(0,0,0,0.22); color:#45453f; }
.section-light .chip:hover{ border-color:#0a0a0a; color:#0a0a0a; background: rgba(0,0,0,.04); }
.section-light .chip.active{ background:#0a0a0a; color:#fff; border-color:#0a0a0a; }

/* On a phone, five category chips wrapping to three rows push the search bar
   and the first cards below the fold before a visitor sees anything. One
   swipeable row reads instantly, the way the sort/search bar above it
   already reads as "controls, not content" without demanding vertical space. */
@media (max-width: 640px){
  .catalog-page .filter-row{
    flex-wrap:nowrap; overflow-x:auto; -webkit-overflow-scrolling:touch;
    margin-left:-24px; margin-right:-24px; padding:0 24px 4px 0;
    /* hide the scrollbar itself; the row scrolling is the affordance */
    scrollbar-width:none;
  }
  .catalog-page .filter-row::-webkit-scrollbar{ display:none; }
  .catalog-page .filter-row .chip{ flex:0 0 auto; white-space:nowrap; }
  /* Chrome drops a scroll container's leading padding once its content
     overflows, so "All" sat flush against the screen edge instead of lining
     up with the search box above it. Trailing padding survives fine — this
     margin does the same job for the edge that doesn't. scroll-snap was the
     actual culprit for a worse version of the same bug: its first "start"
     snap point pulled the row's initial scroll position past this margin
     the instant the page painted, so it's gone rather than fought. */
  .catalog-page .filter-row .chip:first-child{ margin-left:24px; }
}

.section-light .btn-outline{
  border-color: rgba(0,0,0,0.35); color:#0a0a0a;
}
.section-light .btn-outline:hover{
  border-color:#0a0a0a; background: rgba(0,0,0,0.05); color:#0a0a0a;
  box-shadow: none;
}
.catalog-view-all{ display:flex; justify-content:center; margin-top: 40px; }

.catalog-head{
  display:flex; align-items:flex-end; justify-content:space-between; gap: 24px;
  max-width: none;
}
.catalog-head-copy{ max-width: 640px; }
.catalog-head .eyebrow{ margin-bottom: 10px; }
.catalog-head h2{ margin:0; }
.catalog-head .btn-outline{ flex-shrink:0; white-space:nowrap; }
/* Stays a row on a phone rather than stacking, which is what the wider
   layout above already does and what the heading is short enough to allow:
   "Peptides" and the link fit one line together at 360px once the link stops
   claiming the full width. It is the same relationship either way then,
   heading left and the way out of the section on the right, instead of a
   full-width button reading as the section's own call to action. */
@media (max-width: 640px){
  .catalog-head{ align-items:flex-end; gap: 12px; margin-bottom: 34px; }
  .catalog-head .btn-outline{ width:auto; padding: 10px 13px; font-size: 0.76rem; }
}
/* The row is 16px too wide for a 320px screen at the sizes above, and what
   overflows is the arrow on the end of the link. Both sides give a little
   rather than the heading alone, which would leave it smaller than the h2
   on every other section. */
@media (max-width: 359px){
  .catalog-head h2{ font-size: 1.6rem; }
  .catalog-head .btn-outline{ padding: 9px 10px; font-size: 0.72rem; }
}

/* ---------- product grid ---------- */
.product-grid{
  display:grid; grid-template-columns: repeat(auto-fill, minmax(310px,1fr)); gap: 24px;
}
/* Four across on the full desktop nav width. auto-fill only reaches 3 at
   this container width with a 310px floor, so pin it explicitly rather than
   shrinking the floor sitewide (which would also shrink the 2-column tablet
   range below, where 3-per-row auto-fill still reads better). */
@media (min-width: 981px){
  .product-grid{ grid-template-columns: repeat(4, minmax(0,1fr)); }
}
/* squared off with a defined edge so each tile reads as an actual box on the
   page, matching the sharp-cornered language the buttons already use — the
   12px radius was rounding them into soft blobs */
/* A grid row stretches every card in it to the height of the tallest one
   (a two-line name from a longer product like a blend, most often) — that's
   the grid, not this rule. Without flex-column here, only .product-visual's
   fixed aspect-ratio and .product-footer's own content claimed any of that
   extra height, leaving the difference as blank white .product-card
   background below the black footer. flex + .product-footer{flex:1} below
   gives that space to the footer instead, so the black block always reaches
   the card's bottom edge. */
.product-card{
  background:#fff;
  border: 1px solid rgba(0,0,0,0.16);
  border-radius: 0;
  overflow:hidden;
  position: relative;
  display:flex; flex-direction:column;
  transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}
.product-card:hover{
  transform: translateY(-5px);
  border-color: rgba(0,0,0,.38);
  box-shadow: 0 20px 40px -20px rgba(0,0,0,.35);
}

/* A vial is a tall, narrow subject, so a short wide tile letterboxes it and
   the bottle ends up tiny. Keeping the tile roughly square gives the vial the
   height it needs to fill the frame. */
.product-visual{
  aspect-ratio: 1 / 1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  position:relative; overflow:hidden;
  background: linear-gradient(180deg,#f6f6f4,#e7e7e4);
}
/* Every product shot ships photographed on white, which is already what
   .product-visual fades to — so it drops straight in with no card or border
   around it, and multiply drops the shot's white backdrop out entirely
   rather than sitting on top of the gradient as its own visible rectangle. */
.product-photo{
  height:100%; width:100%; object-fit:contain; padding: 2% 0;
  mix-blend-mode: multiply;
}
/* the small square thumb used by search, the cart drawer and checkout — they
   all share the same light gradient tile, so one rule covers all three */
.thumb-photo{
  height:92%; width:92%; object-fit:contain;
  mix-blend-mode: multiply;
}
/* square to match the tile; z-index keeps them above the photo, and the two
   badges are capped so a long status ("Best Seller") can't run under the
   category chip on a narrow two-up mobile tile */
/* One inset row across the top of the tile rather than two chips pinned to
   opposite corners. The corner-pinned version needed a max-width on each to
   stop them meeting in the middle, and a fixed cap cannot know that "Sale" is
   short and "Best Seller" is not: it clipped the status chip to half the tile
   with 39px still free beside it. Flex gives each chip the width it needs and
   only shrinks them when they genuinely run out of room. */
.product-badges{
  position:absolute; top:12px; left:12px; right:12px; z-index:2;
  display:flex; align-items:flex-start; gap:8px;
}
.product-badge{
  font-size: 0.62rem; font-weight:700;
  padding: 5px 10px; border-radius: 0; text-transform:uppercase; letter-spacing:.06em;
  font-family:'JetBrains Mono', monospace;
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.product-badge.cat{ background: rgba(10,10,10,0.72); color:#fff; }
.product-badge.status{ margin-left:auto; background:#0a0a0a; color:#fff; }
/* Left slot, opposite the status chip, and solid rather than the recessive
   .cat treatment: the tile behind it is a light gradient, so a white or
   translucent chip loses its edge against the product shot. Reads as a pair
   with .status, told apart by side and wording rather than by colour, since
   the palette above is monochrome by decision. Only rendered when the
   catalog actually holds a list price above the charged one. */
.product-badge.sale{ background:#0a0a0a; color:#fff; }
/* Out of stock replaces the promo badge rather than sitting beside it —
   two badges on one card is the clutter the badge was meant to cut through. */
.product-badge.status.is-out{ background:#8a8a86; color:#fff; }
.product-footer{
  background:#0a0a0a; color:#fff; padding: 20px 22px 22px; position:relative;
  flex:1; display:flex; flex-direction:column;
}
/* fades out at each edge instead of a hard-edged rule, so it reads as a
   seam rather than a box border. Used both at the top of the footer (where
   the tag line used to sit) and between the name and the price. */
.product-footer::before{
  content:''; position:absolute; top:0; left:22px; right:22px; height:1px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.35), transparent);
}
.card-divider{
  display:block; height:1px; margin: 2px 0 14px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.35), transparent);
}
.product-card h3{ font-size: 1.15rem; margin: 4px 0 0; color:#fff; }
/* The catalog name is Glow's own shorthand (BPC-157/TB-500 is "Wolverine"
   among researchers), not what most people searching for the compound type.
   Namesake in plain words, once, directly under the name it belongs to. */
.product-alias{ font-size:.82rem; color:rgba(255,255,255,.55); margin: 2px 0 10px; }
/* The card's one number, so it is sized to be read as one. It carried a
   "/ vial" qualifier until the sizes moved onto the product page, where the
   tier cards state a per-vial rate against a quantity and the words are
   actually load-bearing. On a card showing a single figure they only made it
   smaller. Tabular figures so the prices line up down a column of cards
   rather than drifting with the width of a 1 against a 9. */
.price{
  display:block; font-family:var(--font-display); font-size:1.45rem;
  color:#fff; font-weight:600; letter-spacing:-.01em;
  font-variant-numeric: tabular-nums; margin-bottom:14px;
}
/* the struck list price sits a step back from the live one — dimmer, lighter
   and a size down, so the eye lands on what you actually pay */
.price-was{
  font-size:0.85rem; font-weight:400; color:#6f6f6c;
  text-decoration-thickness: 1px; margin-left:7px;
}
.search-row-meta s{ color:#6f6f6c; margin-left:4px; }

/* white on the card's black footer, flips to the reverse on hover — the same
   invert every button on the site does, plus a lift so the hover has a
   little life to it */
.add-btn{
  width:100%; height:44px; border-radius:0; border:1px solid #fff;
  background:#fff; color:#0a0a0a; font-family:'Sora', sans-serif; font-size:.85rem; font-weight:700;
  cursor:pointer; display:flex; align-items:center; justify-content:center;
  transition: background .2s ease, color .2s ease, transform .15s ease, box-shadow .2s ease;
  /* footer is a flex column now, so a taller neighbour's title no longer
     leaves this button floating above a gap of black — it stays put and the
     gap (if any) opens up above the price instead, right below the divider */
  margin-top:auto;
}
.add-btn:hover{
  background:#0a0a0a; color:#fff; border-color: rgba(255,255,255,.5);
  transform: translateY(-2px); box-shadow: 0 8px 18px -8px rgba(0,0,0,.6);
}
.add-btn.added{ background:#0a0a0a; color:#fff; border-color: rgba(255,255,255,.5); }
/* the card footer is black, so a disabled button reads as recessed rather
   than greyed — it must not still look like the primary action */
.add-btn:disabled{
  background:transparent; color:#8a8a86; border-color:#3a3a38;
  cursor:not-allowed; transform:none; box-shadow:none;
}


/* ---------- the glow standard ---------- */
/* True black rather than --bg-2, so the card art reads as lit against the page
   instead of sitting in a lighter box on a dark one. */
/* Tighter than the sitewide 110px: the cards carry their own internal padding,
   so the section's own margin can come in without the row feeling crowded. */
.standard-sec{ background:#000; padding: 84px 0; }
.standard-grid{
  display:grid; grid-template-columns: repeat(3, 1fr); gap: 20px;
}
.standard-card{
  /* square corners, same as .btn: nothing on this site is rounded */
  position:relative; overflow:hidden; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface);
  display:flex; flex-direction:column;
  /* The copy is bottom-anchored, so this trims the image band above it rather
     than the text. The scrim is already solid black across everything being
     cut, so nothing of the vial is lost. */
  min-height: 440px;
}
/* The art fills the card and the copy sits over its lower half, so the media
   is a background layer rather than a block above the text. */
.standard-media{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit: cover; object-position: center top;
}
/* The scrim lives on the card, not on the image: a pseudo-element on a
   replaced element like <img> is not rendered, so the gradient has to hang
   off the parent to sit between the art and the copy. */
.standard-card::after{
  content:''; position:absolute; inset:0; z-index:0; pointer-events:none;
  /* Weighted to the bottom: the art stays open at the top and the scrim does
     almost all its work over the lower half, where the copy sits. It reaches
     solid black rather than stopping short, so the text never has to rely on
     a dark patch of image happening to fall behind it. */
  background: linear-gradient(180deg,
    rgba(0,0,0,0) 0%,
    rgba(0,0,0,.06) 20%,
    rgba(0,0,0,.34) 32%,
    rgba(0,0,0,.76) 42%,
    rgba(0,0,0,.94) 50%,
    #000 60%);
}
.standard-body{
  position:relative; z-index:1; margin-top:auto; padding: 26px 28px 22px;
}
.standard-eyebrow{
  display:block; font-family:'JetBrains Mono', monospace;
  font-size: 0.68rem; font-weight:600; letter-spacing:.14em;
  text-transform:uppercase; color: var(--text-dim); margin-bottom: 12px;
}
/* The row reads as one band only if the three cards break their text the same
   way, so both blocks are floored at the line count the copy is written to:
   one line of heading, three of body. A card whose copy runs shorter still
   occupies the same height, and the eyebrows, headings and links stay on a
   shared baseline instead of drifting by a line. Copy that outgrows the floor
   pushes the card taller, which is the visible signal to cut it back. */
.standard-card h3{
  font-size: 1.4rem; font-weight:600; color: var(--text); margin: 0 0 12px;
  line-height: 1.2; min-height: 1.2em;
}
.standard-card p{
  font-size: 0.94rem; color: var(--text-dim); line-height:1.62; margin: 0 0 20px;
  /* three lines, which is what all three bodies wrap to at full column width */
  min-height: 4.86em;
}
/* Between the single-column breakpoint and full width the columns narrow
   enough to push the bodies to a fourth line, and the shortest of them can
   still fit in three, which would pull that card's heading up a line. Floor
   all three at the tallest through that range so the row stays level. */
@media (min-width: 901px) and (max-width: 1150px){
  .standard-card p{ min-height: 6.48em; }
  /* the two longer headings wrap to a second line down here, the short one
     does not, so it is floored to match rather than riding a line higher */
  .standard-card h3{ min-height: 2.4em; }
}
.standard-link{
  display:inline-flex; align-items:center; gap:8px;
  font-family:'JetBrains Mono', monospace; font-size:0.7rem; font-weight:600;
  letter-spacing:.12em; text-transform:uppercase; color: var(--text);
  padding-bottom: 5px; border-bottom: 1px solid rgba(255,255,255,.32);
  transition: border-color .2s ease, gap .2s ease;
}
.standard-link:hover{ border-bottom-color: var(--text); gap: 12px; }
/* Kept for the disclosure that has to reappear under the cards if the art is
   ever scaled or brightened enough to make the vial label readable again.
   check-claims.js enforces the pairing. */
.standard-note{
  margin: 26px 0 0; text-align:center;
  font-family:'JetBrains Mono', monospace; font-size:0.68rem;
  letter-spacing:.06em; color: var(--text-dimmer);
}

@media (max-width: 900px){
  .standard-grid{ grid-template-columns: 1fr; gap: 16px; }
  .standard-card{ min-height: 380px; }
}

/* ---------- welcome (ad landing page) ----------
   welcome.html only. Its hero is now index.html's hero outright: same .hero
   section, same grid, same type classes, same scrim, same image, and nothing
   scoped here repositioning or resizing any of it. Earlier passes kept a
   taller section and a crop of its own, and those two overrides were the
   whole remaining difference between "styled like the homepage" and "is the
   homepage". What this page still does differently it does in markup, not
   CSS: one call to action instead of two, and the cutoff line below.

   Everything else on the page reuses the header, the promises cards, the FAQ
   and the footer as they are. Prefixed .wl- so none of it can reach another
   page by accident. */

/* The hero's button row and stat row are .hero-cta and .hero-stats
   unmodified, shared with index.html. .wl-cta is nothing but a hook now:
   js/welcome.js watches that row to know when the hero buttons have
   scrolled away and the sticky bar below should take over.

   Two differences from .pd-sticky, the product page's own sticky bar. It
   runs at every width rather than only under 560px, because this page's
   whole job is the one click and a reader scrolling past the hero on a
   laptop has the same problem as one on a phone. And it hides with
   visibility, not just the transform, so that off screen it is out of the
   tab order and the accessibility tree: otherwise the page carries two Shop
   now buttons for anyone not using a mouse. */
.wl-sticky{
  position:fixed; left:0; right:0; bottom:0; z-index:150;
  background: rgba(12,12,12,.94);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border-top:1px solid var(--border);
  padding: 12px 0 calc(12px + env(safe-area-inset-bottom));
  box-shadow: 0 -8px 26px -16px rgba(0,0,0,.9);
  transform: translateY(100%); visibility:hidden;
  transition: transform .22s ease, visibility 0s .22s;
}
.wl-sticky[hidden]{ display:none; }
.wl-sticky.is-shown{
  transform: translateY(0); visibility:visible;
  transition: transform .22s ease;
}
/* min-width:0 so the copy truncates rather than widening the row past the
   screen: a flex item's default min-width is auto, which would let the
   sentence push the button off the edge. */
.wl-sticky-in{ display:flex; align-items:center; justify-content:space-between; gap:20px; min-width:0; }
.wl-sticky-copy{
  margin:0; font-size:0.86rem; color: var(--text-dim); min-width:0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.wl-sticky-btn{ flex:0 0 auto; padding: 13px 26px; font-size:0.92rem; }
/* Under 640px the sentence and the button cannot share a row without the
   button losing its tap target, and the button is the reason the bar exists.
   The copy goes rather than shrinking both — mobile first here means the
   bar is a full-width button with nothing to distract from it. */
@media (max-width: 640px){
  .wl-sticky-copy{ display:none; }
  .wl-sticky-in{ justify-content:stretch; }
  .wl-sticky-btn{ flex:1 1 auto; height:50px; }
}
@media (prefers-reduced-motion: reduce){
  .wl-sticky{ transition: none; }
  .wl-sticky.is-shown{ transition: none; }
}

/* The accepted-payment row stood here, six card marks under a "We accept"
   label, directly beneath the hero. Removed: it sat immediately under the one
   button this page exists to get pressed and competed with it for the eye,
   which is a poor trade for a reassurance nobody has asked for yet at the top
   of a landing page. Checkout is where that question actually gets asked, and
   Stripe answers it there.

   PAYMENT_METHODS in js/products-data.js is now unread by any page. Left in
   place rather than deleted: it is the list this row was built from, and the
   four guards that held the row to it are described in check-claims.js. */

/* The three facts the cards above do not cover, set as a hairline row rather
   than as boxes.

   Four bordered boxes used to sit here, directly under three bordered photo
   cards, which made seven framed things in one screen and read as a second,
   weaker grid competing with the first. Nothing about these three needs a
   frame: one rule above the row and a divider between each is enough to group
   them, and the row then reads as a footnote to the cards, which is what it
   is. Modern here means fewer lines, not more.

   Icon and heading share a line now instead of the icon sitting in its own
   column beside a two-line block. That is what lets the whole row be one
   short band rather than a second tier of cards. */
.wl-trust{
  display:grid; grid-template-columns: repeat(3, 1fr);
  gap: 0; margin-top: 56px;
  border-top:1px solid var(--border);
}
.wl-trust-item{
  padding: 26px clamp(20px, 2.6vw, 34px) 4px;
  border-left:1px solid var(--border);
}
.wl-trust-item:first-child{ border-left:0; padding-left:0; }
.wl-trust-item:last-child{ padding-right:0; }
.wl-trust-head{ display:flex; align-items:center; gap:10px; margin-bottom:8px; }
.wl-trust-item svg{ color: var(--text); flex:0 0 auto; }
.wl-trust-item h4{
  margin:0; font-size:0.95rem; font-weight:600; color: var(--text);
  letter-spacing:-0.01em;
}
.wl-trust-item p{ margin:0; font-size:0.82rem; line-height:1.55; color: var(--text-dim); }
@media (max-width: 760px){
  /* Stacked, and the dividers turn horizontal with them: three columns at this
     width give each line two words. */
  .wl-trust{ grid-template-columns: 1fr; margin-top: 40px; }
  .wl-trust-item{
    border-left:0; border-top:1px solid var(--border);
    padding: 18px 0 4px;
  }
  .wl-trust-item:first-child{ border-top:0; padding-top: 22px; }
}

/* This page's sections run tighter than the sitewide 110/84. Nothing here is a
   destination the way the homepage's catalog is; it is one scroll from a
   headline to a button, and the standard band's own air plus a full section
   gap either side left two runs of nearly 200 blank pixels in the middle of
   it. Scoped to welcome.html so index.html keeps its own pace. */
.wl-standard{ padding: 76px 0 84px; }
/* Closing the gap between the hero and this section on a phone, which took
   three numbers rather than one. Measured from the bottom of the stat row to
   the top of the eyebrow, the blank was 128px on a 390x844 screen, and only
   38 of it belonged to this section. The other 90 were inside the hero: 34 of
   grid padding under the stats and 56 of section padding under that. Trimming
   only the visible one moved it barely at all, which is what measuring rather
   than guessing at the stack turned up.

   Scoped to .wl-standard so index.html keeps its own spacing: its hero is
   the top of a long page and can afford the air, where this one is the
   whole pitch and the proof underneath should arrive before a thumb has to
   reach for it.

   The hero itself is untouched here now: it ran the sitewide mobile .hero
   padding tightened further, which read as cramped against index.html's
   own hero rather than merely brisk. It now keeps the same breathing room
   index.html's hero has, and only this section's own top padding is closed
   up to remove the blank run below it.

   Only the space above. What sits under this section is separating the cards
   from the FAQ and is untouched, and desktop keeps all of it: the same gap
   reads as composure at 900px where it reads as a hole at 844. */
@media (max-width: 760px){
  .wl-standard{ padding-top: 20px; }
}

/* Welcome-only, mobile-only: the hero's copy runs larger and looser than
   index.html's own hero, and the two buttons stack full-width instead of
   sitting side by side. Scoped to .wl-hero at the same 900px breakpoint the
   shared hero rules use, so index.html's hero is untouched.

   The photograph is deliberately not part of this: .hero-visual-img keeps
   the exact rules above, sized and positioned off .hero's own box.

   This block no longer forces the section to the full viewport height.
   That was tried, and it pinned "Why researchers buy from Glow" off the
   first screen, but left the actual hero content (eyebrow through stats)
   floating in the top third of the phone with roughly 340px of plain black
   under it before that section began — worse than the un-forced height,
   which sizes the hero to its own content and lets Why Glow follow right
   after the stat row. The title is still sized so "Research Peptides."
   (the <br> after "Lab-Grade" already fixes the first line) fits the
   second line without wrapping again, down to a 320px-wide phone. */
@media (max-width: 900px){
  .hero.wl-hero{
    padding-top: 56px;
    padding-bottom: 40px;
  }
  .wl-hero .hero-grid{ min-height: 0; padding-top: 0; padding-bottom: 0; }
  .wl-hero .hero-eyebrow{ font-size: 0.86rem; margin-bottom: 18px; }
  .wl-hero .hero-title{ font-size: clamp(2.05rem, 9.2vw, 3.3rem); margin-bottom: 22px; }
  .wl-hero .hero-sub{ font-size: 0.88rem; margin-bottom: 36px; }
  .wl-hero .hero-cta{ flex-direction: column; align-items: stretch; gap: 18px; margin-bottom: 36px; }
  .wl-hero .hero-cta .btn-lg{ width: 100%; flex: none; padding: 16px 24px; font-size: 1rem; }
  .wl-hero .hero-stats{ gap: 32px; }
}
@media (max-width: 560px){
  .wl-trust{ grid-template-columns: 1fr; margin-top: 36px; }
}

/* The sticky call to action, welcome.html only. Same construction as the
   product page's .pd-sticky: fixed to the bottom, z-index under the cart
   drawer and search overlay (300-340) so opening either covers it rather than
   fighting it, and safe-area padding so it clears the home indicator on a
   notched phone.

   Two differences from .pd-sticky. It runs at every width rather than only
   under 560px, because this page's whole job is the one click and the reader
   scrolling past the hero on a laptop has the same problem as one on a phone.
   And it hides with visibility, not just the transform, so that off screen it
   is out of the tab order and the accessibility tree: otherwise the page
   carries two Shop now buttons for anyone not using a mouse. */
/* The FAQ sits on the page background here, not the white section the homepage
   gives it, so it inherits the dark .faq-* rules with nothing to override. */
/* #000, the same ground as .standard-sec directly above it, rather than
   var(--bg)'s #0a0a0a. The two dark sections meet with nothing between them,
   and two blacks a shade apart read as a seam where one reads as one page. */
/* No background of its own: the section carries .section-light and takes the
   white from it, the same as index.html's FAQ. A colour here would win on
   source order and quietly undo that. */
/* Black, not the white index.html's own FAQ carries: this page runs the one
   dark section (Why Glow) into this one rather than breaking to white for
   it, so the whole page after the hero reads as one continuous ground until
   the footer. #000 rather than var(--bg) (#0a0a0a) for the same reason
   .standard-sec above chose it: they sit back to back, and any visible seam
   between two near-blacks would read as a mistake rather than a boundary. */
.wl-faq-sec{ background:#000; padding: 78px 0 88px; }
.wl-faq-sec .faq-list{ max-width: 860px; }
/* One column at every width: four questions split into two columns of two
   leaves a short, lopsided pair of stacks rather than a list. */
@media (min-width: 860px){
  .wl-faq-sec .faq-list{ grid-template-columns: 1fr; max-width: 860px; }
}

/* ---------- faq ---------- */
.faq-list{ max-width: 780px; margin: 0 auto; }
/* Eleven questions in one column is a long scroll of nothing but headings, and
   at desktop widths every row was a short question, a stretch of empty, then
   the +. Two columns spend that empty space on the second half of the list.
   The columns are real elements from faqHtml(), not a grid laid over one flat
   list: paired into rows, an opened answer would leave a hole its own height
   beside it. Below 860px they stack, which reads in the same order. */
@media (min-width: 860px){
  .faq-list{
    max-width: 1040px;
    display:grid; grid-template-columns: 1fr 1fr; column-gap: 56px;
    align-items:start;
  }
  /* Below FAQ_SPLIT_AT, faqHtml() emits one .faq-col instead of two. Falling
     back to the wide two-column grid in that case leaves the single column
     stuck on the left with the whole right half of the section empty. */
  .faq-list:has(> .faq-col:only-child){
    max-width: 780px; grid-template-columns: 1fr;
  }
}
/* Boxed rows: each question is its own bordered card rather than a shared
   list with dividers, so .faq-col supplies the gap a border-bottom list
   didn't need. */
.faq-col{ display:flex; flex-direction:column; gap: 14px; }
/* Same edge as .btn-outline, not the faint var(--border) list dividers use
   elsewhere: asked to read as an actual box, like the buttons on the site,
   rather than a subtle rule. Square corners for the same reason. */
.faq-item{ border: 1.5px solid rgba(255,255,255,0.55); border-radius: 0; }
.faq-q{
  width:100%; text-align:left; background:none; border:none; color: var(--text);
  padding: 22px 24px; font-size: 1.02rem; font-weight:600; cursor:pointer;
  display:flex; justify-content:space-between; align-items:center; gap: 20px;
  font-family:var(--font-display);
}
.faq-q .icon{ flex-shrink:0; width:20px;height:20px; display:flex;align-items:center;justify-content:center; transition: transform .3s ease, color .2s ease; }
/* the row is a button but gave no feedback at all before it was clicked */
.faq-q{ transition: color .2s ease; }
.faq-q:hover{ color:#fff; }
.faq-item.open .faq-q .icon{ transform: rotate(180deg); }
.faq-a{ max-height:0; overflow:hidden; transition: max-height .4s ease; }
/* Collapsed only when there is script to open it again. The answers are in the
   served markup either way, which is what a crawler needs, but a reader with no
   JavaScript would otherwise get five questions and no way to reach an answer.
   `js` is set on <html> by an inline script in the head, so there is no flash. */
html:not(.js) .faq-a{ max-height: none; }
.faq-a p{ padding: 0 24px 22px; font-size:0.92rem; }

/* The rows are sized for a desktop column and were carried onto a phone
   unchanged: 22px of padding above and below a 16px question, which put a
   one-line row at 67px and a two-line row at 88, and four of them at 373px of
   a 844px screen for four short questions.

   Padding and type both come down. The type matters as much as the padding
   here, because at 16px three of these four questions wrap to a second line at
   390px and at 15 they mostly do not, so the saving compounds: a row that
   stops wrapping loses a whole line, not just the padding either side of it.
   The gap between rows tightens with them, since boxes that are shorter need
   less air between them to stay distinct.

   Sitewide, not scoped to one page. This is the same component on index.html
   and welcome.html, and it is the same too-large on both. */
@media (max-width: 640px){
  .faq-q{ padding: 15px 16px; font-size: 0.94rem; gap: 14px; }
  .faq-a p{ padding: 0 16px 16px; font-size: 0.87rem; }
  .faq-col{ gap: 10px; }
  .faq-q .icon{ width:18px; height:18px; }
}

/* The homepage FAQ sits on white. Everything above is written for a dark
   section, so the rules that name a light colour outright, rather than
   inheriting one, are inverted here. The chevron just inherits .faq-q's
   color, so it needs no override of its own. */
.section-light .faq-item{ border-color: rgba(0,0,0,0.35); }
.section-light .faq-q{ color:#0a0a0a; }
.section-light .faq-q:hover{ color:#000; }

/* ---------- footer offer band ----------
   The standing launch-offer form, built by js/launch-offer.js, and the only
   place the offer is asked for since the popups were removed. The .lo-* type
   and control styles it uses are further down; only the band around them is
   defined here. */
.footer-offer:empty{ display:none; }
.footer-offer{
  display:flex; align-items:center; justify-content:space-between;
  gap: 24px 32px; flex-wrap:wrap;
  padding: 28px 0 44px; border-bottom: 1px solid var(--border);
}
.footer-offer .lo-copy{ flex:1 1 320px; min-width:0; }
.footer-offer .lo-body{ flex:0 1 420px; }
.footer-offer .lo-title{ font-size: 1.15rem; margin: 0 0 4px; }
.footer-offer .lo-facts{ flex:1 1 100%; margin: 0; }
.footer-offer .lo-reveal{ text-align:left; }
.footer-offer .lo-copy-btn{ margin-top: 12px; }
@media (max-width: 640px){
  .footer-offer{ flex-direction:column; align-items:flex-start; }
  /* Both were sized with flex:_ _ 320px/420px for the row layout above, where
     that number sets a width. In this column layout the same flex-basis sets
     a height instead, which is what stretched .lo-copy to 320px tall and
     opened the huge gap before the input on mobile. flex-basis:auto lets each
     block size to its own content again. */
  .footer-offer .lo-copy{ width:100%; flex-basis:auto; }
  .footer-offer .lo-body{ width:100%; flex-basis:auto; }
}

/* ---------- footer ---------- */
.site-footer{ background:#000; border-top:1px solid var(--border); padding: 80px 0 30px; }
.site-footer .logo{ color: var(--text); }
.site-footer .spark{ color: var(--text); }
.footer-top{ display:grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 40px; margin: 44px 0 50px; }
.footer-col h4{ font-family:'Sora',sans-serif; font-size:0.8rem; text-transform:uppercase; letter-spacing:.08em; color: var(--text-dimmer); margin-bottom: 16px; }
.footer-col a{ display:block; font-size:0.9rem; color: var(--text-dim); margin-bottom: 12px; transition: color .2s; }
.footer-col a:hover{ color: var(--pink-soft); }

.disclaimer{
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
  padding: 26px 0 30px; margin-bottom: 26px;
}
.disclaimer h5{ font-family:'Sora',sans-serif; text-transform:uppercase; letter-spacing:.08em; font-size:0.78rem; color: var(--pink-soft); margin-bottom:12px; }
.disclaimer p{ font-size: 0.78rem; line-height:1.8; color: var(--text-dimmer); }

.footer-bottom{ display:flex; justify-content:center; flex-wrap:wrap; gap:8px; font-size:0.78rem; color: var(--text-dimmer); }

/* Sits directly under the wordmark and says what the company sells before
   the link columns start naming pages. Body-text sizing, not the mono
   treatment below it: this is a sentence to read, not a legal line. */
.footer-tagline{
  margin: 16px 0 0; max-width: 34ch;
  font-size: 0.86rem; line-height: 1.65; color: var(--text-dim);
}

/* ---------- footer copyright, tucked under the tagline as a small line
   rather than its own full-width band ---------- */
.footer-legal{
  margin-top: 18px;
  display:flex; flex-direction:column; gap: 4px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: .03em;
  line-height: 1.5;
  color: var(--text-dimmer);
}
.footer-legal strong{
  color: var(--text-dim); font-weight:700; letter-spacing:.06em; text-transform: uppercase; font-size: 0.7rem;
}


/* ---------- reveal on scroll ---------- */
.js .reveal{ opacity:0; transform: translateY(26px); transition: opacity .7s ease, transform .7s ease; }
.reveal.in{ opacity:1; transform: translateY(0); }

/* Product cards are the exception: they are there the moment they are in the
   frame, no fade and no rise. Everything else revealing is copy, where the
   animation is the page introducing a new thought. A grid of products is
   merchandise, and merchandise that fades up as you reach it reads as a shop
   still loading rather than one showing you what it has, most of all on a
   phone where a flick puts a whole row on screen at once. The cards still
   carry .reveal and the observer still marks them, and productCardHtml keeps
   writing its 60ms-per-column stagger, all of it inert while this rule stands
   and all of it working again the moment the rule goes. Deleting this line is
   the whole way back. js/product.js already shows its related row outright
   for the same reason. */
.js .product-card.reveal{ opacity:1; transform:none; transition:none; }

/* ---------- responsive ---------- */
@media (max-width: 980px){
  .main-nav{ display:none; }
  .hamburger{ display:flex; }
  .footer-top{ grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px){
  /* Two products per row on phones. auto-fill with a 260px floor collapses
     to one column here, which wastes most of the screen on a single tile
     and turns the catalog into a long scroll. Forcing two columns needs
     minmax(0,1fr) rather than 1fr, or a long compound name refuses to
     shrink below its content width and overflows the row. */
  .product-grid{ grid-template-columns: repeat(2, minmax(0,1fr)); gap: 6px; }

  /* everything inside the tile steps down to suit the narrower column */
  .product-footer{ padding: 14px 13px 15px; }
  .product-footer::before{ left:13px; right:13px; }
  .product-card h3{ font-size: .92rem; margin: 3px 0 0; }
  .product-alias{ font-size:.74rem; margin: 1px 0 7px; }
  .card-divider{ margin: 2px 0 10px; }
  .price{ font-size: 1.18rem; margin-bottom: 10px; }
  .add-btn{ height: 38px; font-size: .78rem; }
  /* Both badges cannot coexist on a half-width tile: "recovery" + "Best
     Seller" measures 139px against a 130px card at 320px, so they collide.
     The category is the one to drop — the catalog has filter chips for it
     and the product page repeats it, whereas the status badge is the only
     place "Best Seller" appears at all. */
  .product-badges{ top: 9px; left: 9px; right: 9px; gap: 6px; }
  .product-badge{ font-size: .55rem; padding: 3px 7px; }
  .product-badge.cat{ display:none; }

  .hero{ padding: 48px 0 56px; }
  /* .hero-stats spacing is set with its own layout up at .hero-stats, so the
     row's grid and its gap stay in one place rather than half here */
  /* the three link columns are short enough now to sit side by side rather
     than stacking into one long list — the brand block takes its own row */
  .footer-top{ grid-template-columns: repeat(3, 1fr); }
  .footer-brand{ grid-column: 1 / -1; }
  .btn-lg{ width:100%; padding: 14px 24px; font-size: 0.95rem; }
  .hero-cta{ flex-direction:column; align-items:stretch; gap: 16px; margin-bottom: 24px; }

  /* footer runs very tall stacked on mobile — tighten every gap in the
     chain so it reads as one compact column instead of a scroll marathon */
  .site-footer{ padding: 48px 0 24px; }
  .footer-offer{ padding: 20px 0; gap: 16px; }
  .footer-top{ gap: 26px 14px; margin: 30px 0; }
  /* the disclaimer is the tallest thing down here. Nothing is removed from
     it — the leading just comes in so it takes fewer screens to scroll past. */
  .disclaimer{ padding: 20px 0; margin-bottom: 16px; }
  .disclaimer p{ line-height: 1.62; }
  .footer-legal-bar{ margin: 0 0 24px; padding: 14px 0; text-align:left; line-height: 1.7; }
  .footer-legal-bar .divider{ display:block; margin: 4px 0 0; width:0; overflow:hidden; }
  .footer-bottom{ justify-content:flex-start; text-align:left; }
}

/* mobile nav drawer */
.main-nav.open{
  display:flex; position:absolute; top:100%; left:0; right:0;
  flex-direction:column; background: #ececea; border-bottom:1px solid rgba(0,0,0,0.1);
  padding: 20px 24px; gap: 18px;
}

/* ---------- hero responsive ---------- */
@media (max-width: 900px){
  /* padding-top/bottom only — this element is also .container, and a
     shorthand here was wiping out its 24px left/right padding, which is
     what made everything (buttons, nav) look flush against the edges */
  /* 74px of dead air stood above the eyebrow here: 48 from the section's own
     padding (set at 640, in a shorthand) and 26 more from the grid. The hero
     is the first thing under the header on a phone and does not need to be
     announced by a gap. The foot comes in with it, and further: 56px of that
     was holding the scroll cue, which is no longer here, and what was left
     read as the hero trailing off rather than ending. The stat row is the
     last thing in the section and the catalog should start under it. */
  .hero{ padding-top: 22px; padding-bottom: 14px; }
  .hero-grid{ min-height: 0; padding-top: 12px; padding-bottom: 14px; }
  .hero-copy{ max-width: none; }
  /* Side by side, against the sitewide mobile rule further up that stacks
     every .btn-lg full width. Two stacked full-width buttons cost about 70px
     of height, and the whole point at this width is that the eyebrow, the
     headline, the sub, both calls to action and the stat row all sit on the
     one screen the photograph fills. Scoped to the hero so the sticky bar
     and the interior pages keep the stacked buttons that suit them. */
  .hero-cta{ flex-direction:row; align-items:stretch; gap: 12px; margin-bottom: 24px; }
  /* The scroll cue goes at this width. It is a skip link to the catalog, and
     the catalog is already the next thing a thumb reaches; what it actually
     did here was sit on top of the photograph. The link stays on wide screens
     where the hero is tall enough to hide the section under it. */
  .scroll-cue{ display:none; }
  .hero-cta .btn-lg{ width:auto; flex:1 1 0; padding: 13px 14px; font-size: 0.9rem; }
  /* The desktop treatment compacted, not a different one: the photograph
     fills the section edge to edge and the copy sits on top of it, exactly
     as it does on a wide screen. All that changes is which part of the frame
     survives the crop, because the section is portrait here and the shot is
     landscape.

     contain, and it still reads as full bleed: this frame is pure black to
     its own edges and the section behind it is the same black, so the bands
     contain leaves above and below the photograph are invisible. That is the
     whole trick, and it is why cover is the wrong tool here. cover fills the
     box by throwing away half the width, which at this aspect zooms so far in
     that the vials pass behind the copy as a wall of labels. What was wrong
     before was that, plus scale(1.3) pushing past it and .38 opacity leaving
     a smudge where the product should be. */
  .hero-visual-img{
    display:block;
    /* No transform: this element also carries .reveal, whose .in state sets
       transform: translateY(0) at two-class specificity and silently wins
       over anything set here. */
    /* Wider than the section on purpose. contain sizes to the box, so a box
       the width of the phone draws the trio at about 210px tall and the vials
       read as small. Overhanging each side lets them come up to something
       like the presence they have on a wide screen, and what runs off the
       edges is the frame's own black.

       Capped in pixels as well as percent, and centred off the same figure:
       132% of a 900px-wide screen would draw the vials half again the size
       they are on a desktop, which is where they stop being the backdrop and
       start crowding the words in front of them. */
    position:absolute; right:auto; top:0; bottom:0; z-index:0;
    width: min(132%, 560px);
    left: calc((100% - min(132%, 560px)) / 2);
    height:100%; max-width:none; margin:0;
    object-fit:contain; object-position: 50% 34%;
    opacity:.42;
    /* No mask. The frame fades to its own black on every side, so there is no
       edge here to hide, and the vignette that used to be here is most of what
       swallowed the vial. */
  }
  /* Even cover rather than the desktop scrim's left-to-right fade: the copy
     stacks down the middle at this width, so every line of it is over the
     photograph and needs the same backing. Heaviest at the very top, where
     the eyebrow is smallest and the crop is brightest. */
  .hero-copy-scrim{
    display:block;
    background:
      radial-gradient(ellipse 120% 70% at 50% 34%, rgba(0,0,0,.25) 0%, rgba(0,0,0,.7) 70%, rgba(0,0,0,.92) 100%),
      linear-gradient(180deg,
        rgba(0,0,0,.88) 0%, rgba(0,0,0,.62) 22%, rgba(0,0,0,.52) 46%,
        rgba(0,0,0,.72) 74%, rgba(0,0,0,.92) 92%, #000 100%);
  }

  /* the bigger button size added for the desktop CTAs was inflating every
     button site-wide (nav, newsletter, etc.) once it lost the width
     constraint of a wide screen — scale back down for breathing room */
  .btn{ padding: 12px 20px; font-size: 0.88rem; }
}

/* ---------- reduced motion ---------- */
/* honour the OS-level "reduce motion" setting: kill the ambient loops
   (marquee, glow drift, twinkle) and the reveal slide, but keep opacity
   transitions so nothing ends up stuck invisible */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .reveal{ opacity:1; transform:none; }
}

/* =====================================================================
   INTERIOR PAGE: shipping, wholesale, partner program, and the legal set.
   Near-black and square-buttoned like the homepage, but the type is
   presented plainly: one typeface doing the work, a headline whose
   second line is simply dimmer rather than hollow, and no monospace.
   The tricks that were carrying these pages before — an outlined
   headline, all-caps mono eyebrows on .18em tracking, gradient-filled
   numerals, a hairline over every section — each drew attention to the
   styling instead of the sentence. What replaces them is size, weight
   and space: content centred on a measured column so a 1180px container
   is not left half empty, and one clear step between a heading, its
   subhead and its detail.
   ===================================================================== */
.lp-page{
  background: var(--bg); color: var(--text);
  /* #6b6b69 is 3.7:1 on this background — under 4.5:1, and it carries the
     smallest type on these pages (the "last updated" line, the clause
     numbers, the card labels). Lifted here rather than on :root so the
     homepage and the catalog keep the tone they were designed with. */
  --text-dimmer: #8a8a87;
}

/* ---------- hero ----------
   Ranged left and stacked exactly like the homepage hero, down to the
   rule above the figures. */
.lp-hero{ padding: clamp(72px, 10vw, 132px) 0 clamp(56px, 7vw, 88px); text-align:center; }
/* a quiet label, not a machine readout: sentence case, no mono, no
   letter-spacing to fight the words apart */
.lp-eyebrow, .lp-page .eyebrow{
  display:block; margin-bottom: 18px;
  font-family:'Sora', system-ui, sans-serif; font-size:.98rem; font-weight:500;
  letter-spacing:0; text-transform:none; color: var(--text-dim);
}
.lp-hero h1{
  font-size: clamp(2.7rem, 6.4vw, 5rem); line-height:1.04;
  letter-spacing:-.035em; margin: 0 auto 22px; max-width: 16ch;
}
/* The second line was drawn hollow, which reads as an effect rather than
   a sentence. Dimming it says the same thing — this half is the
   qualifier — without the letters losing their shape. */
.lp-hero h1 .outline{ color: var(--text-dim); }
.lp-lede{
  font-size: clamp(1.12rem, 1.7vw, 1.32rem); line-height:1.5;
  max-width: 44ch; margin: 0 auto; color: var(--text-dim);
}
/* Not a flex row: on two of these pages the note is a bare text node plus a
   <strong>, which flex turns into two items with the gap wedged between
   them — "answered in [gap] one business day". As normal inline content the
   sentence just wraps. */
.lp-hero-note{
  display:block; margin: 24px auto 0; max-width: 46ch;
  font-size:.92rem; line-height:1.6; color: var(--text-dim);
}
.lp-hero-note strong{ color: var(--text); font-weight:600; }
.lp-dot{
  display:inline-block; vertical-align: middle; position:relative; top:-1px;
  width:7px; height:7px; margin-right:9px; background:#fff; flex-shrink:0; border-radius:50%;
  animation: lpPulse 2.4s ease-out infinite;
}
@keyframes lpPulse{
  0%{ box-shadow: 0 0 0 0 rgba(255,255,255,.5); }
  70%{ box-shadow: 0 0 0 8px rgba(255,255,255,0); }
  100%{ box-shadow: 0 0 0 0 rgba(255,255,255,0); }
}
.lp-actions{ display:flex; flex-wrap:wrap; justify-content:center; gap:14px; margin-top: 34px; }

/* ---------- figures ----------
   The homepage stat row: hairline, gradient numerals, mono caption. */
.lp-figs{
  display:flex; flex-wrap:wrap; justify-content:center;
  gap: clamp(34px, 6vw, 76px);
  margin: clamp(52px, 7vw, 78px) auto 0; padding-top: 0;
}
/* solid, not gradient-filled: a washed-out numeral is the one thing on
   the page that should read instantly */
.lp-fig b{
  display:block; font-family:var(--font-display); font-size: clamp(2rem, 3vw, 2.5rem);
  font-weight:500; letter-spacing:-.02em; font-variant-numeric: tabular-nums;
  color: var(--text);
}
/* direct child only: the counting numeral is a span inside the <b>, and an
   unscoped .lp-fig span caption rule swallowed it, rendering "2" as a tiny
   uppercase caption stacked above "days" */
.lp-fig > span{
  display:block; margin-top:6px; font-size:.9rem; letter-spacing:0;
  text-transform:none; color: var(--text-dim);
}

/* ---------- sections ---------- */
/* no divider rule: the tint alternation already separates one section
   from the next, and a hairline every 96px reads as a form */
.lp-section{ padding: clamp(80px, 10vw, 128px) 0; }
.lp-section.tint{ background: var(--bg-2); }
.lp-h2{
  font-size: clamp(2rem, 4vw, 3.1rem); line-height:1.06; letter-spacing:-.03em;
  margin: 0 auto 16px; max-width: 20ch; text-wrap: balance;
}
.lp-sub{
  font-size: clamp(1.05rem, 1.5vw, 1.2rem); line-height:1.55; color: var(--text-dim);
  max-width: 48ch; margin: 0 auto;
}
/* Section headings centre over their detail; the detail itself stays
   ranged left, because a centred table or a centred form is unreadable.
   The legal pages use .lp-section for running prose and have no .lp-h2,
   so none of this reaches them. */
.lp-section:has(> .container > .lp-h2) > .container{ text-align:center; }
.lp-section > .container > .lp-points,
.lp-section > .container > .af-tiers,
.lp-section > .container > .af-steps,
.lp-section > .container > .af-rules,
.lp-section > .container > .lp-fine,
.lp-section > .container > .ws-form,
.lp-section > .container > .ws-done,
.lp-section > .container > .pr-chain{ text-align:left; }

/* ---------- points ---------- */
.lp-points{
  display:grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(38px, 5vw, 68px); margin-top: clamp(60px, 8vw, 88px);
}
.lp-point svg{ display:block; margin-bottom: 20px; color:#fff; }
.lp-point h3{ font-size:1.22rem; margin:0 0 10px; letter-spacing:-.02em; }
.lp-point p{ font-size:1.02rem; line-height:1.6; margin:0; color: var(--text-dim); }

/* ---------- fine print ---------- */
.lp-fine{
  margin: clamp(52px, 6vw, 72px) auto 0; max-width: 66ch;
  font-size:.92rem; line-height:1.7; color: var(--text-dim);
}
.lp-fine strong{ color: var(--text-dim); font-weight:600; }

/* =====================================================================
   MOTION
   Deliberate rather than decorative: things arrive in reading order,
   numbers count, and line art draws itself the way it would be drawn by
   hand. One easing curve so it reads as one hand. Everything degrades to
   a static page under prefers-reduced-motion via the global block above,
   and to a fully visible one without scripting via the .js gate.
   ===================================================================== */
:root{ --lp-ease: cubic-bezier(.22,1,.36,1); }

/* hero, on load: above the fold, so it cannot wait for a scroll observer */
.lp-hero > .container > *{ opacity:0; animation: lpRise .9s var(--lp-ease) forwards; }
.lp-hero .lp-eyebrow{ animation-delay: .02s; }
.lp-hero h1{ animation-delay: .08s; }
.lp-hero .lp-lede{ animation-delay: .18s; }
.lp-hero .lp-hero-note{ animation-delay: .26s; }
.lp-hero .lp-actions{ animation-delay: .34s; }
.lp-hero .lp-figs{ animation-delay: .42s; }
@keyframes lpRise{ from{ opacity:0; transform: translateY(16px); } to{ opacity:1; transform:none; } }

/* the site-wide .reveal travels 26px over .7s, which suits big marketing
   blocks and is too heavy for these */
.js .lp-page .reveal{
  transform: translateY(16px);
  transition: opacity .75s var(--lp-ease), transform .75s var(--lp-ease);
}

/* the container is the observer target but hands its motion to the
   children, so a row arrives one item at a time rather than as a slab */
.lp-points.reveal{ opacity:1; transform:none; }
.js .lp-point{
  opacity:0; transform: translateY(18px);
  transition: opacity .75s var(--lp-ease), transform .75s var(--lp-ease);
}
.lp-points.in .lp-point{ opacity:1; transform:none; }
.lp-points.in .lp-point:nth-child(2){ transition-delay: .1s; }
.lp-points.in .lp-point:nth-child(3){ transition-delay: .2s; }

/* Line art that draws itself. Every animated shape carries pathLength="1",
   so one dasharray works for a 4px tick and a 300px curve without
   measuring in JS. Selected by class, not by [pathLength]: an attribute
   selector matches these in querySelector but does not apply through the
   cascade, because attribute names on SVG-namespaced elements are
   case-sensitive while the CSS parser has folded the selector to
   lowercase. */
.js .lp-point .lp-draw{
  stroke-dasharray: 1; stroke-dashoffset: 1;
  transition: stroke-dashoffset 1.1s var(--lp-ease);
}
.lp-points.in .lp-point .lp-draw{ stroke-dashoffset: 0; }
.lp-points.in .lp-point:nth-child(2) .lp-draw{ transition-delay: .16s; }
.lp-points.in .lp-point:nth-child(3) .lp-draw{ transition-delay: .32s; }

.lp-arrow{ display:inline-block; transition: transform .32s var(--lp-ease); }
.btn:hover .lp-arrow{ transform: translateX(4px); }

@media (max-width: 860px){
  .lp-points{ grid-template-columns:1fr; gap: 40px; }
  .lp-hero h1{ max-width: none; }
}

/* =====================================================================
   BLOG
   ===================================================================== */

/* =====================================================================
   BLOG LISTING
   Same language as shipping / wholesale / COA: white, centred hero,
   soft #f5f5f7 panels, no rules. The old listing leaned on a mono
   masthead, hairline separators and a generated constellation graphic
   that looked like every other peptide supplier. Each post now carries
   one drawn mark of its own subject instead.
   ===================================================================== */
/* The listing keeps the light editorial surface it shares with the
   articles, so it carries its own page and hero rather than borrowing the
   interior pages' now-dark .lp-* shell. */
.bl-page{ background:#fff; color:#1d1d1f; }
.bl-page p{ color:#6e6e73; }
.bl-page h1, .bl-page h2, .bl-page h3{ color:#1d1d1f; letter-spacing:-.04em; font-weight:700; }
.bl-page :focus-visible{ outline: 2px solid #1d1d1f; outline-offset: 3px; }
.bl-hero{ padding: clamp(72px, 11vw, 130px) 0 clamp(48px, 7vw, 80px); text-align:center; }
.bl-hero h1{
  font-size: clamp(2.6rem, 7vw, 4.9rem); line-height:1.04; margin:0 0 20px;
  max-width: 17ch; margin-inline:auto; text-wrap: balance;
}
.bl-hero .bl-lede{
  font-size: clamp(1.12rem, 2vw, 1.45rem); line-height:1.42; color:#6e6e73;
  max-width: 36ch; margin: 0 auto; text-wrap: balance;
}
.bl-hero > .container > *{ opacity:0; animation: lpRise .9s var(--lp-ease) forwards; }
.bl-hero h1{ animation-delay: .04s; }
.bl-hero .bl-lede{ animation-delay: .16s; }
.js .bl-page .reveal{
  transform: translateY(16px);
  transition: opacity .75s var(--lp-ease), transform .75s var(--lp-ease);
}

.bl{ padding: 0 0 clamp(80px, 10vw, 124px); }

/* ---------- the art panel ---------- */
.bl-art{
  background:#f5f5f7; border-radius: 20px; aspect-ratio: 16 / 10;
  display:flex; align-items:center; justify-content:center; overflow:hidden;
  transition: background .3s ease;
}
.bl-art svg{
  width: 66%; height:auto; color:#1d1d1f;
  transition: transform .55s cubic-bezier(.2,.8,.2,1);
}

/* ---------- shared card bits ---------- */
.bl-tag{
  display:block; margin: 22px 0 10px;
  font-size:.82rem; font-weight:600; color:#6e6e73;
}
.bl-meta{
  /* #86868b is 3.6:1 on the card — under the bar, on the date line */
  display:block; margin-top: 14px; font-size:.88rem; color:#6e6e73;
}
.bl-more{
  display:inline-flex; align-items:center; gap:7px; margin-top:18px;
  font-size:1rem; font-weight:500; color:#1d1d1f;
}
.bl-arrow{ display:inline-block; transition: transform .35s cubic-bezier(.2,.8,.2,1); }

/* the whole card is the link, so the hover lives on it rather than on
   any one child */
.bl-lead:hover .bl-art, .bl-card:hover .bl-art{ background:#e9e9ec; }
.bl-lead:hover .bl-art svg, .bl-card:hover .bl-art svg{ transform: scale(1.06); }
.bl-lead:hover .bl-arrow, .bl-card:hover .bl-arrow{ transform: translateX(5px); }
.bl-lead:focus-visible, .bl-card:focus-visible{ outline: 2px solid #1d1d1f; outline-offset: 6px; border-radius: 20px; }

/* ---------- lead post ---------- */
.bl-lead{
  display:grid; grid-template-columns: 1.02fr .98fr;
  gap: clamp(30px, 4.5vw, 62px); align-items:center;
  padding-bottom: clamp(56px, 7vw, 88px);
}
.bl-lead .bl-art{ aspect-ratio: 4 / 3; border-radius: 24px; }
.bl-lead .bl-tag{ margin-top:0; }
/* These cards are reused at the foot of an article, outside .lp-page and
   outside .post, where they would otherwise inherit the dark site's
   near-white body colour and vanish against the tint. Colour them here so
   a card is self-contained wherever it is dropped. */
.bl-lead h2{
  font-size: clamp(1.75rem, 3.4vw, 2.6rem); line-height:1.1; margin:0 0 14px;
  letter-spacing:-.035em; text-wrap: balance; color:#1d1d1f;
}
.bl-lead p{ font-size: 1.08rem; line-height:1.55; margin:0; color:#6e6e73; }

/* ---------- the rest ---------- */
.bl-grid{
  display:grid; grid-template-columns: repeat(auto-fit, minmax(310px, 1fr));
  gap: clamp(30px, 4vw, 46px);
}
.bl-card h3{
  font-size: clamp(1.2rem, 1.9vw, 1.45rem); line-height:1.2; margin:0 0 10px;
  letter-spacing:-.03em; text-wrap: balance; color:#1d1d1f;
}
.bl-card p{ font-size:1rem; line-height:1.55; margin:0; color:#6e6e73; }

@media (max-width: 860px){
  .bl-lead{ grid-template-columns:1fr; gap: 26px; }
  /* the mark should lead the card on a phone, the way it does in the grid */
  .bl-lead .bl-art{ order:-1; aspect-ratio: 16 / 10; }
  .bl-lead .bl-tag{ margin-top: 4px; }
}

/* ---------- post cards (used by "keep reading" on articles) ---------- */
.section-blog{ padding: 70px 0 90px; }
.blog-grid{
  display:grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 24px;
}
.blog-card{
  display:flex; flex-direction:column;
  background:#fff; border:1px solid rgba(0,0,0,0.1); border-radius: var(--radius-sm);
  padding: 26px 24px 22px;
  transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}
.blog-card:hover{
  transform: translateY(-4px); border-color: rgba(0,0,0,0.28);
  box-shadow: 0 20px 40px -22px rgba(0,0,0,.4);
}
.blog-card-cat{
  font-family:'JetBrains Mono', monospace; font-size:0.66rem; font-weight:600;
  letter-spacing:.14em; text-transform:uppercase; color:#8a8a86; margin-bottom: 14px;
}
.blog-card h3{
  font-size: 1.24rem; line-height:1.25; color:#0a0a0a; margin: 0 0 10px;
  letter-spacing:-0.015em;
}
.blog-card p{
  font-size: 0.92rem; line-height:1.65; color:#55554f; margin: 0 0 20px;
  /* keep every card's summary to the same visual weight */
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;
}
.blog-card-meta{
  margin-top:auto; display:flex; align-items:center; gap: 8px;
  font-family:'JetBrains Mono', monospace; font-size:0.7rem; color:#8a8a86;
  letter-spacing:.04em;
}

/* ---------- article: light reading surface ----------
   Same language as the listing and the shipping/wholesale/COA pages.
   Long-form gets a white page even though the rest of the site is dark:
   sustained reading on a dark background is more fatiguing, and this is
   the one place people read continuously rather than scan. */
.post{ background:#fff; color:#1d1d1f; }

/* reading progress, pinned to the bottom edge of the sticky header. Two
   pixels of hairline is the only chrome the article gets. */
.read-progress{
  position:absolute; left:0; bottom:-1px; height:2px; width:0;
  background:#1d1d1f; transition: width .1s linear; z-index:2;
}

/* one centred column. The sticky contents rail that used to sit beside
   this was the last piece of furniture on the page. */
.post-shell{
  max-width: 740px; margin: 0 auto; padding: 0 24px;
}
.post-main{ min-width:0; }

/* ---------- header ----------
   Centred over the column, the way the hero is on every other page, then
   the body drops back to ranged left where it is actually read. */
.post-header{ padding: clamp(46px, 7vw, 78px) 0 clamp(30px, 4vw, 44px); text-align:center; }
.post-breadcrumb{
  display:flex; justify-content:center; gap:9px;
  font-size:.88rem; color:#86868b; margin-bottom: 26px;
}
.post-breadcrumb a{ color:#86868b; transition: color .2s ease; }
.post-breadcrumb a:hover{ color:#1d1d1f; }
.post-kicker{
  display:block; margin-bottom: 14px;
  font-size:.85rem; font-weight:600; color:#6e6e73;
}
.post-header h1{
  font-family:var(--font-display); font-weight:700;
  font-size: clamp(2.1rem, 4.8vw, 3.3rem); line-height:1.06; letter-spacing:-.04em;
  color:#1d1d1f; margin: 0 0 18px; text-wrap: balance;
}
.post-standfirst{
  font-size: 1.16rem; line-height:1.5; color:#6e6e73; margin: 0 auto 26px;
  max-width: 46ch; text-wrap: balance;
}
.post-meta{
  display:flex; justify-content:center; align-items:center; flex-wrap:wrap; gap:9px;
  font-size:.9rem; color:#86868b;
}
.post-byline{ color:#1d1d1f; font-weight:600; }

/* ---------- cover ----------
   The same drawn mark the post carries on the listing, so arriving here
   from a card is continuous. */
.post-cover{
  background:#f5f5f7; border-radius: 24px; aspect-ratio: 16 / 7;
  display:flex; align-items:center; justify-content:center; overflow:hidden;
  margin-bottom: 4px;
}
.post-cover svg{ width: 30%; min-width: 150px; height:auto; color:#1d1d1f; }

/* ---------- body copy ----------
   ~70 characters per line, where long-form reading speed peaks. */
.post-body{
  padding-top: clamp(36px, 5vw, 54px); padding-bottom: 20px;
  font-size: 1.12rem; line-height: 1.72; color:#2b2b30;
}
.post-body p{ margin: 0 0 1.4em; color: inherit; font-size: inherit; line-height: inherit; }
.post-body .lede{ font-size: 1.28rem; line-height:1.55; color:#1d1d1f; margin-bottom: 1.3em; }
.post-body h2{
  font-family:var(--font-display); font-weight:700;
  font-size: clamp(1.5rem, 2.6vw, 1.85rem); letter-spacing:-.035em; color:#1d1d1f;
  margin: 2em 0 .55em;      /* more space above than below, binding a heading to its section */
  scroll-margin-top: 100px; /* so anchor jumps clear the sticky header */
}
.post-body h3{
  font-size: 1.12rem; letter-spacing:-.02em; color:#1d1d1f; font-weight:600;
  margin: 1.8em 0 .5em;
}
.post-body h2:first-child, .post-body h3:first-child, .post-body .lede:first-child{ margin-top:0; }
.post-body ul, .post-body ol{ margin: 0 0 1.4em; padding-left: 1.2em; }
.post-body li{ margin-bottom: .55em; }
.post-body li::marker{ color:#c7c7cc; }
.post-body strong{ color:#1d1d1f; font-weight:600; }
.post-body em{ color:#1d1d1f; }
.post-body a{
  color:#1d1d1f; text-decoration: underline;
  text-decoration-color: rgba(0,0,0,.28); text-underline-offset: 3px;
  transition: text-decoration-color .2s ease;
}
.post-body a:hover{ text-decoration-color:#1d1d1f; }

/* a soft panel rather than a ruled box */
.post-callout{
  background:#f5f5f7; border-radius: 16px;
  padding: 24px 28px; margin: 2em 0;
  font-size: 1.04rem; line-height:1.65; color:#4a4a52;
}
.post-callout strong{ color:#1d1d1f; }

.post-disclaimer{
  margin: clamp(40px, 5vw, 58px) 0 0; padding: 0 0 clamp(56px, 7vw, 80px);
  font-size: .86rem; line-height:1.7; color:#86868b;
}
.post-disclaimer strong{ color:#6e6e73; }

/* ---------- keep reading ----------
   Light now, and using the listing's own cards so the end of an article
   looks like the page it came from. */
.section-related{ background:#f5f5f7; padding: clamp(56px, 7vw, 84px) 0 clamp(64px, 8vw, 96px); }
.section-related h2{
  font-size: clamp(1.5rem, 2.6vw, 1.9rem); letter-spacing:-.035em; color:#1d1d1f;
  margin: 0 0 clamp(28px, 4vw, 40px); text-align:center;
}
/* the tint panel inverts against this section so the marks still sit on
   a surface, not on the section colour itself */
.section-related .bl-art{ background:#fff; }
.section-related .bl-card:hover .bl-art{ background:#ececef; }

@media (max-width: 640px){
  .post-cover{ aspect-ratio: 16 / 10; }
  .post-cover svg{ width: 40%; }
  .post-body{ font-size: 1.06rem; }
}

/* =====================================================================
   MICRO-POLISH
   ===================================================================== */

/* text selection — the browser default blue is the only non-monochrome
   colour that ever appears on the site */
::selection{ background:#fff; color:#0a0a0a; }
.section-light ::selection, .post ::selection{ background:#0a0a0a; color:#fff; }

/* scrollbar, so a bright default bar doesn't sit against the black page */
html{ scrollbar-color: #3a3a38 #0a0a0a; }
::-webkit-scrollbar{ width:11px; height:11px; }
::-webkit-scrollbar-track{ background:#0a0a0a; }
::-webkit-scrollbar-thumb{ background:#2e2e2c; border:3px solid #0a0a0a; border-radius:99px; }
::-webkit-scrollbar-thumb:hover{ background:#454542; }

/* press feedback everywhere something is clickable — buttons already had it,
   these did not, so they felt dead on tap */
.chip:active,
.add-btn:active,
.nav-signin:active{ transform: translateY(1px) scale(.98); transition-duration:.06s; }
.faq-q:active{ opacity:.72; transition-duration:.06s; }
.jr-lead:active,
.jr-row > a:active{ transform: translateY(-2px) scale(.995); transition-duration:.09s; }
.blog-card:active{ transform: translateY(-2px) scale(.992); transition-duration:.08s; }
.product-card:active{ transition-duration:.08s; }

/* keyboard focus for the blog surfaces, which the earlier pass missed */
.jr-lead:focus-visible,
.jr-row > a:focus-visible,
.blog-card:focus-visible,
.post-toc a:focus-visible,
.post-body a:focus-visible{ outline:2px solid currentColor; outline-offset:3px; border-radius:2px; }

/* scroll cue is a real control now, not decoration */
.scroll-cue{ cursor:pointer; background:none; border:2px solid var(--border); padding:0; }
.scroll-cue:hover{ border-color: rgba(255,255,255,.4); }
.scroll-cue:focus-visible{ outline:2px solid #fff; outline-offset:4px; }

/* ---------- search modal ---------- */
body.search-locked{ overflow:hidden; }
.search-overlay{
  position: fixed; inset:0; z-index: 300;
  background: rgba(10,10,10,0.55);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  display:flex; align-items:flex-start; justify-content:center;
  padding: 90px 20px 40px;
  opacity:0; pointer-events:none;
  transition: opacity .2s ease;
}
.search-overlay.open{ opacity:1; pointer-events:auto; }
.search-modal{
  width: 100%; max-width: 720px; max-height: 78vh;
  background:#fff; color:#0a0a0a;
  border-radius: 0;
  box-shadow: 0 40px 80px -20px rgba(0,0,0,.5);
  display:flex; flex-direction:column; overflow:hidden;
  transform: translateY(-14px); transition: transform .2s ease;
}
.search-overlay.open .search-modal{ transform: translateY(0); }
.search-input-row{
  display:flex; align-items:center; gap:14px;
  padding: 20px 22px; border-bottom: 1px solid rgba(0,0,0,0.08); flex-shrink:0;
}
.search-input-icon{ color:#8a8a86; flex-shrink:0; }
.search-input-row input{
  flex:1; border:none; outline:none; font-family:inherit;
  font-size: 1.15rem; color:#0a0a0a; background:transparent;
}
.search-input-row input::placeholder{ color:#a3a3a1; }
.search-esc{
  flex-shrink:0; font-family:'JetBrains Mono', monospace; font-size:0.72rem; font-weight:600;
  padding: 5px 9px; border:1px solid rgba(0,0,0,0.15); border-radius:4px; color:#8a8a86;
  background:transparent; cursor:pointer;
}
.search-esc:hover{ border-color:#0a0a0a; color:#0a0a0a; }
.search-results{ overflow-y:auto; padding: 0; }
.search-label{
  font-family:'JetBrains Mono', monospace; font-size:0.68rem; font-weight:700;
  letter-spacing:.1em; text-transform:uppercase; color:#a3a3a1;
  padding: 16px 22px 10px;
}
.search-row{
  display:flex; align-items:center; gap:16px; padding: 12px 22px;
  border-radius: 0; text-decoration:none; color:#0a0a0a;
  border-top: 1px solid rgba(0,0,0,0.07);
  transition: background .15s ease;
}
.search-row:hover, .search-row:focus-visible{ background:#f7f7f5; }
.search-thumb{
  width:48px; height:48px; border-radius:0; flex-shrink:0;
  background: linear-gradient(180deg,#f6f6f4,#e7e7e4);
  border: 1px solid rgba(0,0,0,0.08);
  display:flex; align-items:center; justify-content:center; overflow:hidden;
}
.search-row-copy{ flex:1; min-width:0; }
.search-row-name{ display:block; font-weight:700; font-size:0.95rem; }
.search-row-meta{
  display:block; font-size:0.8rem; color:#8a8a86; margin-top:2px;
  font-family:'JetBrains Mono', monospace;
}
.search-row-arrow{ color:#a3a3a1; flex-shrink:0; }
.search-row:hover .search-row-arrow{ color:#0a0a0a; }
.search-empty{ padding: 30px 22px; text-align:center; color:#8a8a86; font-size:0.9rem; }
@media (max-width:640px){
  .search-overlay{ padding: 60px 14px 24px; }
  .search-input-row input{ font-size:1rem; }
}

/* ---------- quick-add modal ----------
   A centered card on anything wide enough to have room to center in — the
   bottom-sheet pattern is a phone convention, and pinning it to the bottom
   edge on a 1440px display just wastes the screen around it. Rounded
   corners, a soft shadow and a scale-in read as a native macOS/iOS dialog
   rather than a mobile sheet borrowed onto desktop. The sheet pattern comes
   back at the phone breakpoint below, where it is the right idiom again. */
.qa-overlay{
  position: fixed; inset:0; z-index: 300;
  background: rgba(10,10,10,0.55);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  display:flex; align-items:center; justify-content:center;
  padding: 24px;
  opacity:0; pointer-events:none;
  transition: opacity .2s ease;
}
.qa-overlay.open{ opacity:1; pointer-events:auto; }
.qa-sheet{
  width: 100%; max-width: 440px; max-height: 82vh;
  background:#fff; color:#0a0a0a;
  border-radius: 20px;
  box-shadow: 0 24px 70px -12px rgba(0,0,0,.4), 0 2px 10px rgba(0,0,0,.08);
  display:flex; flex-direction:column; overflow:hidden;
  transform: scale(.94); opacity:0;
  transition: transform .28s cubic-bezier(.2,.8,.2,1), opacity .2s ease;
}
.qa-overlay.open .qa-sheet{ transform: scale(1); opacity:1; }
.qa-handle{ display:none; }
.qa-head{
  display:flex; align-items:flex-start; gap:14px;
  padding: 24px 24px 18px; flex-shrink:0;
}
.qa-thumb{
  width:52px; height:52px; flex-shrink:0; border-radius:12px;
  background: linear-gradient(180deg,#f6f6f4,#e7e7e4);
  border:1px solid rgba(0,0,0,0.08);
  display:flex; align-items:center; justify-content:center; overflow:hidden;
}
.qa-thumb .thumb-photo{ width:100%; height:100%; object-fit:cover; }
.qa-head-copy{ flex:1; min-width:0; }
.qa-name{ font-size:1.3rem; letter-spacing:-.025em; margin:0 0 3px; }
.qa-sub{ font-size:0.92rem; color:#6e6e73; margin:0; }
.qa-close{
  flex-shrink:0; width:28px; height:28px; border-radius:50%;
  border:none; background:#f0f0f2; color:#6e6e73;
  display:flex; align-items:center; justify-content:center; cursor:pointer;
  transition: background .15s ease, color .15s ease;
}
.qa-close svg{ width:13px; height:13px; }
.qa-close:hover{ background:#e5e5e8; color:#0a0a0a; }

.qa-rows{
  overflow-y:auto; margin: 0 16px 16px; border-top: none;
  border: 1px solid rgba(0,0,0,0.08); border-radius: 14px;
}
/* the row itself is the control, so the target is the full width of the
   sheet rather than a 34px square at the end of it */
.qa-row{
  width:100%; display:flex; align-items:center; gap:16px;
  padding: 16px 18px; border:none; border-bottom: 1px solid rgba(0,0,0,0.08);
  background:#fff; color:#0a0a0a; font-family:inherit; text-align:left;
  cursor:pointer; transition: background .15s ease;
}
.qa-row:last-child{ border-bottom:none; }
.qa-row:hover{ background:#f5f5f7; }
.qa-row:active{ background:#ebebef; }
.qa-row-label{ flex:1; min-width:0; font-weight:600; font-size:1.05rem; }
.qa-row-price{ display:flex; align-items:baseline; gap:9px; white-space:nowrap; font-size:0.98rem; }
.qa-row-was{ color:#a1a1a6; text-decoration: line-through; font-size:0.9rem; }
.qa-row-out{
  font-family:'JetBrains Mono', monospace; font-size:.72rem; font-weight:700;
  text-transform:uppercase; letter-spacing:.06em; color:#8a8a86;
}
.qa-row.is-out{ cursor:not-allowed; }
.qa-row.is-out .qa-row-label{ color:#8a8a86; text-decoration: line-through; }
.qa-row.is-out:hover{ background:#fff; }
.qa-row.is-out .qa-row-mark{ opacity:0; }
.qa-row-now{ color:#0a0a0a; font-weight:600; }
.qa-row-mark{
  flex-shrink:0; width:26px; height:26px; position:relative; color:#fff;
  background:#0a0a0a; border-radius:50%;
  transition: background .15s ease;
}
.qa-row:hover .qa-row-mark{ background:#2a2a2a; }
.qa-row-mark svg{ position:absolute; inset:0; margin:auto; transition: opacity .18s ease; }
.qa-tick{ opacity:0; }
.qa-row.added .qa-plus{ opacity:0; }
.qa-row.added .qa-row-mark{ background:#0a0a0a; }
.qa-row.added .qa-tick{ opacity:1; }
@media (max-width:640px){
  .qa-overlay{ align-items:flex-end; padding:0; }
  .qa-sheet{
    max-width:none; max-height: 86vh;
    border-radius: 20px 20px 0 0;
    transform: translateY(100%); opacity:1;
  }
  .qa-overlay.open .qa-sheet{ transform: translateY(0); }
  .qa-handle{
    display:block; width:36px; height:4px; border-radius:2px;
    background: rgba(0,0,0,0.15); margin: 12px auto 4px; flex-shrink:0;
  }
  .qa-head{ padding: 12px 20px 16px; }
  .qa-rows{ margin: 0 12px 12px; }
}

/* ---------- add-to-cart toast ----------
   Stood here: a fixed banner across the bottom of the screen saying "Added
   to cart" with a button to open the drawer. Removed once js/cart.js started
   opening the drawer directly on every add — a banner pointing at something
   already open in front of you has nothing left to point at. */

/* ---------- cart drawer ----------
   Slides in from the right on desktop, fills the screen on mobile. Square
   corners throughout to match the search modal and the quick-add sheet. */
.cart-overlay{
  position: fixed; inset:0; z-index: 320;
  background: rgba(10,10,10,0.55);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  display:flex; justify-content:flex-end;
  opacity:0; pointer-events:none;
  transition: opacity .22s ease;
}
.cart-overlay.open{ opacity:1; pointer-events:auto; }
.cart-drawer{
  width: 100%; max-width: 430px; height:100%;
  background:#fff; color:#0a0a0a;
  border-radius:0;
  box-shadow: -30px 0 70px -30px rgba(0,0,0,.6);
  display:flex; flex-direction:column; overflow:hidden;
  transform: translateX(100%);
  transition: transform .3s cubic-bezier(.2,.8,.2,1);
}
.cart-overlay.open .cart-drawer{ transform: translateX(0); }

.cart-head{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  padding: 22px 24px; border-bottom:1px solid rgba(0,0,0,0.1); flex-shrink:0;
}
.cart-title{ font-size:1.3rem; margin:0; display:flex; align-items:center; gap:11px; }
.cart-count{
  font-family:'JetBrains Mono', monospace; font-size:.72rem; font-weight:700;
  background:#0a0a0a; color:#fff; padding: 3px 9px; min-width:24px; text-align:center;
}
.cart-close{
  flex-shrink:0; width:38px; height:38px; border-radius:0;
  border:1px solid rgba(0,0,0,0.14); background:transparent; color:#0a0a0a;
  display:flex; align-items:center; justify-content:center; cursor:pointer;
  transition: border-color .15s ease, background .15s ease;
}
.cart-close:hover{ border-color:#0a0a0a; background: rgba(0,0,0,0.05); }

/* Deliberately quieter than .cart-ship-msg below it: that one is telling you
   about a discount within reach, worth a beat of attention, and this one is
   a fact about the shipping schedule that happens to be true right now. Mono
   numerals only in the bold span, so the one thing worth a glance at a
   glance is the time, not the whole sentence. */
.cart-cutoff{
  margin:0; padding: 11px 24px; border-bottom:1px solid rgba(0,0,0,0.1);
  font-size:.78rem; color:#7a7a76; background:#f7f7f5;
}
.cart-cutoff strong{
  font-family:'JetBrains Mono', monospace; font-weight:700; color:#45453f;
  font-variant-numeric: tabular-nums;
}
.cart-cutoff[hidden]{ display:none; }

.cart-body{ flex:1; overflow-y:auto; }

/* free-shipping progress */
.cart-ship{ padding: 18px 24px 20px; border-bottom:1px solid rgba(0,0,0,0.1); }
.cart-ship-msg{ margin:0 0 12px; font-size:.92rem; color:#0a0a0a; }
.cart-ship-msg strong{ font-weight:700; }
.cart-ship-bar{ height:6px; background:#ececea; border:1px solid rgba(0,0,0,0.08); overflow:hidden; }
.cart-ship-bar span{
  display:block; height:100%; background:#0a0a0a;
  transition: width .35s cubic-bezier(.2,.8,.2,1);
}

/* line items */
.cart-row{
  display:flex; gap:15px; padding: 18px 24px;
  border-bottom:1px solid rgba(0,0,0,0.08);
}
.cart-thumb{
  width:62px; height:62px; flex-shrink:0; border-radius:0;
  background: linear-gradient(180deg,#f6f6f4,#e7e7e4);
  border:1px solid rgba(0,0,0,0.08);
  display:flex; align-items:center; justify-content:center; overflow:hidden;
}
.cart-row-main{ flex:1; min-width:0; display:flex; flex-direction:column; gap:12px; }
.cart-row-top{ display:flex; align-items:flex-start; justify-content:space-between; gap:10px; }
.cart-row-name{ margin:0; font-weight:700; font-size:.97rem; line-height:1.3; }
.cart-row-variant{
  margin:3px 0 0; font-family:'JetBrains Mono', monospace;
  font-size:.76rem; color:#8a8a86;
}
.cart-remove{
  flex-shrink:0; width:26px; height:26px; border-radius:0;
  border:1px solid rgba(0,0,0,0.12); background:transparent; color:#8a8a86;
  display:flex; align-items:center; justify-content:center; cursor:pointer;
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.cart-remove:hover{ border-color:#0a0a0a; color:#0a0a0a; background: rgba(0,0,0,0.04); }
.cart-row-bottom{ display:flex; align-items:center; justify-content:space-between; gap:12px; }

.cart-qty{ display:flex; align-items:stretch; border:1px solid rgba(0,0,0,0.14); }
.cart-qty button{
  width:30px; height:30px; border:none; background:transparent; color:#0a0a0a;
  font-size:1rem; line-height:1; cursor:pointer; border-radius:0;
  display:flex; align-items:center; justify-content:center;
  transition: background .15s ease, color .15s ease;
}
.cart-qty button:hover{ background:#0a0a0a; color:#fff; }
.cart-qty-n{
  min-width:34px; display:flex; align-items:center; justify-content:center;
  font-family:'JetBrains Mono', monospace; font-size:.86rem; font-weight:600;
  border-left:1px solid rgba(0,0,0,0.14); border-right:1px solid rgba(0,0,0,0.14);
}
.cart-row-price{ display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; justify-content:flex-end; }
.cart-was{ font-family:'JetBrains Mono', monospace; font-size:.8rem; color:#a3a3a1; text-decoration: line-through; }
.cart-now{ font-family:var(--font-display); font-weight:700; font-size:1rem; }
.cart-off{
  font-family:'JetBrains Mono', monospace; font-size:.6rem; font-weight:700;
  letter-spacing:.08em; text-transform:uppercase;
  background:#0a0a0a; color:#fff; padding: 3px 7px;
}

/* accessory offer — sits under the line items, tinted so it reads as an
   addition to the cart rather than another thing already in it */
.cart-upsell{
  display:flex; align-items:center; gap:13px; padding: 15px 24px;
  border-bottom:1px solid rgba(0,0,0,0.08);
  background:#f7f7f5;
}
.cart-upsell-thumb{
  width:46px; height:46px; flex-shrink:0;
  background: linear-gradient(180deg,#fbfbfa,#eeeeeb);
  border:1px solid rgba(0,0,0,0.08);
  display:flex; align-items:center; justify-content:center; overflow:hidden;
}
.cart-upsell-main{ flex:1; min-width:0; }
/* the name is long enough to wrap in the drawer's width; balance stops it
   breaking one word onto a line of its own */
.cart-upsell-t{
  margin:0; font-weight:700; font-size:.9rem; line-height:1.3; color:#0a0a0a;
  text-wrap: balance;
}
.cart-upsell-d{
  margin:3px 0 0; font-family:'JetBrains Mono', monospace;
  font-size:.76rem; color:#8a8a86;
}
.cart-upsell-add{
  flex-shrink:0; display:flex; align-items:center; gap:6px;
  padding: 9px 14px; border-radius:0; cursor:pointer;
  border:1px solid #0a0a0a; background:transparent; color:#0a0a0a;
  font-family:inherit; font-size:.82rem; font-weight:700; line-height:1;
  transition: background .15s ease, color .15s ease;
}
.cart-upsell-add:hover{ background:#0a0a0a; color:#fff; }

/* empty state */
.cart-empty{ padding: 60px 30px; text-align:center; }
.cart-empty-t{ font-weight:700; font-size:1.05rem; margin:0 0 6px; color:#0a0a0a; }
.cart-empty-d{ font-size:.9rem; color:#8a8a86; margin:0 0 24px; }

/* footer */
.cart-foot{
  flex-shrink:0; padding: 20px 24px 24px;
  border-top:1px solid rgba(0,0,0,0.12); background:#fff;
  box-shadow: 0 -14px 26px -22px rgba(0,0,0,.5);
}
.cart-saving{
  display:flex; align-items:center; gap:8px; margin:0 0 12px;
  font-size:.9rem; font-weight:600; color:#0a0a0a;
}
.cart-subtotal{
  display:flex; align-items:baseline; justify-content:space-between; gap:14px;
  font-size:1.05rem; font-weight:700; color:#0a0a0a;
  padding-top:12px; border-top:1px solid rgba(0,0,0,0.1);
}
.cart-subtotal span:last-child{ font-family:var(--font-display); }
.cart-tax{ margin:6px 0 16px; font-size:.8rem; color:#8a8a86; }
/* Apple Pay / Google Pay, mounted by initCartExpressPay(). Hidden until
   canMakePayment() confirms the device actually has a wallet configured,
   the same gate js/express-pay.js's other two mounts use — nothing here
   claims wallet support a browser cannot show. */
.cart-express[hidden]{ display:none; }
.cart-express{ margin: 0 0 10px; }
.cart-express-btn{ width:100%; min-height:48px; }
.cart-express-msg{ margin:8px 0 0; font-size:.82rem; color:#b3261e; text-align:center; }
.cart-express-msg:empty{ display:none; }
.cart-checkout{ width:100%; justify-content:center; }
.cart-checkout-msg{ margin:10px 0 0; font-size:.82rem; color:#8a8a86; text-align:center; }
.cart-trust{
  list-style:none; margin:16px 0 0; padding:0;
  display:flex; flex-wrap:wrap; justify-content:center; gap:8px 20px;
  font-size:.78rem; color:#8a8a86;
}
.cart-trust li{ display:flex; align-items:center; gap:7px; }
.cart-trust li::before{ content:''; width:4px; height:4px; background:#8a8a86; flex-shrink:0; }

/* fills the screen on mobile */
@media (max-width: 640px){
  .cart-drawer{ max-width:none; }
  .cart-head{ padding: 18px 18px; }
  .cart-ship, .cart-row, .cart-upsell{ padding-left:18px; padding-right:18px; }
  .cart-foot{ padding: 18px 18px 22px; }
}
@media (prefers-reduced-motion: reduce){
  .cart-drawer{ transition:none; }
  .cart-ship-bar span{ transition:none; }
}

/* the account panels reuse the sign-in field styling */
.ac-page .si-field{ margin-bottom:16px; }
.ac-page .si-field label{
  display:block; margin-bottom:8px;
  font-size:.88rem; font-weight:500; letter-spacing:0; text-transform:none; color:#6e6e73;
}
.ac-page .si-field input, .ac-page .si-field select{
  width:100%; padding:13px 15px; border-radius:0;
  border:1px solid rgba(0,0,0,.22); background:#fff; color:#0a0a0a;
  font-family:inherit; font-size:.96rem; transition: border-color .18s ease;
}
.ac-page .si-field select{ appearance:none; cursor:pointer; }
.ac-page .si-field input:hover, .ac-page .si-field select:hover{ border-color: rgba(0,0,0,.45); }
.ac-page .si-field input:focus, .ac-page .si-field select:focus{ outline:none; border-color:#0a0a0a; }

/* ---------- very narrow phones ----------
   At 320px the header actions ran past the viewport and clipped the
   hamburger, so the menu could not be opened at all. The Sign In / Account
   label is the widest non-essential thing in that row — the person icon
   still carries the meaning — so it drops out and the row fits again. */
@media (max-width: 359px){
  .nav-signin-label{
    position:absolute; width:1px; height:1px; overflow:hidden;
    clip-path: inset(50%); white-space:nowrap;
  }
  .nav-actions{ gap: 7px; }
  .nav-icon-btn{ padding: 9px; }
}

/* ---------- disclosure ("read details") ----------
   One implementation, used by the process chain, the About page, Shipping and
   Wholesale. Every one of those pages had the same problem: paragraphs that
   are correct, worth keeping, and read by almost nobody on a phone. The
   scannable line stays on the page; the paragraph moves in here.

   Native <details>, so it needs no script and the copy sits in the served
   markup whether the panel is open or shut. That matters more here than it
   did on the product page: this is the text that carries the compliance
   context and most of the page's indexable substance, so it has to be in the
   HTML a crawler receives, not injected on click. Collapsing it is a reading
   decision, not a loading one. */
.disc{ margin-top: 14px; }
.disc > summary{
  display:inline-flex; align-items:center; gap: 9px;
  cursor:pointer; list-style:none; padding: 4px 0;
  font-family:'JetBrains Mono', monospace; font-size:.68rem; font-weight:600;
  letter-spacing:.14em; text-transform:uppercase; color:#8a8a86;
  transition: color .18s ease;
}
.disc > summary::-webkit-details-marker{ display:none; }
.disc > summary::marker{ content:''; }
.disc > summary:hover{ color:#0a0a0a; }
.disc > summary:focus-visible{ outline: 2px solid #0a0a0a; outline-offset: 3px; }
.disc[open] > summary{ color:#0a0a0a; }
/* a plus that drops its upright stroke when the panel opens */
.disc-mark{ position:relative; flex-shrink:0; width:9px; height:9px; }
.disc-mark::before, .disc-mark::after{ content:''; position:absolute; background:currentColor; }
.disc-mark::before{ top:4px; left:0; width:9px; height:1.5px; }
.disc-mark::after{ left:4px; top:0; width:1.5px; height:9px; }
.disc[open] .disc-mark::after{ display:none; }
.disc-body{ padding: 12px 0 2px; }
.disc-body > *:last-child{ margin-bottom: 0 !important; }

/* ---------- launch offer ----------
   One surface: the standing form in the footer band above. There were two
   others, a bar along the foot of the homepage and a centred dialog on the
   catalog and the product pages, and their frames, transitions, scroll lock
   and close control were all defined here. They went with the popup itself.
   Almost all of this store's traffic arrives from a Facebook ad, and an offer
   thrown over a page the visitor landed on seconds ago reads as spam.

   Colours are the site palette now. The popup declared --lo-ink and --lo-dim
   on its own frames, and those were aliases of --text and --text-dim, so with
   the frames gone the aliases had nowhere left to live.

   Worth knowing before touching the greys: .lo-eyebrow, .lo-ask and .lo-msg
   referenced --lo-dim from outside the selector that defined it, so in the
   footer they never resolved and have always inherited the footer's own ink.
   They are written as var(--text) here to keep that exactly as it renders
   today. Swapping those three to var(--text-dim) is the one-line change if
   they should read quieter, and is what the popup looked like. */

/* shared type */
.lo-eyebrow{
  display:block; font-family:'JetBrains Mono', monospace;
  font-size:.66rem; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
  color: var(--text); margin-bottom: 10px;
}
.lo-title{
  font-family: var(--font-display); font-weight:600; letter-spacing:-.02em;
  color: var(--text); margin:0 0 6px; line-height:1.2;
}
.lo-ask{ margin:0; font-size:.92rem; line-height:1.55; color: var(--text); }
.lo-facts{
  margin: 14px 0 0; font-family:'JetBrains Mono', monospace;
  font-size:.62rem; letter-spacing:.08em; color:#6b6b69; line-height:1.7;
}

/* the form */
.lo-form{ display:flex; gap:10px; align-items:stretch; }
.lo-input{
  flex:1 1 auto; min-width:0;
  background:#141414; color: var(--text);
  border:1px solid rgba(255,255,255,.22); border-radius:0;
  padding: 14px 16px; font-family:inherit; font-size:.95rem; font-weight:500;
}
.lo-input::placeholder{ color:#6b6b69; }
.lo-input:focus{ outline:none; border-color: rgba(255,255,255,.6); background:#181818; }
.lo-submit{ flex:0 0 auto; padding: 14px 22px; font-size:.92rem; }
.lo-submit:disabled{ opacity:.6; cursor:progress; }
/* off-screen rather than display:none, which some bots skip */
.lo-hp{ position:absolute; left:-9999px; width:1px; height:1px; opacity:0; }
.lo-sr{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.lo-msg{ margin: 10px 0 0; font-size:.82rem; line-height:1.5; color: var(--text); min-height:0; }
.lo-msg.is-error{ color:#ffb4a8; }

/* after the address is in */
.lo-reveal{ text-align:center; }
.lo-code{
  font-family:'JetBrains Mono', monospace; font-weight:700;
  font-size: 1.15rem; letter-spacing:.06em; color: var(--text);
  margin: 0 0 8px;
}
.lo-copy-btn{ margin-top: 18px; padding: 12px 24px; font-size:.9rem; }

@media (max-width: 780px){
  .lo-form{ flex-direction:column; }
  .lo-submit{ width:100%; }
}

/* ---------- launch offer popup ----------
   The behavioural surface: exit intent on desktop, a time-or-scroll nudge on
   mobile (js/launch-offer.js). Built to spec rather than matched to the
   site's other overlays on purpose — literal black rather than var(--bg)'s
   near-black, a border instead of the shadow every other panel here uses,
   square corners like everything else on the site already is. The shared
   type and form styles above (.lo-eyebrow, .lo-form, .lo-input and the rest)
   are untouched: they already render white-on-dark and need no popup-specific
   override, which is the payoff of the two surfaces sharing one set of
   classes instead of each carrying its own copy. */
.lo-pop-overlay{
  position:fixed; inset:0; z-index:340;
  background: rgba(0,0,0,.72);
  display:flex; align-items:center; justify-content:center;
  padding: 24px;
  opacity:0; visibility:hidden; pointer-events:none;
  transition: opacity .2s ease, visibility 0s .2s;
}
.lo-pop-overlay[hidden]{ display:none; }
.lo-pop-overlay.is-shown{
  opacity:1; visibility:visible; pointer-events:auto;
  transition: opacity .2s ease;
}
.lo-pop{
  position:relative; width:100%; max-width:440px;
  background:#000; border:1px solid rgba(255,255,255,.16);
  border-radius:0; box-shadow:none;
  padding: 40px 32px 32px;
  transform: scale(.97);
  transition: transform .2s ease;
}
.lo-pop-overlay.is-shown .lo-pop{ transform: scale(1); }
.lo-pop-close{
  position:absolute; top:14px; right:14px; width:32px; height:32px;
  border:1px solid rgba(255,255,255,.22); border-radius:0; background:transparent;
  color:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer;
  transition: border-color .15s ease, background .15s ease;
}
.lo-pop-close:hover{ border-color:#fff; background:rgba(255,255,255,.08); }
.lo-pop .lo-title{ font-size:1.5rem; }

/* Bottom sheet, not a centred card: the mobile trigger fires mid-scroll or
   mid-read, so it should read as something sliding up from underneath the
   page rather than a dialog dropped over the middle of it. Square corners
   kept even on the top edge, against the usual bottom-sheet convention,
   because the brief was explicit that nothing here should be rounded. */
@media (max-width: 640px){
  .lo-pop-overlay{ align-items:flex-end; padding:0; }
  .lo-pop{
    max-width:none; width:100%;
    border-left:0; border-right:0; border-bottom:0;
    padding: 30px 22px calc(26px + env(safe-area-inset-bottom));
    transform: translateY(100%);
  }
  .lo-pop-overlay.is-shown .lo-pop{ transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce){
  .lo-pop-overlay, .lo-pop-overlay .lo-pop{ transition:none; }
}
