/* ==========================================================================
   PEPTiQX Biotech — Liquid Glass design system
   Palette derived from the brand mark: slate #3D4C63, steel #5B8AC4.
   ========================================================================== */

/* --- tokens --------------------------------------------------------------- */

:root {
  /* brand */
  --slate-900: #1c2433;
  --slate-800: #26303f;
  --slate-700: #313c4f;
  --slate-600: #3d4c63;   /* logo mark */
  --slate-500: #52627d;
  --slate-400: #74849c;
  --slate-300: #9aa7ba;
  --slate-200: #c3ccd8;
  --slate-100: #e2e7ee;
  --slate-050: #f2f4f8;

  --steel-800: #35598a;
  --steel-700: #3f6ba3;
  --steel-600: #4d7cb8;
  --steel-500: #5b8ac4;   /* logo reversed */
  --steel-400: #7ba4d4;
  --steel-300: #a3c1e3;
  --steel-100: #dceaf7;

  --mint-500: #3fae90;
  --amber-500: #d59a3c;
  --rose-500:  #c85a5a;

  /* semantic — light */
  color-scheme: light;

  --bg:            #f6f7f9;
  --bg-elev:       #ffffff;
  --ink:           #1c2433;
  --ink-soft:      #52627d;
  --ink-faint:     #8494a8;
  --accent:        var(--steel-600);
  --accent-ink:    #ffffff;
  --hairline:      rgba(28, 36, 51, 0.10);
  --hairline-soft: rgba(28, 36, 51, 0.06);

  /* Opaque counterpart to --glass-bg-strong, for panels nested inside another
     glass surface where backdrop-filter cannot reach the page behind. */
  --drawer-bg:     #ffffff;

  /* glass */
  --glass-bg:      rgba(255, 255, 255, 0.62);
  --glass-bg-strong: rgba(255, 255, 255, 0.80);
  --glass-brd:     rgba(255, 255, 255, 0.75);
  --glass-blur:    saturate(180%) blur(28px);
  --glass-sheen:   linear-gradient(
                     160deg,
                     rgba(255, 255, 255, 0.65) 0%,
                     rgba(255, 255, 255, 0.12) 42%,
                     rgba(255, 255, 255, 0) 70%);

  --shadow-sm: 0 1px 2px rgba(20, 28, 42, 0.06),
               0 2px 6px rgba(20, 28, 42, 0.04);
  --shadow-md: 0 1px 2px rgba(20, 28, 42, 0.06),
               0 8px 24px rgba(20, 28, 42, 0.08);
  --shadow-lg: 0 2px 4px rgba(20, 28, 42, 0.05),
               0 18px 48px rgba(20, 28, 42, 0.14);
  --shadow-ring: inset 0 1px 0 rgba(255, 255, 255, 0.7);

  /* geometry */
  --r-xs: 8px;
  --r-sm: 12px;
  --r-md: 18px;
  --r-lg: 24px;
  --r-xl: 32px;
  --r-pill: 999px;

  /* One height for every control on a form row. Padding alone does not get
     there: an input, a select and a button each compute their own line box, so
     a button beside a field lands a pixel or two off and the row reads as
     misaligned without anybody being able to say why. */
  --control-h: 46px;

  --maxw: 1200px;
  --gut: clamp(1.1rem, 4vw, 2.5rem);

  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
          "Inter", "Helvetica Neue", Segoe UI, Roboto, sans-serif;
  --mono: "SF Mono", ui-monospace, "JetBrains Mono", Menlo, monospace;
}

/* Dark theme keys off [data-theme], never prefers-color-scheme: the head script
   resolves the OS setting into that attribute so one switch serves both the
   system default and the header toggle. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --bg:            #0e131c;
  --bg-elev:       #161d29;
  --ink:           #eef2f7;
  --ink-soft:      #a8b6c9;
  --ink-faint:     #6f8098;
  --accent:        var(--steel-400);
  --accent-ink:    #0e131c;
  --hairline:      rgba(255, 255, 255, 0.12);
  --hairline-soft: rgba(255, 255, 255, 0.07);

  --drawer-bg:       #161d29;

  --glass-bg:        rgba(30, 39, 54, 0.58);
  --glass-bg-strong: rgba(30, 39, 54, 0.82);
  --glass-brd:       rgba(255, 255, 255, 0.12);
  --glass-sheen:     linear-gradient(
                       160deg,
                       rgba(255, 255, 255, 0.12) 0%,
                       rgba(255, 255, 255, 0.03) 42%,
                       rgba(255, 255, 255, 0) 70%);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.45), 0 12px 32px rgba(0, 0, 0, 0.35);
  --shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.5), 0 24px 60px rgba(0, 0, 0, 0.45);
  --shadow-ring: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

/* --- base ----------------------------------------------------------------- */

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

/* ambient mesh — the light the glass refracts */
body::before {
  content: "";
  position: fixed;
  inset: -20vh -10vw;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(48vw 42vw at 12% 4%,  rgba(91, 138, 196, 0.22), transparent 62%),
    radial-gradient(42vw 38vw at 88% 12%, rgba(61, 76, 99, 0.18),  transparent 60%),
    radial-gradient(52vw 40vw at 62% 88%, rgba(63, 174, 144, 0.12), transparent 64%);
  filter: blur(10px);
}

:root[data-theme="dark"] body::before {
  background:
    radial-gradient(48vw 42vw at 12% 4%,  rgba(91, 138, 196, 0.20), transparent 62%),
    radial-gradient(42vw 38vw at 88% 12%, rgba(122, 92, 196, 0.14), transparent 60%),
    radial-gradient(52vw 40vw at 62% 88%, rgba(63, 174, 144, 0.10), transparent 64%);
}

/* --- particle field -------------------------------------------------------
   Sits between the ambient mesh (z -2) and page content (z 0), so the glass
   surfaces blur it the same way they blur the mesh. No background of its own:
   the canvas contributes particles and nothing else.
   `fixed` rather than `absolute` so it stays put on long scrolling pages, and
   `pointer-events: none` so it can never intercept a click meant for the UI —
   hover still works because particles.js listens on `window`. */

#particles-js {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: none;
  pointer-events: none;
}

#particles-js canvas {
  display: block;
  width: 100% !important;
  height: 100% !important;
}

@media (prefers-reduced-motion: reduce) {
  #particles-js { display: none; }
}

h1, h2, h3, h4 {
  margin: 0 0 0.5em;
  line-height: 1.12;
  letter-spacing: -0.022em;
  font-weight: 640;
}

h1 { font-size: clamp(2.1rem, 5vw, 3.5rem); letter-spacing: -0.035em; }
h2 { font-size: clamp(1.6rem, 3.4vw, 2.4rem); letter-spacing: -0.028em; }
h3 { font-size: clamp(1.1rem, 2vw, 1.35rem); }

p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

a { color: var(--accent); text-decoration: none; transition: opacity 0.2s var(--ease); }
a:hover { opacity: 0.75; }

img, svg { max-width: 100%; height: auto; display: block; }

::selection { background: rgba(91, 138, 196, 0.28); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}

/* --- layout --------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gut);
}

.wrap-narrow { max-width: 780px; }

.section { padding-block: clamp(3rem, 7vw, 6rem); }
.section-tight { padding-block: clamp(2rem, 4vw, 3.25rem); }

.grid { display: grid; gap: clamp(1rem, 2vw, 1.5rem); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); }

.stack   { display: flex; flex-direction: column; gap: 1rem; }
.row     { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.row-end { margin-left: auto; }
.center  { text-align: center; }
.center-x{ margin-inline: auto; }

/* --- glass surfaces ------------------------------------------------------- */

.glass {
  position: relative;
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-brd);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md), var(--shadow-ring);
  overflow: hidden;
}

/* the specular highlight that sells the material */
.glass::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--glass-sheen);
  opacity: 0.85;
}

.glass > * { position: relative; z-index: 1; }

.glass-strong { background: var(--glass-bg-strong); }
.glass-flat   { box-shadow: var(--shadow-sm), var(--shadow-ring); }

.pad    { padding: clamp(1.25rem, 3vw, 2rem); }
.pad-lg { padding: clamp(1.75rem, 4vw, 3rem); }
.pad-sm { padding: 1rem 1.15rem; }

.card-hover {
  transition: transform 0.45s var(--ease), box-shadow 0.45s var(--ease);
}
.card-hover:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg), var(--shadow-ring);
}

/* --- type helpers --------------------------------------------------------- */

.eyebrow {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 620;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.85rem;
}

.lede {
  font-size: clamp(1.02rem, 1.6vw, 1.18rem);
  color: var(--ink-soft);
  line-height: 1.62;
}

.muted  { color: var(--ink-soft); }
.faint  { color: var(--ink-faint); }
.tiny   { font-size: 0.8rem; }
.mono   { font-family: var(--mono); font-size: 0.86em; letter-spacing: -0.01em; }
.nowrap { white-space: nowrap; }

.section-head { max-width: 660px; margin-bottom: clamp(1.75rem, 4vw, 2.75rem); }
.section-head.center { margin-inline: auto; }

/* --- buttons -------------------------------------------------------------- */

.btn {
  --btn-bg: var(--accent);
  --btn-fg: var(--accent-ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  /* Same height as a field, from the same token, so a button sitting at the end
     of a form row matches the inputs beside it rather than nearly matching
     them. The padding already lands within a pixel of this on its own — the
     min-height is what stops a shorter label or a different font from drifting
     off it. */
  min-height: var(--control-h);
  padding: 0.72rem 1.4rem;
  font: inherit;
  font-size: 0.94rem;
  font-weight: 560;
  letter-spacing: -0.01em;
  color: var(--btn-fg);
  background: var(--btn-bg);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  cursor: pointer;
  white-space: nowrap;
  box-shadow: var(--shadow-sm);
  transition: transform 0.28s var(--ease-spring), box-shadow 0.28s var(--ease),
              background 0.28s var(--ease), opacity 0.2s var(--ease);
}

.btn:hover  { transform: translateY(-1.5px); box-shadow: var(--shadow-md); opacity: 1; }
.btn:active { transform: translateY(0) scale(0.985); }

.btn-ghost {
  --btn-bg: var(--glass-bg);
  --btn-fg: var(--ink);
  border-color: var(--glass-brd);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
}

.btn-outline {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  border-color: var(--hairline);
  box-shadow: none;
}
.btn-outline:hover { border-color: var(--accent); color: var(--accent); }

.btn-dark { --btn-bg: var(--slate-800); --btn-fg: #fff; }

/* Blue theme. Pinned to the steel brand blue rather than --accent so the pair
   stays a matched set — filled and stroked — wherever it is used. The 700 step
   is the lightest one that still clears 4.5:1 against white at btn-sm size. */
.btn-blue {
  --btn-bg: var(--steel-700);
  --btn-fg: #fff;
}
.btn-blue:hover, .btn-blue.is-active { --btn-bg: var(--steel-800); }

.btn-blue-outline {
  --btn-bg: transparent;
  --btn-fg: var(--steel-700);
  border-color: var(--steel-500);
  box-shadow: none;
}
.btn-blue-outline:hover {
  --btn-bg: rgba(91, 138, 196, 0.10);
  border-color: var(--steel-700);
  box-shadow: none;
}

/* On dark the fill flips the way --accent does: light blue plate, dark ink. */
:root[data-theme="dark"] .btn-blue { --btn-bg: var(--steel-400); --btn-fg: #0e131c; }
:root[data-theme="dark"] .btn-blue:hover,
:root[data-theme="dark"] .btn-blue.is-active { --btn-bg: var(--steel-300); }

:root[data-theme="dark"] .btn-blue-outline {
  --btn-fg: var(--steel-300);
  border-color: var(--steel-600);
}
:root[data-theme="dark"] .btn-blue-outline:hover {
  --btn-bg: rgba(123, 164, 212, 0.14);
  border-color: var(--steel-400);
}

.btn-sm  { min-height: 36px; padding: 0.46rem 0.95rem; font-size: 0.84rem; }
.btn-lg  { min-height: 52px; padding: 0.9rem 1.9rem;  font-size: 1rem; }
.btn-full{ width: 100%; }

.btn[disabled], .btn.is-disabled {
  opacity: 0.45; pointer-events: none;
}

/* A <summary> wearing .btn: the control that opens a <details> reads as a
   button rather than as a link, which is what it behaves like. The browser's
   own disclosure triangle has to go in two places — list-style covers Firefox
   and the pseudo-element covers WebKit — or it sits inside the button. */
summary.btn { list-style: none; cursor: pointer; }
summary.btn::-webkit-details-marker { display: none; }
summary.btn::marker { content: ""; }

.link-arrow {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-weight: 540; font-size: 0.92rem;
}
.link-arrow svg, .link-arrow .chev { transition: transform 0.3s var(--ease); }
.link-arrow:hover .chev { transform: translateX(3px); }

/* --- pills and badges ----------------------------------------------------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.32rem 0.72rem;
  font-size: 0.76rem;
  font-weight: 560;
  letter-spacing: 0.01em;
  border-radius: var(--r-pill);
  background: var(--glass-bg);
  border: 1px solid var(--glass-brd);
  color: var(--ink-soft);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.pill-accent { color: var(--accent); border-color: rgba(91, 138, 196, 0.35);
               background: rgba(91, 138, 196, 0.12); }
.pill-mint   { color: var(--mint-500); border-color: rgba(63, 174, 144, 0.35);
               background: rgba(63, 174, 144, 0.12); }
.pill-amber  { color: var(--amber-500); border-color: rgba(213, 154, 60, 0.35);
               background: rgba(213, 154, 60, 0.12); }
.pill-rose   { color: var(--rose-500); border-color: rgba(200, 90, 90, 0.35);
               background: rgba(200, 90, 90, 0.12); }

.dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* --- header --------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid var(--hairline-soft);
}

.announce {
  background: linear-gradient(90deg, var(--slate-700), var(--steel-700));
  color: #fff;
  font-size: 0.78rem;
  letter-spacing: 0.01em;
  text-align: center;
  padding: 0.5rem 1rem;
}
.announce a { color: #fff; text-decoration: underline; text-underline-offset: 2px; }

/* The header runs wider than the 1200px prose column. The nav is chrome, not
   reading material, and at --maxw the row had only ~1120px of content box no
   matter how wide the viewport got — which is what put the Spanish labels and
   the actions cluster in the same pixels. */
.site-header .wrap { max-width: 1360px; }

.nav {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  height: 68px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  color: var(--ink);
  transition: opacity 0.25s var(--ease);
}
.brand:hover { opacity: 0.78; }

/* Official lockup.
   Deliberately NO `width` here. The macro emits width/height attributes on the
   <img>; those are presentational hints that any CSS `width` would beat, and
   `width: auto` on a replaced element resolves to the file's intrinsic 900px —
   which blows the logo up to fill whatever space the flex row allows. Leaving
   width unset lets the attribute size it, and `height: auto` keeps the 900:233
   ratio if a container ever has to shrink it. */
.brand-logo {
  display: block;
  max-width: 100%;
  height: auto;
  flex: none;
}

.brand-stacked {
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
}

/* Decorative inline mark — still used for hero and split-media figures. */
.brand-mark { width: 34px; height: 34px; flex: none; }

.brand-sub {
  font-size: 0.56rem; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 560;
}

/* Narrow phones: step down to ~28px tall so the nav actions keep their room. */
@media (max-width: 420px) {
  .site-header .brand-logo { width: 108px; }
}

/* Deliberately NO `min-width: 0`. As a flex item this cluster would then be free
   to shrink below its own contents, and because every child is `white-space:
   nowrap` and unshrinkable, the labels spill straight out of the shrunken box
   and print on top of the actions cluster to its right — the sign-in button and
   cart sitting under the last nav link. Leaving the automatic min-content floor
   in place means a row that is too tight pushes instead of overlapping, and the
   collapse breakpoint below is what actually keeps it from getting that tight. */
.nav-links {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin-left: 0.5rem;
}

.nav-link {
  position: relative;
  padding: 0.5rem 0.7rem;
  border-radius: var(--r-pill);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink-soft);
  /* The header is a fixed 68px row. Without this a two-word label like
     "COA Library" breaks onto a second line the moment the row gets tight,
     which shoves the whole header out of alignment. */
  white-space: nowrap;
  transition: color 0.25s var(--ease), background 0.25s var(--ease);
}
.nav-link:hover, .nav-link.is-active {
  color: var(--ink);
  background: var(--hairline-soft);
  opacity: 1;
}

/* The buttons living among the text links — they need their own breathing room
   so they do not read as part of the FAQ link next to them, but stay tight to
   each other so the pair reads as one cluster. */
.nav-cta { margin-left: 0.4rem; }
.nav-cta + .nav-cta { margin-left: 0; }

.nav-actions { margin-left: auto; display: flex; align-items: center; gap: 0.5rem; }

.icon-btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px;
  border-radius: var(--r-pill);
  border: 1px solid var(--hairline-soft);
  background: var(--glass-bg);
  color: var(--ink);
  cursor: pointer;
  transition: background 0.25s var(--ease), transform 0.25s var(--ease-spring);
}
.icon-btn:hover { background: var(--hairline-soft); transform: translateY(-1px); opacity: 1; }

.cart-count {
  position: absolute; top: -4px; right: -4px;
  min-width: 18px; height: 18px; padding: 0 4px;
  display: grid; place-items: center;
  font-size: 0.66rem; font-weight: 640;
  color: #fff; background: var(--accent);
  border-radius: var(--r-pill);
  border: 2px solid var(--bg);
}

.nav-toggle { display: none; }

/* --- language switch ------------------------------------------------------ */

.lang-switch {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  padding: 3px;
  border-radius: var(--r-pill);
  border: 1px solid var(--hairline-soft);
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.lang-opt {
  padding: 0.3rem 0.6rem;
  border-radius: var(--r-pill);
  font-size: 0.72rem;
  font-weight: 640;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  transition: color 0.25s var(--ease), background 0.25s var(--ease);
}
.lang-opt:hover { color: var(--ink); opacity: 1; }
.lang-opt.is-active {
  color: var(--accent-ink);
  background: var(--accent);
  opacity: 1;
}

.lang-switch-inline { display: none; }

/* --- theme toggle --------------------------------------------------------- */

/* One button, both glyphs: whichever theme is active hides its own icon and
   shows the other, so the button always pictures where pressing it lands. */
.theme-toggle .theme-icon-sun  { display: none; }
.theme-toggle .theme-icon-moon { display: inline-flex; }
:root[data-theme="dark"] .theme-toggle .theme-icon-sun  { display: inline-flex; }
:root[data-theme="dark"] .theme-toggle .theme-icon-moon { display: none; }

.theme-toggle svg { display: block; }

/* The drawer copy reads as a labelled row, not a bare icon, so it matches the
   nav links it sits under rather than the icon cluster it came from. */
.theme-toggle-inline {
  display: none;
  align-items: center;
  gap: 0.55rem;
  width: auto;
  padding: 0.7rem 0.9rem;
  border: 0;
  background: none;
  color: var(--ink-soft);
  font: inherit;
  font-size: 0.95rem;
  cursor: pointer;
  border-radius: var(--r-sm);
}
.theme-toggle-inline:hover { color: var(--ink); background: var(--hairline-soft); }

/* One step of tightening before the drawer, so locales with long labels get a
   little more runway on 13" laptops instead of collapsing to a burger early. */
@media (max-width: 1400px) {
  .nav { gap: 1rem; }
  .nav-link { padding: 0.5rem 0.55rem; }
}

/* Collapse point for the whole header. The brand lockup, six nav links and the
   actions cluster need ~1120px of content box in Spanish — the labels there run
   noticeably longer than their English sources — so below this hand the links to
   the drawer rather than squeezing them into the actions cluster. */
@media (max-width: 1200px) {
  .lang-switch-desktop { display: none; }
  .theme-toggle-desktop { display: none; }
  .theme-toggle-inline { display: flex; }
  .lang-switch-inline {
    display: inline-flex;
    align-self: flex-start;
    margin: 0.65rem 0.9rem 0;
  }
}

@media (max-width: 1200px) {
  .nav-links { display: none; }
  .nav-toggle { display: inline-flex; }
  .nav-links.is-open {
    display: flex;
    position: absolute;
    top: 100%; left: 0; right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    padding: 0.85rem var(--gut) 1.35rem;
    /* Opaque, not glass. The drawer lives inside .site-header, which carries its
       own backdrop-filter and so becomes the backdrop root — this element can
       only sample the header, never the page it overhangs, so the blur silently
       no-ops and a translucent panel would leave the hero legible underneath. */
    background: var(--drawer-bg);
    border-bottom: 1px solid var(--hairline);
  }
  .nav-links.is-open .nav-link { padding: 0.7rem 0.9rem; }
  /* The drawer stretches its children; let the CTAs hug their labels instead. */
  .nav-links.is-open .nav-cta {
    align-self: flex-start;
    margin: 0.6rem 0 0 0.9rem;
    padding: 0.55rem 1.15rem;
  }
  .nav-links.is-open .nav-cta + .nav-cta { margin-top: 0.4rem; }
}

/* --- hero ----------------------------------------------------------------- */

.hero { padding-block: clamp(3rem, 8vw, 6.5rem); position: relative; }

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}

@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; }
}

.hero h1 { margin-bottom: 1.1rem; }
.hero .lede { max-width: 34em; margin-bottom: 1.9rem; }

.hero-badges { display: flex; flex-wrap: wrap; gap: 0.55rem; margin-top: 2.1rem; }

.hero-figure {
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  border-radius: var(--r-xl);
  padding: clamp(1.5rem, 4vw, 2.75rem);
}

.hero-figure svg { width: 100%; height: auto; }

/* The hero mark is a raster, so it needs to be told to fill the card the way the
   old inline SVG did. Capped at the asset's native 512px so it never upscales
   into softness on a wide viewport. */
.hero-figure .brand-logo {
  width: 100%;
  max-width: 512px;
  height: auto;
}

/* --- stat band ------------------------------------------------------------ */

.stat-band {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  border-radius: var(--r-lg);
  overflow: hidden;
}

.stat {
  padding: clamp(1.3rem, 3vw, 2rem) 1.25rem;
  text-align: center;
  border-right: 1px solid var(--hairline-soft);
}
.stat:last-child { border-right: none; }

.stat-value {
  font-size: clamp(1.6rem, 3.2vw, 2.35rem);
  font-weight: 660;
  letter-spacing: -0.035em;
  line-height: 1;
  background: linear-gradient(135deg, var(--slate-700), var(--steel-500));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
:root[data-theme="dark"] .stat-value {
  background: linear-gradient(135deg, #dfe8f4, var(--steel-400));
  -webkit-background-clip: text; background-clip: text;
}

.stat-label {
  margin-top: 0.45rem;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 540;
}

@media (max-width: 620px) {
  .stat { border-right: none; border-bottom: 1px solid var(--hairline-soft); }
  .stat:last-child { border-bottom: none; }
}

/* --- feature cards -------------------------------------------------------- */

.feature { padding: clamp(1.4rem, 3vw, 1.9rem); height: 100%; }

.feature-icon {
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: 14px;
  font-size: 1.2rem;
  margin-bottom: 1.05rem;
  color: var(--accent);
  background: linear-gradient(150deg, rgba(91,138,196,0.22), rgba(91,138,196,0.06));
  border: 1px solid rgba(91, 138, 196, 0.22);
}

.feature h3 { font-size: 1.06rem; margin-bottom: 0.4rem; }
.feature p  { font-size: 0.93rem; color: var(--ink-soft); margin: 0; }

/* --- article infographics -------------------------------------------------
   Two sizes of one figure: `.ig-strip` teases it on a research card, `.ig`
   draws it in full on the opened article. Both are pure layout over data —
   nothing here depends on an uploaded image, so the figure follows the theme.
   -------------------------------------------------------------------------- */

/* A card carrying a strip pins its date row to the bottom, so a grid of cards
   with figures of different heights still lines its meta row up. Becoming a flex
   column would otherwise stretch the inline-flex pills across the whole card. */
.feature.has-ig { display: flex; flex-direction: column; }
.feature.has-ig > .row:last-child { margin-top: auto; }
.feature.has-ig > .pill,
.feature.has-ig > .ig-badge { align-self: flex-start; }

.ig-strip {
  margin-top: 1.05rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--hairline-soft);
  display: grid;
  gap: 0.4rem;
}

.ig-strip-bar {
  display: grid;
  grid-template-columns: minmax(0, 5.2rem) 1fr auto;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.68rem;
  line-height: 1.3;
}

.ig-strip-bar-label {
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ig-strip-bar-value {
  font-family: var(--mono);
  font-size: 0.66rem;
  color: var(--ink-soft);
}

.ig-track {
  display: block;
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--hairline);
  overflow: hidden;
}

.ig-track-lg { height: 10px; }

.ig-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--steel-600), var(--steel-400));
  transition: width 0.7s var(--ease);
}

.ig-fill.ig-mint  { background: linear-gradient(90deg, #2f9077, var(--mint-500)); }
.ig-fill.ig-amber { background: linear-gradient(90deg, #b9822c, var(--amber-500)); }
.ig-fill.ig-rose  { background: linear-gradient(90deg, #a94747, var(--rose-500)); }

/* Inner element classes stay clear of `.ig-strip-<kind>`, which the wrapper
   already carries as a hook — a shared name there would restyle the wrapper. */
/* Uploaded figure, cropped to a consistent band so a grid of cards stays even
   whatever aspect ratio the author exported. */
.ig-shot {
  display: block;
  height: 96px;
  border-radius: var(--r-sm);
  overflow: hidden;
  border: 1px solid var(--hairline);
  background: var(--hairline-soft);
}

.ig-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

.ig-image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  background: var(--bg-elev);
}

.ig-dots { display: flex; align-items: center; gap: 0.3rem; }

.ig-strip-dot {
  width: 19px; height: 19px;
  flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-pill);
  font-size: 0.6rem;
  font-family: var(--mono);
  color: var(--accent);
  background: rgba(91, 138, 196, 0.16);
  border: 1px solid rgba(91, 138, 196, 0.28);
}

.ig-strip-link { flex: 1; height: 1px; background: var(--hairline); }

.ig-versus {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.7rem;
}

.ig-strip-col {
  flex: 1;
  padding: 0.32rem 0.5rem;
  border-radius: var(--r-xs);
  background: var(--hairline-soft);
  border: 1px solid var(--hairline);
  color: var(--ink-soft);
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ig-strip-vs { font-size: 0.6rem; color: var(--ink-faint); text-transform: uppercase; }

.ig-strip-note {
  font-size: 0.68rem;
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ig-tiles { display: flex; gap: 0.9rem; }

.ig-strip-stat { display: grid; gap: 0.05rem; min-width: 0; }
.ig-strip-stat b {
  font-size: 1rem;
  letter-spacing: -0.02em;
  color: var(--accent);
}
.ig-strip-stat span {
  font-size: 0.62rem;
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ig-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-top: 0.75rem;
  font-size: 0.64rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

/* full figure */

.ig {
  margin: 1.6rem 0 0;
  padding: clamp(1.15rem, 3vw, 1.65rem);
  border-radius: var(--r-lg);
  border: 1px solid var(--hairline);
  background: linear-gradient(160deg, rgba(91, 138, 196, 0.10), rgba(91, 138, 196, 0.02));
}

.ig-head {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  margin-bottom: 1.25rem;
}

.ig-mark {
  width: 38px; height: 38px;
  flex: none;
  display: grid; place-items: center;
  border-radius: 12px;
  font-size: 1rem;
  color: var(--accent);
  background: linear-gradient(150deg, rgba(91, 138, 196, 0.22), rgba(91, 138, 196, 0.06));
  border: 1px solid rgba(91, 138, 196, 0.22);
}

.ig-title { font-size: 1.05rem; margin: 0.15rem 0 0; }

.ig-bars { display: grid; gap: 0.95rem; }

.ig-bar-head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin-bottom: 0.4rem;
}

.ig-bar-label { font-size: 0.88rem; color: var(--ink-soft); }
.ig-bar-value {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--ink);
}

.ig-steps { list-style: none; display: grid; gap: 0.15rem; margin: 0; padding: 0; }

.ig-steps li {
  position: relative;
  display: flex;
  gap: 0.85rem;
  padding-bottom: 1rem;
}

/* The connector is drawn behind the numbers rather than between them, so a step
   whose detail wraps to three lines keeps an unbroken spine. */
.ig-steps li:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 13px;
  top: 28px;
  bottom: 0;
  width: 1px;
  background: var(--hairline);
}

.ig-steps li:last-child { padding-bottom: 0; }

.ig-step-n {
  position: relative;
  width: 27px; height: 27px;
  flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-pill);
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--accent-ink);
  background: var(--accent);
}

.ig-steps strong { font-size: 0.95rem; display: block; }
.ig-steps p { margin: 0.2rem 0 0; font-size: 0.88rem; color: var(--ink-soft); }

.ig-compare { overflow-x: auto; }

.ig-compare-row {
  display: grid;
  grid-template-columns: 1fr 1.35fr 1.35fr;
  gap: 0.9rem;
  min-width: 430px;
  padding: 0.65rem 0;
  border-bottom: 1px solid var(--hairline-soft);
  font-size: 0.88rem;
  color: var(--ink-soft);
}

.ig-compare-row:last-child { border-bottom: 0; }

.ig-compare-head { padding-top: 0; border-bottom: 1px solid var(--hairline); }

.ig-compare-col {
  font-size: 0.66rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
}

.ig-compare-aspect { color: var(--ink); font-weight: 550; }

.ig-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1rem;
}

.ig-stat {
  padding: 0.9rem 1rem;
  border-radius: var(--r-md);
  background: var(--glass-bg-strong);
  border: 1px solid var(--hairline);
}

.ig-stat strong {
  display: block;
  font-size: 1.6rem;
  letter-spacing: -0.03em;
  color: var(--accent);
}

.ig-stat-label { display: block; font-size: 0.85rem; margin-top: 0.15rem; }
.ig-stat-note { display: block; font-size: 0.75rem; color: var(--ink-faint); margin-top: 0.3rem; }

.ig-foot {
  margin-top: 1.15rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--hairline-soft);
}

.ig-foot p { margin: 0 0 0.3rem; font-size: 0.85rem; color: var(--ink-soft); }

/* --- product cards -------------------------------------------------------- */

.product-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  border-radius: var(--r-lg);
}

.product-media {
  position: relative;
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  /* Two things pin the frame to its ratio. `min-height` because the frame is a
     flex item in the column above, so `auto` would let its content set a
     taller floor than 4:3; `overflow` because a portrait vial render used to
     spill straight over the flags and the category line beneath it. */
  min-height: 0;
  overflow: hidden;
  background:
    radial-gradient(70% 70% at 50% 30%, rgba(91,138,196,0.16), transparent 70%),
    var(--hairline-soft);
  border-bottom: 1px solid var(--hairline-soft);
}
.product-media svg { width: 46%; opacity: 0.92; }

/* Contained rather than cropped, matching .pdp-media. Filling the frame was the
   right call for a rectangular photo, but the catalog's artwork is portrait
   vial renders on a transparent ground: cover scales them until the cap and the
   base are both outside the frame, which is the one part of a vial anybody is
   looking at. Contained, the render floats on the frame's own glow — the same
   treatment the product page gives it — and the gradient around it reads as the
   backdrop it is rather than as letterboxing. */
/* Out of flow rather than laid out in the grid, which is what makes the two
   percentages above mean what they say. Laid out, the picture is what sizes the
   row, so its own `height: 100%` resolves against a track it is defining — a
   cycle, so the height falls back to the render's intrinsic one, the frame is
   handed a box the shape of the artwork, and `contain` has nothing left to fit
   into. Positioned, both percentages resolve against the frame's padding box,
   which `aspect-ratio` has already made definite. */
.product-media .product-photo {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  padding: clamp(0.6rem, 2.5%, 1.1rem);
}

/* In the body, above the category line — see partials/product_card.html. The
   card's own gap spaces it, so no margin here. */
.product-flags {
  display: flex; flex-wrap: wrap; gap: 0.35rem;
}

.product-body {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 1.15rem 1.25rem 1.35rem;
  flex: 1;
}

.product-cat {
  font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 580;
}

.product-name { font-size: 1.04rem; font-weight: 600; margin: 0; color: var(--ink); }
.product-name a { color: inherit; }

.product-summary {
  font-size: 0.86rem; color: var(--ink-soft); margin: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

.product-meta {
  display: flex; align-items: baseline; gap: 0.5rem;
  margin-top: auto; padding-top: 0.65rem;
}

/* One card per row on a phone — `.grid-4` is auto-fit at a 215px minimum, so it
   drops to a single column just under 490px of viewport. A 4:3 frame that wide
   is a letterbox for a portrait vial: contained, the render ends up small and
   stranded in the middle of a lot of gradient. Square gives the artwork the
   height it wants, and the card reads as a product shot with a caption. The
   cutoff sits above the single-column point so the last two-up row squares off
   too rather than changing shape one card before the columns collapse. */
@media (max-width: 520px) {
  .product-media { aspect-ratio: 1 / 1; }
  .product-media svg { width: 55%; }
  .product-media .product-photo { padding: clamp(0.9rem, 5%, 1.6rem); }
}

.price { font-size: 1.16rem; font-weight: 650; letter-spacing: -0.02em; }
.price-was { font-size: 0.85rem; color: var(--ink-faint); text-decoration: line-through; }
.price-from { font-size: 0.72rem; color: var(--ink-faint); }

/* --- specs table ---------------------------------------------------------- */

.spec-list { display: grid; gap: 0; }

.spec-row {
  display: grid;
  grid-template-columns: minmax(120px, 0.4fr) 1fr;
  gap: 1rem;
  padding: 0.8rem 0;
  border-bottom: 1px solid var(--hairline-soft);
  font-size: 0.9rem;
}
.spec-row:last-child { border-bottom: none; }
.spec-key { color: var(--ink-faint); font-weight: 520; }
.spec-val { color: var(--ink); word-break: break-word; }

.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.89rem;
  min-width: 620px;
}
table.data th {
  text-align: left;
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 580;
  padding: 0.8rem 0.9rem;
  border-bottom: 1px solid var(--hairline);
  white-space: nowrap;
}
table.data td {
  padding: 0.85rem 0.9rem;
  border-bottom: 1px solid var(--hairline-soft);
  vertical-align: middle;
}
table.data tbody tr { transition: background 0.2s var(--ease); }
table.data tbody tr:hover { background: var(--hairline-soft); }
table.data tr:last-child td { border-bottom: none; }

/* --- forms ---------------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1rem; }
/* An author `display` beats the UA rule for [hidden], so a field toggled off
   from script stays visible without this. */
.field[hidden] { display: none; }

.field label,
.label {
  font-size: 0.8rem;
  font-weight: 560;
  color: var(--ink-soft);
  letter-spacing: 0.005em;
}

/* Every text-ish control, in one rule.
 *
 * Written as an exclusion list rather than as `input[type="text"],
 * input[type="email"], …` because that older form had a hole you could drive a
 * form through: a CSS attribute selector matches the *attribute*, not the
 * implied default, so `<input name="first_name">` — no type written, which is
 * how nearly every text field on this platform is written — matched nothing and
 * fell through to the browser's own square, undersized box. Sitting next to an
 * `type="email"` field on the same row, the two looked like they came from
 * different sites.
 *
 * Excluding the handful of inputs that must keep their native shape is both
 * shorter and safe in the other direction: a field added later, or a type
 * nobody has used here yet, is styled without anyone remembering to come back
 * and extend a list.
 *
 * The exclusions sit inside :where() so the whole thing weighs exactly as much
 * as a bare `input` — one element, no more. Written as a plain chain of :not()s
 * it would score nine attribute selectors and quietly outrank every rule below
 * that refines a field in context (`.qty input`, `.copy-field input`, the
 * visually-hidden radios), which is a far worse bug than the one being fixed:
 * it would look right on this form and wrong in ten places nobody thought to
 * check.
 */
.input,
input:not(:where([type="checkbox"], [type="radio"], [type="file"], [type="submit"],
                 [type="button"], [type="reset"], [type="image"], [type="range"],
                 [type="color"], [type="hidden"])),
select, textarea {
  width: 100%;
  min-height: var(--control-h);
  padding: 0.72rem 0.95rem;
  font: inherit;
  font-size: 0.93rem;
  line-height: 1.4;
  color: var(--ink);
  background: var(--glass-bg-strong);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
  -webkit-appearance: none;
  appearance: none;
}

select {
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-faint) 50%),
    linear-gradient(135deg, var(--ink-faint) 50%, transparent 50%);
  background-position: calc(100% - 18px) 52%, calc(100% - 13px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 2.4rem;
}

textarea { min-height: 130px; resize: vertical; line-height: 1.55; }

/* A file input is excluded from the block above because `appearance: none` on
   one hides the button that makes it usable at all. Inside a labelled field it
   still has to look like it belongs to the form around it, so the box takes the
   field's shape and the native button inside takes the small-button treatment.
   Scoped to .field on purpose: the same input appears bare in a table row on
   the COA library, where a full-width bordered box would swell every row. */
.field input[type="file"] {
  width: 100%;
  min-height: var(--control-h);
  padding: 0.55rem 0.55rem;
  font-size: 0.86rem;
  color: var(--ink-soft);
  background: var(--glass-bg-strong);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
}

.field input[type="file"]::file-selector-button {
  margin-right: 0.7rem;
  padding: 0.36rem 0.9rem;
  font: inherit;
  font-size: 0.83rem;
  font-weight: 560;
  color: var(--ink);
  background: var(--glass-bg);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}

.field input[type="file"]::file-selector-button:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* A date or time field carries a native picker button that ignores the
   appearance reset and pushes the text off centre; giving it the field's own
   ink is the whole fix. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator { opacity: 0.5; cursor: pointer; }

/* Chrome paints its own pale plate over anything it autofilled, and no
   background declaration reaches it — only an inset shadow large enough to
   cover the box does. Without this, the two fields the browser happened to
   remember look nothing like the ones beside them, which is exactly the
   inconsistency this section exists to prevent. The absurd transition delay is
   the standard trick for the same reason: it parks the plate's own animation
   past the heat death of the tab. */
.input:-webkit-autofill,
input:-webkit-autofill,
select:-webkit-autofill,
textarea:-webkit-autofill {
  -webkit-text-fill-color: var(--ink);
  caret-color: var(--ink);
  -webkit-box-shadow: 0 0 0 100px var(--drawer-bg) inset;
  box-shadow: 0 0 0 100px var(--drawer-bg) inset;
  border-radius: var(--r-sm);
  transition: background-color 600000s 0s, color 600000s 0s;
}

.input:focus, input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3.5px rgba(91, 138, 196, 0.16);
}

/* Focus on an autofilled field needs both shadows named together, or the ring
   replaces the plate cover and the blue comes back the moment it is clicked. */
.input:-webkit-autofill:focus,
input:-webkit-autofill:focus,
select:-webkit-autofill:focus,
textarea:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 100px var(--drawer-bg) inset,
                      0 0 0 3.5px rgba(91, 138, 196, 0.16);
  box-shadow: 0 0 0 100px var(--drawer-bg) inset,
              0 0 0 3.5px rgba(91, 138, 196, 0.16);
}

::placeholder { color: var(--ink-faint); opacity: 0.75; }

.check {
  display: flex; align-items: flex-start; gap: 0.6rem;
  font-size: 0.87rem; color: var(--ink-soft); line-height: 1.5;
  cursor: pointer;
}
.check input { width: 17px; height: 17px; margin-top: 2px; flex: none; accent-color: var(--accent); }

.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1rem; }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }

.form-note { font-size: 0.8rem; color: var(--ink-faint); }

/* --- editable size rows (staff console) ----------------------------------
   One form per size, stacked rather than tabulated: six controls do not fit
   across the console's right column, and a table that scrolls sideways hides
   the delete button off the edge — which is exactly how it hid before. */

.variant-edit + .variant-edit {
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--hairline-soft);
}

/* --- file upload fields (staff console) ----------------------------------
   Shared by the research infographic and the catalog's product image; the
   ig- prefix is where it started, not who it belongs to. */

.ig-drop {
  display: block;
  position: relative;
  padding: 1.4rem 1.1rem;
  border: 1px dashed var(--hairline);
  border-radius: var(--r-md);
  background: var(--hairline-soft);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.ig-drop:hover, .ig-drop.is-over {
  border-color: var(--accent);
  background: rgba(91, 138, 196, 0.08);
}

/* The input stays in the layout and focusable — display:none would take it out
   of the tab order and off the accessibility tree. */
.ig-drop input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  cursor: pointer;
}

.ig-drop-inner {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 0.3rem;
  pointer-events: none;
}

.ig-drop-mark { font-size: 1.35rem; color: var(--accent); }
.ig-drop-inner strong { font-size: 0.92rem; }

.ig-preview {
  margin-top: 0.75rem;
  border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  background: var(--bg-elev);
  overflow: hidden;
}

.ig-preview[hidden] { display: none; }
.ig-preview img { display: block; width: 100%; height: auto; }

/* --- flash ---------------------------------------------------------------- */

.flash-stack {
  position: fixed;
  top: 84px; right: var(--gut);
  z-index: 200;
  display: flex; flex-direction: column; gap: 0.6rem;
  width: min(380px, calc(100vw - 2 * var(--gut)));
}

.flash {
  display: flex; align-items: flex-start; gap: 0.7rem;
  padding: 0.9rem 1.05rem;
  border-radius: var(--r-md);
  font-size: 0.89rem;
  background: var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-brd);
  box-shadow: var(--shadow-lg);
  animation: flash-in 0.5s var(--ease-spring) both;
}

@keyframes flash-in {
  from { opacity: 0; transform: translateX(18px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

.flash-bar { width: 3px; align-self: stretch; border-radius: 2px; background: var(--accent); flex: none; }
.flash-success .flash-bar { background: var(--mint-500); }
.flash-error   .flash-bar { background: var(--rose-500); }
.flash-info    .flash-bar { background: var(--steel-500); }

.flash-close {
  margin-left: auto; background: none; border: none; cursor: pointer;
  color: var(--ink-faint); font-size: 1.05rem; line-height: 1; padding: 0 0 0 0.4rem;
}

/* --- accordion ------------------------------------------------------------ */

.accordion { border-radius: var(--r-lg); overflow: hidden; }

.acc-item { border-bottom: 1px solid var(--hairline-soft); }
.acc-item:last-child { border-bottom: none; }

.acc-item summary {
  display: flex; align-items: center; gap: 1rem;
  padding: 1.15rem clamp(1.1rem, 3vw, 1.6rem);
  cursor: pointer;
  font-weight: 560;
  font-size: 0.98rem;
  list-style: none;
  transition: background 0.25s var(--ease);
}
.acc-item summary::-webkit-details-marker { display: none; }
.acc-item summary:hover { background: var(--hairline-soft); }

.acc-item summary::after {
  content: "+";
  margin-left: auto;
  font-size: 1.25rem;
  font-weight: 300;
  color: var(--ink-faint);
  transition: transform 0.35s var(--ease);
}
.acc-item[open] summary::after { transform: rotate(45deg); }

.acc-body {
  padding: 0 clamp(1.1rem, 3vw, 1.6rem) 1.35rem;
  color: var(--ink-soft);
  font-size: 0.93rem;
  max-width: 76ch;
}

/* --- steps ---------------------------------------------------------------- */

.steps { counter-reset: step; display: grid; gap: 1rem; }

.step { display: flex; gap: 1.1rem; padding: 1.25rem 1.4rem; }

.step-num {
  counter-increment: step;
  flex: none;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border-radius: 50%;
  font-size: 0.85rem; font-weight: 640;
  color: var(--accent-ink);
  background: linear-gradient(140deg, var(--steel-500), var(--slate-600));
}
.step-num::before { content: counter(step); }
.step h3 { font-size: 1rem; margin-bottom: 0.25rem; }
.step p  { font-size: 0.9rem; color: var(--ink-soft); margin: 0; }

/* --- COA ------------------------------------------------------------------ */

.coa-card { padding: 1.35rem 1.5rem; height: 100%; display: flex; flex-direction: column; gap: 0.85rem; }

.coa-head { display: flex; align-items: flex-start; gap: 0.85rem; }
.coa-purity {
  margin-left: auto;
  font-size: 1.32rem; font-weight: 660; letter-spacing: -0.03em;
  color: var(--mint-500);
}
.coa-batch { font-family: var(--mono); font-size: 0.8rem; color: var(--ink-faint); }

.coa-checks { display: grid; gap: 0.4rem; font-size: 0.84rem; }
.coa-check { display: flex; align-items: center; gap: 0.5rem; color: var(--ink-soft); }
.coa-tick {
  width: 16px; height: 16px; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(63, 174, 144, 0.16);
  color: var(--mint-500);
  font-size: 0.62rem; font-weight: 700;
}

/* purity meter */
.meter {
  height: 5px; border-radius: 3px; overflow: hidden;
  background: var(--hairline-soft);
}
.meter-fill {
  height: 100%; border-radius: 3px;
  background: linear-gradient(90deg, var(--steel-500), var(--mint-500));
}

/* --- disclaimer ----------------------------------------------------------- */

.disclaimer {
  border-left: 3px solid var(--amber-500);
  background: rgba(213, 154, 60, 0.08);
  border-radius: var(--r-md);
  padding: 1.1rem 1.35rem;
  font-size: 0.87rem;
  color: var(--ink-soft);
  line-height: 1.55;
}
.disclaimer strong { color: var(--ink); }

/* --- CTA ------------------------------------------------------------------ */

.cta-banner {
  position: relative;
  padding: clamp(2.25rem, 6vw, 4rem);
  border-radius: var(--r-xl);
  text-align: center;
  color: #fff;
  background: linear-gradient(135deg, var(--slate-800) 0%, var(--slate-600) 48%, var(--steel-700) 100%);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}
.cta-banner::before {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(60% 90% at 82% 0%, rgba(255,255,255,0.22), transparent 62%);
  pointer-events: none;
}
.cta-banner > * { position: relative; }
.cta-banner h2 { color: #fff; }
.cta-banner p  { color: rgba(255,255,255,0.82); max-width: 56ch; margin-inline: auto; }
.cta-banner .btn-ghost {
  --btn-bg: rgba(255,255,255,0.16);
  --btn-fg: #fff;
  border-color: rgba(255,255,255,0.3);
}
.cta-banner .btn:not(.btn-ghost) { --btn-bg: #fff; --btn-fg: var(--slate-800); }

/* --- footer --------------------------------------------------------------- */

.site-footer {
  margin-top: clamp(3rem, 7vw, 5rem);
  border-top: 1px solid var(--hairline-soft);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  padding-block: clamp(2.5rem, 5vw, 3.75rem) 1.75rem;
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.6fr repeat(3, 1fr);
  gap: clamp(1.75rem, 4vw, 3rem);
}
@media (max-width: 860px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .footer-grid { grid-template-columns: 1fr; } }

.footer-col h4 {
  font-size: 0.74rem; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: 0.95rem; font-weight: 600;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; }
.footer-col a { color: var(--ink-soft); font-size: 0.89rem; }
.footer-col a:hover { color: var(--accent); opacity: 1; }

.footer-bottom {
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: 1.4rem;
  border-top: 1px solid var(--hairline-soft);
  display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem;
  align-items: center;
  font-size: 0.79rem;
  color: var(--ink-faint);
}

/* Contact details are links, but shouldn't shout — they inherit the muted
   footer colour and only pick up the accent on hover. */
.footer-bottom a { color: inherit; }
.footer-bottom a:hover { color: var(--accent); opacity: 1; }

/* --- breadcrumb ----------------------------------------------------------- */

.crumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.45rem;
  font-size: 0.82rem; color: var(--ink-faint);
  padding-block: 1.25rem;
}
.crumbs a { color: var(--ink-faint); }
.crumbs a:hover { color: var(--accent); }
.crumbs .sep { opacity: 0.5; }

/* Sits ahead of the trail as a pill so it reads as a control, not a crumb. */
.crumbs a.crumb-back {
  display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.3rem 0.7rem 0.3rem 0.55rem;
  margin-right: 0.35rem;
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  color: var(--ink-soft);
  font-weight: 520;
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.crumbs a.crumb-back:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--hairline-soft);
}
.crumb-back svg { flex: none; }

/* --- filter bar ----------------------------------------------------------- */

.filter-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem;
  padding: 0.85rem 1rem;
  border-radius: var(--r-md);
  margin-bottom: 1.75rem;
}
.filter-bar .input { width: auto; min-width: 200px; flex: 1; }
.filter-bar select { width: auto; min-width: 150px; }

.chip-row { display: flex; flex-wrap: wrap; gap: 0.45rem; }
.chip {
  padding: 0.44rem 0.9rem;
  border-radius: var(--r-pill);
  font-size: 0.83rem;
  font-weight: 520;
  color: var(--ink-soft);
  background: var(--glass-bg);
  border: 1px solid var(--hairline-soft);
  transition: all 0.25s var(--ease);
}
.chip:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }
.chip.is-active {
  background: var(--accent); color: var(--accent-ink); border-color: transparent;
}
/* The count riding along inside a chip. Opacity rather than a colour so it
   stays a half-step back on both the plain and the selected chip. */
.chip-n { margin-left: 0.15rem; opacity: 0.6; font-variant-numeric: tabular-nums; }

/* --- product detail ------------------------------------------------------- */

.pdp-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
  gap: clamp(1.75rem, 4vw, 3.25rem);
  align-items: start;
}
@media (max-width: 900px) { .pdp-grid { grid-template-columns: 1fr; } }

.pdp-media {
  aspect-ratio: 1 / 1;
  display: grid; place-items: center;
  border-radius: var(--r-xl);
  background:
    radial-gradient(65% 65% at 50% 32%, rgba(91,138,196,0.18), transparent 72%),
    var(--glass-bg);
  padding: clamp(2rem, 5vw, 3.5rem);
}
.pdp-media svg { width: 70%; }

/* Contained, not cropped: the product page is where somebody is looking at the
   vial itself, so nothing of it gets cut off. */
.pdp-media .product-photo {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  border-radius: var(--r-lg);
}

.variant-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(112px, 1fr)); gap: 0.6rem; }

.variant {
  position: relative;
  display: block;
  padding: 0.85rem 0.9rem;
  border-radius: var(--r-md);
  border: 1.5px solid var(--hairline);
  background: var(--glass-bg);
  cursor: pointer;
  text-align: center;
  transition: all 0.28s var(--ease);
}
.variant input { position: absolute; opacity: 0; pointer-events: none; }
.variant:hover { border-color: var(--steel-400); }
.variant:has(input:checked) {
  border-color: var(--accent);
  background: rgba(91, 138, 196, 0.1);
  box-shadow: 0 0 0 3px rgba(91, 138, 196, 0.13);
}
.variant-size  { font-weight: 620; font-size: 0.95rem; }
.variant-price { font-size: 0.83rem; color: var(--ink-soft); margin-top: 0.15rem; }
.variant-stock { font-size: 0.7rem; color: var(--ink-faint); margin-top: 0.15rem; }
.variant.is-out { opacity: 0.45; pointer-events: none; }

.qty {
  display: inline-flex; align-items: center;
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  background: var(--glass-bg);
  overflow: hidden;
}
.qty button {
  width: 40px; height: 44px;
  background: none; border: none; cursor: pointer;
  color: var(--ink-soft); font-size: 1.1rem;
  transition: background 0.2s var(--ease);
}
.qty button:hover { background: var(--hairline-soft); }
.qty input {
  /* min-height beats height, so the shared control height has to be released
     here or this stepper grows two pixels taller than the pill it sits in. */
  min-height: 0;
  width: 46px; height: 44px; padding: 0; text-align: center;
  border: none; background: none; box-shadow: none;
  font-weight: 580;
}
.qty input:focus { box-shadow: none; }

.tabs { display: flex; gap: 0.3rem; border-bottom: 1px solid var(--hairline); margin-bottom: 1.6rem;
        overflow-x: auto; }
.tab {
  padding: 0.75rem 1.05rem;
  font-size: 0.9rem; font-weight: 540;
  color: var(--ink-faint);
  border: none; background: none; cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.tab:hover { color: var(--ink); }
.tab.is-active { color: var(--accent); border-bottom-color: var(--accent); }
.tab-panel { display: none; }
.tab-panel.is-active { display: block; animation: fade-up 0.4s var(--ease) both; }

@keyframes fade-up {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* --- cart / checkout ------------------------------------------------------ */

.checkout-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(300px, 0.65fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
  align-items: start;
}
@media (max-width: 880px) { .checkout-grid { grid-template-columns: 1fr; } }

.summary-sticky { position: sticky; top: 92px; }

.line-item {
  display: grid;
  grid-template-columns: 66px 1fr auto;
  gap: 1rem;
  align-items: center;
  padding: 1rem 0;
  border-bottom: 1px solid var(--hairline-soft);
}
.line-item:last-of-type { border-bottom: none; }
.line-thumb {
  width: 66px; height: 66px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--hairline-soft);
}
.line-thumb svg { width: 60%; }

.total-row {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: 0.5rem 0; font-size: 0.91rem; color: var(--ink-soft);
}
.total-row.is-total {
  margin-top: 0.5rem; padding-top: 0.9rem;
  border-top: 1px solid var(--hairline);
  font-size: 1.1rem; font-weight: 650; color: var(--ink);
}

.empty-state { text-align: center; padding: clamp(2.5rem, 6vw, 4.5rem) 1.5rem; }
.empty-state .glyph { font-size: 2.5rem; opacity: 0.35; margin-bottom: 0.85rem; }

/* --- admin ---------------------------------------------------------------- */

.admin-shell { display: grid; grid-template-columns: 232px minmax(0, 1fr); min-height: 100vh; }

.admin-side {
  border-right: 1px solid var(--hairline-soft);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  padding: 1.5rem 1rem;
}

.admin-nav { display: grid; gap: 0.15rem; margin-top: 1.5rem; }
.admin-nav a {
  display: flex; align-items: center; gap: 0.65rem;
  padding: 0.6rem 0.8rem;
  border-radius: var(--r-sm);
  font-size: 0.89rem; font-weight: 500;
  color: var(--ink-soft);
  transition: all 0.22s var(--ease);
}
.admin-nav a:hover { background: var(--hairline-soft); color: var(--ink); opacity: 1; }
.admin-nav a.is-active { background: var(--accent); color: var(--accent-ink); }

.admin-main { padding: clamp(1.4rem, 3vw, 2.4rem); min-width: 0; }

/* No storefront header up there to clear, so console toasts sit near the top —
   except on a phone, where they would land on the burger for their whole
   lifetime and lock you out of the menu. */
.console-flash { top: 1.25rem; }

/* --- console on a phone --------------------------------------------------- */
/* The console bar, the scrim and the drawer close button are phone-only chrome:
   on a wide screen the sidebar is simply always there. */
.console-bar,
.console-scrim,
.console-nav-close { display: none; }

/* Same breakpoint the sidebar always collapsed at, but instead of stacking five
   groups and up to twenty room links above the page, the sidebar goes
   off-canvas and a sticky bar carries the burger.
   Everything that hides the sidebar is gated on html.js — opening the drawer
   takes a script, so with scripts off the nav stays in the flow as it was. */
@media (max-width: 860px) {
  .admin-shell { grid-template-columns: 1fr; }
  .admin-side { border-right: none; border-bottom: 1px solid var(--hairline); }

  .js .admin-shell { min-height: 0; }

  .js .console-bar {
    position: sticky; top: 0; z-index: 90;
    display: flex; align-items: center; gap: 0.8rem;
    min-height: 56px;
    padding: 0.5rem var(--gut);
    background: var(--glass-bg);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-bottom: 1px solid var(--hairline);
  }
  .console-bar-toggle { flex: none; }
  /* The page's own <title> block, so the bar always says where you are — the
     h1 scrolls away, this does not. */
  .console-bar-title {
    flex: 1 1 auto; min-width: 0;
    font-size: 0.97rem; font-weight: 620; letter-spacing: -0.015em;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .console-bar .brand { flex: none; }

  .js .admin-side {
    position: fixed; top: 0; left: 0; bottom: 0;
    z-index: 130;
    width: min(290px, 84vw);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 1.25rem 1rem 2rem;
    border-bottom: none;
    border-right: 1px solid var(--hairline);
    /* Opaque, not glass: the drawer floats over page content, and a translucent
       panel would leave the table underneath legible through the links. With an
       opaque fill the inherited blur only costs compositing. */
    background: var(--drawer-bg);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow: var(--shadow-lg);
    transform: translateX(-100%);
    /* visibility keeps the closed drawer out of the tab order — an off-screen
       transform alone still hands twenty links to the keyboard. */
    visibility: hidden;
    transition: transform 0.28s var(--ease), visibility 0.28s;
  }
  .js body.console-nav-open .admin-side { transform: none; visibility: visible; }

  .js .console-scrim {
    display: block;
    position: fixed; inset: 0; z-index: 120;
    background: rgba(6, 12, 22, 0.5);
    opacity: 0; visibility: hidden; pointer-events: none;
    transition: opacity 0.28s var(--ease), visibility 0.28s;
  }
  .js body.console-nav-open .console-scrim {
    opacity: 1; visibility: visible; pointer-events: auto;
  }

  .js .console-nav-close {
    display: grid; place-items: center;
    position: absolute; top: 1rem; right: 1rem;
    width: 34px; height: 34px;
    border: 1px solid var(--hairline-soft);
    border-radius: var(--r-pill);
    background: none;
    color: var(--ink-soft);
    font-size: 1.35rem; line-height: 1;
    cursor: pointer;
  }

  .js .console-flash { top: 68px; }

  /* Touch targets: the desktop rows are sized for a cursor. */
  .admin-nav a { padding: 0.72rem 0.85rem; font-size: 0.95rem; }
  .admin-main { padding: 1.15rem var(--gut) 2.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .admin-side, .console-scrim { transition: none; }
}

/* Stat tiles two-up on a phone. `.grid-4` is auto-fit at a 215px minimum, which
   below ~485px of viewport has room for exactly one column — so a console page
   opening on eight tiles spent eight full screens saying very little. Two-up is
   the same information in a quarter of the scroll.
   Scoped to .admin-main: the storefront's own .grid-4 rows hold cards with
   images and copy, which do want the full width down here.
   Every selector carries .admin-main, so these beat the unscoped .kpi rules
   further down the file on specificity rather than on source order. */
@media (max-width: 520px) {
  .admin-main .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .admin-main .grid-4 .kpi { padding: 0.9rem 0.95rem; }
  /* A tile is now ~105px of inner width on the narrowest phone, so the number
     scales with the viewport rather than spilling past the card edge once
     revenue reaches five figures. */
  .admin-main .grid-4 .kpi-value { font-size: clamp(1.1rem, 5.6vw, 1.75rem); }
  .admin-main .grid-4 .kpi-label { font-size: 0.66rem; letter-spacing: 0.07em; }
}

.admin-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem;
  margin-bottom: 1.75rem;
}
.admin-head h1 { font-size: clamp(1.5rem, 3vw, 1.95rem); margin: 0; }

.kpi { padding: 1.15rem 1.3rem; }
.kpi-label { font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase;
             color: var(--ink-faint); font-weight: 580; }
.kpi-value { font-size: 1.75rem; font-weight: 660; letter-spacing: -0.03em; margin-top: 0.3rem; }
/* What window the number above was measured over. Sits under every tile on the
   overview once a range is picked, so a period figure is never mistaken for a
   live one. */
.kpi-note { font-size: 0.7rem; color: var(--ink-faint); margin-top: 0.35rem; }
a.kpi { display: block; color: inherit; }
a.kpi:hover { border-color: var(--accent); }
a.kpi.is-static { cursor: default; }
a.kpi.is-static:hover { border-color: inherit; }

.block-row {
  display: flex; align-items: center; gap: 0.9rem;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--hairline-soft);
}
.block-row:last-child { border-bottom: none; }
.block-glyph {
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center;
  border-radius: 10px;
  background: var(--hairline-soft);
  color: var(--accent);
  font-size: 0.95rem;
}
.block-type { font-weight: 560; font-size: 0.93rem; }
.block-preview { font-size: 0.8rem; color: var(--ink-faint);
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46ch; }
.block-actions { margin-left: auto; display: flex; gap: 0.3rem; align-items: center; }
.block-row.is-off { opacity: 0.45; }

.mini-btn {
  display: inline-grid; place-items: center;
  width: 30px; height: 30px;
  border-radius: 8px;
  border: 1px solid var(--hairline-soft);
  background: var(--glass-bg);
  color: var(--ink-soft);
  font-size: 0.8rem; cursor: pointer;
  transition: all 0.2s var(--ease);
}
.mini-btn:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }
.mini-btn.is-danger:hover { border-color: var(--rose-500); color: var(--rose-500); }

.picker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 0.55rem; }
.picker-item {
  display: flex; flex-direction: column; align-items: center; gap: 0.4rem;
  padding: 0.9rem 0.6rem;
  border-radius: var(--r-sm);
  border: 1px solid var(--hairline-soft);
  background: var(--glass-bg);
  font-size: 0.79rem; font-weight: 520;
  color: var(--ink-soft);
  cursor: pointer;
  text-align: center;
  transition: all 0.25s var(--ease);
}
.picker-item:hover {
  border-color: var(--accent); color: var(--accent);
  transform: translateY(-2px); opacity: 1;
}
.picker-item .glyph { font-size: 1.15rem; color: var(--accent); }

/* --- charts ---------------------------------------------------------------
   Single-series area chart. Each theme gets its own validated colour step
   rather than an automatic flip — both pass the lightness-band, chroma-floor
   and 3:1 contrast checks against their own surface. */

:root {
  --chart-ink:  #4d7cb8;   /* light: validated on #f6f7f9 */
  --chart-grid: rgba(28, 36, 51, 0.10);
  --chart-surface: #ffffff;
}

:root[data-theme="dark"] {
  --chart-ink:  #5b95e8; /* dark: validated on #161d29 */
  --chart-grid: rgba(255, 255, 255, 0.11);
  --chart-surface: #161d29;
}

.chart-caption {
  display: flex; align-items: baseline; gap: 0.75rem;
  margin-bottom: 0.85rem;
}
.chart-caption .tiny { margin-left: auto; }

.chart-frame { position: relative; }
.chart-svg { width: 100%; height: 200px; overflow: visible; }

.chart-grid { stroke: var(--chart-grid); stroke-width: 1; }

.chart-tick {
  fill: var(--ink-faint);
  font-size: 10px;
  font-family: var(--font);
  font-variant-numeric: tabular-nums;
}

.chart-area { fill: var(--chart-ink); fill-opacity: 0.10; stroke: none; }

.chart-line {
  fill: none;
  stroke: var(--chart-ink);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* 2px surface ring keeps the marker legible where it sits on the line */
.chart-dot {
  fill: var(--chart-ink);
  stroke: var(--chart-surface);
  stroke-width: 2;
}

.chart-endlabel {
  fill: var(--ink);              /* text tokens, never the series colour */
  font-size: 11px;
  font-weight: 620;
  font-family: var(--font);
}

.chart-hit { fill: transparent; cursor: crosshair; }
.chart-hit:hover { fill: var(--chart-ink); fill-opacity: 0.06; }

.chart-tip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  transform: translate(-50%, -110%);
  padding: 0.5rem 0.7rem;
  border-radius: var(--r-sm);
  background: var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-brd);
  box-shadow: var(--shadow-md);
  font-size: 0.78rem;
  white-space: nowrap;
}
.chart-tip strong { display: block; font-size: 0.85rem; }

.chart-table { margin-top: 1rem; }
.chart-table summary {
  cursor: pointer; font-size: 0.82rem; font-weight: 540; color: var(--accent);
}
.chart-table[open] summary { margin-bottom: 0.75rem; }

td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* --- affiliate ------------------------------------------------------------ */

.copy-field {
  display: flex; align-items: stretch; gap: 0.5rem;
  margin-top: 0.5rem;
}
.copy-field input {
  font-family: var(--mono);
  font-size: 0.84rem;
  background: var(--hairline-soft);
}
.copy-field .btn { flex: none; }

.affil-code {
  font-family: var(--mono);
  font-size: 1.35rem;
  font-weight: 640;
  letter-spacing: 0.06em;
  color: var(--accent);
}

/* The host sits inside the field rather than above it, so what a partner is
   choosing reads as the URL it becomes: peptiqxbiotech.com/[______]. */
.code-prefix {
  display: flex; align-items: stretch;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  background: var(--hairline-soft);
  overflow: hidden;
}
.code-prefix-host {
  display: flex; align-items: center;
  padding: 0 0.1rem 0 0.6rem;
  font-family: var(--mono); font-size: 0.78rem;
  color: var(--ink-faint); white-space: nowrap;
}
.code-prefix .input {
  flex: 1; min-width: 0;
  border: none; background: transparent;
  font-family: var(--mono); font-size: 0.84rem;
  padding-left: 0.1rem;
}
.code-prefix:focus-within { border-color: var(--accent); }

.code-request {
  display: flex; gap: 0.75rem; align-items: center; justify-content: space-between;
  padding: 0.7rem 0.9rem;
  border-radius: var(--r-md);
  border: 1px dashed var(--hairline);
  background: var(--hairline-soft);
}

.status-note {
  display: flex; gap: 0.85rem; align-items: flex-start;
  padding: 1.05rem 1.25rem;
  border-radius: var(--r-md);
  border-left: 3px solid var(--accent);
  background: rgba(91, 138, 196, 0.09);
  font-size: 0.88rem;
  color: var(--ink-soft);
}
.status-note.is-warn { border-left-color: var(--amber-500); background: rgba(213,154,60,0.09); }
.status-note.is-stop { border-left-color: var(--rose-500); background: rgba(200,90,90,0.09); }

/* --- PEPCOiNS -------------------------------------------------------------
   The balance is the one number a holder came to see, so it gets display
   weight and tabular figures — a balance that reflows as it ticks up reads as
   unreliable. Everything else here stays deliberately quiet: a rewards balance
   that shouts on every page starts to feel like a slot machine. */

.coin-banner {
  display: flex; flex-wrap: wrap; gap: 1.25rem;
  align-items: center; justify-content: space-between;
}
.coin-balance {
  font-size: clamp(2rem, 5vw, 2.9rem);
  font-weight: 680;
  letter-spacing: -0.035em;
  line-height: 1.05;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  margin: 0.25rem 0 0.35rem;
}
.coin-unit {
  font-size: 0.8rem; font-weight: 560; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-faint);
  margin-left: 0.4rem; vertical-align: 0.55em;
}

.coin-chip {
  display: inline-flex; align-items: center; gap: 0.34rem;
  padding: 0.28rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: var(--hairline-soft);
  font-size: 0.8rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
}
.coin-chip:hover { border-color: var(--accent); }
.coin-chip-mark { font-size: 0.72rem; opacity: 0.85; }

.coin-applied {
  display: flex; gap: 0.75rem; align-items: center; justify-content: space-between;
  padding: 0.8rem 0.95rem;
  border-radius: var(--r-md);
  border: 1px solid rgba(91, 138, 196, 0.35);
  background: rgba(91, 138, 196, 0.09);
  font-size: 0.86rem;
}
.coin-form .label { font-size: 0.8rem; }

/* --- cart holds and the comeback offer ------------------------------------ *
 * The timer is information, not pressure: it sits at the size of a SKU line
 * and only turns warm in its last minute, because a countdown that is red for
 * a quarter of an hour is just noise the customer learns to ignore.          */
.hold-note {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.74rem; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.hold-clock {
  font-family: var(--mono, ui-monospace, monospace);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.hold-clock.is-urgent,
.hold-clock.is-expired { color: var(--rose-500); }

/* The headline timer, in the order summary. Green while there is time to
   think, red in its last minute. Colours are on the whole row rather than the
   digits so the change is visible from the corner of an eye — which is the
   entire job of a countdown somebody is not staring at. */
.hold-timer {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.6rem;
  padding: 0.55rem 0.8rem;
  margin: 0.9rem 0 1rem;
  border-radius: var(--r-md);
  border: 1px solid rgba(63, 174, 144, 0.4);
  background: rgba(63, 174, 144, 0.1);
  color: var(--mint-500);
  font-size: 0.8rem;
  transition: color 0.35s var(--ease), background 0.35s var(--ease),
              border-color 0.35s var(--ease);
}
.hold-timer .hold-clock {
  color: inherit; font-size: 1.05rem; font-weight: 620;
}
.hold-timer.is-urgent,
.hold-timer.is-expired {
  border-color: rgba(200, 90, 90, 0.45);
  background: rgba(200, 90, 90, 0.1);
  color: var(--rose-500);
}

.offer-banner {
  display: grid; gap: 0.35rem;
  padding: 0.9rem 1rem; margin-bottom: 1.25rem;
  border-radius: var(--r-md);
  border: 1px solid rgba(63, 174, 144, 0.4);
  background: rgba(63, 174, 144, 0.1);
}
.offer-banner strong { color: var(--mint-500); }
.offer-banner .hold-clock { color: var(--mint-500); font-size: 1rem; }

/* Terms read as a document, not as marketing chrome. */
.legal-body h2 {
  font-size: 1.05rem; margin: 2rem 0 0.6rem;
}
.legal-body h2:first-child { margin-top: 0; }
.legal-body p, .legal-body li {
  font-size: 0.9rem; color: var(--ink-soft); line-height: 1.7;
}
.legal-body ul { margin: 0.6rem 0 0.6rem 1.1rem; display: grid; gap: 0.5rem; }
.legal-body p + p { margin-top: 0.7rem; }
.legal-body .disclaimer { margin: 0.9rem 0; }

.milestone {
  display: grid; gap: 0.55rem;
}
.milestone-row {
  display: flex; align-items: center; gap: 0.7rem;
  font-size: 0.86rem; color: var(--ink-soft);
}
.milestone-row .coa-tick { flex: none; }

/* --- misc ----------------------------------------------------------------- */

.divider { height: 1px; background: var(--hairline-soft); border: none; margin-block: 1.5rem; }

.prose { max-width: 72ch; }
.prose h2 { margin-top: 2.25rem; }
.prose h3 { margin-top: 1.75rem; }
.prose ul, .prose ol { padding-left: 1.35rem; color: var(--ink-soft); }
.prose li { margin-bottom: 0.45rem; }
.prose blockquote {
  margin: 1.5rem 0; padding: 0.25rem 0 0.25rem 1.25rem;
  border-left: 3px solid var(--accent); color: var(--ink-soft); font-style: italic;
}

/* Account wall — stands where a product grid or COA showcase would be for a
   signed-out visitor. Deliberately quiet: it is an invitation, not an error. */
.lock-mark {
  font-size: 1.9rem; line-height: 1; margin-bottom: 0.85rem;
  color: var(--steel-400); opacity: 0.7;
}

.quote-card { padding: 1.5rem 1.65rem; height: 100%; display: flex; flex-direction: column; gap: 1rem; }
.quote-mark { font-size: 2rem; line-height: 1; color: var(--steel-400); opacity: 0.55; }
.quote-body { font-size: 0.95rem; color: var(--ink-soft); flex: 1; }
.quote-who  { font-size: 0.84rem; font-weight: 580; }
.quote-role { font-size: 0.78rem; color: var(--ink-faint); }

/* --- age / research-use gate ---------------------------------------------- */

/* Armed by the inline head script in base.html, before first paint, so the
   catalog is never briefly readable behind a gate that has not opened yet. */
html.gate-pending,
html.gate-pending body { overflow: hidden; }

/* Hidden by default: without JS the attribute stays put and the site behaves
   normally — the announcement bar and footer carry the same notice. */
.age-gate[hidden] { display: none; }

.age-gate {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4vw, 2.5rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.age-gate-scrim {
  position: fixed;
  inset: 0;
  background: rgba(14, 19, 28, 0.62);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  backdrop-filter: saturate(140%) blur(14px);
  animation: gate-fade 0.4s var(--ease) both;
}

@keyframes gate-fade { from { opacity: 0; } to { opacity: 1; } }

.age-gate-panel {
  position: relative;
  width: min(560px, 100%);
  margin: auto;
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg), var(--shadow-ring);
  background: var(--glass-bg-strong);
  animation: gate-rise 0.55s var(--ease) both;
}
.age-gate-panel::after { opacity: 0.5; }

@keyframes gate-rise {
  from { opacity: 0; transform: translateY(22px) scale(0.975); }
  to   { opacity: 1; transform: none; }
}

/* Night plate in both themes — the reversed lockup is the only logo variant
   that belongs on it, so it is referenced directly rather than via the macro. */
.age-gate-head {
  position: relative;
  overflow: hidden;
  padding: clamp(1.6rem, 4vw, 2.25rem) clamp(1.5rem, 4vw, 2.5rem)
           clamp(1.75rem, 4vw, 2.4rem);
  background:
    radial-gradient(120% 140% at 78% 6%, rgba(91, 138, 196, 0.34), transparent 58%),
    linear-gradient(158deg, var(--slate-800) 0%, #101724 78%);
}

.age-gate-stars {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.age-gate-logo {
  position: relative;
  display: block;
  height: 34px;
  width: auto;
  margin-bottom: clamp(1.15rem, 3vw, 1.6rem);
}

.age-gate-title {
  position: relative;
  margin: 0;
  font-size: clamp(1.42rem, 3.4vw, 1.78rem);
  line-height: 1.22;
  letter-spacing: -0.022em;
  color: #fff;
}
.age-gate-title span   { display: block; font-weight: 400; color: var(--slate-100); }
.age-gate-title strong { display: block; font-weight: 660; }

.age-gate-body {
  padding: clamp(1.5rem, 4vw, 2rem) clamp(1.5rem, 4vw, 2.5rem)
           clamp(1.5rem, 4vw, 2rem);
  background: var(--bg-elev);
}

.age-gate-lede {
  margin: 0 0 1.5rem;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

.age-gate-check {
  position: relative;   /* contains the visually-hidden input */
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  padding: 0.35rem 0;
  margin-bottom: 1.1rem;
  cursor: pointer;
}

/* The native input stays in the layout, unpainted, so it keeps its own focus
   ring target and keyboard behaviour; .age-gate-box is what you actually see. */
.age-gate-check input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  margin: 0;
}

.age-gate-box {
  flex: none;
  width: 26px;
  height: 26px;
  margin-top: 1px;
  border: 2px solid var(--steel-500);
  border-radius: var(--r-xs);
  background: transparent;
  transition: background 0.22s var(--ease), border-color 0.22s var(--ease);
}
.age-gate-box::before {
  content: "";
  display: block;
  width: 100%; height: 100%;
  background: no-repeat center / 14px 11px
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 11'%3E%3Cpath d='M1.6 5.7 5.1 9.2 12.4 1.8' fill='none' stroke='%23fff' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.2s var(--ease), transform 0.24s var(--ease-spring);
}
.age-gate-check:hover .age-gate-box { border-color: var(--steel-700); }
.age-gate-check input:checked ~ .age-gate-box {
  background: var(--steel-700);
  border-color: var(--steel-700);
}
.age-gate-check input:checked ~ .age-gate-box::before {
  opacity: 1;
  transform: none;
}
.age-gate-check input:focus-visible ~ .age-gate-box {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.age-gate-copy {
  font-size: 0.97rem;
  line-height: 1.5;
  color: var(--ink);
}
.age-gate-copy strong { font-weight: 640; }

.age-gate-rule {
  height: 1px;
  margin: 1.6rem 0 1.35rem;
  background: var(--hairline);
}

.age-gate-go   { margin-bottom: 0.75rem; }
.age-gate-back { font-weight: 620; }

.age-gate-declined { text-align: center; }

@media (max-width: 560px) {
  .age-gate { padding: 0; align-items: stretch; }
  .age-gate-panel { border-radius: 0; min-height: 100%; width: 100%; margin: 0; }
}

:root[data-theme="dark"] .age-gate-box { border-color: var(--steel-400); }
:root[data-theme="dark"] .age-gate-check:hover .age-gate-box { border-color: var(--steel-300); }
:root[data-theme="dark"] .age-gate-check input:checked ~ .age-gate-box {
  background: var(--steel-500);
  border-color: var(--steel-500);
}

.reveal { opacity: 0; transform: translateY(18px); }
.reveal.is-in { animation: fade-up 0.75s var(--ease) both; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1; transform: none; }
}

/* fallback where backdrop-filter is unsupported */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  :root { --glass-bg: rgba(255,255,255,0.94); --glass-bg-strong: rgba(255,255,255,0.98); }
  :root[data-theme="dark"] {
    --glass-bg: rgba(24,31,44,0.95); --glass-bg-strong: rgba(24,31,44,0.99);
  }
}
